Skip to content

Commit 7cbcb84

Browse files
committed
feat(events): re-fire the tabchange and change events to expose them as non-bubling
1 parent b2af5c8 commit 7cbcb84

File tree

5 files changed

+65
-2
lines changed

5 files changed

+65
-2
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,13 @@ All-in-one project, editor, file switcher, and preview with a horizontal side-by
756756
| `default` | Inline files ([details](#inline-scripts)). |
757757
| `extensions` | Declarative CodeMirror extensions ([details](#extending-the-editor)). |
758758

759+
### Events
760+
761+
| Event | Description |
762+
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
763+
| `tabchange` | A tab has been activated. |
764+
| `change` | User made an edit to the active file (note: this event is not fired for programmatic changes to the value property nor for the user changing tabs) |
765+
759766
---
760767

761768
## `<playground-project>`
@@ -813,6 +820,12 @@ project element.
813820

814821
---
815822

823+
### Events
824+
825+
| Event | Description |
826+
| ----------- | ------------------------- |
827+
| `tabchange` | A tab has been activated. |
828+
816829
## `<playground-file-editor>`
817830

818831
### Properties
@@ -834,6 +847,12 @@ project element.
834847
| ------------ | --------------------------------------------------------------------- |
835848
| `extensions` | Declarative CodeMirror extensions ([details](#extending-the-editor)). |
836849

850+
### Events
851+
852+
| Event | Description |
853+
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
854+
| `change` | User made an edit to the active file (note: this event is not fired for programmatic changes to the value property nor for the user changing tabs) |
855+
837856
---
838857

839858
## `<playground-code-editor>`

src/playground-file-editor.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {Extension} from '@codemirror/state';
1515
import {PlaygroundProject} from './playground-project.js';
1616
import {PlaygroundCodeEditor} from './playground-code-editor.js';
1717
import {CodeEditorChangeData} from './shared/worker-api.js';
18+
import {refireEvent} from './shared/util.js';
1819

1920
/**
2021
* A text editor associated with a <playground-project>.
@@ -172,7 +173,7 @@ export class PlaygroundFileEditor extends PlaygroundConnectedElement {
172173
)}
173174
.extensions=${this.extensions}
174175
.noCompletions=${this.noCompletions}
175-
@change=${this._onEdit}
176+
@change=${this._onChange}
176177
@request-completions=${this._onRequestCompletions}
177178
>
178179
<slot name="extensions" slot="extensions"></slot>
@@ -197,7 +198,7 @@ export class PlaygroundFileEditor extends PlaygroundConnectedElement {
197198
this.requestUpdate();
198199
};
199200

200-
private _onEdit() {
201+
private _onChange(event: Event) {
201202
if (
202203
this._project === undefined ||
203204
this._currentFile === undefined ||
@@ -206,6 +207,8 @@ export class PlaygroundFileEditor extends PlaygroundConnectedElement {
206207
return;
207208
}
208209
this._project.editFile(this._currentFile, this._editor.value);
210+
// Re-fire the change event on the host element for external consumers
211+
refireEvent(this, event);
209212
}
210213

211214
private async _onRequestCompletions(e: CustomEvent) {

src/playground-ide.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import './playground-preview.js';
1515
import {PlaygroundProject} from './playground-project.js';
1616
import {ProjectManifest} from './shared/worker-api.js';
1717
import {npmVersion, serviceWorkerHash} from './shared/version.js';
18+
import {refireEvent} from './shared/util.js';
1819

1920
/**
2021
* A multi-file code editor component with live preview that works without a
@@ -327,6 +328,7 @@ export class PlaygroundIde extends LitElement {
327328
.project=${projectId}
328329
.editor=${editorId}
329330
.editableFileSystem=${this.editableFileSystem}
331+
@tabchange=${this._onTabChange}
330332
>
331333
</playground-tab-bar>
332334
@@ -339,6 +341,7 @@ export class PlaygroundIde extends LitElement {
339341
.pragmas=${this.pragmas}
340342
.noCompletions=${this.noCompletions}
341343
.extensions=${this.extensions}
344+
@change=${this._onChange}
342345
>
343346
<slot name="extensions" slot="extensions"></slot>
344347
</playground-file-editor>
@@ -388,6 +391,16 @@ export class PlaygroundIde extends LitElement {
388391
super.update(changedProperties);
389392
}
390393

394+
private _onTabChange(event: Event) {
395+
// Re-fire the tabchange event on the host element for external consumers
396+
refireEvent(this, event);
397+
}
398+
399+
private _onChange(event: Event) {
400+
// Re-fire the change event on the host element for external consumers
401+
refireEvent(this, event);
402+
}
403+
391404
private _onResizeBarPointerdown({pointerId}: PointerEvent) {
392405
const bar = this._resizeBar;
393406
bar.setPointerCapture(pointerId);

src/playground-tab-bar.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {PlaygroundConnectedElement} from './playground-connected-element.js';
1818
import {PlaygroundFileEditor} from './playground-file-editor.js';
1919
import {PlaygroundFileSystemControls} from './playground-file-system-controls.js';
2020
import {FilesChangedEvent, PlaygroundProject} from './playground-project.js';
21+
import {refireEvent} from './shared/util.js';
2122
import {PlaygroundInternalTab} from './internal/tab.js';
2223

2324
/**
@@ -257,6 +258,13 @@ export class PlaygroundTabBar extends PlaygroundConnectedElement {
257258
if (name !== this._activeFileName) {
258259
this._activeFileName = name;
259260
this._activeFileIndex = index;
261+
// Re-fire the tabchange event on the host element for external consumers
262+
refireEvent(
263+
this,
264+
new CustomEvent('tabchange', {
265+
detail: {filename: name},
266+
})
267+
);
260268
}
261269
}
262270

src/shared/util.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,23 @@ export const forceSkypackRawMode = (url: URL): URL => {
2828
export type Result<V, E> =
2929
| {result: V; error?: undefined}
3030
| {result?: undefined; error: E};
31+
32+
/**
33+
* Re-fires an event on the given element, without bubbles or composed flags.
34+
* This is useful for re-firing internal events on public component boundaries
35+
* to avoid them crossing shadow DOM boundaries while still making them available
36+
* to external consumers.
37+
*/
38+
export const refireEvent = (
39+
element: EventTarget,
40+
event: Event | CustomEvent
41+
): void => {
42+
const detail = (event as CustomEvent).detail;
43+
element.dispatchEvent(
44+
new CustomEvent(event.type, {
45+
detail,
46+
bubbles: false,
47+
composed: false,
48+
})
49+
);
50+
};

0 commit comments

Comments
 (0)