@infinite-canvas-tutorial/webcomponents
Version:
WebComponents UI implementation
308 lines (301 loc) • 15.6 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { html, css, LitElement } from 'lit';
import { Task } from '@lit/task';
import { ContextProvider } from '@lit/context';
import { customElement, property } from 'lit/decorators.js';
import { getDefaultAppState, toDomPrecision, } from '@infinite-canvas-tutorial/ecs';
import { apiContext, appStateContext, nodesContext } from '../context';
import { pendingCanvases } from '../API';
import '@spectrum-web-components/theme/sp-theme.js';
import '@spectrum-web-components/theme/src/themes.js';
import '@spectrum-web-components/accordion/sp-accordion.js';
import '@spectrum-web-components/accordion/sp-accordion-item.js';
import '@spectrum-web-components/action-group/sp-action-group.js';
import '@spectrum-web-components/action-menu/sp-action-menu.js';
import '@spectrum-web-components/alert-banner/sp-alert-banner.js';
import '@spectrum-web-components/color-slider/sp-color-slider.js';
import '@spectrum-web-components/color-area/sp-color-area.js';
import '@spectrum-web-components/color-field/sp-color-field.js';
import '@spectrum-web-components/color-wheel/sp-color-wheel.js';
import '@spectrum-web-components/menu/sp-menu-item.js';
import '@spectrum-web-components/menu/sp-menu-divider.js';
import '@spectrum-web-components/menu/sp-menu-group.js';
import '@spectrum-web-components/number-field/sp-number-field.js';
import '@spectrum-web-components/slider/sp-slider.js';
import '@spectrum-web-components/swatch/sp-swatch.js';
import '@spectrum-web-components/swatch/sp-swatch-group.js';
import '@spectrum-web-components/switch/sp-switch.js';
import '@spectrum-web-components/overlay/sp-overlay.js';
import '@spectrum-web-components/progress-circle/sp-progress-circle.js';
import '@spectrum-web-components/textfield/sp-textfield.js';
import '@spectrum-web-components/thumbnail/sp-thumbnail.js';
import '@spectrum-web-components/tooltip/sp-tooltip.js';
import '@spectrum-web-components/picker/sp-picker.js';
import '@spectrum-web-components/popover/sp-popover.js';
import '@spectrum-web-components/overlay/overlay-trigger.js';
import '@spectrum-web-components/divider/sp-divider.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-add.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-add-to.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-delete.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-remove.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-text.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-visibility.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-visibility-off.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-properties.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-close.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-chevron-down.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-chevron-right.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-layers.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-properties.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-show-menu.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-stroke-width.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-hand.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-select.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-settings.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-shapes.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-undo.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-redo.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-lock-closed.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-lock-open.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-text-bold.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-text-italic.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-text-underline.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-text-align-left.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-text-align-center.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-text-align-right.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-layers-backward.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-layers-forward.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-layers-bring-to-front.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-layers-send-to-back.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-show-all-layers.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-copy.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-cut.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-paste.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-rectangle.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-ellipse.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-line.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-annotate-pen.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-code.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-brush.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-rect-select.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-image.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-arrow-up-right.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-send.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-find-and-replace.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-polygon-select.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-comment.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-image-auto-mode.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-vector-draw.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-region-select.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-crop.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-checkmark.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-cancel.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-group.js';
export const TOP_NAVBAR_HEIGHT = 48;
let InfiniteCanvas = class InfiniteCanvas extends LitElement {
constructor() {
super(...arguments);
this.renderer = 'webgl';
this.shaderCompilerPath = 'https://unpkg.com/@antv/g-device-api@1.6.8/dist/pkg/glsl_wgsl_compiler_bg.wasm';
this.theme = 'light';
this.nodes = [];
this.appStateProvider = new ContextProvider(this, { context: appStateContext });
this.nodesProvider = new ContextProvider(this, { context: nodesContext });
this.apiProvider = new ContextProvider(this, { context: apiContext });
this.initCanvas = new Task(this, {
task: ([renderer, shaderCompilerPath]) => {
// if (renderer === 'webgpu') {
// await checkWebGPUSupport();
// }
this.appStateProvider.value = Object.assign(Object.assign({}, getDefaultAppState()), this.appState);
this.nodesProvider.value = this.nodes;
const { topbarVisible, cameraZoom, cameraX, cameraY, cameraRotation } = this.appStateProvider.value;
const $canvas = document.createElement('canvas');
$canvas.style.width = '100%';
$canvas.style.height = topbarVisible
? `calc(100% - ${TOP_NAVBAR_HEIGHT}px)`
: '100%';
$canvas.tabIndex = 0; // Make canvas focusable
const $htmlLayer = document.createElement('div');
$htmlLayer.style.position = 'absolute';
$htmlLayer.style.top = topbarVisible ? `${TOP_NAVBAR_HEIGHT}px` : '0px';
$htmlLayer.style.left = '0px';
$htmlLayer.style.height = '1px';
$htmlLayer.style.width = '1px';
$htmlLayer.style.contain = 'layout style size';
$htmlLayer.style.userSelect = 'none';
$htmlLayer.style.outline = 'none';
$htmlLayer.style.transform = `scale(${toDomPrecision(cameraZoom)}) translate(${toDomPrecision(-cameraX)}px, ${toDomPrecision(-cameraY)}px)`;
const $svgLayer = document.createElement('div');
$svgLayer.style.position = 'absolute';
$svgLayer.style.top = topbarVisible ? `${TOP_NAVBAR_HEIGHT}px` : '0px';
$svgLayer.style.left = '0px';
$svgLayer.style.height = '100%';
$svgLayer.style.width = '100%';
$svgLayer.style.contain = 'layout style size';
$svgLayer.style.userSelect = 'none';
$svgLayer.style.outline = 'none';
$svgLayer.style.pointerEvents = 'none';
const { width, height } = this.getBoundingClientRect();
pendingCanvases.push({
container: this,
canvas: {
element: $canvas,
htmlLayer: $htmlLayer,
svgLayer: $svgLayer,
width,
height: topbarVisible ? height - TOP_NAVBAR_HEIGHT : height,
devicePixelRatio: window.devicePixelRatio,
renderer,
shaderCompilerPath,
},
camera: {
zoom: cameraZoom,
x: cameraX,
y: cameraY,
rotation: cameraRotation,
},
});
return [$canvas, $htmlLayer, $svgLayer];
},
args: () => [this.renderer, this.shaderCompilerPath],
});
}
connectedCallback() {
super.connectedCallback();
this.resizeObserver = new ResizeObserver((entries) => this.handleResize(entries));
this.updateComplete.then(() => this.resizeObserver.observe(this));
this.addEventListener('theme-change', (e) => {
this.theme = e.detail.themeMode;
});
}
disconnectedCallback() {
var _a, _b;
super.disconnectedCallback();
(_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.unobserve(this);
(_b = this.apiProvider.value) === null || _b === void 0 ? void 0 : _b.destroy();
}
handleResize(entries) {
var _a, _b;
const { topbarVisible } = this.appStateProvider.value;
const { width, height } = entries[0].contentRect;
const dpr = window.devicePixelRatio;
if (width && height) {
const $canvas = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('canvas');
$canvas.width = Math.floor(width * dpr);
$canvas.height = Math.floor((height - (topbarVisible ? TOP_NAVBAR_HEIGHT : 0)) * dpr);
(_b = this.apiProvider.value) === null || _b === void 0 ? void 0 : _b.resizeCanvas($canvas.width / dpr, $canvas.height / dpr);
}
}
render() {
const { topbarVisible } = this.appStateProvider.value;
const themeWrapper = (content) => html `<sp-theme system="spectrum" color="${this.theme}" scale="medium"
>${typeof content === 'string' ? html `${content}` : content}</sp-theme
>`;
return this.initCanvas.render({
pending: () => themeWrapper(html `<sp-progress-circle
label="A small representation of an unclear amount of work"
indeterminate
size="s"
></sp-progress-circle>`),
complete: ([$canvas, $htmlLayer, $svgLayer]) => themeWrapper(html `${$svgLayer}${$htmlLayer}<ic-spectrum-top-navbar
></ic-spectrum-top-navbar>${$canvas}
<ic-spectrum-penbar
style=${`top: ${topbarVisible ? TOP_NAVBAR_HEIGHT : 0}px; left: 0;`}
>
<slot name="penbar-item" slot="penbar-item"></slot>
</ic-spectrum-penbar>
<ic-spectrum-penbar-crop></ic-spectrum-penbar-crop>
<ic-spectrum-taskbar
style=${`top: ${topbarVisible ? TOP_NAVBAR_HEIGHT : 0}px; right: 0;`}
>
<slot name="taskbar-item" slot="taskbar-item"></slot>
<slot name="taskbar-panel" slot="taskbar-panel"></slot>
</ic-spectrum-taskbar>
<ic-spectrum-context-bar></ic-spectrum-context-bar>
<ic-spectrum-context-menu></ic-spectrum-context-menu>
<ic-spectrum-text-editor
style=${`top: ${topbarVisible ? TOP_NAVBAR_HEIGHT : 0}px; left: 0;`}
></ic-spectrum-text-editor>
<ic-spectrum-comments
style=${`top: ${topbarVisible ? TOP_NAVBAR_HEIGHT : 0}px; left: 0;`}
></ic-spectrum-comments>
<ic-spectrum-mask></ic-spectrum-mask>`),
error: (e) => {
console.error(e);
return themeWrapper(html `<sp-alert-banner open variant="negative">
Initialize canvas failed<br />
<strong></strong><br />
${e.message}
</sp-alert-banner>`);
},
});
}
};
InfiniteCanvas.styles = css `
:host {
display: block;
position: relative;
overflow: clip;
content-visibility: auto;
touch-action: none;
contain: strict;
--mod-popover-content-area-spacing-vertical: 0;
--spectrum-popover-content-area-spacing-vertical: 0;
}
ic-spectrum-penbar {
position: absolute;
top: 0;
left: 0;
}
ic-spectrum-penbar-crop {
position: absolute;
bottom: 0;
left: 50%;
}
ic-spectrum-taskbar {
position: absolute;
top: 0;
right: 0;
}
ic-spectrum-text-editor {
position: absolute;
top: 0;
left: 0;
}
canvas {
width: 100%;
height: 100%;
outline: none;
padding: 0;
margin: 0;
touch-action: none;
}
`;
__decorate([
property()
], InfiniteCanvas.prototype, "renderer", void 0);
__decorate([
property({
attribute: 'shader-compiler-path',
})
], InfiniteCanvas.prototype, "shaderCompilerPath", void 0);
__decorate([
property()
], InfiniteCanvas.prototype, "theme", void 0);
__decorate([
property({ type: Object, attribute: 'app-state' })
], InfiniteCanvas.prototype, "appState", void 0);
__decorate([
property({ type: Array })
], InfiniteCanvas.prototype, "nodes", void 0);
InfiniteCanvas = __decorate([
customElement('ic-spectrum-canvas')
], InfiniteCanvas);
export { InfiniteCanvas };
//# sourceMappingURL=infinite-canvas.js.map