@wallarm-org/design-system 1.30.0 → 1.31.0-rc-fix-filter-input-nested-menu-screenshot.1
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/CodeEditorContent.d.ts +17 -0
- package/dist/components/CodeEditor/CodeEditorContent.js +197 -0
- package/dist/components/CodeEditor/CodeEditorContext.d.ts +17 -0
- package/dist/components/CodeEditor/CodeEditorContext.js +8 -0
- package/dist/components/CodeEditor/CodeEditorRoot.d.ts +77 -0
- package/dist/components/CodeEditor/CodeEditorRoot.js +203 -0
- package/dist/components/CodeEditor/classes.d.ts +21 -0
- package/dist/components/CodeEditor/classes.js +41 -0
- package/dist/components/CodeEditor/engine/SearchPanel.d.ts +13 -0
- package/dist/components/CodeEditor/engine/SearchPanel.js +201 -0
- package/dist/components/CodeEditor/engine/adapterPainter.d.ts +23 -0
- package/dist/components/CodeEditor/engine/adapterPainter.js +110 -0
- package/dist/components/CodeEditor/engine/babelSyntax.d.ts +15 -0
- package/dist/components/CodeEditor/engine/babelSyntax.js +54 -0
- package/dist/components/CodeEditor/engine/completion.d.ts +17 -0
- package/dist/components/CodeEditor/engine/completion.js +129 -0
- package/dist/components/CodeEditor/engine/contentAttributes.d.ts +12 -0
- package/dist/components/CodeEditor/engine/contentAttributes.js +24 -0
- package/dist/components/CodeEditor/engine/diagnostics.d.ts +73 -0
- package/dist/components/CodeEditor/engine/diagnostics.js +327 -0
- package/dist/components/CodeEditor/engine/diff.d.ts +37 -0
- package/dist/components/CodeEditor/engine/diff.js +145 -0
- package/dist/components/CodeEditor/engine/folds.d.ts +25 -0
- package/dist/components/CodeEditor/engine/folds.js +458 -0
- package/dist/components/CodeEditor/engine/gutters.d.ts +22 -0
- package/dist/components/CodeEditor/engine/gutters.js +160 -0
- package/dist/components/CodeEditor/engine/index.d.ts +12 -0
- package/dist/components/CodeEditor/engine/index.js +387 -0
- package/dist/components/CodeEditor/engine/languages/http/index.d.ts +20 -0
- package/dist/components/CodeEditor/engine/languages/http/index.js +69 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.d.ts +2 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.js +31 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.terms.d.ts +1 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.terms.js +2 -0
- package/dist/components/CodeEditor/engine/languages/index.d.ts +8 -0
- package/dist/components/CodeEditor/engine/languages/index.js +41 -0
- package/dist/components/CodeEditor/engine/languages/json.d.ts +8 -0
- package/dist/components/CodeEditor/engine/languages/json.js +11 -0
- package/dist/components/CodeEditor/engine/languages/jsonPointers.d.ts +47 -0
- package/dist/components/CodeEditor/engine/languages/jsonPointers.js +125 -0
- package/dist/components/CodeEditor/engine/languages/yaml.d.ts +4 -0
- package/dist/components/CodeEditor/engine/languages/yaml.js +11 -0
- package/dist/components/CodeEditor/engine/lines.d.ts +22 -0
- package/dist/components/CodeEditor/engine/lines.js +131 -0
- package/dist/components/CodeEditor/engine/luaSyntax.d.ts +14 -0
- package/dist/components/CodeEditor/engine/luaSyntax.js +36 -0
- package/dist/components/CodeEditor/engine/positions.d.ts +19 -0
- package/dist/components/CodeEditor/engine/positions.js +22 -0
- package/dist/components/CodeEditor/engine/schema/complete.d.ts +12 -0
- package/dist/components/CodeEditor/engine/schema/complete.js +211 -0
- package/dist/components/CodeEditor/engine/schema/hover.d.ts +12 -0
- package/dist/components/CodeEditor/engine/schema/hover.js +73 -0
- package/dist/components/CodeEditor/engine/schema/loadSchema.d.ts +19 -0
- package/dist/components/CodeEditor/engine/schema/loadSchema.js +32 -0
- package/dist/components/CodeEditor/engine/schema/validate.d.ts +9 -0
- package/dist/components/CodeEditor/engine/schema/validate.js +67 -0
- package/dist/components/CodeEditor/engine/search.d.ts +13 -0
- package/dist/components/CodeEditor/engine/search.js +84 -0
- package/dist/components/CodeEditor/engine/searchMatches.d.ts +8 -0
- package/dist/components/CodeEditor/engine/searchMatches.js +15 -0
- package/dist/components/CodeEditor/engine/theme.d.ts +8 -0
- package/dist/components/CodeEditor/engine/theme.js +89 -0
- package/dist/components/CodeEditor/engine/types.d.ts +44 -0
- package/dist/components/CodeEditor/engine/types.js +0 -0
- package/dist/components/CodeEditor/hooks/useCodeEditor.d.ts +6 -0
- package/dist/components/CodeEditor/hooks/useCodeEditor.js +3 -0
- package/dist/components/CodeEditor/index.d.ts +6 -0
- package/dist/components/CodeEditor/index.js +5 -0
- package/dist/components/CodeEditor/lib/PortalOutlet.d.ts +11 -0
- package/dist/components/CodeEditor/lib/PortalOutlet.js +11 -0
- package/dist/components/CodeEditor/lib/httpCompletions.d.ts +7 -0
- package/dist/components/CodeEditor/lib/httpCompletions.js +102 -0
- package/dist/components/CodeEditor/lib/httpCompletionsData.d.ts +29 -0
- package/dist/components/CodeEditor/lib/httpCompletionsData.js +650 -0
- package/dist/components/CodeEditor/lib/keyboardHint.d.ts +5 -0
- package/dist/components/CodeEditor/lib/keyboardHint.js +2 -0
- package/dist/components/CodeEditor/lib/loadEngine.d.ts +7 -0
- package/dist/components/CodeEditor/lib/loadEngine.js +9 -0
- package/dist/components/CodeEditor/lib/portalRegistry.d.ts +20 -0
- package/dist/components/CodeEditor/lib/portalRegistry.js +47 -0
- package/dist/components/CodeEditor/lib/splitContentProps.d.ts +12 -0
- package/dist/components/CodeEditor/lib/splitContentProps.js +28 -0
- package/dist/components/CodeEditor/types.d.ts +72 -0
- package/dist/components/CodeEditor/types.js +0 -0
- package/dist/components/CodeSnippet/CodeSnippetCode.js +6 -23
- package/dist/components/CodeSnippet/CodeSnippetCopyButton.js +4 -3
- package/dist/components/CodeSnippet/CodeSnippetFrameContext.d.ts +25 -0
- package/dist/components/CodeSnippet/CodeSnippetFrameContext.js +3 -0
- package/dist/components/CodeSnippet/CodeSnippetFullscreenButton.js +2 -2
- package/dist/components/CodeSnippet/CodeSnippetRoot.d.ts +3 -5
- package/dist/components/CodeSnippet/CodeSnippetRoot.js +56 -50
- package/dist/components/CodeSnippet/CodeSnippetShowMoreButton.js +7 -5
- package/dist/components/CodeSnippet/CodeSnippetWrapButton.js +2 -2
- package/dist/components/CodeSnippet/adapters/highlightjs.js +1 -0
- package/dist/components/CodeSnippet/adapters/prism.js +3 -1
- package/dist/components/CodeSnippet/adapters/shiki.js +1 -0
- package/dist/components/CodeSnippet/adapters/types.d.ts +3 -3
- package/dist/components/CodeSnippet/classes.d.ts +3 -0
- package/dist/components/CodeSnippet/classes.js +14 -0
- package/dist/components/CodeSnippet/hooks/index.d.ts +1 -0
- package/dist/components/CodeSnippet/hooks/index.js +1 -0
- package/dist/components/CodeSnippet/hooks/useCodeSnippetFrame.d.ts +2 -0
- package/dist/components/CodeSnippet/hooks/useCodeSnippetFrame.js +8 -0
- package/dist/components/CodeSnippet/index.d.ts +2 -1
- package/dist/components/CodeSnippet/index.js +1 -1
- package/dist/components/CodeSnippet/internal/CodeSnippetFrame.d.ts +22 -0
- package/dist/components/CodeSnippet/internal/CodeSnippetFrame.js +81 -0
- package/dist/components/CodeSnippet/internal/CodeToken.d.ts +2 -1
- package/dist/components/CodeSnippet/internal/CodeToken.js +1 -1
- package/dist/components/CodeSnippet/internal/FoldSummary.d.ts +11 -0
- package/dist/components/CodeSnippet/internal/FoldSummary.js +27 -0
- package/dist/components/CodeSnippet/internal/FoldToggle.d.ts +2 -0
- package/dist/components/CodeSnippet/internal/FoldToggle.js +2 -1
- package/dist/components/CodeSnippet/internal/index.d.ts +1 -0
- package/dist/components/CodeSnippet/internal/index.js +1 -0
- package/dist/components/CodeSnippet/lib/foldUtils.d.ts +9 -2
- package/dist/components/CodeSnippet/lib/foldUtils.js +2 -2
- package/dist/components/CodeSnippet/lib/showMore.d.ts +7 -0
- package/dist/components/CodeSnippet/lib/showMore.js +7 -0
- package/dist/components/Copyable/Copyable.d.ts +5 -2
- package/dist/components/Copyable/Copyable.js +1 -1
- package/dist/metadata/components.json +1235 -5
- package/package.json +24 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FC, HTMLAttributes, Ref } from 'react';
|
|
2
|
+
import { type TestableProps } from '../../utils/testId';
|
|
3
|
+
type CodeEditorContentNativeProps = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onChange' | 'contentEditable' | 'role' | 'defaultValue'>;
|
|
4
|
+
export interface CodeEditorContentProps extends CodeEditorContentNativeProps, TestableProps {
|
|
5
|
+
/** Content wrapper element */
|
|
6
|
+
ref?: Ref<HTMLDivElement>;
|
|
7
|
+
/** Show the line-number gutter (CodeSnippet's `<CodeSnippetLineNumbers />` equivalent) */
|
|
8
|
+
lineNumbers?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Editing surface of `CodeEditorRoot`. Loads the CodeMirror engine on mount and
|
|
12
|
+
* shows the value as static text until it is ready. `data-*`, `aria-*`, `id`,
|
|
13
|
+
* `title` and `tabIndex` land on the typing surface; `className`, `style`, `ref`
|
|
14
|
+
* and event handlers stay on this wrapper (events from the editor bubble to it).
|
|
15
|
+
*/
|
|
16
|
+
export declare const CodeEditorContent: FC<CodeEditorContentProps>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { flushSync } from "react-dom";
|
|
4
|
+
import { cn } from "../../utils/cn.js";
|
|
5
|
+
import { useTestId } from "../../utils/testId.js";
|
|
6
|
+
import { useCodeSnippetFrame } from "../CodeSnippet/hooks/index.js";
|
|
7
|
+
import { useCodeEditorContext } from "./CodeEditorContext.js";
|
|
8
|
+
import { codeEditorContentVariants, codeEditorFallbackCodeVariants, codeEditorFallbackGutterVariants, codeEditorFallbackGuttersVariants, codeEditorFallbackLineVariants, codeEditorFallbackVariants, codeEditorHostVariants } from "./classes.js";
|
|
9
|
+
import { loadEngine } from "./lib/loadEngine.js";
|
|
10
|
+
import { PortalOutlet } from "./lib/PortalOutlet.js";
|
|
11
|
+
import { createPortalRegistry } from "./lib/portalRegistry.js";
|
|
12
|
+
import { splitContentProps } from "./lib/splitContentProps.js";
|
|
13
|
+
const ROW_HEIGHT = 20;
|
|
14
|
+
const VERTICAL_PADDING = 16;
|
|
15
|
+
const fallbackGuttersFor = (options)=>{
|
|
16
|
+
const configs = Object.values(options.lines);
|
|
17
|
+
const diff = void 0 !== options.original;
|
|
18
|
+
const { folds } = options;
|
|
19
|
+
return {
|
|
20
|
+
stick: diff || configs.some((line)=>null != line.color),
|
|
21
|
+
folds: void 0 !== folds && !(Array.isArray(folds) && 0 === folds.length),
|
|
22
|
+
prefix: diff || configs.some((line)=>null != line.prefix)
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const buildFallbackRows = (value, startingLineNumber, maxHeight)=>{
|
|
26
|
+
const lines = value.split('\n');
|
|
27
|
+
const visible = null === maxHeight ? lines : lines.slice(0, Math.max(0, Math.round((maxHeight - VERTICAL_PADDING) / ROW_HEIGHT)));
|
|
28
|
+
return visible.map((text, index)=>({
|
|
29
|
+
number: startingLineNumber + index,
|
|
30
|
+
text
|
|
31
|
+
}));
|
|
32
|
+
};
|
|
33
|
+
const CodeEditorContent = ({ ref, lineNumbers = false, className, 'data-testid': testIdProp, ...props })=>{
|
|
34
|
+
const testId = useTestId('content', testIdProp);
|
|
35
|
+
const fallbackTestId = useTestId('fallback');
|
|
36
|
+
const fallbackGuttersTestId = useTestId('fallback-gutters');
|
|
37
|
+
const { options, callbacks, setHandle } = useCodeEditorContext();
|
|
38
|
+
const { isFullscreen } = useCodeSnippetFrame();
|
|
39
|
+
const [registry] = useState(createPortalRegistry);
|
|
40
|
+
const [handle, setLocalHandle] = useState(null);
|
|
41
|
+
const [loadFailed, setLoadFailed] = useState(false);
|
|
42
|
+
const hostRef = useRef(null);
|
|
43
|
+
const { contentAttributes: routedAttributes, wrapperProps } = splitContentProps(props);
|
|
44
|
+
const attributesKey = JSON.stringify(routedAttributes);
|
|
45
|
+
const contentAttributes = useMemo(()=>JSON.parse(attributesKey), [
|
|
46
|
+
attributesKey
|
|
47
|
+
]);
|
|
48
|
+
const engineOptions = useMemo(()=>({
|
|
49
|
+
...options,
|
|
50
|
+
lineNumbers,
|
|
51
|
+
contentAttributes
|
|
52
|
+
}), [
|
|
53
|
+
options,
|
|
54
|
+
lineNumbers,
|
|
55
|
+
contentAttributes
|
|
56
|
+
]);
|
|
57
|
+
const optionsRef = useRef(engineOptions);
|
|
58
|
+
useEffect(()=>{
|
|
59
|
+
optionsRef.current = engineOptions;
|
|
60
|
+
}, [
|
|
61
|
+
engineOptions
|
|
62
|
+
]);
|
|
63
|
+
useEffect(()=>{
|
|
64
|
+
const host = hostRef.current;
|
|
65
|
+
if (!host) return;
|
|
66
|
+
let cancelled = false;
|
|
67
|
+
let created = null;
|
|
68
|
+
loadEngine().then((engine)=>{
|
|
69
|
+
if (cancelled) return;
|
|
70
|
+
created = engine.createEditor(host, optionsRef.current, {
|
|
71
|
+
...callbacks,
|
|
72
|
+
portals: registry
|
|
73
|
+
});
|
|
74
|
+
const mounted = created;
|
|
75
|
+
flushSync(()=>{
|
|
76
|
+
setHandle(mounted);
|
|
77
|
+
setLocalHandle(mounted);
|
|
78
|
+
});
|
|
79
|
+
}, (error)=>{
|
|
80
|
+
if (cancelled) return;
|
|
81
|
+
console.error('[CodeEditor] Failed to load the editor engine; showing read-only text.', error);
|
|
82
|
+
setLoadFailed(true);
|
|
83
|
+
});
|
|
84
|
+
return ()=>{
|
|
85
|
+
cancelled = true;
|
|
86
|
+
if (created) {
|
|
87
|
+
created.destroy();
|
|
88
|
+
setHandle(null);
|
|
89
|
+
}
|
|
90
|
+
setLocalHandle(null);
|
|
91
|
+
};
|
|
92
|
+
}, [
|
|
93
|
+
callbacks,
|
|
94
|
+
registry,
|
|
95
|
+
setHandle
|
|
96
|
+
]);
|
|
97
|
+
useEffect(()=>{
|
|
98
|
+
handle?.update(engineOptions);
|
|
99
|
+
}, [
|
|
100
|
+
handle,
|
|
101
|
+
engineOptions
|
|
102
|
+
]);
|
|
103
|
+
useEffect(()=>{
|
|
104
|
+
handle?.view.requestMeasure();
|
|
105
|
+
}, [
|
|
106
|
+
handle,
|
|
107
|
+
isFullscreen
|
|
108
|
+
]);
|
|
109
|
+
const hasAccessibleName = void 0 !== contentAttributes['aria-label'] || void 0 !== contentAttributes['aria-labelledby'];
|
|
110
|
+
useEffect(()=>{
|
|
111
|
+
if ('production' !== process.env.NODE_ENV && !hasAccessibleName) console.warn('[CodeEditor] CodeEditorContent has no accessible name. Pass `aria-label` or `aria-labelledby`.');
|
|
112
|
+
}, [
|
|
113
|
+
hasAccessibleName
|
|
114
|
+
]);
|
|
115
|
+
const fallbackRows = null === handle ? buildFallbackRows(options.value, options.startingLineNumber, options.maxHeight) : null;
|
|
116
|
+
const fallbackGutters = fallbackGuttersFor(options);
|
|
117
|
+
const hasGutter = lineNumbers || fallbackGutters.stick || fallbackGutters.folds || fallbackGutters.prefix;
|
|
118
|
+
const isDiff = void 0 !== options.original;
|
|
119
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
120
|
+
...wrapperProps,
|
|
121
|
+
ref: ref,
|
|
122
|
+
"data-slot": "code-editor-content",
|
|
123
|
+
"data-testid": testId,
|
|
124
|
+
"data-ds-suppress-parent-click": "",
|
|
125
|
+
className: cn(codeEditorContentVariants(), className),
|
|
126
|
+
children: [
|
|
127
|
+
fallbackRows && /*#__PURE__*/ jsxs("pre", {
|
|
128
|
+
"data-testid": fallbackTestId,
|
|
129
|
+
"aria-busy": loadFailed ? 'false' : 'true',
|
|
130
|
+
className: codeEditorFallbackVariants(),
|
|
131
|
+
children: [
|
|
132
|
+
hasGutter && /*#__PURE__*/ jsxs("span", {
|
|
133
|
+
"aria-hidden": "true",
|
|
134
|
+
"data-testid": fallbackGuttersTestId,
|
|
135
|
+
className: codeEditorFallbackGuttersVariants(),
|
|
136
|
+
children: [
|
|
137
|
+
fallbackGutters.stick && /*#__PURE__*/ jsx("span", {
|
|
138
|
+
className: codeEditorFallbackGutterVariants({
|
|
139
|
+
column: 'stick'
|
|
140
|
+
})
|
|
141
|
+
}),
|
|
142
|
+
lineNumbers && /*#__PURE__*/ jsx("span", {
|
|
143
|
+
className: codeEditorFallbackGutterVariants({
|
|
144
|
+
column: 'lineNumbers'
|
|
145
|
+
}),
|
|
146
|
+
children: fallbackRows.map((row)=>/*#__PURE__*/ jsx("span", {
|
|
147
|
+
className: codeEditorFallbackLineVariants(),
|
|
148
|
+
children: row.number
|
|
149
|
+
}, row.number))
|
|
150
|
+
}),
|
|
151
|
+
fallbackGutters.folds && /*#__PURE__*/ jsx("span", {
|
|
152
|
+
className: codeEditorFallbackGutterVariants({
|
|
153
|
+
column: 'folds'
|
|
154
|
+
})
|
|
155
|
+
}),
|
|
156
|
+
fallbackGutters.prefix && /*#__PURE__*/ jsxs("span", {
|
|
157
|
+
className: codeEditorFallbackGutterVariants({
|
|
158
|
+
column: 'prefix'
|
|
159
|
+
}),
|
|
160
|
+
children: [
|
|
161
|
+
fallbackRows.map((row)=>/*#__PURE__*/ jsx("span", {
|
|
162
|
+
className: codeEditorFallbackLineVariants(),
|
|
163
|
+
children: isDiff ? null : options.lines[row.number]?.prefix
|
|
164
|
+
}, row.number)),
|
|
165
|
+
isDiff && /*#__PURE__*/ jsx("span", {
|
|
166
|
+
className: "invisible h-0 overflow-hidden",
|
|
167
|
+
children: "+"
|
|
168
|
+
})
|
|
169
|
+
]
|
|
170
|
+
})
|
|
171
|
+
]
|
|
172
|
+
}),
|
|
173
|
+
/*#__PURE__*/ jsx("code", {
|
|
174
|
+
className: codeEditorFallbackCodeVariants({
|
|
175
|
+
hasGutter
|
|
176
|
+
}),
|
|
177
|
+
children: fallbackRows.map((row)=>/*#__PURE__*/ jsx("span", {
|
|
178
|
+
className: codeEditorFallbackLineVariants({
|
|
179
|
+
wrapLines: options.wrapLines
|
|
180
|
+
}),
|
|
181
|
+
children: row.text
|
|
182
|
+
}, row.number))
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
}),
|
|
186
|
+
/*#__PURE__*/ jsx("div", {
|
|
187
|
+
ref: hostRef,
|
|
188
|
+
className: codeEditorHostVariants()
|
|
189
|
+
}),
|
|
190
|
+
/*#__PURE__*/ jsx(PortalOutlet, {
|
|
191
|
+
registry: registry
|
|
192
|
+
})
|
|
193
|
+
]
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
CodeEditorContent.displayName = 'CodeEditorContent';
|
|
197
|
+
export { CodeEditorContent };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EditorHandle, EngineCallbacks, EngineOptions } from './engine/types';
|
|
2
|
+
import type { CodeEditorApi } from './types';
|
|
3
|
+
/** Internal bridge between `CodeEditorRoot` (state owner) and `CodeEditorContent` (engine host). */
|
|
4
|
+
export interface CodeEditorContextValue {
|
|
5
|
+
/** Engine options owned by Root; Content adds `lineNumbers` and `contentAttributes` */
|
|
6
|
+
options: Omit<EngineOptions, 'lineNumbers' | 'contentAttributes' | 'maxHeight'> & {
|
|
7
|
+
maxHeight: number | null;
|
|
8
|
+
};
|
|
9
|
+
/** Stable callbacks (same identity for the Root's lifetime) */
|
|
10
|
+
callbacks: Omit<EngineCallbacks, 'portals'>;
|
|
11
|
+
/** Content registers the live handle after `createEditor()` and clears it on destroy */
|
|
12
|
+
setHandle: (handle: EditorHandle | null) => void;
|
|
13
|
+
/** Stable object delegating to the current handle (no-ops before the engine loads) */
|
|
14
|
+
api: CodeEditorApi;
|
|
15
|
+
}
|
|
16
|
+
export declare const CodeEditorContext: import("react").Context<CodeEditorContextValue | null>;
|
|
17
|
+
export declare const useCodeEditorContext: () => CodeEditorContextValue;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
const CodeEditorContext = createContext(null);
|
|
3
|
+
const useCodeEditorContext = ()=>{
|
|
4
|
+
const context = useContext(CodeEditorContext);
|
|
5
|
+
if (!context) throw new Error('CodeEditor components must be used within CodeEditorRoot');
|
|
6
|
+
return context;
|
|
7
|
+
};
|
|
8
|
+
export { CodeEditorContext, useCodeEditorContext };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode, Ref } from 'react';
|
|
2
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
3
|
+
import { type TestableProps } from '../../utils/testId';
|
|
4
|
+
import type { LineConfig } from '../CodeSnippet/CodeSnippetContext';
|
|
5
|
+
import { codeSnippetRootVariants } from '../CodeSnippet/classes';
|
|
6
|
+
import type { CodeEditorApi, CodeEditorCompletionSource, CodeEditorDiagnostic, CodeEditorFolds, CodeEditorLanguage, JsonSchema } from './types';
|
|
7
|
+
type CodeEditorRootNativeProps = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onChange' | 'defaultValue' | 'onCopy'>;
|
|
8
|
+
export interface CodeEditorRootProps extends CodeEditorRootNativeProps, TestableProps, VariantProps<typeof codeSnippetRootVariants> {
|
|
9
|
+
/** Root element, including in fullscreen */
|
|
10
|
+
ref?: Ref<HTMLDivElement>;
|
|
11
|
+
/** Controlled document */
|
|
12
|
+
value?: string;
|
|
13
|
+
/** Uncontrolled initial document (controlled vs uncontrolled is locked on first render) */
|
|
14
|
+
defaultValue?: string;
|
|
15
|
+
/** Fired for user edits only, never for external `value` syncs */
|
|
16
|
+
onChange?: (value: string) => void;
|
|
17
|
+
/** Passed verbatim to the syntax adapter; selects the structural parser */
|
|
18
|
+
language?: CodeEditorLanguage;
|
|
19
|
+
/**
|
|
20
|
+
* Document identity: changing it swaps to that document's cached state (undo history,
|
|
21
|
+
* selection, folds). Requires a controlled `value`: when the id changes, `value` must already
|
|
22
|
+
* hold that document's text (an uncached id starts from `value`). In uncontrolled mode the new
|
|
23
|
+
* document would be seeded with the previous document's text.
|
|
24
|
+
*/
|
|
25
|
+
documentId?: string;
|
|
26
|
+
/** Focusable, selectable, searchable, copyable — but not editable by the user */
|
|
27
|
+
readOnly?: boolean;
|
|
28
|
+
/** Offsets gutter numbers; `lines`, `folds`, `diagnostics` use absolute numbers */
|
|
29
|
+
startingLineNumber?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Per-line configuration keyed by absolute line number (static, see spec D6).
|
|
32
|
+
* Compared by identity: memoise it (or hoist it to a constant) — a new object
|
|
33
|
+
* every render reconfigures the lines and gutters on every render.
|
|
34
|
+
*/
|
|
35
|
+
lines?: Record<number, LineConfig>;
|
|
36
|
+
/**
|
|
37
|
+
* Fold regions, or a function re-run after edits; collapsed state is kept by `id`.
|
|
38
|
+
* Compared by identity: memoise the array or pass a stable function (e.g. `getHttpFolds`) —
|
|
39
|
+
* an inline array/arrow reconfigures folds on every render and bypasses the re-run debounce.
|
|
40
|
+
*/
|
|
41
|
+
folds?: CodeEditorFolds;
|
|
42
|
+
/** Controlled line wrapping */
|
|
43
|
+
wrapLines?: boolean;
|
|
44
|
+
/** Uncontrolled initial line wrapping */
|
|
45
|
+
defaultWrapLines?: boolean;
|
|
46
|
+
/** Fired when the wrap button toggles wrapping */
|
|
47
|
+
onWrapLinesChange?: (wrap: boolean) => void;
|
|
48
|
+
/** Height clamp in rows; auto-renders `CodeSnippetShowMoreButton` unless one is a direct child */
|
|
49
|
+
maxLines?: number;
|
|
50
|
+
/** Enables diff mode against this text */
|
|
51
|
+
original?: string;
|
|
52
|
+
/** JSON Schema for the document (`json`) or the JSON body (`http`) */
|
|
53
|
+
schema?: JsonSchema;
|
|
54
|
+
/**
|
|
55
|
+
* Consumer completion sources, added after the built-in ones. Compared by identity: a new
|
|
56
|
+
* array reconfigures autocomplete and closes an open list, so keep it stable (module constant
|
|
57
|
+
* or `useMemo`).
|
|
58
|
+
*/
|
|
59
|
+
completions?: CodeEditorCompletionSource[];
|
|
60
|
+
/** External diagnostics, merged with syntax and schema ones */
|
|
61
|
+
diagnostics?: CodeEditorDiagnostic[];
|
|
62
|
+
/** Fired when the merged diagnostics change */
|
|
63
|
+
onDiagnosticsChange?: (diagnostics: CodeEditorDiagnostic[]) => void;
|
|
64
|
+
/** Imperative handle */
|
|
65
|
+
apiRef?: Ref<CodeEditorApi>;
|
|
66
|
+
/** Nonce for the style tags the editor injects */
|
|
67
|
+
cspNonce?: string;
|
|
68
|
+
/** Fired by `CodeSnippetCopyButton` after a copy, with the current document */
|
|
69
|
+
onCopy?: (value: string) => void;
|
|
70
|
+
/** Chrome (header, floating actions, show more) and `CodeEditorContent` */
|
|
71
|
+
children?: ReactNode;
|
|
72
|
+
}
|
|
73
|
+
export declare function CodeEditorRoot({ ref, value: valueProp, defaultValue, onChange, language, documentId, size, readOnly, startingLineNumber, lines, folds, wrapLines: wrapLinesProp, defaultWrapLines, onWrapLinesChange, maxLines, original, schema, completions, diagnostics, onDiagnosticsChange, apiRef, cspNonce, onCopy, className, children, 'data-testid': testId, ...props }: CodeEditorRootProps): import("react").JSX.Element;
|
|
74
|
+
export declare namespace CodeEditorRoot {
|
|
75
|
+
var displayName: string;
|
|
76
|
+
}
|
|
77
|
+
export {};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { useControlled } from "../../hooks/useControlled.js";
|
|
4
|
+
import { cn } from "../../utils/cn.js";
|
|
5
|
+
import { TestIdProvider } from "../../utils/testId.js";
|
|
6
|
+
import { plainAdapter } from "../CodeSnippet/adapters/plain.js";
|
|
7
|
+
import { CodeSnippetFrameContext } from "../CodeSnippet/CodeSnippetFrameContext.js";
|
|
8
|
+
import { CodeSnippetShowMoreButton } from "../CodeSnippet/CodeSnippetShowMoreButton.js";
|
|
9
|
+
import { codeSnippetRootVariants } from "../CodeSnippet/classes.js";
|
|
10
|
+
import { useAdapter } from "../CodeSnippet/hooks/index.js";
|
|
11
|
+
import { CodeSnippetFrame } from "../CodeSnippet/internal/CodeSnippetFrame.js";
|
|
12
|
+
import { getHiddenLineCount, hasExplicitShowMoreButton, isClamped } from "../CodeSnippet/lib/showMore.js";
|
|
13
|
+
import { CodeEditorContext } from "./CodeEditorContext.js";
|
|
14
|
+
const EMPTY_LINES = {};
|
|
15
|
+
const EMPTY_COMPLETIONS = [];
|
|
16
|
+
const EMPTY_DIAGNOSTICS = [];
|
|
17
|
+
const ROW_HEIGHT = 20;
|
|
18
|
+
const VERTICAL_PADDING = 16;
|
|
19
|
+
const countLines = (value)=>{
|
|
20
|
+
let count = 1;
|
|
21
|
+
for(let index = value.indexOf('\n'); -1 !== index; index = value.indexOf('\n', index + 1))count += 1;
|
|
22
|
+
return count;
|
|
23
|
+
};
|
|
24
|
+
const CodeEditorRoot = ({ ref, value: valueProp, defaultValue = '', onChange, language = 'text', documentId, size = 'sm', readOnly = false, startingLineNumber = 1, lines = EMPTY_LINES, folds, wrapLines: wrapLinesProp, defaultWrapLines = false, onWrapLinesChange, maxLines = 0, original, schema, completions = EMPTY_COMPLETIONS, diagnostics = EMPTY_DIAGNOSTICS, onDiagnosticsChange, apiRef, cspNonce, onCopy, className, children, 'data-testid': testId, ...props })=>{
|
|
25
|
+
const adapterContext = useAdapter();
|
|
26
|
+
const adapter = adapterContext?.adapter ?? plainAdapter;
|
|
27
|
+
const [valueState, setValue] = useControlled({
|
|
28
|
+
controlled: valueProp,
|
|
29
|
+
default: defaultValue
|
|
30
|
+
});
|
|
31
|
+
const value = valueState ?? '';
|
|
32
|
+
const [wrapState, setWrapState] = useControlled({
|
|
33
|
+
controlled: wrapLinesProp,
|
|
34
|
+
default: defaultWrapLines
|
|
35
|
+
});
|
|
36
|
+
const wrapLines = wrapState ?? false;
|
|
37
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
38
|
+
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
39
|
+
const [engineRows, setEngineRows] = useState(null);
|
|
40
|
+
const latest = useRef({
|
|
41
|
+
value,
|
|
42
|
+
onChange,
|
|
43
|
+
onDiagnosticsChange,
|
|
44
|
+
onWrapLinesChange,
|
|
45
|
+
onCopy
|
|
46
|
+
});
|
|
47
|
+
useEffect(()=>{
|
|
48
|
+
latest.current = {
|
|
49
|
+
value,
|
|
50
|
+
onChange,
|
|
51
|
+
onDiagnosticsChange,
|
|
52
|
+
onWrapLinesChange,
|
|
53
|
+
onCopy
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
const documentIdWithoutValue = void 0 !== documentId && void 0 === valueProp;
|
|
57
|
+
const warnedDocumentId = useRef(false);
|
|
58
|
+
useEffect(()=>{
|
|
59
|
+
if ('production' === process.env.NODE_ENV) return;
|
|
60
|
+
if (!documentIdWithoutValue || warnedDocumentId.current) return;
|
|
61
|
+
warnedDocumentId.current = true;
|
|
62
|
+
console.warn("[CodeEditor] `documentId` requires a controlled `value`: in uncontrolled mode a new document starts from the previous document's text.");
|
|
63
|
+
}, [
|
|
64
|
+
documentIdWithoutValue
|
|
65
|
+
]);
|
|
66
|
+
const handleRef = useRef(null);
|
|
67
|
+
const [api] = useState(()=>({
|
|
68
|
+
focus: ()=>handleRef.current?.api.focus(),
|
|
69
|
+
getValue: ()=>handleRef.current?.api.getValue() ?? latest.current.value,
|
|
70
|
+
insertText: (text)=>handleRef.current?.api.insertText(text),
|
|
71
|
+
openSearch: ()=>handleRef.current?.api.openSearch(),
|
|
72
|
+
foldAll: ()=>handleRef.current?.api.foldAll(),
|
|
73
|
+
unfoldAll: ()=>handleRef.current?.api.unfoldAll()
|
|
74
|
+
}));
|
|
75
|
+
useImperativeHandle(apiRef, ()=>api, [
|
|
76
|
+
api
|
|
77
|
+
]);
|
|
78
|
+
const [callbacks] = useState(()=>({
|
|
79
|
+
onChange: (next)=>{
|
|
80
|
+
setValue(next);
|
|
81
|
+
latest.current.onChange?.(next);
|
|
82
|
+
},
|
|
83
|
+
onDiagnosticsChange: (next)=>latest.current.onDiagnosticsChange?.(next),
|
|
84
|
+
onVisibleRowCountChange: (rows)=>setEngineRows(rows)
|
|
85
|
+
}));
|
|
86
|
+
const setHandle = useCallback((handle)=>{
|
|
87
|
+
handleRef.current = handle;
|
|
88
|
+
if (null === handle) setEngineRows(null);
|
|
89
|
+
}, []);
|
|
90
|
+
const setWrapLines = useCallback((wrap)=>{
|
|
91
|
+
setWrapState(wrap);
|
|
92
|
+
latest.current.onWrapLinesChange?.(wrap);
|
|
93
|
+
}, [
|
|
94
|
+
setWrapState
|
|
95
|
+
]);
|
|
96
|
+
const getCode = useCallback(()=>api.getValue(), [
|
|
97
|
+
api
|
|
98
|
+
]);
|
|
99
|
+
const notifyCopied = useCallback(()=>latest.current.onCopy?.(api.getValue()), [
|
|
100
|
+
api
|
|
101
|
+
]);
|
|
102
|
+
const lineCount = useMemo(()=>countLines(value), [
|
|
103
|
+
value
|
|
104
|
+
]);
|
|
105
|
+
const hiddenLineCount = getHiddenLineCount(engineRows ?? lineCount, maxLines);
|
|
106
|
+
const maxHeight = isClamped(hiddenLineCount, isExpanded) ? maxLines * ROW_HEIGHT + VERTICAL_PADDING : null;
|
|
107
|
+
const resolvedSize = size ?? 'sm';
|
|
108
|
+
const frameValue = useMemo(()=>({
|
|
109
|
+
size: resolvedSize,
|
|
110
|
+
getCode,
|
|
111
|
+
notifyCopied,
|
|
112
|
+
wrapLines,
|
|
113
|
+
setWrapLines,
|
|
114
|
+
isFullscreen,
|
|
115
|
+
setIsFullscreen,
|
|
116
|
+
maxLines,
|
|
117
|
+
isExpanded,
|
|
118
|
+
setIsExpanded,
|
|
119
|
+
hiddenLineCount
|
|
120
|
+
}), [
|
|
121
|
+
resolvedSize,
|
|
122
|
+
getCode,
|
|
123
|
+
notifyCopied,
|
|
124
|
+
wrapLines,
|
|
125
|
+
setWrapLines,
|
|
126
|
+
isFullscreen,
|
|
127
|
+
maxLines,
|
|
128
|
+
isExpanded,
|
|
129
|
+
hiddenLineCount
|
|
130
|
+
]);
|
|
131
|
+
const options = useMemo(()=>({
|
|
132
|
+
value,
|
|
133
|
+
documentId,
|
|
134
|
+
language,
|
|
135
|
+
readOnly,
|
|
136
|
+
wrapLines,
|
|
137
|
+
startingLineNumber,
|
|
138
|
+
lines,
|
|
139
|
+
folds,
|
|
140
|
+
adapter,
|
|
141
|
+
original,
|
|
142
|
+
schema,
|
|
143
|
+
completions,
|
|
144
|
+
diagnostics,
|
|
145
|
+
testId,
|
|
146
|
+
cspNonce,
|
|
147
|
+
maxHeight
|
|
148
|
+
}), [
|
|
149
|
+
value,
|
|
150
|
+
documentId,
|
|
151
|
+
language,
|
|
152
|
+
readOnly,
|
|
153
|
+
wrapLines,
|
|
154
|
+
startingLineNumber,
|
|
155
|
+
lines,
|
|
156
|
+
folds,
|
|
157
|
+
adapter,
|
|
158
|
+
original,
|
|
159
|
+
schema,
|
|
160
|
+
completions,
|
|
161
|
+
diagnostics,
|
|
162
|
+
testId,
|
|
163
|
+
cspNonce,
|
|
164
|
+
maxHeight
|
|
165
|
+
]);
|
|
166
|
+
const editorContext = useMemo(()=>({
|
|
167
|
+
options,
|
|
168
|
+
callbacks,
|
|
169
|
+
setHandle,
|
|
170
|
+
api
|
|
171
|
+
}), [
|
|
172
|
+
options,
|
|
173
|
+
callbacks,
|
|
174
|
+
setHandle,
|
|
175
|
+
api
|
|
176
|
+
]);
|
|
177
|
+
return /*#__PURE__*/ jsx(TestIdProvider, {
|
|
178
|
+
value: testId,
|
|
179
|
+
children: /*#__PURE__*/ jsx(CodeSnippetFrameContext.Provider, {
|
|
180
|
+
value: frameValue,
|
|
181
|
+
children: /*#__PURE__*/ jsx(CodeEditorContext.Provider, {
|
|
182
|
+
value: editorContext,
|
|
183
|
+
children: /*#__PURE__*/ jsxs(CodeSnippetFrame, {
|
|
184
|
+
...props,
|
|
185
|
+
ref: ref,
|
|
186
|
+
"data-slot": "code-editor",
|
|
187
|
+
"data-testid": testId,
|
|
188
|
+
className: cn(codeSnippetRootVariants({
|
|
189
|
+
size
|
|
190
|
+
}), 'focus-within:ring-focus-primary focus-within:ring-2', className),
|
|
191
|
+
isFullscreen: isFullscreen,
|
|
192
|
+
setIsFullscreen: setIsFullscreen,
|
|
193
|
+
children: [
|
|
194
|
+
children,
|
|
195
|
+
maxLines > 0 && !hasExplicitShowMoreButton(children) && /*#__PURE__*/ jsx(CodeSnippetShowMoreButton, {})
|
|
196
|
+
]
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
CodeEditorRoot.displayName = 'CodeEditorRoot';
|
|
203
|
+
export { CodeEditorRoot };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** `CodeEditorContent` wrapper: fills the root's flex column (fullscreen) and clips by height. */
|
|
2
|
+
export declare const codeEditorContentVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
3
|
+
/** Element CodeMirror mounts into; `.cm-editor` fills it in the fullscreen column. */
|
|
4
|
+
export declare const codeEditorHostVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
5
|
+
/** Static text shown until the engine chunk resolves: CodeSnippet's 20px rows and `py-8`. */
|
|
6
|
+
export declare const codeEditorFallbackVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
7
|
+
/** Gutter group of the fallback: the columns below, then the 8px gap the editor leaves. */
|
|
8
|
+
export declare const codeEditorFallbackGuttersVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Fallback gutter columns, same widths as the engine gutters so nothing shifts when the editor
|
|
11
|
+
* mounts (spec §7.2): colour stick, line numbers, fold toggles (16px + 4px padding), prefixes.
|
|
12
|
+
*/
|
|
13
|
+
export declare const codeEditorFallbackGutterVariants: (props?: ({
|
|
14
|
+
column?: "folds" | "lineNumbers" | "prefix" | "stick" | null | undefined;
|
|
15
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
16
|
+
export declare const codeEditorFallbackCodeVariants: (props?: ({
|
|
17
|
+
hasGutter?: boolean | null | undefined;
|
|
18
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
19
|
+
export declare const codeEditorFallbackLineVariants: (props?: ({
|
|
20
|
+
wrapLines?: boolean | null | undefined;
|
|
21
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
const codeEditorContentVariants = cva('relative flex-1 min-h-0');
|
|
3
|
+
const codeEditorHostVariants = cva('h-full [&>.cm-editor]:h-full');
|
|
4
|
+
const codeEditorFallbackVariants = cva('m-0 flex overflow-hidden py-8 font-mono');
|
|
5
|
+
const codeEditorFallbackGuttersVariants = cva('mr-8 flex shrink-0 select-none');
|
|
6
|
+
const codeEditorFallbackGutterVariants = cva('flex shrink-0 flex-col', {
|
|
7
|
+
variants: {
|
|
8
|
+
column: {
|
|
9
|
+
stick: 'border-l-2 border-transparent pl-12',
|
|
10
|
+
lineNumbers: 'px-8 text-right text-text-secondary',
|
|
11
|
+
folds: 'w-24',
|
|
12
|
+
prefix: 'px-8 text-center'
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
column: 'lineNumbers'
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const codeEditorFallbackCodeVariants = cva('flex min-w-0 flex-1 flex-col pr-12', {
|
|
20
|
+
variants: {
|
|
21
|
+
hasGutter: {
|
|
22
|
+
true: '',
|
|
23
|
+
false: 'pl-12'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
hasGutter: false
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const codeEditorFallbackLineVariants = cva('block min-h-lh leading-sm', {
|
|
31
|
+
variants: {
|
|
32
|
+
wrapLines: {
|
|
33
|
+
true: 'whitespace-pre-wrap break-all',
|
|
34
|
+
false: 'whitespace-pre'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
wrapLines: false
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
export { codeEditorContentVariants, codeEditorFallbackCodeVariants, codeEditorFallbackGutterVariants, codeEditorFallbackGuttersVariants, codeEditorFallbackLineVariants, codeEditorFallbackVariants, codeEditorHostVariants };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type FC } from 'react';
|
|
2
|
+
import { SearchQuery } from '@codemirror/search';
|
|
3
|
+
import { EditorView } from '@codemirror/view';
|
|
4
|
+
export interface SearchPanelProps {
|
|
5
|
+
view: EditorView;
|
|
6
|
+
query: SearchQuery;
|
|
7
|
+
/** `null` while the query is empty or an invalid regular expression. */
|
|
8
|
+
matchCount: number | null;
|
|
9
|
+
readOnly: boolean;
|
|
10
|
+
testId: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
/** Internal: rendered by `searchExtension` into CodeMirror's top panel through the portal registry. */
|
|
13
|
+
export declare const SearchPanel: FC<SearchPanelProps>;
|