@api-client/ui 0.5.11 → 0.5.13

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 (49) hide show
  1. package/build/src/elements/code-editor/code-editor.d.ts +13 -0
  2. package/build/src/elements/code-editor/code-editor.d.ts.map +1 -0
  3. package/build/src/elements/code-editor/code-editor.js +28 -0
  4. package/build/src/elements/code-editor/code-editor.js.map +1 -0
  5. package/build/src/elements/code-editor/internals/CodeEditor.d.ts +159 -0
  6. package/build/src/elements/code-editor/internals/CodeEditor.d.ts.map +1 -0
  7. package/build/src/elements/code-editor/internals/CodeEditor.js +643 -0
  8. package/build/src/elements/code-editor/internals/CodeEditor.js.map +1 -0
  9. package/build/src/elements/code-editor/internals/CodeEditor.styles.d.ts +3 -0
  10. package/build/src/elements/code-editor/internals/CodeEditor.styles.d.ts.map +1 -0
  11. package/build/src/elements/code-editor/internals/CodeEditor.styles.js +154 -0
  12. package/build/src/elements/code-editor/internals/CodeEditor.styles.js.map +1 -0
  13. package/build/src/elements/code-editor/internals/Linter.d.ts +5 -0
  14. package/build/src/elements/code-editor/internals/Linter.d.ts.map +1 -0
  15. package/build/src/elements/code-editor/internals/Linter.js +69 -0
  16. package/build/src/elements/code-editor/internals/Linter.js.map +1 -0
  17. package/build/src/elements/code-editor/internals/PlaceholderWidget.d.ts +20 -0
  18. package/build/src/elements/code-editor/internals/PlaceholderWidget.d.ts.map +1 -0
  19. package/build/src/elements/code-editor/internals/PlaceholderWidget.js +46 -0
  20. package/build/src/elements/code-editor/internals/PlaceholderWidget.js.map +1 -0
  21. package/build/src/elements/code-editor/internals/SuggestionMatchDecorator.d.ts +17 -0
  22. package/build/src/elements/code-editor/internals/SuggestionMatchDecorator.d.ts.map +1 -0
  23. package/build/src/elements/code-editor/internals/SuggestionMatchDecorator.js +31 -0
  24. package/build/src/elements/code-editor/internals/SuggestionMatchDecorator.js.map +1 -0
  25. package/build/src/elements/code-editor/internals/types.d.ts +51 -0
  26. package/build/src/elements/code-editor/internals/types.d.ts.map +1 -0
  27. package/build/src/elements/code-editor/internals/types.js +2 -0
  28. package/build/src/elements/code-editor/internals/types.js.map +1 -0
  29. package/build/src/index.d.ts +2 -0
  30. package/build/src/index.d.ts.map +1 -1
  31. package/build/src/index.js +2 -0
  32. package/build/src/index.js.map +1 -1
  33. package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
  34. package/build/src/md/chip/internals/Chip.styles.js +1 -0
  35. package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
  36. package/demo/elements/code-editor/CodeEditorDemo.ts +212 -0
  37. package/demo/elements/code-editor/index.html +19 -0
  38. package/demo/elements/index.html +3 -0
  39. package/package.json +10 -2
  40. package/src/elements/code-editor/README.md +204 -0
  41. package/src/elements/code-editor/code-editor.ts +24 -0
  42. package/src/elements/code-editor/internals/CodeEditor.styles.ts +154 -0
  43. package/src/elements/code-editor/internals/CodeEditor.ts +589 -0
  44. package/src/elements/code-editor/internals/Linter.ts +85 -0
  45. package/src/elements/code-editor/internals/PlaceholderWidget.ts +50 -0
  46. package/src/elements/code-editor/internals/SuggestionMatchDecorator.ts +36 -0
  47. package/src/elements/code-editor/internals/types.ts +54 -0
  48. package/src/index.ts +10 -0
  49. package/src/md/chip/internals/Chip.styles.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"Chip.styles.js","sourceRoot":"","sources":["../../../../../src/md/chip/internals/Chip.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuLjB,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport default css`\n :host {\n display: inline-block;\n vertical-align: middle;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n position: relative;\n user-select: none;\n\n font-family: var(--md-sys-typescale-label-large-font);\n font-size: var(--md-sys-typescale-label-large-size);\n letter-spacing: var(--md-sys-typescale-label-large-tracking);\n line-height: var(--md-sys-typescale-label-large-height);\n\n white-space: normal;\n\n --md-ripple-hover-state-layer-color: var(--md-sys-color-on-surface);\n --md-ripple-focus-state-layer-color: var(--md-sys-color-on-surface);\n --md-ripple-pressed-state-layer-color: var(--md-sys-color-primary);\n\n --_leading-icon-color: currentColor;\n --_trailing-icon-color: currentColor;\n --_background-color: transparent;\n --_color: inherit;\n --_shadow: var(--md-sys-elevation-0);\n --_outline-color: transparent;\n --_outline-size: 0;\n --_inline-padding-start: 16px;\n --_inline-padding-end: 16px;\n --_avatar-size: 24px;\n --_avatar-shape: 24px;\n --_icon-size: 18px;\n\n height: 32px;\n border-radius: var(--md-sys-shape-corner-small);\n box-shadow: var(--_shadow);\n border: var(--_outline-size) solid var(--_outline-color);\n }\n\n .ripple {\n border-radius: inherit;\n transition: border-radius var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-standard);\n }\n\n .ripple.activated {\n z-index: 1;\n }\n\n :host([disabled]) {\n --_background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);\n --_color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);\n --_leading-icon-color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);\n --_trailing-icon-color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);\n --_outline-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);\n box-shadow: none;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .surface {\n height: inherit;\n display: flex;\n align-items: center;\n justify-content: start;\n box-sizing: border-box;\n padding: 0 var(--_inline-padding-end) 0 var(--_inline-padding-start);\n border-radius: inherit;\n background-color: var(--_background-color);\n color: var(--_color);\n gap: 0;\n }\n\n .leading-icon {\n height: var(--_icon-size);\n width: 0px;\n overflow: hidden;\n transition: width 230ms var(--md-sys-animation-easing-standard);\n color: var(--_leading-icon-color);\n fill: currentColor;\n }\n\n .leading-icon::slotted(*) {\n width: var(--_icon-size);\n height: var(--_icon-size);\n margin-right: 8px;\n }\n\n slot[name='avatar']::slotted(*) {\n width: var(--_avatar-size);\n height: var(--_avatar-size);\n border-radius: var(--_avatar-shape);\n flex-shrink: 0;\n flex-grow: 0;\n margin-right: 8px;\n }\n\n .check-mark.checked {\n width: 18px;\n margin-right: 8px;\n }\n\n .trailing-icon {\n margin-left: 8px;\n width: 18px;\n height: 18px;\n color: var(--_trailing-icon-color);\n fill: currentColor;\n }\n\n :host([elevated]) {\n --_shadow: var(--md-sys-elevation-1);\n border: none;\n --_background-color: var(--md-sys-color-surface-container-low);\n }\n\n :host([elevated]:hover:not([disabled])) {\n --_shadow: var(--md-sys-elevation-2);\n }\n\n .surface.has-trailing-icon {\n --_inline-padding-end: 8px;\n }\n\n :host([type='assist']:not([disabled])) {\n --_outline-color: var(--md-sys-color-outline-variant);\n --_outline-size: 1px;\n --_color: var(--md-sys-color-on-surface);\n --_leading-icon-color: var(--md-sys-color-primary);\n }\n\n :host .has-icon {\n --_inline-padding-start: 8px;\n }\n\n :host .has-avatar {\n --_inline-padding-start: 4px;\n }\n\n :host([checked]) {\n --_inline-padding-start: 8px;\n --_background-color: var(--md-sys-color-secondary-container);\n }\n\n :host([type='filter']:not([disabled])) {\n --_leading-icon-color: var(--md-sys-color-primary);\n --_trailing-icon-color: var(--md-sys-on-surface-variant);\n --_outline-color: var(--md-sys-color-outline-variant);\n --_outline-size: 1px;\n --_color: var(--md-sys-color-on-surface-variant);\n }\n\n :host([type='filter'][checked]) {\n --_outline-size: 1px;\n }\n\n :host([type='filter'][checked]:not([disabled])) {\n --_leading-icon-color: var(--md-sys-color-on-secondary-container);\n --_trailing-icon-color: var(--md-sys-on-secondary-container);\n --_outline-color: var(--md-sys-color-secondary-container);\n --_color: var(--md-sys-color-on-secondary-container);\n }\n\n :host([type='input']:not([disabled])) {\n --_outline-color: var(--md-sys-color-outline-variant);\n --_outline-size: 1px;\n --_leading-icon-color: var(--md-sys-color-primary);\n --_trailing-icon-color: var(--md-sys-color-on-surface-variant);\n --_color: var(--md-sys-color-on-surface-variant);\n }\n\n :host([type='input'][checked]) {\n --_outline-size: 0px;\n }\n\n :host([type='suggestion']) {\n --_outline-size: 1px;\n --_outline-color: var(--md-sys-color-outline-variant);\n --_color: var(--md-sys-color-on-surface-variant);\n }\n\n :host([type='suggestion']:not([disabled])) {\n --_leading-icon-color: var(--md-sys-color-primary);\n }\n`\n"]}
1
+ {"version":3,"file":"Chip.styles.js","sourceRoot":"","sources":["../../../../../src/md/chip/internals/Chip.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwLjB,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport default css`\n :host {\n display: inline-block;\n vertical-align: middle;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n position: relative;\n user-select: none;\n\n font-family: var(--md-sys-typescale-label-large-font);\n font-size: var(--md-sys-typescale-label-large-size);\n letter-spacing: var(--md-sys-typescale-label-large-tracking);\n line-height: var(--md-sys-typescale-label-large-height);\n\n white-space: normal;\n\n --md-ripple-hover-state-layer-color: var(--md-sys-color-on-surface);\n --md-ripple-focus-state-layer-color: var(--md-sys-color-on-surface);\n --md-ripple-pressed-state-layer-color: var(--md-sys-color-primary);\n\n --_leading-icon-color: currentColor;\n --_trailing-icon-color: currentColor;\n --_background-color: transparent;\n --_color: inherit;\n --_shadow: var(--md-sys-elevation-0);\n --_outline-color: transparent;\n --_outline-size: 0;\n --_inline-padding-start: 16px;\n --_inline-padding-end: 16px;\n --_avatar-size: 24px;\n --_avatar-shape: 24px;\n --_icon-size: 18px;\n\n height: 32px;\n box-sizing: border-box;\n border-radius: var(--md-sys-shape-corner-small);\n box-shadow: var(--_shadow);\n border: var(--_outline-size) solid var(--_outline-color);\n }\n\n .ripple {\n border-radius: inherit;\n transition: border-radius var(--md-sys-motion-duration-medium1) var(--md-sys-motion-easing-standard);\n }\n\n .ripple.activated {\n z-index: 1;\n }\n\n :host([disabled]) {\n --_background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);\n --_color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);\n --_leading-icon-color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);\n --_trailing-icon-color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);\n --_outline-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);\n box-shadow: none;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .surface {\n height: inherit;\n display: flex;\n align-items: center;\n justify-content: start;\n box-sizing: border-box;\n padding: 0 var(--_inline-padding-end) 0 var(--_inline-padding-start);\n border-radius: inherit;\n background-color: var(--_background-color);\n color: var(--_color);\n gap: 0;\n }\n\n .leading-icon {\n height: var(--_icon-size);\n width: 0px;\n overflow: hidden;\n transition: width 230ms var(--md-sys-animation-easing-standard);\n color: var(--_leading-icon-color);\n fill: currentColor;\n }\n\n .leading-icon::slotted(*) {\n width: var(--_icon-size);\n height: var(--_icon-size);\n margin-right: 8px;\n }\n\n slot[name='avatar']::slotted(*) {\n width: var(--_avatar-size);\n height: var(--_avatar-size);\n border-radius: var(--_avatar-shape);\n flex-shrink: 0;\n flex-grow: 0;\n margin-right: 8px;\n }\n\n .check-mark.checked {\n width: 18px;\n margin-right: 8px;\n }\n\n .trailing-icon {\n margin-left: 8px;\n width: 18px;\n height: 18px;\n color: var(--_trailing-icon-color);\n fill: currentColor;\n }\n\n :host([elevated]) {\n --_shadow: var(--md-sys-elevation-1);\n border: none;\n --_background-color: var(--md-sys-color-surface-container-low);\n }\n\n :host([elevated]:hover:not([disabled])) {\n --_shadow: var(--md-sys-elevation-2);\n }\n\n .surface.has-trailing-icon {\n --_inline-padding-end: 8px;\n }\n\n :host([type='assist']:not([disabled])) {\n --_outline-color: var(--md-sys-color-outline-variant);\n --_outline-size: 1px;\n --_color: var(--md-sys-color-on-surface);\n --_leading-icon-color: var(--md-sys-color-primary);\n }\n\n :host .has-icon {\n --_inline-padding-start: 8px;\n }\n\n :host .has-avatar {\n --_inline-padding-start: 4px;\n }\n\n :host([checked]) {\n --_inline-padding-start: 8px;\n --_background-color: var(--md-sys-color-secondary-container);\n }\n\n :host([type='filter']:not([disabled])) {\n --_leading-icon-color: var(--md-sys-color-primary);\n --_trailing-icon-color: var(--md-sys-on-surface-variant);\n --_outline-color: var(--md-sys-color-outline-variant);\n --_outline-size: 1px;\n --_color: var(--md-sys-color-on-surface-variant);\n }\n\n :host([type='filter'][checked]) {\n --_outline-size: 1px;\n }\n\n :host([type='filter'][checked]:not([disabled])) {\n --_leading-icon-color: var(--md-sys-color-on-secondary-container);\n --_trailing-icon-color: var(--md-sys-on-secondary-container);\n --_outline-color: var(--md-sys-color-secondary-container);\n --_color: var(--md-sys-color-on-secondary-container);\n }\n\n :host([type='input']:not([disabled])) {\n --_outline-color: var(--md-sys-color-outline-variant);\n --_outline-size: 1px;\n --_leading-icon-color: var(--md-sys-color-primary);\n --_trailing-icon-color: var(--md-sys-color-on-surface-variant);\n --_color: var(--md-sys-color-on-surface-variant);\n }\n\n :host([type='input'][checked]) {\n --_outline-size: 0px;\n }\n\n :host([type='suggestion']) {\n --_outline-size: 1px;\n --_outline-color: var(--md-sys-color-outline-variant);\n --_color: var(--md-sys-color-on-surface-variant);\n }\n\n :host([type='suggestion']:not([disabled])) {\n --_leading-icon-color: var(--md-sys-color-primary);\n }\n`\n"]}
@@ -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/code-editor.js'
10
+ import '../../../src/elements/code-editor/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 {{fnName}} 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>
@@ -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.11",
3
+ "version": "0.5.13",
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/commands": "^6.8.1",
185
+ "@codemirror/lang-javascript": "^6.2.4",
186
+ "@codemirror/language": "^6.11.2",
187
+ "@codemirror/lint": "^6.8.5",
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/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,24 @@
1
+ import type { CSSResultOrNative } from 'lit'
2
+ import { customElement } from 'lit/decorators.js'
3
+ import Element from './internals/CodeEditor.js'
4
+ import styles from './internals/CodeEditor.styles.js'
5
+
6
+ @customElement('code-editor')
7
+ export class CodeEditorElement extends Element {
8
+ static override styles: CSSResultOrNative[] = [styles]
9
+ }
10
+
11
+ declare global {
12
+ interface HTMLElementTagNameMap {
13
+ 'code-editor': CodeEditorElement
14
+ }
15
+ }
16
+
17
+ export default CodeEditorElement
18
+ export type {
19
+ FunctionSchema,
20
+ FunctionParameter,
21
+ Suggestion,
22
+ FunctionInsertEvent,
23
+ SuggestionInsertEvent,
24
+ } from './internals/types.js'
@@ -0,0 +1,154 @@
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
+ .function-info {
133
+ padding: 8px;
134
+ }
135
+
136
+ /* Responsive */
137
+ @media (max-width: 600px) {
138
+ :host {
139
+ min-width: 0;
140
+ }
141
+
142
+ .label {
143
+ padding: 6px 6px 3px 6px;
144
+ }
145
+
146
+ .editor-container {
147
+ padding: 6px;
148
+ }
149
+
150
+ .supporting-text {
151
+ padding: 3px 6px 6px 6px;
152
+ }
153
+ }
154
+ `