@api-client/ui 0.5.11 → 0.5.12
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/build/src/elements/code-editor/internals/CodeEditor.d.ts +197 -0
- package/build/src/elements/code-editor/internals/CodeEditor.d.ts.map +1 -0
- package/build/src/elements/code-editor/internals/CodeEditor.js +612 -0
- package/build/src/elements/code-editor/internals/CodeEditor.js.map +1 -0
- package/build/src/elements/code-editor/internals/CodeEditor.styles.d.ts +3 -0
- package/build/src/elements/code-editor/internals/CodeEditor.styles.d.ts.map +1 -0
- package/build/src/elements/code-editor/internals/CodeEditor.styles.js +150 -0
- package/build/src/elements/code-editor/internals/CodeEditor.styles.js.map +1 -0
- package/build/src/elements/code-editor/internals/Linter.d.ts +5 -0
- package/build/src/elements/code-editor/internals/Linter.d.ts.map +1 -0
- package/build/src/elements/code-editor/internals/Linter.js +74 -0
- package/build/src/elements/code-editor/internals/Linter.js.map +1 -0
- package/build/src/elements/code-editor/ui-code-editor.d.ts +13 -0
- package/build/src/elements/code-editor/ui-code-editor.d.ts.map +1 -0
- package/build/src/elements/code-editor/ui-code-editor.js +28 -0
- package/build/src/elements/code-editor/ui-code-editor.js.map +1 -0
- package/build/src/index.d.ts +2 -0
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +2 -0
- package/build/src/index.js.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.js +1 -0
- package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
- package/demo/elements/code-editor/CodeEditorDemo.ts +212 -0
- package/demo/elements/code-editor/index.html +19 -0
- package/demo/elements/index.html +3 -0
- package/package.json +10 -2
- package/src/elements/code-editor/README.md +204 -0
- package/src/elements/code-editor/internals/CodeEditor.styles.ts +150 -0
- package/src/elements/code-editor/internals/CodeEditor.ts +595 -0
- package/src/elements/code-editor/internals/Linter.ts +87 -0
- package/src/elements/code-editor/ui-code-editor.ts +24 -0
- package/src/index.ts +10 -0
- package/src/md/chip/internals/Chip.styles.ts +1 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { html } from 'lit'
|
|
2
|
+
import reactive from '../../../src/decorators/reactive.js'
|
|
3
|
+
import { DemoPage } from '../../../src/demo/DemoPage.js'
|
|
4
|
+
import type {
|
|
5
|
+
FunctionSchema,
|
|
6
|
+
Suggestion,
|
|
7
|
+
FunctionInsertEvent,
|
|
8
|
+
SuggestionInsertEvent,
|
|
9
|
+
} from '../../../src/elements/code-editor/ui-code-editor.js'
|
|
10
|
+
import '../../../src/elements/code-editor/ui-code-editor.js'
|
|
11
|
+
|
|
12
|
+
class CodeEditorDemo extends DemoPage {
|
|
13
|
+
override accessor componentName = 'Code Editor'
|
|
14
|
+
|
|
15
|
+
@reactive()
|
|
16
|
+
private accessor output = ''
|
|
17
|
+
|
|
18
|
+
@reactive()
|
|
19
|
+
private accessor editorValue = [
|
|
20
|
+
'// Start typing function names or {{ mentions }}',
|
|
21
|
+
'console.log("Hello, world!")',
|
|
22
|
+
'',
|
|
23
|
+
'// Try: getData, processData, or @John, @Jane',
|
|
24
|
+
'// Example: Ask {{John}} about the API design',
|
|
25
|
+
'// Example: {{Jane}} can review the implementation',
|
|
26
|
+
'',
|
|
27
|
+
'"Hello {{John}}! Can you help with the " + getData() + " function?"',
|
|
28
|
+
].join('\n')
|
|
29
|
+
|
|
30
|
+
private functionSchemas: FunctionSchema[] = [
|
|
31
|
+
{
|
|
32
|
+
id: 'getData',
|
|
33
|
+
name: 'getData',
|
|
34
|
+
description: 'Fetches data from the server',
|
|
35
|
+
parameters: [
|
|
36
|
+
{
|
|
37
|
+
name: 'url',
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: 'The URL to fetch data from',
|
|
40
|
+
required: true,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'options',
|
|
44
|
+
type: 'RequestInit',
|
|
45
|
+
description: 'Fetch options',
|
|
46
|
+
required: false,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
returns: 'Promise<Response>',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'processData',
|
|
53
|
+
name: 'processData',
|
|
54
|
+
description: 'Processes raw data into a usable format',
|
|
55
|
+
parameters: [
|
|
56
|
+
{
|
|
57
|
+
name: 'data',
|
|
58
|
+
type: 'any',
|
|
59
|
+
description: 'Raw data to process',
|
|
60
|
+
required: true,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'transformer',
|
|
64
|
+
type: 'Function',
|
|
65
|
+
description: 'Optional transformer function',
|
|
66
|
+
required: false,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
returns: 'any',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'validateInput',
|
|
73
|
+
name: 'validateInput',
|
|
74
|
+
description: 'Validates user input against a schema',
|
|
75
|
+
parameters: [
|
|
76
|
+
{
|
|
77
|
+
name: 'input',
|
|
78
|
+
type: 'object',
|
|
79
|
+
description: 'The input to validate',
|
|
80
|
+
required: true,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'schema',
|
|
84
|
+
type: 'Schema',
|
|
85
|
+
description: 'Validation schema',
|
|
86
|
+
required: true,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
returns: 'ValidationResult',
|
|
90
|
+
},
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
private suggestions: Suggestion[] = [
|
|
94
|
+
{
|
|
95
|
+
id: 'user-john',
|
|
96
|
+
label: 'John',
|
|
97
|
+
description: 'Software Engineer',
|
|
98
|
+
suffix: 'Engineering',
|
|
99
|
+
data: { role: 'developer', department: 'engineering' },
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'user-jane',
|
|
103
|
+
label: 'Jane',
|
|
104
|
+
description: 'Product Manager',
|
|
105
|
+
suffix: 'Product',
|
|
106
|
+
data: { role: 'manager', department: 'product' },
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'admin-system',
|
|
110
|
+
label: 'System',
|
|
111
|
+
description: 'System Administrator',
|
|
112
|
+
suffix: 'Admin',
|
|
113
|
+
data: { role: 'admin', department: 'infrastructure' },
|
|
114
|
+
},
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
private handleFunctionInsert(event: CustomEvent<FunctionInsertEvent>): void {
|
|
118
|
+
const { functionSchema, position } = event.detail
|
|
119
|
+
this.output = `Function inserted: ${functionSchema.name} at position ${position}`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private handleSuggestionInsert(event: CustomEvent<SuggestionInsertEvent>): void {
|
|
123
|
+
const { suggestion, position } = event.detail
|
|
124
|
+
this.output = `Suggestion inserted: ${suggestion.label} at position ${position}`
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private handleInput(event: Event): void {
|
|
128
|
+
const target = event.target as HTMLElement & { value: string }
|
|
129
|
+
this.editorValue = target.value
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
contentTemplate() {
|
|
133
|
+
return html`
|
|
134
|
+
<a href="../">Back</a>
|
|
135
|
+
|
|
136
|
+
<section class="demo-section">
|
|
137
|
+
<h2 class="display-large">Basic Code Editor</h2>
|
|
138
|
+
<p>A CodeMirror 6 based editor with function autocomplete and suggestion pills.</p>
|
|
139
|
+
|
|
140
|
+
<code-editor
|
|
141
|
+
label="Code Editor"
|
|
142
|
+
supporting-text="Type function names or @mentions to see autocomplete"
|
|
143
|
+
.value=${this.editorValue}
|
|
144
|
+
.functionSchemas=${this.functionSchemas}
|
|
145
|
+
.suggestions=${this.suggestions}
|
|
146
|
+
@function-insert=${this.handleFunctionInsert}
|
|
147
|
+
@suggestion-insert=${this.handleSuggestionInsert}
|
|
148
|
+
@input=${this.handleInput}
|
|
149
|
+
></code-editor>
|
|
150
|
+
|
|
151
|
+
${this.output ? html`<p><strong>Output:</strong> ${this.output}</p>` : ''}
|
|
152
|
+
</section>
|
|
153
|
+
|
|
154
|
+
<section class="demo-section">
|
|
155
|
+
<h2 class="display-large">Dark Theme</h2>
|
|
156
|
+
<p>The same editor with dark theme enabled.</p>
|
|
157
|
+
|
|
158
|
+
<code-editor
|
|
159
|
+
label="Dark Theme Editor"
|
|
160
|
+
supporting-text="Dark theme variant"
|
|
161
|
+
.value=${'// Dark theme example\nconst theme = "dark"'}
|
|
162
|
+
.functionSchemas=${this.functionSchemas}
|
|
163
|
+
.suggestions=${this.suggestions}
|
|
164
|
+
dark-theme
|
|
165
|
+
></code-editor>
|
|
166
|
+
</section>
|
|
167
|
+
|
|
168
|
+
<section class="demo-section">
|
|
169
|
+
<h2 class="display-large">Disabled State</h2>
|
|
170
|
+
<p>A disabled editor for read-only content.</p>
|
|
171
|
+
|
|
172
|
+
<code-editor
|
|
173
|
+
label="Disabled Editor"
|
|
174
|
+
supporting-text="This editor is disabled"
|
|
175
|
+
.value=${'// This editor is disabled\nconst readOnly = true'}
|
|
176
|
+
.functionSchemas=${this.functionSchemas}
|
|
177
|
+
.suggestions=${this.suggestions}
|
|
178
|
+
disabled
|
|
179
|
+
></code-editor>
|
|
180
|
+
</section>
|
|
181
|
+
|
|
182
|
+
<section class="demo-section">
|
|
183
|
+
<h2 class="display-large">Error State</h2>
|
|
184
|
+
<p>An editor in error state.</p>
|
|
185
|
+
|
|
186
|
+
<code-editor
|
|
187
|
+
label="Error Editor"
|
|
188
|
+
supporting-text="This editor has an error"
|
|
189
|
+
.value=${'// This editor has an error\nconst invalid = true'}
|
|
190
|
+
.functionSchemas=${this.functionSchemas}
|
|
191
|
+
.suggestions=${this.suggestions}
|
|
192
|
+
invalid
|
|
193
|
+
></code-editor>
|
|
194
|
+
</section>
|
|
195
|
+
|
|
196
|
+
<section class="demo-section">
|
|
197
|
+
<h2 class="display-large">Function Schemas</h2>
|
|
198
|
+
<p>Available function schemas for autocomplete:</p>
|
|
199
|
+
<pre>${JSON.stringify(this.functionSchemas, null, 2)}</pre>
|
|
200
|
+
</section>
|
|
201
|
+
|
|
202
|
+
<section class="demo-section">
|
|
203
|
+
<h2 class="display-large">Suggestions</h2>
|
|
204
|
+
<p>Available suggestions (use @mention syntax):</p>
|
|
205
|
+
<pre>${JSON.stringify(this.suggestions, null, 2)}</pre>
|
|
206
|
+
</section>
|
|
207
|
+
`
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const instance = new CodeEditorDemo()
|
|
212
|
+
instance.render()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Code Editor Demo</title>
|
|
7
|
+
<link
|
|
8
|
+
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
|
|
9
|
+
rel="stylesheet"
|
|
10
|
+
/>
|
|
11
|
+
<link href="../../../src/styles/m3/tokens.css" rel="stylesheet" type="text/css" />
|
|
12
|
+
<link href="../../../src/styles/m3/theme.css" rel="stylesheet" type="text/css" />
|
|
13
|
+
<link href="../../page.css" rel="stylesheet" type="text/css" />
|
|
14
|
+
</head>
|
|
15
|
+
<body data-gr-ext-disabled="next">
|
|
16
|
+
<div id="app"></div>
|
|
17
|
+
<script type="module" src="/.tmp/demo/elements/code-editor/CodeEditorDemo.js"></script>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
package/demo/elements/index.html
CHANGED
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
<dt><a href="authorization/index.html">Authorization element</a></dt>
|
|
26
26
|
<dd>Elements to define HTTP authorization.</dd>
|
|
27
27
|
|
|
28
|
+
<dt><a href="code-editor/index.html">Code editor</a></dt>
|
|
29
|
+
<dd>An element that render a code editor.</dd>
|
|
30
|
+
|
|
28
31
|
<dt><a href="context-menu/index.html">Context menu</a></dt>
|
|
29
32
|
<dd>An element that render a context menu.</dd>
|
|
30
33
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@api-client/ui",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"description": "Internal UI component library for the API Client ecosystem.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"main": "build/src/index.js",
|
|
@@ -180,6 +180,14 @@
|
|
|
180
180
|
"@api-client/core": "^0.18.0",
|
|
181
181
|
"@api-client/graph": "^0.3.6",
|
|
182
182
|
"@api-client/json": "^0.2.0",
|
|
183
|
+
"@codemirror/autocomplete": "^6.18.6",
|
|
184
|
+
"@codemirror/basic-setup": "^0.20.0",
|
|
185
|
+
"@codemirror/commands": "^6.8.1",
|
|
186
|
+
"@codemirror/lang-javascript": "^6.2.4",
|
|
187
|
+
"@codemirror/language": "^6.11.2",
|
|
188
|
+
"@codemirror/state": "^6.5.2",
|
|
189
|
+
"@codemirror/theme-one-dark": "^6.1.3",
|
|
190
|
+
"@codemirror/view": "^6.38.0",
|
|
183
191
|
"@github/relative-time-element": "^4.4.6",
|
|
184
192
|
"@material/web": "^2.3.0",
|
|
185
193
|
"@types/har-format": "^1.2.8",
|
|
@@ -198,6 +206,7 @@
|
|
|
198
206
|
"@commitlint/config-conventional": "^19.2.2",
|
|
199
207
|
"@eslint/compat": "^1.2.8",
|
|
200
208
|
"@eslint/js": "^9.26.0",
|
|
209
|
+
"@jarrodek/ts-lit-plugin": "^3.0.0",
|
|
201
210
|
"@open-wc/testing": "^4.0.0",
|
|
202
211
|
"@pawel-up/semver": "^0.1.4",
|
|
203
212
|
"@types/marked": "^5.0.2",
|
|
@@ -222,7 +231,6 @@
|
|
|
222
231
|
"oauth2-mock-server": "^8.0.0",
|
|
223
232
|
"prettier": "^3.5.1",
|
|
224
233
|
"sinon": "^21.0.0",
|
|
225
|
-
"@jarrodek/ts-lit-plugin": "^3.0.0",
|
|
226
234
|
"typescript": "^5.5.2",
|
|
227
235
|
"typescript-eslint": "^8.24.1",
|
|
228
236
|
"wireit": "^0.14.4"
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Code Editor Component
|
|
2
|
+
|
|
3
|
+
A CodeMirror 6 based editor component that supports function autocomplete and suggestion pills, built with Material Design styling.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **CodeMirror 6 Integration**: Uses the latest CodeMirror 6 for a modern editing experience
|
|
8
|
+
- **Function Autocomplete**: Dynamic function schema loading with intelligent autocomplete
|
|
9
|
+
- **Suggestion Pills**: Inline suggestion pills that are visually distinct from plain text
|
|
10
|
+
- **Material Design Styling**: Follows Material Design v3 guidelines
|
|
11
|
+
- **Keyboard Navigation**: Full keyboard support for autocomplete and editor interactions
|
|
12
|
+
- **Accessibility**: Built with accessibility in mind, including proper ARIA attributes
|
|
13
|
+
- **Dark Theme Support**: Optional dark theme variant
|
|
14
|
+
- **Syntax Highlighting**: JavaScript syntax highlighting with extensible language support
|
|
15
|
+
|
|
16
|
+
## Basic Usage
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { CodeEditorElement } from '@api-client/ui'
|
|
20
|
+
|
|
21
|
+
// Or import the component directly
|
|
22
|
+
import '@api-client/ui/elements/code-editor/ui-code-editor.js'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<code-editor
|
|
27
|
+
label="Code Editor"
|
|
28
|
+
supporting-text="Enter your code here"
|
|
29
|
+
.value=${this.code}
|
|
30
|
+
.functionSchemas=${this.functions}
|
|
31
|
+
.suggestions=${this.suggestions}
|
|
32
|
+
@function-insert=${this.handleFunctionInsert}
|
|
33
|
+
@suggestion-insert=${this.handleSuggestionInsert}
|
|
34
|
+
@input=${this.handleInput}
|
|
35
|
+
></code-editor>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Function Schemas
|
|
39
|
+
|
|
40
|
+
Function schemas define the available functions for autocomplete:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const functionSchemas: FunctionSchema[] = [
|
|
44
|
+
{
|
|
45
|
+
id: 'getData',
|
|
46
|
+
name: 'getData',
|
|
47
|
+
description: 'Fetches data from the server',
|
|
48
|
+
parameters: [
|
|
49
|
+
{
|
|
50
|
+
name: 'url',
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'The URL to fetch data from',
|
|
53
|
+
required: true,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'options',
|
|
57
|
+
type: 'RequestInit',
|
|
58
|
+
description: 'Fetch options',
|
|
59
|
+
required: false,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
returns: 'Promise<Response>',
|
|
63
|
+
},
|
|
64
|
+
]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Suggestions
|
|
68
|
+
|
|
69
|
+
Suggestions are used for @mention-style autocomplete with pill rendering:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
const suggestions: Suggestion[] = [
|
|
73
|
+
{
|
|
74
|
+
id: 'user-john',
|
|
75
|
+
label: 'John Doe',
|
|
76
|
+
description: 'Software Engineer',
|
|
77
|
+
suffix: 'Engineering',
|
|
78
|
+
data: { role: 'developer', department: 'engineering' },
|
|
79
|
+
},
|
|
80
|
+
]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Events
|
|
84
|
+
|
|
85
|
+
The component dispatches several events:
|
|
86
|
+
|
|
87
|
+
- `function-insert`: When a function is inserted via autocomplete
|
|
88
|
+
- `suggestion-insert`: When a suggestion is inserted as a pill
|
|
89
|
+
- `input`: When the editor content changes (standard input event)
|
|
90
|
+
- `change`: When the editor loses focus and content has changed
|
|
91
|
+
|
|
92
|
+
## Properties
|
|
93
|
+
|
|
94
|
+
| Property | Type | Default | Description |
|
|
95
|
+
|----------|------|---------|-------------|
|
|
96
|
+
| `label` | `string` | `''` | Label text displayed as placeholder/floating label |
|
|
97
|
+
| `supporting-text` | `string` | `''` | Supporting text displayed below the editor |
|
|
98
|
+
| `disabled` | `boolean` | `false` | Whether the component is disabled |
|
|
99
|
+
| `invalid` | `boolean` | `false` | Whether the component is in an invalid state |
|
|
100
|
+
| `name` | `string` | `''` | Name attribute for form integration |
|
|
101
|
+
| `required` | `boolean` | `false` | Whether the input is required |
|
|
102
|
+
| `placeholder` | `string` | `''` | Placeholder text shown when editor is empty |
|
|
103
|
+
| `value` | `string` | `''` | The editor content value |
|
|
104
|
+
| `functionSchemas` | `FunctionSchema[]` | `[]` | Available function schemas for autocomplete |
|
|
105
|
+
| `suggestions` | `Suggestion[]` | `[]` | Available suggestions for autocomplete |
|
|
106
|
+
| `dark-theme` | `boolean` | `false` | Whether to use dark theme |
|
|
107
|
+
| `language` | `string` | `'javascript'` | Programming language for syntax highlighting |
|
|
108
|
+
|
|
109
|
+
## Methods
|
|
110
|
+
|
|
111
|
+
| Method | Description |
|
|
112
|
+
|--------|-------------|
|
|
113
|
+
| `focus()` | Focus the editor |
|
|
114
|
+
| `getSelection()` | Get the current selection range |
|
|
115
|
+
| `insertText(text: string)` | Insert text at the current cursor position |
|
|
116
|
+
|
|
117
|
+
## Styling
|
|
118
|
+
|
|
119
|
+
The component uses CSS custom properties for theming and follows Material Design tokens:
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
code-editor {
|
|
123
|
+
--md-sys-color-primary: #1976d2;
|
|
124
|
+
--md-sys-color-on-surface: #1c1b1f;
|
|
125
|
+
/* ... other Material Design tokens */
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Accessibility
|
|
130
|
+
|
|
131
|
+
The component includes:
|
|
132
|
+
- Proper ARIA attributes for screen readers
|
|
133
|
+
- Keyboard navigation support
|
|
134
|
+
- Focus management
|
|
135
|
+
- Semantic HTML structure
|
|
136
|
+
- High contrast support
|
|
137
|
+
|
|
138
|
+
## Examples
|
|
139
|
+
|
|
140
|
+
### Basic Editor
|
|
141
|
+
|
|
142
|
+
```html
|
|
143
|
+
<code-editor
|
|
144
|
+
label="JavaScript Code"
|
|
145
|
+
.value=${'console.log("Hello, world!")'}
|
|
146
|
+
>
|
|
147
|
+
</code-editor>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### With Function Autocomplete
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
<code-editor
|
|
154
|
+
label="Code with Functions"
|
|
155
|
+
.value=${this.code}
|
|
156
|
+
.functionSchemas=${this.functions}
|
|
157
|
+
@function-insert=${this.onFunctionInsert}
|
|
158
|
+
>
|
|
159
|
+
</code-editor>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### With Suggestions (Pills)
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<code-editor
|
|
166
|
+
label="Code with Mentions"
|
|
167
|
+
.value=${this.code}
|
|
168
|
+
.suggestions=${this.users}
|
|
169
|
+
@suggestion-insert=${this.onSuggestionInsert}
|
|
170
|
+
>
|
|
171
|
+
</code-editor>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Dark Theme
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<code-editor
|
|
178
|
+
label="Dark Code Editor"
|
|
179
|
+
.value=${this.code}
|
|
180
|
+
dark-theme
|
|
181
|
+
>
|
|
182
|
+
</code-editor>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Demo
|
|
186
|
+
|
|
187
|
+
A comprehensive demo is available at `/demo/elements/code-editor/` when running the development server.
|
|
188
|
+
|
|
189
|
+
## Browser Support
|
|
190
|
+
|
|
191
|
+
The component works in all modern browsers that support:
|
|
192
|
+
- ES2020+ features
|
|
193
|
+
- Web Components
|
|
194
|
+
- CSS Custom Properties
|
|
195
|
+
- CodeMirror 6
|
|
196
|
+
|
|
197
|
+
## Contributing
|
|
198
|
+
|
|
199
|
+
When contributing to this component, please follow the established patterns:
|
|
200
|
+
- Use TypeScript for type safety
|
|
201
|
+
- Follow the existing code style
|
|
202
|
+
- Include tests for new features
|
|
203
|
+
- Update documentation as needed
|
|
204
|
+
- Follow Material Design guidelines for styling
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { css } from 'lit'
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
outline: none;
|
|
8
|
+
min-width: 200px;
|
|
9
|
+
background-color: transparent;
|
|
10
|
+
color: var(--md-sys-color-on-surface);
|
|
11
|
+
|
|
12
|
+
font-family: var(--md-sys-typescale-body-large-font);
|
|
13
|
+
font-size: var(--md-sys-typescale-body-large-size);
|
|
14
|
+
letter-spacing: var(--md-sys-typescale-body-large-tracking);
|
|
15
|
+
line-height: var(--md-sys-typescale-body-large-height);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.surface {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.content {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
flex: 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.label {
|
|
30
|
+
font-family: var(--md-sys-typescale-label-medium-font);
|
|
31
|
+
font-weight: var(--md-sys-typescale-label-medium-weight);
|
|
32
|
+
font-size: var(--md-sys-typescale-label-medium-size);
|
|
33
|
+
letter-spacing: var(--md-sys-typescale-label-medium-tracking);
|
|
34
|
+
line-height: var(--md-sys-typescale-label-medium-height);
|
|
35
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
36
|
+
|
|
37
|
+
padding: 8px 16px 4px 16px;
|
|
38
|
+
margin: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.has-focus .label {
|
|
42
|
+
color: var(--md-sys-color-primary);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.editor-container {
|
|
46
|
+
position: relative;
|
|
47
|
+
flex: 1;
|
|
48
|
+
|
|
49
|
+
border-radius: var(--md-sys-shape-corner-extra-small);
|
|
50
|
+
border: 1px solid var(--md-sys-color-outline);
|
|
51
|
+
outline: 0px solid var(--md-sys-color-primary);
|
|
52
|
+
outline-offset: -1px;
|
|
53
|
+
transition:
|
|
54
|
+
border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
55
|
+
outline-width 150ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
56
|
+
outline-offset 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.editor-container:focus-within {
|
|
60
|
+
border-color: var(--md-sys-color-primary);
|
|
61
|
+
outline-width: 2px;
|
|
62
|
+
outline-offset: -2px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.supporting-text {
|
|
66
|
+
margin-top: 4px;
|
|
67
|
+
padding: 0 16px;
|
|
68
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
69
|
+
font-family: var(--md-sys-typescale-body-small-font);
|
|
70
|
+
font-weight: var(--md-sys-typescale-body-small-weight);
|
|
71
|
+
font-size: var(--md-sys-typescale-body-small-size);
|
|
72
|
+
letter-spacing: var(--md-sys-typescale-body-small-tracking);
|
|
73
|
+
line-height: var(--md-sys-typescale-body-small-height);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
:host([disabled]) {
|
|
77
|
+
pointer-events: none;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
:host([disabled]) .editor-container {
|
|
81
|
+
border-color: var(--md-sys-color-on-surface);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
:host([disabled]) .label,
|
|
85
|
+
:host([disabled]) .editor-container {
|
|
86
|
+
color: var(--md-sys-color-on-surface);
|
|
87
|
+
opacity: 0.38;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:host([invalid]) .label,
|
|
91
|
+
:host([invalid]) .supporting-text {
|
|
92
|
+
color: var(--md-sys-color-error);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
:host([invalid]) .editor-container {
|
|
96
|
+
border-color: var(--md-sys-color-error);
|
|
97
|
+
outline-color: var(--md-sys-color-error);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.mention-chip {
|
|
101
|
+
display: inline-flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
margin: 0 2px;
|
|
104
|
+
vertical-align: baseline;
|
|
105
|
+
user-select: none;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.mention-chip ui-chip {
|
|
109
|
+
margin: 0;
|
|
110
|
+
font-size: inherit;
|
|
111
|
+
line-height: inherit;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.cm-gutters {
|
|
115
|
+
background: var(--md-sys-color-surface-variant);
|
|
116
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
117
|
+
border: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.cm-focused .cm-cursor {
|
|
121
|
+
border-left-color: var(--md-sys-color-primary);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
::selection {
|
|
125
|
+
background: var(--md-sys-color-surface-variant);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.ͼ1.cm-focused {
|
|
129
|
+
outline: none;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Responsive */
|
|
133
|
+
@media (max-width: 600px) {
|
|
134
|
+
:host {
|
|
135
|
+
min-width: 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.label {
|
|
139
|
+
padding: 6px 6px 3px 6px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.editor-container {
|
|
143
|
+
padding: 6px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.supporting-text {
|
|
147
|
+
padding: 3px 6px 6px 6px;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
`
|