Skip to content

Commit 0ac19a5

Browse files
committed
playground: add Web Component sample and Monaco options IntelliSense
1 parent 84ebcef commit 0ac19a5

1 file changed

Lines changed: 148 additions & 2 deletions

File tree

docs/playground.html

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@
171171
<div class="sep"></div>
172172
<h1>Playground</h1>
173173
<div class="spacer"></div>
174+
<label for="sample-select" style="font-size:.8rem;color:var(--muted)">Sample:</label>
175+
<select id="sample-select">
176+
<option value="default" selected>Default</option>
177+
<option value="webComponent">Web Component</option>
178+
</select>
179+
<div class="sep"></div>
174180
<label for="lang-select" style="font-size:.8rem;color:var(--muted)">Language:</label>
175181
<select id="lang-select">
176182
<option value="javascript" selected>JavaScript</option>
@@ -263,7 +269,9 @@ <h1>Playground</h1>
263269
self.MonacoEnvironment = { getWorker: (_id, label) => makeWorker(label) };
264270

265271
// ─── Default setup code ──────────────────────────────────────────────────────
266-
const DEFAULT_CODE = `\
272+
// ─── Samples ─────────────────────────────────────────────────────────────────
273+
const SAMPLES = {
274+
default: `\
267275
// This code runs every time you click ▶ Run.
268276
// The variable \`monaco\` is already available.
269277
// Use \`container\` to target the preview pane's host element.
@@ -283,7 +291,61 @@ <h1>Playground</h1>
283291
automaticLayout: true,
284292
minimap: { enabled: false },
285293
});
286-
`;
294+
`,
295+
webComponent: `\
296+
// Monaco Editor inside a Web Component (Shadow DOM)
297+
// This demonstrates that @node-projects/monaco-editor-esm supports Shadow DOM.
298+
299+
const MONACO_CSS = 'https://cdn.jsdelivr.net/npm/@node-projects/monaco-editor-esm@latest/min/vs/editor/editor.main.css';
300+
301+
class MonacoEditorElement extends HTMLElement {
302+
constructor() {
303+
super();
304+
const shadow = this.attachShadow({ mode: 'open' });
305+
306+
// Add Monaco CSS inside the shadow root
307+
const link = document.createElement('link');
308+
link.rel = 'stylesheet';
309+
link.href = MONACO_CSS;
310+
shadow.appendChild(link);
311+
312+
// Editor host div
313+
const div = document.createElement('div');
314+
div.style.cssText = 'position:absolute;inset:0';
315+
shadow.appendChild(div);
316+
this.style.cssText = 'display:block;position:absolute;inset:0';
317+
318+
link.onload = () => {
319+
monaco.editor.create(div, {
320+
value: [
321+
'// Monaco Editor inside a Web Component!',
322+
'',
323+
'class Greeter {',
324+
' greet(name) { return \`Hello, \${name}!\`; }',
325+
'}',
326+
'',
327+
'new Greeter().greet("Shadow DOM");',
328+
].join('\\n'),
329+
language: 'javascript',
330+
theme: 'vs-dark',
331+
automaticLayout: true,
332+
minimap: { enabled: false },
333+
});
334+
};
335+
}
336+
}
337+
338+
if (!customElements.get('monaco-editor-wc')) {
339+
customElements.define('monaco-editor-wc', MonacoEditorElement);
340+
}
341+
342+
container.style.cssText = 'position:relative';
343+
const el = document.createElement('monaco-editor-wc');
344+
container.appendChild(el);
345+
`,
346+
};
347+
348+
const DEFAULT_CODE = SAMPLES.default;
287349

288350
// ─── Load Monaco once ────────────────────────────────────────────────────────
289351
let monaco;
@@ -309,7 +371,91 @@ <h1>Playground</h1>
309371
}
310372
);
311373

374+
// ─── Code completion for monaco / container ───────────────────────────────
375+
monaco.languages.typescript.javascriptDefaults.addExtraLib(`
376+
declare const container: HTMLElement;
377+
declare namespace monaco {
378+
namespace editor {
379+
function create(container: HTMLElement, options?: IStandaloneEditorConstructionOptions): IStandaloneCodeEditor;
380+
function setTheme(theme: string): void;
381+
function setModelLanguage(model: ITextModel, languageId: string): void;
382+
function getEditors(): ICodeEditor[];
383+
interface IStandaloneEditorConstructionOptions {
384+
value?: string;
385+
language?: string;
386+
theme?: string;
387+
automaticLayout?: boolean;
388+
minimap?: { enabled?: boolean; size?: 'proportional' | 'fill' | 'actual' };
389+
fontSize?: number;
390+
readOnly?: boolean;
391+
wordWrap?: 'off' | 'on' | 'wordWrapColumn' | 'bounded';
392+
lineNumbers?: 'on' | 'off' | 'relative' | 'interval';
393+
scrollBeyondLastLine?: boolean;
394+
folding?: boolean;
395+
glyphMargin?: boolean;
396+
tabSize?: number;
397+
insertSpaces?: boolean;
398+
renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all';
399+
renderLineHighlight?: 'none' | 'gutter' | 'line' | 'all';
400+
cursorStyle?: 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin';
401+
cursorBlinking?: 'blink' | 'smooth' | 'phase' | 'expand' | 'solid';
402+
scrollbar?: { vertical?: 'auto' | 'visible' | 'hidden'; horizontal?: 'auto' | 'visible' | 'hidden'; verticalScrollbarSize?: number; horizontalScrollbarSize?: number };
403+
suggest?: { showKeywords?: boolean; showSnippets?: boolean };
404+
quickSuggestions?: boolean | { other?: boolean; comments?: boolean; strings?: boolean };
405+
acceptSuggestionOnEnter?: 'on' | 'smart' | 'off';
406+
bracketPairColorization?: { enabled?: boolean };
407+
guides?: { bracketPairs?: boolean | 'active'; indentation?: boolean };
408+
padding?: { top?: number; bottom?: number };
409+
stickyScroll?: { enabled?: boolean };
410+
contextmenu?: boolean;
411+
smoothScrolling?: boolean;
412+
fontFamily?: string;
413+
fontLigatures?: boolean | string;
414+
fontWeight?: string;
415+
letterSpacing?: number;
416+
lineHeight?: number;
417+
}
418+
interface IStandaloneCodeEditor {
419+
getValue(): string;
420+
setValue(value: string): void;
421+
getModel(): ITextModel | null;
422+
dispose(): void;
423+
focus(): void;
424+
layout(dimension?: { width: number; height: number }): void;
425+
onDidChangeModelContent(listener: (e: any) => void): { dispose(): void };
426+
}
427+
interface ITextModel {
428+
getValue(): string;
429+
setValue(value: string): void;
430+
getLanguageId(): string;
431+
dispose(): void;
432+
}
433+
interface ICodeEditor extends IStandaloneCodeEditor {}
434+
}
435+
namespace languages {
436+
namespace typescript {
437+
const javascriptDefaults: { addExtraLib(content: string, filePath?: string): { dispose(): void }; setCompilerOptions(options: any): void };
438+
const typescriptDefaults: { addExtraLib(content: string, filePath?: string): { dispose(): void }; setCompilerOptions(options: any): void };
439+
}
440+
function register(language: { id: string }): void;
441+
function registerCompletionItemProvider(languageId: string, provider: any): { dispose(): void };
442+
function setMonarchTokensProvider(languageId: string, languageDef: any): { dispose(): void };
443+
}
444+
const KeyMod: { CtrlCmd: number; Shift: number; Alt: number; WinCtrl: number };
445+
const KeyCode: { [key: string]: number };
446+
}
447+
`, 'ts:monaco-playground.d.ts');
448+
312449
// ─── Language / Theme selectors ───────────────────────────────────────────
450+
// ─── Sample selector ──────────────────────────────────────────────────────
451+
document.getElementById('sample-select').addEventListener('change', (e) => {
452+
const sample = SAMPLES[e.target.value];
453+
if (sample) {
454+
setupEditor.setValue(sample);
455+
runSetupCode();
456+
}
457+
});
458+
313459
document.getElementById('lang-select').addEventListener('change', (e) => {
314460
monaco.editor.setModelLanguage(setupEditor.getModel(), e.target.value);
315461
});

0 commit comments

Comments
 (0)