Skip to content

Commit bef4d5d

Browse files
committed
Merge branch 'master' into release
2 parents 058c31f + 090886b commit bef4d5d

File tree

23 files changed

+190
-258
lines changed

23 files changed

+190
-258
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var context = infiniteCanvas.getContext("2d");
2323
Include `InfiniteCanvas` in your web page:
2424

2525
```html
26-
<script src="https://cdn.jsdelivr.net/npm/[email protected].3/dist/infinite-canvas.umd.cjs"></script>
26+
<script src="https://cdn.jsdelivr.net/npm/[email protected].4/dist/infinite-canvas.umd.cjs"></script>
2727
```
2828

2929
or install it using npm:

docs/.vitepress/config.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { addExamples } from '../../examples/backend/vite-plugin'
44
import addApi from './api-docs/serve-plugin'
55
import replaceVersion from './replace-version'
66

7+
const exampleInfiniteCanvasPath = fileURLToPath(new URL('./theme/infinite-canvas-example/example-infinite-canvas', import.meta.url))
8+
79
// https://vitepress.dev/reference/site-config
810
export default defineConfig({
911
title: "InfiniteCanvas",
@@ -49,7 +51,12 @@ export default defineConfig({
4951
}
5052
},
5153
plugins: [
52-
addExamples({external: {publicPath: '/examples'}}),
54+
addExamples({
55+
external: {
56+
publicPath: '/examples'
57+
},
58+
infiniteCanvasPath: exampleInfiniteCanvasPath
59+
}),
5360
addApi(),
5461
replaceVersion()
5562
]

docs/.vitepress/theme/constants.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { InjectionKey } from "vue";
22
import type { ExampleProject } from '../../../examples/shared/examples';
3-
import type { ExampleRegistry } from "./example-registry";
3+
import { ExampleInfiniteCanvasRegistry } from "./infinite-canvas-example/example-infinite-canvas-registry";
44

55
export const exampleDataInjectionKey: InjectionKey<ExampleProject[]> = Symbol();
6-
export const exampleRegistryInjectionKey: InjectionKey<ExampleRegistry> = Symbol();
6+
export const exampleInfiniteCanvasRegistryInjectionKey: InjectionKey<ExampleInfiniteCanvasRegistry> = Symbol();

docs/.vitepress/theme/env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />

docs/.vitepress/theme/example-registry.ts

-111
This file was deleted.

docs/.vitepress/theme/example.vue

+8-26
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<template>
1+
exampleInfiniteCanvasRegistryInjectionKeyExampleInfiniteCanvasRegistry<template>
22
<div class="example-container language-infinite-canvas">
33
<div class="iframe-container">
44
<div
@@ -15,10 +15,10 @@
1515
</template>
1616

1717
<script setup lang="ts">
18-
import { inject, onMounted, ref, onBeforeUnmount } from 'vue'
18+
import { onMounted, ref, inject } from 'vue'
1919
import StackblitzButton from './StackblitzButton.vue'
20-
import { exampleRegistryInjectionKey } from './constants';
21-
import type { ExampleRegistry, ExamplePageInfiniteCanvasProxy } from './example-registry'
20+
import { exampleInfiniteCanvasRegistryInjectionKey } from './constants';
21+
import type { ExampleInfiniteCanvasRegistry } from './infinite-canvas-example/parent-example-infinite-canvas-registry';
2222
2323
const props = defineProps({
2424
exampleId: {
@@ -36,22 +36,12 @@ const props = defineProps({
3636
default: 250
3737
}
3838
})
39-
const registry = inject(exampleRegistryInjectionKey) as ExampleRegistry
39+
const registry = inject(exampleInfiniteCanvasRegistryInjectionKey) as ExampleInfiniteCanvasRegistry
4040
const iFrame = ref<HTMLIFrameElement | null>(null)
4141
const overlayActive = ref<boolean>(false);
4242
const overlayDisappearing = ref<boolean>(false)
4343
const overlayMessage = ref<string | null>(null)
4444
45-
function onExampleInfiniteCanvasInitialized(source: MessageEventSource, proxy: ExamplePageInfiniteCanvasProxy): void{
46-
const iFrameValue = iFrame.value;
47-
if(!iFrameValue || source !== iFrameValue.contentWindow){
48-
return;
49-
}
50-
proxy.disableGreedyGestureHandling();
51-
proxy.addWheelIgnoredListener(onWheelIgnored)
52-
proxy.addTouchIgnoredListener(onTouchIgnored)
53-
}
54-
5545
async function displayOverlay(message: string): Promise<void>{
5646
if(overlayActive.value){
5747
return;
@@ -65,25 +55,17 @@ async function displayOverlay(message: string): Promise<void>{
6555
overlayDisappearing.value = false;
6656
}
6757
68-
function onWheelIgnored(): void{
69-
displayOverlay('Use Ctrl + scroll to zoom');
70-
}
71-
72-
function onTouchIgnored(): void{
73-
displayOverlay('Use two fingers to move')
74-
}
7558
onMounted(() => {
7659
const iFrameValue = iFrame.value;
7760
if(!iFrameValue){
7861
return;
7962
}
8063
const url = new URL(`/examples/${props.exampleId}/`, location.href).toString();
81-
registry.addInitializedListener(onExampleInfiniteCanvasInitialized);
64+
const subscription = registry.subscribeToExampleInfiniteCanvases(iFrameValue);
65+
subscription.wheelIgnored.subscribe(() => displayOverlay('Use Ctrl + scroll to zoom'))
66+
subscription.touchIgnored.subscribe(() => displayOverlay('Use two fingers to move'))
8267
iFrameValue.src = url;
8368
})
84-
onBeforeUnmount(() => {
85-
registry.removeInitializedListener(onExampleInfiniteCanvasInitialized, iFrame.value ? iFrame.value.contentWindow || undefined : undefined);
86-
})
8769
</script>
8870

8971
<style scoped lang="css">

docs/.vitepress/theme/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
import Theme from 'vitepress/theme'
33
import MyLayout from './my-layout.vue';
44
import type { EnhanceAppContext } from 'vitepress'
5-
import Example from './example.vue';
5+
import Example from './example.vue'
66
import { data } from '../shared/examples.data';
7-
import { exampleDataInjectionKey, exampleRegistryInjectionKey } from './constants';
8-
import { createExampleRegistry } from './example-registry'
7+
import { exampleDataInjectionKey, exampleInfiniteCanvasRegistryInjectionKey } from './constants';
98
import './style.css'
9+
import { ExampleInfiniteCanvasRegistry } from './infinite-canvas-example/example-infinite-canvas-registry';
1010

1111
export default {
1212
extends: Theme,
@@ -15,6 +15,6 @@ export default {
1515
// ...
1616
app.component('inf-example', Example);
1717
app.provide(exampleDataInjectionKey, data);
18-
app.provide(exampleRegistryInjectionKey, createExampleRegistry())
18+
app.provide(exampleInfiniteCanvasRegistryInjectionKey, ExampleInfiniteCanvasRegistry.create())
1919
}
2020
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const EXAMPLE_INFINITE_CANVAS_INITIALIZED = 'EXAMPLE_INFINITE_CANVAS_INITIALIZED'
2+
export const EXAMPLE_INFINITE_CANVAS_REGISTERED = 'EXAMPLE_INFINITE_CANVAS_REGISTERED'
3+
export const WHEEL_IGNORED = 'WHEEL_IGNORED'
4+
export const TOUCH_IGNORED = 'TOUCH_IGNORED'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { fromEvent, filter, map, mergeMap, type Observable } from 'rxjs'
2+
import { EXAMPLE_INFINITE_CANVAS_INITIALIZED, EXAMPLE_INFINITE_CANVAS_REGISTERED, WHEEL_IGNORED, TOUCH_IGNORED } from './constants'
3+
4+
class ExampleInfiniteCanvasConnection{
5+
public readonly wheelIgnored: Observable<void>
6+
public readonly touchIgnored: Observable<void>
7+
constructor(portMessages: Observable<MessageEvent>){
8+
this.wheelIgnored = portMessages.pipe(
9+
filter(({data}) => data.type === WHEEL_IGNORED),
10+
map(() => {})
11+
)
12+
this.touchIgnored = portMessages.pipe(
13+
filter(({data}) => data.type === TOUCH_IGNORED),
14+
map(() => {})
15+
)
16+
}
17+
public static create(port: MessagePort): ExampleInfiniteCanvasConnection{
18+
const portMessages = fromEvent<MessageEvent>(port, 'message')
19+
port.start();
20+
port.postMessage({type: EXAMPLE_INFINITE_CANVAS_REGISTERED})
21+
return new ExampleInfiniteCanvasConnection(portMessages);
22+
}
23+
}
24+
25+
export class ExampleInfiniteCanvasesSubscription{
26+
public readonly wheelIgnored: Observable<void>
27+
public readonly touchIgnored: Observable<void>
28+
constructor(connections: Observable<ExampleInfiniteCanvasConnection>){
29+
this.wheelIgnored = connections.pipe(mergeMap(c => c.wheelIgnored))
30+
this.touchIgnored = connections.pipe(mergeMap(c => c.touchIgnored))
31+
}
32+
33+
public static create(windowMessages: Observable<MessageEvent>, iFrame: HTMLIFrameElement): ExampleInfiniteCanvasesSubscription{
34+
const iFrameWindow = iFrame.contentWindow
35+
const connectionsFromIFrame = windowMessages.pipe(
36+
filter(e => e.source === iFrameWindow && e.data && e.data.type === EXAMPLE_INFINITE_CANVAS_INITIALIZED),
37+
map(e => ExampleInfiniteCanvasConnection.create(e.ports[0])))
38+
39+
return new ExampleInfiniteCanvasesSubscription(connectionsFromIFrame);
40+
}
41+
}
42+
43+
export class ExampleInfiniteCanvasRegistry{
44+
constructor(private readonly windowMessages: Observable<MessageEvent>){
45+
46+
}
47+
48+
public subscribeToExampleInfiniteCanvases(iFrame: HTMLIFrameElement): ExampleInfiniteCanvasesSubscription{
49+
return ExampleInfiniteCanvasesSubscription.create(this.windowMessages, iFrame);
50+
}
51+
public static create(): ExampleInfiniteCanvasRegistry | undefined{
52+
if(typeof window === 'undefined' || !window){
53+
return undefined;
54+
}
55+
return new ExampleInfiniteCanvasRegistry(fromEvent<MessageEvent>(window, 'message'));
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { fromEvent, filter, firstValueFrom } from 'rxjs'
2+
import InfiniteCanvas, { Units } from 'infinite-canvas'
3+
import {
4+
EXAMPLE_INFINITE_CANVAS_INITIALIZED,
5+
EXAMPLE_INFINITE_CANVAS_REGISTERED,
6+
TOUCH_IGNORED,
7+
WHEEL_IGNORED
8+
} from './constants';
9+
10+
class ParentConnection{
11+
constructor(private readonly port: MessagePort){}
12+
13+
public notifyWheelIgnored(): void{
14+
this.port.postMessage({type: WHEEL_IGNORED})
15+
}
16+
public notifyTouchIgnored(): void{
17+
this.port.postMessage({type: TOUCH_IGNORED})
18+
}
19+
}
20+
21+
function connectToParent(): Promise<ParentConnection | undefined>{
22+
return new Promise((res) => {
23+
const parent = window.parent;
24+
if(!parent){
25+
res(undefined)
26+
}
27+
const channel = new MessageChannel();
28+
const port = channel.port2;
29+
const portMessages = fromEvent<MessageEvent>(port, 'message')
30+
firstValueFrom(portMessages.pipe(
31+
filter(({data}) => data.type === EXAMPLE_INFINITE_CANVAS_REGISTERED),
32+
)).then(() => res(new ParentConnection(port)))
33+
port.start()
34+
parent.postMessage({type: EXAMPLE_INFINITE_CANVAS_INITIALIZED}, location.origin, [channel.port1]);
35+
})
36+
}
37+
38+
export default class ExampleInfiniteCanvas extends InfiniteCanvas{
39+
constructor(canvasEl: HTMLCanvasElement){
40+
super(canvasEl, {units: Units.CSS})
41+
const { width, height } = canvasEl.getBoundingClientRect();
42+
canvasEl.width = width * devicePixelRatio;
43+
canvasEl.height = height * devicePixelRatio;
44+
connectToParent().then((connection) => {
45+
if(!connection){
46+
return;
47+
}
48+
const wheelIgnoredSubscription = fromEvent(this, 'wheelignored').subscribe((e) => {
49+
e.preventDefault();
50+
connection.notifyWheelIgnored();
51+
})
52+
const touchIgnoredSubscription = fromEvent(this, 'touchignored').subscribe((e) => {
53+
e.preventDefault();
54+
connection.notifyTouchIgnored();
55+
})
56+
});
57+
}
58+
}
59+
export { Units }

examples/backend/retrieval/add-example.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ export async function addExample(request: CreateExampleRequest): Promise<string>
2828
const title = request.title.trim();
2929
const id = createExampleId(title, dirs);
3030
const indexHtml = `<canvas id='canvas'></canvas>`;
31-
const indexJs = `import InfiniteCanvas from './example-infinite-canvas.js';\nimport './index.css';\n\nconst infCanvas = new InfiniteCanvas(document.getElementById('canvas'))`;
31+
const indexJs = `import InfiniteCanvas from 'ef-infinite-canvas';\nimport './index.css';\n\nconst infCanvas = new InfiniteCanvas(document.getElementById('canvas'))`;
3232
const indexCss = `#canvas{border: 1px solid #000}`;
3333
const exampleJson: ExampleJson = {
3434
title,
3535
files: {
3636
'index.html': 'index.html',
3737
'index.js': 'index.js',
38-
'index.css': 'index.css',
39-
'example-infinite-canvas.js': '../example-infinite-canvas.js'
38+
'index.css': 'index.css'
4039
}
4140
}
4241
const exampleDirName = path.resolve(catalogPath, id);

0 commit comments

Comments
 (0)