snapshot of https://github.com/dream-num/univer.git (dev, 2026-09-16; blobs > 131072B omitted)
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
This commit is contained in:
37
packages/slides/README.md
Normal file
37
packages/slides/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# @univerjs/slides
|
||||
|
||||
[](https://npmjs.com/package/@univerjs/slides)
|
||||
[](https://npmjs.com/package/@univerjs/slides)
|
||||
[](https://npmjs.com/package/@univerjs/slides)
|
||||
|
||||
`@univerjs/slides` provides the core presentation data model and services for Univer Slides.
|
||||
|
||||
## Package Overview
|
||||
|
||||
| Package | UMD global | CSS | Locales | Facade entry |
|
||||
| --- | --- | :---: | :---: | :---: |
|
||||
| `@univerjs/slides` | `UniverSlides` | No | No | No |
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @univerjs/slides
|
||||
# or
|
||||
npm install @univerjs/slides
|
||||
```
|
||||
|
||||
Keep all `@univerjs/*` packages on the same version.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { UniverSlidesPlugin } from '@univerjs/slides';
|
||||
|
||||
univer.registerPlugin(UniverSlidesPlugin);
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Documentation](https://docs.univer.ai)
|
||||
- [NPM package](https://npmjs.com/package/@univerjs/slides)
|
||||
- [GitHub repository](https://github.com/dream-num/univer)
|
||||
79
packages/slides/package.json
Normal file
79
packages/slides/package.json
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"name": "@univerjs/slides",
|
||||
"version": "1.0.0-rc.0",
|
||||
"private": false,
|
||||
"description": "Core presentation model and services for Univer Slides.",
|
||||
"author": "DreamNum Co., Ltd. <developer@univer.ai>",
|
||||
"license": "Apache-2.0",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/univer"
|
||||
},
|
||||
"homepage": "https://univer.ai",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dream-num/univer"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/dream-num/univer/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"univer",
|
||||
"slides",
|
||||
"presentation",
|
||||
"deck",
|
||||
"plugin"
|
||||
],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/*"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "./lib/es/index.js",
|
||||
"module": "./lib/es/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./lib/es/index.js",
|
||||
"require": "./lib/cjs/index.js",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./*": {
|
||||
"import": "./lib/es/*",
|
||||
"require": "./lib/cjs/*",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./lib/*": "./lib/*"
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build:bundle": "univer-cli build",
|
||||
"build:types": "tsc -p tsconfig.node.json",
|
||||
"build": "pnpm run build:bundle && pnpm run build:types"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@univerjs-infra/shared": "workspace:*",
|
||||
"rxjs": "^7.8.2",
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "^5.0.0"
|
||||
}
|
||||
}
|
||||
24
packages/slides/src/basics/const/default-slide.ts
Normal file
24
packages/slides/src/basics/const/default-slide.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const DEFAULT_SLIDE = {
|
||||
id: 'default_slide',
|
||||
title: 'defaultSlide',
|
||||
pageSize: {
|
||||
width: 300,
|
||||
height: 300,
|
||||
},
|
||||
};
|
||||
17
packages/slides/src/basics/const/plugin-name.ts
Normal file
17
packages/slides/src/basics/const/plugin-name.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const SLIDES_PLUGIN_NAME = 'slides';
|
||||
219
packages/slides/src/basics/demo-data.ts
Normal file
219
packages/slides/src/basics/demo-data.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IDocumentData } from '@univerjs/core';
|
||||
import {
|
||||
BaselineOffset,
|
||||
BooleanNumber,
|
||||
ColumnSeparatorType,
|
||||
DocumentFlavor,
|
||||
DrawingTypeEnum,
|
||||
ObjectRelativeFromH,
|
||||
ObjectRelativeFromV,
|
||||
PositionedObjectLayoutType,
|
||||
PresetListType,
|
||||
SectionType,
|
||||
WrapTextType,
|
||||
} from '@univerjs/core';
|
||||
|
||||
export const docsDemoData: IDocumentData = {
|
||||
id: 'd',
|
||||
drawings: {
|
||||
shapeTest1: {
|
||||
unitId: 'd',
|
||||
subUnitId: 'd',
|
||||
drawingType: DrawingTypeEnum.DRAWING_SHAPE,
|
||||
drawingId: 'shapeTest1',
|
||||
title: 'test shape',
|
||||
description: 'test shape',
|
||||
docTransform: {
|
||||
size: {
|
||||
width: 100,
|
||||
height: 400,
|
||||
},
|
||||
positionH: {
|
||||
relativeFrom: ObjectRelativeFromH.COLUMN,
|
||||
posOffset: 100,
|
||||
},
|
||||
positionV: {
|
||||
relativeFrom: ObjectRelativeFromV.PARAGRAPH,
|
||||
posOffset: 160,
|
||||
},
|
||||
angle: 0,
|
||||
// imageProperties: {
|
||||
// contentUrl: 'https://cnbabylon.com/assets/img/agents.png',
|
||||
// },
|
||||
},
|
||||
layoutType: PositionedObjectLayoutType.WRAP_SQUARE,
|
||||
behindDoc: BooleanNumber.FALSE,
|
||||
wrapText: WrapTextType.BOTH_SIDES,
|
||||
},
|
||||
},
|
||||
body: {
|
||||
dataStream: '在“第1题”工作表中完成以下操作\b上标日期列单元格数据验证,限制只能输入日期(介于1949年1月1日至2099年1月1日)\r细化埋点上报链路和指标方案梳理目前通过每日定时任务发送报告\r自2018年首届进博会举办以来,进博会已经成为全球新品的首发地、前沿技术的首选地、创新服务的首推地。中国这十年”对外开放成就展湖北展区主要以图文、图表、数据、视频、企业展品、实物模型、光电科技等体现湖北十年开放成就、重大开放平台及产业。湖北省共计17家企业、机构的展品将在这一展区展示\r国家主席习近平以视频方式出席在上海举行的第五届中国国际进口博览会开幕式并发表题为《共创开放繁荣的美好未来》的致辞中国将推动各国各方共享深化国际合作机遇,全面深入参与世界贸易组织改革谈判\r中国男排两名现役国手彭世坤和张秉龙分别效力的三得利太阳鸟和东京大熊本轮遭遇。双方经过激战,主场作战的卫冕冠军三得利技高一筹3-2逆转击败对手,力夺第3场胜利\r\n',
|
||||
textRuns: [
|
||||
{
|
||||
st: 0,
|
||||
ed: 15,
|
||||
ts: {
|
||||
fs: 12,
|
||||
bg: {
|
||||
rgb: 'rgb(200,0,90)',
|
||||
},
|
||||
cl: {
|
||||
rgb: 'rgb(255,130,0)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
st: 17,
|
||||
ed: 18,
|
||||
ts: {
|
||||
fs: 14,
|
||||
bg: {
|
||||
rgb: 'rgb(2,128,2)',
|
||||
},
|
||||
cl: {
|
||||
rgb: 'rgb(0,1,55)',
|
||||
},
|
||||
va: BaselineOffset.SUPERSCRIPT,
|
||||
},
|
||||
},
|
||||
{
|
||||
st: 19,
|
||||
ed: 60,
|
||||
ts: {
|
||||
fs: 14,
|
||||
bg: {
|
||||
rgb: 'rgb(90,128,255)',
|
||||
},
|
||||
cl: {
|
||||
rgb: 'rgb(0,1,255)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
st: 62,
|
||||
ed: 90,
|
||||
},
|
||||
|
||||
{
|
||||
st: 92,
|
||||
ed: 233,
|
||||
},
|
||||
|
||||
{
|
||||
st: 235,
|
||||
ed: 326,
|
||||
},
|
||||
|
||||
{
|
||||
st: 328,
|
||||
ed: 405,
|
||||
},
|
||||
],
|
||||
paragraphs: [
|
||||
{
|
||||
paragraphId: 'para_slides_demo_order_1',
|
||||
startIndex: 60,
|
||||
bullet: {
|
||||
listId: 'orderList',
|
||||
listType: PresetListType.ORDER_LIST,
|
||||
nestingLevel: 0,
|
||||
textStyle: {
|
||||
fs: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
paragraphId: 'para_slides_demo_order_2',
|
||||
startIndex: 91,
|
||||
bullet: {
|
||||
listId: 'orderList',
|
||||
listType: PresetListType.ORDER_LIST,
|
||||
nestingLevel: 1,
|
||||
textStyle: {
|
||||
fs: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
paragraphId: 'para_slides_demo_order_3',
|
||||
startIndex: 234,
|
||||
bullet: {
|
||||
listId: 'orderList',
|
||||
listType: PresetListType.ORDER_LIST,
|
||||
nestingLevel: 1,
|
||||
textStyle: {
|
||||
fs: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
paragraphId: 'para_slides_demo_order_4',
|
||||
startIndex: 327,
|
||||
bullet: {
|
||||
listId: 'orderList',
|
||||
listType: PresetListType.ORDER_LIST,
|
||||
nestingLevel: 1,
|
||||
textStyle: {
|
||||
fs: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
paragraphId: 'para_slides_demo_order_5',
|
||||
startIndex: 406,
|
||||
bullet: {
|
||||
listId: 'orderList',
|
||||
listType: PresetListType.ORDER_LIST,
|
||||
nestingLevel: 2,
|
||||
textStyle: {
|
||||
fs: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
sectionBreaks: [
|
||||
{
|
||||
sectionId: 'section_slides_demo',
|
||||
startIndex: 407,
|
||||
columnProperties: [
|
||||
{
|
||||
width: 200,
|
||||
paddingEnd: 20,
|
||||
},
|
||||
],
|
||||
columnSeparatorType: ColumnSeparatorType.NONE,
|
||||
sectionType: SectionType.SECTION_TYPE_UNSPECIFIED,
|
||||
// textDirection: textDirectionDocument,
|
||||
// contentDirection: textDirection!,
|
||||
},
|
||||
],
|
||||
},
|
||||
documentStyle: {
|
||||
pageSize: {
|
||||
width: 594.3,
|
||||
height: 840.51,
|
||||
},
|
||||
documentFlavor: DocumentFlavor.UNSPECIFIED,
|
||||
marginTop: 72,
|
||||
marginBottom: 72,
|
||||
marginRight: 90,
|
||||
marginLeft: 90,
|
||||
},
|
||||
};
|
||||
24
packages/slides/src/config/config.ts
Normal file
24
packages/slides/src/config/config.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const SLIDES_PLUGIN_CONFIG_KEY = 'slides.config';
|
||||
|
||||
export const configSymbol = Symbol(SLIDES_PLUGIN_CONFIG_KEY);
|
||||
|
||||
export interface IUniverSlidesConfig {
|
||||
}
|
||||
|
||||
export const defaultPluginConfig: IUniverSlidesConfig = {};
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Nullable } from '@univerjs/core';
|
||||
import type { ISlideData, ISlidePage } from '../../types/interfaces/i-slide-data';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { PageElementType, PageType } from '../../types/interfaces/i-slide-data';
|
||||
import { SlideDataModel } from '../slide-data-model';
|
||||
|
||||
const slideSnapshotFactory = (): Partial<ISlideData> => ({
|
||||
id: 'slide-unit',
|
||||
title: 'Quarterly review',
|
||||
pageSize: { width: 960, height: 540 },
|
||||
body: {
|
||||
pageOrder: ['page-1', 'page-2'],
|
||||
pages: {
|
||||
'page-1': {
|
||||
id: 'page-1',
|
||||
pageType: PageType.SLIDE,
|
||||
zIndex: 1,
|
||||
title: 'Overview',
|
||||
description: 'Overview slide',
|
||||
pageBackgroundFill: { rgb: '#ffffff' },
|
||||
pageElements: {
|
||||
'element-1': {
|
||||
id: 'element-1',
|
||||
zIndex: 1,
|
||||
title: 'Intro',
|
||||
description: 'Hero copy',
|
||||
type: PageElementType.TEXT,
|
||||
richText: { text: 'Hello Univer' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'page-2': {
|
||||
id: 'page-2',
|
||||
pageType: PageType.SLIDE,
|
||||
zIndex: 2,
|
||||
title: 'Roadmap',
|
||||
description: 'Roadmap slide',
|
||||
pageBackgroundFill: { rgb: '#f5f5f5' },
|
||||
pageElements: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('SlideDataModel', () => {
|
||||
let slide: SlideDataModel;
|
||||
|
||||
afterEach(() => {
|
||||
slide?.dispose();
|
||||
});
|
||||
|
||||
it('should manage pages and active-page state', () => {
|
||||
slide = new SlideDataModel(slideSnapshotFactory());
|
||||
const activePages: string[] = [];
|
||||
const names: string[] = [];
|
||||
const activePageSubscription = slide.activePage$.subscribe((page: Nullable<ISlidePage>) => {
|
||||
if (page) {
|
||||
activePages.push(page.id);
|
||||
}
|
||||
});
|
||||
const nameSubscription = slide.name$.subscribe((name: string) => names.push(name));
|
||||
|
||||
expect(slide.getUnitId()).toBe('slide-unit');
|
||||
expect(slide.getSnapshot().title).toBe('Quarterly review');
|
||||
expect(slide.getPages()).toHaveProperty('page-1');
|
||||
expect(slide.getPageOrder()).toEqual(['page-1', 'page-2']);
|
||||
expect(slide.getPage('page-1')?.title).toBe('Overview');
|
||||
expect(slide.getElementsByPage('page-1')).toHaveProperty('element-1');
|
||||
expect(slide.getElement('page-1', 'element-1')).toMatchObject({
|
||||
id: 'element-1',
|
||||
type: PageElementType.TEXT,
|
||||
});
|
||||
expect(slide.getPageSize()).toEqual({ width: 960, height: 540 });
|
||||
expect(slide.getActivePage()?.id).toBe('page-1');
|
||||
expect(slide.getRev()).toBe(0);
|
||||
|
||||
slide.incrementRev();
|
||||
slide.setRev(5);
|
||||
expect(slide.getRev()).toBe(0);
|
||||
|
||||
slide.setActivePage(slide.getPage('page-2')!);
|
||||
expect(slide.getActivePage()?.id).toBe('page-2');
|
||||
expect(activePages).toEqual(['page-2']);
|
||||
|
||||
const blankPage = slide.getBlankPage();
|
||||
expect(blankPage).toMatchObject({
|
||||
pageType: PageType.SLIDE,
|
||||
zIndex: 10,
|
||||
pageBackgroundFill: { rgb: 'rgb(255,255,255)' },
|
||||
});
|
||||
|
||||
slide.appendPage(blankPage);
|
||||
expect(slide.getPageOrder()).toEqual(['page-1', 'page-2', blankPage.id]);
|
||||
expect(slide.getPage(blankPage.id)).toEqual(blankPage);
|
||||
|
||||
const updatedPage: ISlidePage = {
|
||||
...blankPage,
|
||||
title: 'Agenda',
|
||||
description: 'Agenda slide',
|
||||
};
|
||||
slide.updatePage(blankPage.id, updatedPage);
|
||||
expect(slide.getPage(blankPage.id)).toEqual(updatedPage);
|
||||
|
||||
slide.setName('Quarterly review - updated');
|
||||
expect(slide.getSnapshot().title).toBe('Quarterly review - updated');
|
||||
expect(slide.getUnitId()).toBe('slide-unit');
|
||||
expect(names.at(-1)).toBe('Quarterly review - updated');
|
||||
|
||||
activePageSubscription.unsubscribe();
|
||||
nameSubscription.unsubscribe();
|
||||
});
|
||||
|
||||
it('should keep empty slide snapshots stable when page collections are absent', () => {
|
||||
slide = new SlideDataModel({
|
||||
id: 'empty-slide',
|
||||
title: 'Empty deck',
|
||||
});
|
||||
|
||||
const blankPage = slide.getBlankPage();
|
||||
|
||||
expect(slide.getActivePage()).toBeNull();
|
||||
expect(slide.getPages()).toBeUndefined();
|
||||
expect(slide.getPageOrder()).toBeUndefined();
|
||||
expect(slide.getPage(blankPage.id)).toBeUndefined();
|
||||
|
||||
slide.appendPage(blankPage);
|
||||
slide.updatePage(blankPage.id, blankPage);
|
||||
|
||||
expect(slide.getPages()).toBeUndefined();
|
||||
expect(slide.getSnapshot()).toMatchObject({
|
||||
id: 'empty-slide',
|
||||
title: 'Empty deck',
|
||||
});
|
||||
});
|
||||
});
|
||||
154
packages/slides/src/data-model/slide-data-model.ts
Normal file
154
packages/slides/src/data-model/slide-data-model.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Nullable } from '@univerjs/core';
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { ISlideData, ISlidePage } from '../types/interfaces/i-slide-data';
|
||||
import { generateRandomId, UnitModel, UniverInstanceType } from '@univerjs/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { DEFAULT_SLIDE } from '../basics/const/default-slide';
|
||||
import { PageType } from '../types/interfaces/i-slide-data';
|
||||
|
||||
export class SlideDataModel extends UnitModel<ISlideData, UniverInstanceType.UNIVER_SLIDE> {
|
||||
override type: UniverInstanceType.UNIVER_SLIDE = UniverInstanceType.UNIVER_SLIDE;
|
||||
private readonly _activePage$ = new BehaviorSubject<Nullable<ISlidePage>>(null);
|
||||
private get _activePage(): Nullable<ISlidePage> {
|
||||
const activePage = this._activePage$.getValue();
|
||||
|
||||
if (!activePage) {
|
||||
const activePageId = this.getPageOrder()?.[0];
|
||||
if (!activePageId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getPages()?.[activePageId];
|
||||
}
|
||||
|
||||
return activePage;
|
||||
}
|
||||
|
||||
readonly activePage$ = this._activePage$.asObservable();
|
||||
|
||||
private readonly _name$: BehaviorSubject<string>;
|
||||
override name$: Observable<string>;
|
||||
|
||||
private _snapshot: ISlideData;
|
||||
|
||||
private _unitId: string;
|
||||
|
||||
constructor(snapshot: Partial<ISlideData>) {
|
||||
super();
|
||||
|
||||
this._snapshot = { ...DEFAULT_SLIDE, ...snapshot };
|
||||
this._unitId = this._snapshot.id ?? generateRandomId(6);
|
||||
|
||||
this._name$ = new BehaviorSubject(this._snapshot.title);
|
||||
this.name$ = this._name$.asObservable();
|
||||
}
|
||||
|
||||
override setName(name: string): void {
|
||||
this._snapshot.title = name;
|
||||
this._name$.next(name);
|
||||
this._unitId = this._snapshot.id ?? generateRandomId(6);
|
||||
}
|
||||
|
||||
override getRev(): number {
|
||||
return 0; // TODO@jikkai: slide has not implement collaborative editing yet
|
||||
}
|
||||
|
||||
override incrementRev(): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
override setRev(_rev: number): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
getSnapshot() {
|
||||
return this._snapshot;
|
||||
}
|
||||
|
||||
getUnitId(): string {
|
||||
return this._unitId;
|
||||
}
|
||||
|
||||
getPages() {
|
||||
return this._snapshot.body?.pages;
|
||||
}
|
||||
|
||||
getPageOrder() {
|
||||
return this._snapshot.body?.pageOrder;
|
||||
}
|
||||
|
||||
getPage(pageId: string) {
|
||||
const pages = this.getPages();
|
||||
return pages?.[pageId];
|
||||
}
|
||||
|
||||
getElementsByPage(pageId: string) {
|
||||
return this.getPage(pageId)?.pageElements;
|
||||
}
|
||||
|
||||
getElement(pageId: string, elementId: string) {
|
||||
return this.getElementsByPage(pageId)?.[elementId];
|
||||
}
|
||||
|
||||
getPageSize() {
|
||||
return this._snapshot.pageSize;
|
||||
}
|
||||
|
||||
getBlankPage() {
|
||||
const id = generateRandomId(6);
|
||||
|
||||
const page = {
|
||||
id,
|
||||
pageType: PageType.SLIDE,
|
||||
zIndex: 10,
|
||||
title: id,
|
||||
description: '',
|
||||
pageBackgroundFill: {
|
||||
rgb: 'rgb(255,255,255)',
|
||||
},
|
||||
pageElements: {},
|
||||
};
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
setActivePage(page: Nullable<ISlidePage>) {
|
||||
this._activePage$.next(page);
|
||||
}
|
||||
|
||||
getActivePage() {
|
||||
return this._activePage;
|
||||
}
|
||||
|
||||
updatePage(pageId: string, page: ISlidePage) {
|
||||
if (!this._snapshot.body) return;
|
||||
|
||||
this._snapshot.body.pages[pageId] = page;
|
||||
}
|
||||
|
||||
appendPage(page: ISlidePage) {
|
||||
if (!this._snapshot.body) return;
|
||||
|
||||
this._snapshot.body.pages[page.id] = page;
|
||||
|
||||
const activePage = this._activePage;
|
||||
const index = this._snapshot.body.pageOrder.indexOf(activePage?.id ?? '');
|
||||
this._snapshot.body.pageOrder.splice(index + 1, 0, page.id);
|
||||
}
|
||||
}
|
||||
23
packages/slides/src/index.ts
Normal file
23
packages/slides/src/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { DEFAULT_SLIDE } from './basics/const/default-slide';
|
||||
export type { IUniverSlidesConfig } from './config/config';
|
||||
export { SlideDataModel } from './data-model/slide-data-model';
|
||||
export { UniverSlidesPlugin } from './plugin';
|
||||
export * from './types/enum/prst-geom-type';
|
||||
export * from './types/interfaces/i-slide-data';
|
||||
export * from './views/render';
|
||||
79
packages/slides/src/plugin.ts
Normal file
79
packages/slides/src/plugin.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Dependency } from '@univerjs/core';
|
||||
import { DependentOn, IConfigService, Inject, Injector, IUniverInstanceService, merge, Plugin, UniverInstanceType } from '@univerjs/core';
|
||||
import { UniverRenderEnginePlugin } from '@univerjs/engine-render';
|
||||
import pkg from '../package.json';
|
||||
import { defaultPluginConfig, SLIDES_PLUGIN_CONFIG_KEY } from './config/config';
|
||||
import { SlideDataModel } from './data-model/slide-data-model';
|
||||
// import { DocSelectionManagerService } from '@univerjs/docs';
|
||||
// import { CanvasView } from './views/render';
|
||||
|
||||
export interface IUniverSlidesConfig {}
|
||||
|
||||
@DependentOn(UniverRenderEnginePlugin)
|
||||
export class UniverSlidesPlugin extends Plugin {
|
||||
static override pluginName = 'UNIVER_SLIDES_PLUGIN';
|
||||
static override packageName = pkg.name;
|
||||
static override version = pkg.version;
|
||||
static override type = UniverInstanceType.UNIVER_SLIDE;
|
||||
|
||||
// private _canvasView: CanvasView | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly _config: Partial<IUniverSlidesConfig> = defaultPluginConfig,
|
||||
@Inject(Injector) override readonly _injector: Injector,
|
||||
@IConfigService private readonly _configService: IConfigService,
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService
|
||||
) {
|
||||
super();
|
||||
|
||||
// Manage the plugin configuration.
|
||||
const { ...rest } = merge(
|
||||
{},
|
||||
defaultPluginConfig,
|
||||
this._config
|
||||
);
|
||||
this._configService.setConfig(SLIDES_PLUGIN_CONFIG_KEY, rest);
|
||||
|
||||
this._initializeDependencies(this._injector);
|
||||
}
|
||||
|
||||
override onStarting(): void {
|
||||
// Register the SlideDataModel constructor for the UNIVER_SLIDE type.
|
||||
this._univerInstanceService.registerCtorForType(UniverInstanceType.UNIVER_SLIDE, SlideDataModel);
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
|
||||
}
|
||||
|
||||
getConfig() {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
private _initializeDependencies(slideInjector: Injector) {
|
||||
const dependencies: Dependency[] = [
|
||||
// [CanvasView],
|
||||
// [DocSelectionManagerService],
|
||||
];
|
||||
|
||||
dependencies.forEach((d) => {
|
||||
slideInjector.add(d);
|
||||
});
|
||||
}
|
||||
}
|
||||
125
packages/slides/src/types/enum/prst-geom-type.ts
Normal file
125
packages/slides/src/types/enum/prst-geom-type.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// TODO@Jocs: should be moved out of core.
|
||||
|
||||
export enum BasicShapes {
|
||||
Line = 'line',
|
||||
LineInv = 'lineInv',
|
||||
Triangle = 'triangle',
|
||||
RtTriangle = 'rtTriangle',
|
||||
Rect = 'rect',
|
||||
Diamond = 'diamond',
|
||||
Parallelogram = 'parallelogram',
|
||||
Trapezoid = 'trapezoid',
|
||||
NonIsocelesTrapezoid = 'nonIsocelesTrapezoid',
|
||||
Pentagon = 'pentagon',
|
||||
Hexagon = 'hexagon',
|
||||
Heptagon = 'heptagon',
|
||||
Octagon = 'octagon',
|
||||
Decagon = 'decagon',
|
||||
Dodecagon = 'dodecagon',
|
||||
Star4 = 'star4',
|
||||
Star5 = 'star5',
|
||||
Star6 = 'star6',
|
||||
Star7 = 'star7',
|
||||
Star8 = 'star8',
|
||||
Star10 = 'star10',
|
||||
Star12 = 'star12',
|
||||
Star16 = 'star16',
|
||||
Star24 = 'star24',
|
||||
Star32 = 'star32',
|
||||
RoundRect = 'roundRect',
|
||||
Round1Rect = 'round1Rect',
|
||||
Round2SameRect = 'round2SameRect',
|
||||
Round2DiagRect = 'round2DiagRect',
|
||||
Ellipse = 'ellipse',
|
||||
}
|
||||
|
||||
export enum ArrowsAndMarkersShapes {
|
||||
RightArrow = 'rightArrow',
|
||||
LeftArrow = 'leftArrow',
|
||||
UpArrow = 'upArrow',
|
||||
DownArrow = 'downArrow',
|
||||
LeftRightArrow = 'leftRightArrow',
|
||||
UpDownArrow = 'upDownArrow',
|
||||
QuadArrow = 'quadArrow',
|
||||
LeftRightUpArrow = 'leftRightUpArrow',
|
||||
BentArrow = 'bentArrow',
|
||||
UturnArrow = 'uturnArrow',
|
||||
CircularArrow = 'circularArrow',
|
||||
NotchedRightArrow = 'notchedRightArrow',
|
||||
HomePlate = 'homePlate',
|
||||
Chevron = 'chevron',
|
||||
LeftCircularArrow = 'leftCircularArrow',
|
||||
LeftRightCircularArrow = 'leftRightCircularArrow',
|
||||
}
|
||||
|
||||
export enum OtherShapes {
|
||||
Plaque = 'plaque',
|
||||
Can = 'can',
|
||||
Cube = 'cube',
|
||||
Bevel = 'bevel',
|
||||
Donut = 'donut',
|
||||
NoSmoking = 'noSmoking',
|
||||
BlockArc = 'blockArc',
|
||||
FoldedCorner = 'foldedCorner',
|
||||
}
|
||||
|
||||
export enum SpecialShapes {
|
||||
SmileyFace = 'smileyFace',
|
||||
Heart = 'heart',
|
||||
LightningBolt = 'lightningBolt',
|
||||
Sun = 'sun',
|
||||
Moon = 'moon',
|
||||
Cloud = 'cloud',
|
||||
Arc = 'arc',
|
||||
Backpack = 'backpack',
|
||||
Frame = 'frame',
|
||||
HalfFrame = 'halfFrame',
|
||||
Corner = 'corner',
|
||||
Chord = 'chord',
|
||||
Pie = 'pie',
|
||||
Teardrop = 'teardrop',
|
||||
WedgeRectCallout = 'wedgeRectCallout',
|
||||
WedgeRRectCallout = 'wedgeRRectCallout',
|
||||
WedgeEllipseCallout = 'wedgeEllipseCallout',
|
||||
CloudCallout = 'cloudCallout',
|
||||
BorderCallout1 = 'borderCallout1',
|
||||
BorderCallout2 = 'borderCallout2',
|
||||
BorderCallout3 = 'borderCallout3',
|
||||
AccentCallout1 = 'accentCallout1',
|
||||
AccentCallout2 = 'accentCallout2',
|
||||
AccentCallout3 = 'accentCallout3',
|
||||
Callout1 = 'callout1',
|
||||
Callout2 = 'callout2',
|
||||
Callout3 = 'callout3',
|
||||
ActionButtonBackPrevious = 'actionButtonBackPrevious',
|
||||
ActionButtonEnd = 'actionButtonEnd',
|
||||
ActionButtonForwardNext = 'actionButtonForwardNext',
|
||||
ActionButtonHelp = 'actionButtonHelp',
|
||||
ActionButtonHome = 'actionButtonHome',
|
||||
ActionButtonInformation = 'actionButtonInformation',
|
||||
ActionButtonMovie = 'actionButtonMovie',
|
||||
ActionButtonReturn = 'actionButtonReturn',
|
||||
ActionButtonSound = 'actionButtonSound',
|
||||
}
|
||||
|
||||
/**
|
||||
* 20.1.9.18 prstGeom (Preset geometry)
|
||||
*/
|
||||
export type ShapeType = BasicShapes | ArrowsAndMarkersShapes | OtherShapes | SpecialShapes;
|
||||
export type PresetGeometryType = ShapeType | 'custom';
|
||||
187
packages/slides/src/types/interfaces/i-slide-data.ts
Normal file
187
packages/slides/src/types/interfaces/i-slide-data.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type {
|
||||
IColorStyle,
|
||||
ICustomBlock,
|
||||
IDocumentData,
|
||||
IImageProperties,
|
||||
ILists,
|
||||
IPlaceholder,
|
||||
IShapeProperties,
|
||||
ISize,
|
||||
IStyleBase,
|
||||
IStyleData,
|
||||
ITransformState,
|
||||
IWorksheetData,
|
||||
LocaleType,
|
||||
Nullable,
|
||||
ThemeColorType,
|
||||
} from '@univerjs/core';
|
||||
import type { ShapeType } from '../enum/prst-geom-type';
|
||||
|
||||
export interface ISlideData extends IReferenceSource {
|
||||
id: string; // unit id
|
||||
locale?: LocaleType;
|
||||
title: string;
|
||||
pageSize: ISize;
|
||||
body?: ISlidePageBody;
|
||||
}
|
||||
|
||||
interface IReferenceSource {
|
||||
master?: { [id: string]: ISlidePage };
|
||||
handoutMaster?: { [id: string]: ISlidePage };
|
||||
notesMaster?: { [id: string]: ISlidePage };
|
||||
layouts?: { [id: string]: ISlidePage };
|
||||
lists?: ILists;
|
||||
}
|
||||
|
||||
interface ISlidePageBody {
|
||||
pages: { [id: string]: ISlidePage };
|
||||
pageOrder: string[];
|
||||
}
|
||||
|
||||
export interface ISlidePage {
|
||||
id: string;
|
||||
pageType: PageType;
|
||||
zIndex: number;
|
||||
title: string;
|
||||
description: string;
|
||||
pageBackgroundFill: IColorStyle;
|
||||
colorScheme?: ThemeColorType;
|
||||
pageElements: { [elementId: string]: IPageElement };
|
||||
// Union field properties. Properties that are specific for each page type. Masters do not require any additional properties. properties can be only one of the following:
|
||||
slideProperties?: ISlideProperties;
|
||||
layoutProperties?: ILayoutProperties;
|
||||
notesProperties?: INotesProperties;
|
||||
handoutProperties?: IHandoutProperties;
|
||||
masterProperties?: IMasterProperties;
|
||||
}
|
||||
|
||||
interface ISlideProperties {
|
||||
layoutObjectId: string;
|
||||
masterObjectId: string;
|
||||
isSkipped: boolean;
|
||||
}
|
||||
|
||||
interface ILayoutProperties {
|
||||
masterObjectId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface INotesProperties {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface IHandoutProperties {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface IMasterProperties {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ISlideRichTextProps extends ITransformState, IStyleBase {
|
||||
text?: string;
|
||||
rich?: IDocumentData;
|
||||
}
|
||||
|
||||
export interface IPageElement {
|
||||
id: string;
|
||||
zIndex: number;
|
||||
left?: number;
|
||||
top?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
angle?: number;
|
||||
scaleX?: number;
|
||||
scaleY?: number;
|
||||
skewX?: number;
|
||||
skewY?: number;
|
||||
flipX?: boolean;
|
||||
flipY?: boolean;
|
||||
|
||||
title: string;
|
||||
description: string;
|
||||
|
||||
type: PageElementType;
|
||||
|
||||
// Union field element_kind can be only one of the following:
|
||||
// elementGroup: IGroup;
|
||||
shape?: IShape;
|
||||
image?: IImage;
|
||||
richText?: ISlideRichTextProps;
|
||||
|
||||
spreadsheet?: {
|
||||
worksheet: IWorksheetData;
|
||||
styles: Record<string, Nullable<IStyleData>>;
|
||||
};
|
||||
document?: IDocumentData;
|
||||
slide?: ISlideData;
|
||||
// video: IVideo;
|
||||
// line: ILine;
|
||||
// table: ITable;
|
||||
// chart: IChartProperties;
|
||||
customBlock?: ICustomBlock; // customBlock block customized by user through plugin
|
||||
}
|
||||
|
||||
export enum PageType {
|
||||
SLIDE, // A slide page.
|
||||
MASTER, // A master slide page.
|
||||
LAYOUT, // A layout page.
|
||||
HANDOUT_MASTER, // A handout master page.
|
||||
NOTES_MASTER, // A notes master page.
|
||||
}
|
||||
|
||||
export enum PageElementType {
|
||||
SHAPE,
|
||||
IMAGE,
|
||||
TEXT,
|
||||
SPREADSHEET,
|
||||
DOCUMENT,
|
||||
SLIDE,
|
||||
}
|
||||
|
||||
/**
|
||||
* IShape
|
||||
*/
|
||||
export interface IShape {
|
||||
shapeType: ShapeType;
|
||||
text: string;
|
||||
shapeProperties: IShapeProperties;
|
||||
placeholder?: IPlaceholder;
|
||||
link?: ILink;
|
||||
}
|
||||
|
||||
export interface IImage {
|
||||
imageProperties?: IImageProperties;
|
||||
placeholder?: IPlaceholder;
|
||||
link?: ILink;
|
||||
}
|
||||
|
||||
interface ILink {
|
||||
relativeLink: RelativeSlideLink;
|
||||
pageId?: string;
|
||||
slideIndex?: number;
|
||||
}
|
||||
|
||||
export enum RelativeSlideLink {
|
||||
RELATIVE_SLIDE_LINK_UNSPECIFIED, // An unspecified relative slide link.
|
||||
NEXT_SLIDE, // A link to the next slide.
|
||||
PREVIOUS_SLIDE, // A link to the previous slide.
|
||||
FIRST_SLIDE, // A link to the first slide in the presentation.
|
||||
LAST_SLIDE, // A link to the last slide in the presentation.
|
||||
}
|
||||
42
packages/slides/src/views/render/adaptor.ts
Normal file
42
packages/slides/src/views/render/adaptor.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Injector, Nullable } from '@univerjs/core';
|
||||
import type { BaseObject, Scene } from '@univerjs/engine-render';
|
||||
import type { IPageElement, PageElementType } from '../../types/interfaces/i-slide-data';
|
||||
import { Registry } from '@univerjs/core';
|
||||
|
||||
export abstract class ObjectAdaptor {
|
||||
zIndex = 0;
|
||||
|
||||
viewKey: PageElementType | null = null;
|
||||
|
||||
check(type: PageElementType) {
|
||||
if (type !== this.viewKey) {
|
||||
return;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
abstract convert(pageElement: IPageElement, mainScene: Scene): Nullable<BaseObject>;
|
||||
|
||||
create(injector: Injector): void {
|
||||
// FIXME: should not be empty
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
export const CanvasObjectProviderRegistry = Registry.create();
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Rect } from '@univerjs/engine-render';
|
||||
import type { IPageElement, ISlideData } from '../../../../types/interfaces/i-slide-data';
|
||||
import { ConfigService, ContextService, IConfigService, IContextService, Injector, LocaleService } from '@univerjs/core';
|
||||
import { Scene, SceneViewer } from '@univerjs/engine-render';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { BasicShapes } from '../../../../types/enum/prst-geom-type';
|
||||
import { PageElementType, PageType } from '../../../../types/interfaces/i-slide-data';
|
||||
import { SlideAdaptor } from '../slide-adaptor';
|
||||
|
||||
function createRenderTestBed() {
|
||||
const injector = new Injector();
|
||||
|
||||
injector.add([LocaleService, { useClass: LocaleService }]);
|
||||
injector.add([IContextService, { useClass: ContextService }]);
|
||||
injector.add([IConfigService, { useClass: ConfigService }]);
|
||||
|
||||
return {
|
||||
injector,
|
||||
slideAdaptor: injector.createInstance(SlideAdaptor),
|
||||
};
|
||||
}
|
||||
|
||||
function createMainScene() {
|
||||
return new Scene(
|
||||
'main-scene',
|
||||
new SceneViewer('main-viewer', {
|
||||
width: 960,
|
||||
height: 540,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function createDeckElement(): IPageElement {
|
||||
return {
|
||||
id: 'embedded-deck',
|
||||
zIndex: 9,
|
||||
left: 16,
|
||||
top: 24,
|
||||
width: 720,
|
||||
height: 405,
|
||||
title: 'Embedded sales deck',
|
||||
description: 'Deck inside a page element',
|
||||
type: PageElementType.SLIDE,
|
||||
slide: createEmbeddedSlideData(),
|
||||
};
|
||||
}
|
||||
|
||||
function createEmbeddedSlideData(): ISlideData {
|
||||
return {
|
||||
id: 'sales-deck',
|
||||
title: 'Sales update',
|
||||
pageSize: { width: 720, height: 405 },
|
||||
body: {
|
||||
pageOrder: ['summary', 'pipeline'],
|
||||
pages: {
|
||||
summary: {
|
||||
id: 'summary',
|
||||
pageType: PageType.SLIDE,
|
||||
zIndex: 1,
|
||||
title: 'Summary',
|
||||
description: 'Executive summary',
|
||||
pageBackgroundFill: { rgb: '#112233' },
|
||||
pageElements: {
|
||||
'sales-box': {
|
||||
id: 'sales-box',
|
||||
zIndex: 3,
|
||||
left: 40,
|
||||
top: 32,
|
||||
width: 180,
|
||||
height: 72,
|
||||
title: 'Revenue card',
|
||||
description: 'Revenue highlight',
|
||||
type: PageElementType.SHAPE,
|
||||
shape: {
|
||||
shapeType: BasicShapes.RoundRect,
|
||||
text: '',
|
||||
shapeProperties: {
|
||||
shapeBackgroundFill: { rgb: '#44aa88' },
|
||||
radius: 12,
|
||||
outline: {
|
||||
outlineFill: { rgb: '#0b3128' },
|
||||
weight: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pipeline: {
|
||||
id: 'pipeline',
|
||||
pageType: PageType.SLIDE,
|
||||
zIndex: 2,
|
||||
title: 'Pipeline',
|
||||
description: 'Next page',
|
||||
pageBackgroundFill: { rgb: '#ffeecc' },
|
||||
pageElements: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('SlideAdaptor', () => {
|
||||
it('converts slide pages in source order with page backgrounds and child shape styling', () => {
|
||||
const { slideAdaptor } = createRenderTestBed();
|
||||
const deck = slideAdaptor.convert(createDeckElement(), createMainScene())!;
|
||||
|
||||
expect(deck.left).toBe(16);
|
||||
expect(deck.top).toBe(24);
|
||||
expect(deck.width).toBe(720);
|
||||
expect(deck.height).toBe(405);
|
||||
expect(deck.zIndex).toBe(9);
|
||||
expect([...deck.getSubScenes().keys()]).toEqual(['summary', 'pipeline']);
|
||||
expect(deck.getActiveSubScene()?.sceneKey).toBe('summary');
|
||||
|
||||
const summaryScene = deck.getSubScene('summary')!;
|
||||
const summaryBackground = summaryScene.getObject('canvas') as Rect;
|
||||
expect(summaryBackground.fill).toBe('#112233');
|
||||
expect(summaryBackground.width).toBe(720);
|
||||
expect(summaryBackground.height).toBe(405);
|
||||
expect(summaryBackground.evented).toBe(false);
|
||||
|
||||
const salesBox = summaryScene.getObject('sales-box') as Rect;
|
||||
expect(salesBox.left).toBe(40);
|
||||
expect(salesBox.top).toBe(32);
|
||||
expect(salesBox.width).toBe(180);
|
||||
expect(salesBox.height).toBe(72);
|
||||
expect(salesBox.zIndex).toBe(3);
|
||||
expect(salesBox.fill).toBe('#44aa88');
|
||||
expect(salesBox.stroke).toBe('#0b3128');
|
||||
expect(salesBox.strokeWidth).toBe(2);
|
||||
expect(salesBox.getPropByKey('radius')).toBe(12);
|
||||
|
||||
const pipelineScene = deck.getSubScene('pipeline')!;
|
||||
expect((pipelineScene.getObject('canvas') as Rect).fill).toBe('#ffeecc');
|
||||
|
||||
deck.changePage('pipeline');
|
||||
expect(deck.getActiveSubScene()?.sceneKey).toBe('pipeline');
|
||||
});
|
||||
});
|
||||
371
packages/slides/src/views/render/adaptors/docs-adaptor.ts
Normal file
371
packages/slides/src/views/render/adaptors/docs-adaptor.ts
Normal file
@@ -0,0 +1,371 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { EventState, Injector } from '@univerjs/core';
|
||||
import type { BaseObject, IDocumentSkeletonDrawing, IPageRenderConfig, IWheelEvent } from '@univerjs/engine-render';
|
||||
import type { IPageElement } from '../../../types/interfaces/i-slide-data';
|
||||
import { DocumentDataModel, Inject, LocaleService } from '@univerjs/core';
|
||||
import {
|
||||
Documents,
|
||||
DocumentSkeleton,
|
||||
DocumentViewModel,
|
||||
Image,
|
||||
Liquid,
|
||||
PageLayoutType,
|
||||
// Rect,
|
||||
Scene,
|
||||
SceneViewer,
|
||||
ScrollBar,
|
||||
Viewport,
|
||||
} from '@univerjs/engine-render';
|
||||
import { PageElementType } from '../../../types/interfaces/i-slide-data';
|
||||
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../adaptor';
|
||||
|
||||
export enum DOCS_VIEW_KEY {
|
||||
MAIN = '__DocsRender__',
|
||||
SCENE_VIEWER = '__DocsViewer__',
|
||||
SCENE = '__DocsScene__',
|
||||
VIEWPORT = '__DocsViewPort_',
|
||||
}
|
||||
|
||||
export class DocsAdaptor extends ObjectAdaptor {
|
||||
override zIndex = 5;
|
||||
|
||||
override viewKey = PageElementType.DOCUMENT;
|
||||
|
||||
private _liquid = new Liquid();
|
||||
|
||||
constructor(@Inject(LocaleService) private readonly _localeService: LocaleService) {
|
||||
super();
|
||||
}
|
||||
|
||||
override check(type: PageElementType) {
|
||||
if (type !== this.viewKey) {
|
||||
return;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
override convert(pageElement: IPageElement, mainScene: Scene) {
|
||||
const {
|
||||
id,
|
||||
zIndex,
|
||||
left = 0,
|
||||
top = 0,
|
||||
width,
|
||||
height,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
title,
|
||||
description,
|
||||
document: documentData,
|
||||
} = pageElement;
|
||||
if (documentData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const docDataModel = new DocumentDataModel(documentData);
|
||||
const docViewModel = new DocumentViewModel(docDataModel);
|
||||
const documentSkeleton = DocumentSkeleton.create(docViewModel, this._localeService);
|
||||
|
||||
const documents = new Documents(DOCS_VIEW_KEY.MAIN, documentSkeleton);
|
||||
|
||||
documentSkeleton.calculate();
|
||||
|
||||
const sv = new SceneViewer(DOCS_VIEW_KEY.SCENE_VIEWER + id, {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height,
|
||||
zIndex,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
});
|
||||
const scene = new Scene(DOCS_VIEW_KEY.SCENE + id, sv);
|
||||
|
||||
const viewMain = new Viewport(DOCS_VIEW_KEY.VIEWPORT + id, scene, {
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
explicitViewportWidthSet: false,
|
||||
explicitViewportHeightSet: false,
|
||||
isWheelPreventDefaultX: true,
|
||||
});
|
||||
|
||||
scene.attachControl();
|
||||
|
||||
scene.onMouseWheel$.subscribeEvent((evt: unknown, state: EventState) => {
|
||||
const e = evt as IWheelEvent;
|
||||
if (e.ctrlKey) {
|
||||
const deltaFactor = Math.abs(e.deltaX);
|
||||
let scrollNum = deltaFactor < 40 ? 0.2 : deltaFactor < 80 ? 0.4 : 0.2;
|
||||
scrollNum *= e.deltaY > 0 ? -1 : 1;
|
||||
if (scene.scaleX < 1) {
|
||||
scrollNum /= 2;
|
||||
}
|
||||
|
||||
if (scene.scaleX + scrollNum > 4) {
|
||||
scene.scale(4, 4);
|
||||
} else if (scene.scaleX + scrollNum < 0.1) {
|
||||
scene.scale(0.1, 0.1);
|
||||
} else {
|
||||
const value = e.deltaY > 0 ? 0.1 : -0.1;
|
||||
// scene.scaleBy(scrollNum, scrollNum);
|
||||
e.preventDefault();
|
||||
}
|
||||
} else {
|
||||
viewMain.onMouseWheel(e, state);
|
||||
}
|
||||
});
|
||||
|
||||
const scrollbar = new ScrollBar(viewMain, {
|
||||
mainScene,
|
||||
});
|
||||
|
||||
scene.addObject(documents);
|
||||
|
||||
// documents.calculatePagePosition();
|
||||
|
||||
// documents.enableEditor();
|
||||
|
||||
const size = documentSkeleton.getActualSize();
|
||||
|
||||
documents.resize(size.actualWidth, size.actualHeight);
|
||||
|
||||
scene.transformByState({ width: size.actualWidth, height: size.actualHeight + 200 });
|
||||
|
||||
const pageSize = documents.getSkeleton()?.getPageSize();
|
||||
|
||||
documents.pageRender$.subscribe((config: IPageRenderConfig) => {
|
||||
const { page, pageLeft, pageTop, ctx } = config;
|
||||
const { width, height, marginBottom, marginLeft, marginRight, marginTop } = page;
|
||||
ctx.save();
|
||||
ctx.translate(pageLeft - 0.5, pageTop - 0.5);
|
||||
// Rect.drawWith(ctx, {
|
||||
// width: pageSize?.width || width,
|
||||
// height: pageSize?.height || height,
|
||||
// strokeWidth: 1,
|
||||
// stroke: 'rgba(198,198,198, 1)',
|
||||
// fill: 'rgba(255,255,255, 1)',
|
||||
// zIndex: 3,
|
||||
// });
|
||||
ctx.restore();
|
||||
});
|
||||
|
||||
const { left: docsLeft, top: docsTop } = documents;
|
||||
|
||||
const skeletonData = documentSkeleton.getSkeletonData();
|
||||
|
||||
if (skeletonData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { pages } = skeletonData;
|
||||
const objectList: BaseObject[] = [];
|
||||
const pageMarginCache = new Map<string, { marginLeft: number; marginTop: number }>();
|
||||
|
||||
this._recalculateSizeBySkeleton(documents, scene, documentSkeleton);
|
||||
|
||||
for (let i = 0, len = pages.length; i < len; i++) {
|
||||
const page = pages[i];
|
||||
const { skeDrawings, marginLeft, marginTop, pageWidth, pageHeight } = page;
|
||||
|
||||
// cumPageLeft + = pageWidth + documents.pageMarginLeft;
|
||||
|
||||
this._liquid.translatePagePadding(page);
|
||||
|
||||
skeDrawings.forEach((drawing: IDocumentSkeletonDrawing) => {
|
||||
const { aLeft, aTop, height, width, drawingOrigin } = drawing;
|
||||
|
||||
const { docTransform } = drawingOrigin;
|
||||
|
||||
const rect = new Image(drawing.drawingId, {
|
||||
// url: docTransform.imageProperties?.contentUrl || '',
|
||||
left: aLeft + docsLeft + this._liquid.x,
|
||||
top: aTop + docsTop + this._liquid.y,
|
||||
width,
|
||||
height,
|
||||
zIndex: 11,
|
||||
});
|
||||
|
||||
pageMarginCache.set(drawing.drawingId, {
|
||||
marginLeft: this._liquid.x,
|
||||
marginTop: this._liquid.y,
|
||||
});
|
||||
|
||||
objectList.push(rect);
|
||||
});
|
||||
|
||||
this._liquid.translatePage(
|
||||
page,
|
||||
documents.pageLayoutType,
|
||||
documents.pageMarginLeft,
|
||||
documents.pageMarginTop
|
||||
);
|
||||
}
|
||||
|
||||
scene.addObjects(objectList);
|
||||
objectList.forEach((object) => {
|
||||
scene.attachTransformerTo(object);
|
||||
});
|
||||
|
||||
scene.getTransformer()?.changing$.subscribe((state) => {
|
||||
const { objects } = state;
|
||||
|
||||
objects.forEach((object) => {
|
||||
const { oKey, left, top, height, width } = object;
|
||||
const cache = pageMarginCache.get(oKey);
|
||||
const marginLeft = cache?.marginLeft || 0;
|
||||
const marginTop = cache?.marginTop || 0;
|
||||
|
||||
// console.log('onChangingObservable', top, docsTop, marginTop, top - docsTop - marginTop);
|
||||
|
||||
documentSkeleton
|
||||
?.getViewModel()
|
||||
.getDataModel()
|
||||
.updateDrawing(oKey, {
|
||||
left: left - docsLeft - marginLeft,
|
||||
top: top - docsTop - marginTop,
|
||||
height,
|
||||
width,
|
||||
});
|
||||
});
|
||||
|
||||
documentSkeleton?.calculate();
|
||||
});
|
||||
this._calculatePagePosition(documents, scene, viewMain);
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
private _recalculateSizeBySkeleton(docsComponent: Documents, scene: Scene, skeleton: DocumentSkeleton) {
|
||||
const pages = skeleton.getSkeletonData()?.pages;
|
||||
|
||||
if (pages == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
|
||||
for (let i = 0, len = pages.length; i < len; i++) {
|
||||
const page = pages[i];
|
||||
const { pageWidth, pageHeight } = page;
|
||||
|
||||
if (docsComponent.pageLayoutType === PageLayoutType.VERTICAL) {
|
||||
height += pageHeight;
|
||||
|
||||
height += docsComponent.pageMarginTop;
|
||||
|
||||
if (i === len - 1) {
|
||||
height += docsComponent.pageMarginTop;
|
||||
}
|
||||
|
||||
width = Math.max(width, pageWidth);
|
||||
} else if (docsComponent.pageLayoutType === PageLayoutType.HORIZONTAL) {
|
||||
width += pageWidth;
|
||||
|
||||
if (i !== len - 1) {
|
||||
width += docsComponent.pageMarginLeft;
|
||||
}
|
||||
height = Math.max(height, pageHeight);
|
||||
}
|
||||
}
|
||||
|
||||
docsComponent.resize(width, height);
|
||||
|
||||
scene.transformByState({ width, height });
|
||||
}
|
||||
|
||||
private _calculatePagePosition(docsComponent: Documents, scene: Scene, viewport: Viewport, zoomRatio: number = 1) {
|
||||
const parent = scene?.getParent();
|
||||
|
||||
const { width: docsWidth, height: docsHeight, pageMarginLeft, pageMarginTop } = docsComponent;
|
||||
if (parent == null || docsWidth === Number.POSITIVE_INFINITY || docsHeight === Number.POSITIVE_INFINITY) {
|
||||
return;
|
||||
}
|
||||
const { width: engineWidth, height: engineHeight } = parent;
|
||||
let docsLeft = 0;
|
||||
let docsTop = 0;
|
||||
|
||||
let sceneWidth = 0;
|
||||
|
||||
let sceneHeight = 0;
|
||||
|
||||
let scrollToX = Number.POSITIVE_INFINITY;
|
||||
|
||||
if (engineWidth > (docsWidth + pageMarginLeft * 2) * zoomRatio) {
|
||||
docsLeft = engineWidth / 2 - (docsWidth * zoomRatio) / 2;
|
||||
docsLeft /= zoomRatio;
|
||||
sceneWidth = (engineWidth - pageMarginLeft * 2) / zoomRatio;
|
||||
|
||||
scrollToX = 0;
|
||||
} else {
|
||||
docsLeft = pageMarginLeft;
|
||||
sceneWidth = docsWidth + pageMarginLeft * 2;
|
||||
|
||||
scrollToX = (sceneWidth - engineWidth / zoomRatio) / 2;
|
||||
}
|
||||
|
||||
if (engineHeight > docsHeight) {
|
||||
docsTop = engineHeight / 2 - docsHeight / 2;
|
||||
sceneHeight = (engineHeight - pageMarginTop * 2) / zoomRatio;
|
||||
} else {
|
||||
docsTop = pageMarginTop;
|
||||
sceneHeight = docsHeight + pageMarginTop * 2;
|
||||
}
|
||||
|
||||
// this.docsLeft = docsLeft;
|
||||
|
||||
// this.docsTop = docsTop;
|
||||
|
||||
scene.transformByState({ width: sceneWidth, height: sceneHeight + 200 });
|
||||
|
||||
docsComponent.translate(docsLeft, docsTop);
|
||||
|
||||
if (scrollToX !== Number.POSITIVE_INFINITY && viewport != null) {
|
||||
const actualX = viewport.transScroll2ViewportScrollValue(scrollToX, 0).x;
|
||||
viewport.scrollToBarPos({
|
||||
x: actualX,
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class DocsAdaptorFactory {
|
||||
readonly zIndex = 5;
|
||||
|
||||
create(injector: Injector): DocsAdaptor {
|
||||
const docsAdaptor = injector.createInstance(DocsAdaptor);
|
||||
return docsAdaptor;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasObjectProviderRegistry.add(new DocsAdaptorFactory());
|
||||
86
packages/slides/src/views/render/adaptors/image-adaptor.ts
Normal file
86
packages/slides/src/views/render/adaptors/image-adaptor.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Injector } from '@univerjs/core';
|
||||
import type { IPageElement } from '../../../types/interfaces/i-slide-data';
|
||||
import { Image } from '@univerjs/engine-render';
|
||||
import { PageElementType } from '../../../types/interfaces/i-slide-data';
|
||||
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../adaptor';
|
||||
|
||||
export class ImageAdaptor extends ObjectAdaptor {
|
||||
override zIndex = 1;
|
||||
|
||||
override viewKey = PageElementType.IMAGE;
|
||||
|
||||
override check(type: PageElementType) {
|
||||
if (type !== this.viewKey) {
|
||||
return;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
override convert(pageElement: IPageElement) {
|
||||
const {
|
||||
id,
|
||||
zIndex,
|
||||
left = 0,
|
||||
top = 0,
|
||||
width,
|
||||
height,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
title,
|
||||
description,
|
||||
image = {},
|
||||
} = pageElement;
|
||||
const { imageProperties, placeholder, link } = image;
|
||||
|
||||
const contentUrl = imageProperties?.contentUrl || '';
|
||||
|
||||
return new Image(id, {
|
||||
url: contentUrl,
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height,
|
||||
zIndex,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
forceRender: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ImageAdaptorFactory {
|
||||
readonly zIndex = 4;
|
||||
|
||||
create(injector: Injector): ImageAdaptor {
|
||||
const imageAdaptor = injector.createInstance(ImageAdaptor);
|
||||
return imageAdaptor;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasObjectProviderRegistry.add(new ImageAdaptorFactory());
|
||||
22
packages/slides/src/views/render/adaptors/index.ts
Normal file
22
packages/slides/src/views/render/adaptors/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './docs-adaptor';
|
||||
export * from './image-adaptor';
|
||||
export * from './rich-text-adaptor';
|
||||
export * from './shape-adaptor';
|
||||
export * from './slide-adaptor';
|
||||
export * from './spreadsheet-adaptor';
|
||||
101
packages/slides/src/views/render/adaptors/rich-text-adaptor.ts
Normal file
101
packages/slides/src/views/render/adaptors/rich-text-adaptor.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Injector } from '@univerjs/core';
|
||||
import type { IRichTextProps, Scene } from '@univerjs/engine-render';
|
||||
import type { IPageElement } from '../../../types/interfaces/i-slide-data';
|
||||
import { Inject, LocaleService } from '@univerjs/core';
|
||||
import { RichText } from '@univerjs/engine-render';
|
||||
import { PageElementType } from '../../../types/interfaces/i-slide-data';
|
||||
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../adaptor';
|
||||
|
||||
export class RichTextAdaptor extends ObjectAdaptor {
|
||||
override zIndex = 2;
|
||||
|
||||
override viewKey = PageElementType.TEXT;
|
||||
|
||||
constructor(@Inject(LocaleService) private readonly _localeService: LocaleService) {
|
||||
super();
|
||||
}
|
||||
|
||||
override check(type: PageElementType) {
|
||||
if (type !== this.viewKey) {
|
||||
return;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
override convert(pageElement: IPageElement, _mainScene: Scene) {
|
||||
const {
|
||||
id,
|
||||
zIndex,
|
||||
left = 0,
|
||||
top = 0,
|
||||
width,
|
||||
height,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
title,
|
||||
description,
|
||||
richText = {},
|
||||
} = pageElement;
|
||||
const { text, ff, fs, it, bl, ul, st, ol, bg, bd, cl, rich } = richText;
|
||||
let config: IRichTextProps = {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height,
|
||||
zIndex,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
forceRender: true,
|
||||
};
|
||||
let isNotNull = false;
|
||||
if (text != null) {
|
||||
config = { ...config, text, ff, fs, it, bl, ul, st, ol, bg, bd, cl };
|
||||
isNotNull = true;
|
||||
} else if (rich != null) {
|
||||
config = { ...config, richText: rich };
|
||||
isNotNull = true;
|
||||
}
|
||||
|
||||
if (isNotNull === false) {
|
||||
return;
|
||||
}
|
||||
return new RichText(this._localeService, id, config);
|
||||
}
|
||||
}
|
||||
|
||||
export class RichTextAdaptorFactory {
|
||||
readonly zIndex = 0;
|
||||
|
||||
create(injector: Injector): RichTextAdaptor {
|
||||
const richTextAdaptor = injector.createInstance(RichTextAdaptor);
|
||||
return richTextAdaptor;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasObjectProviderRegistry.add(new RichTextAdaptorFactory());
|
||||
143
packages/slides/src/views/render/adaptors/shape-adaptor.ts
Normal file
143
packages/slides/src/views/render/adaptors/shape-adaptor.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Injector } from '@univerjs/core';
|
||||
import type { IPageElement } from '../../../types/interfaces/i-slide-data';
|
||||
import { getColorStyle } from '@univerjs/core';
|
||||
import { Circle, Rect } from '@univerjs/engine-render';
|
||||
import { BasicShapes } from '../../../types/enum/prst-geom-type';
|
||||
import { PageElementType } from '../../../types/interfaces/i-slide-data';
|
||||
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../adaptor';
|
||||
|
||||
export class ShapeAdaptor extends ObjectAdaptor {
|
||||
override zIndex = 2;
|
||||
|
||||
override viewKey = PageElementType.SHAPE;
|
||||
|
||||
override check(type: PageElementType) {
|
||||
if (type !== this.viewKey) {
|
||||
return;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
override convert(pageElement: IPageElement) {
|
||||
const {
|
||||
id,
|
||||
zIndex,
|
||||
left = 0,
|
||||
top = 0,
|
||||
width,
|
||||
height,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
title,
|
||||
description,
|
||||
} = pageElement;
|
||||
const { shapeType, text, shapeProperties, placeholder, link } = pageElement.shape || {};
|
||||
|
||||
const fill =
|
||||
shapeProperties == null ? '' : getColorStyle(shapeProperties.shapeBackgroundFill) || 'rgba(255,255,255,1)';
|
||||
|
||||
const outline = shapeProperties?.outline;
|
||||
const strokeStyle: { [key: string]: string | number } = {};
|
||||
if (outline) {
|
||||
const { outlineFill, weight } = outline;
|
||||
|
||||
strokeStyle.strokeWidth = weight;
|
||||
strokeStyle.stroke = getColorStyle(outlineFill) || 'rgba(0,0,0,1)';
|
||||
}
|
||||
|
||||
if (shapeType === BasicShapes.Rect) {
|
||||
return new Rect(id, {
|
||||
fill,
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height,
|
||||
zIndex,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
forceRender: true,
|
||||
...strokeStyle,
|
||||
});
|
||||
}
|
||||
if (shapeType === BasicShapes.RoundRect) {
|
||||
const radius = shapeProperties?.radius || 0;
|
||||
return new Rect(id, {
|
||||
fill,
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height,
|
||||
zIndex,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
forceRender: true,
|
||||
radius,
|
||||
...strokeStyle,
|
||||
});
|
||||
}
|
||||
if (shapeType === BasicShapes.Ellipse) {
|
||||
console.warn(shapeProperties?.radius);
|
||||
const radius = shapeProperties?.radius || 0;
|
||||
return new Circle(id, {
|
||||
fill,
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height,
|
||||
zIndex,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
forceRender: true,
|
||||
radius,
|
||||
...strokeStyle,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ShapeAdaptorFactory {
|
||||
readonly zIndex = 2;
|
||||
|
||||
create(injector: Injector): ShapeAdaptor {
|
||||
const shapeAdaptor = injector.createInstance(ShapeAdaptor);
|
||||
return shapeAdaptor;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasObjectProviderRegistry.add(new ShapeAdaptorFactory());
|
||||
183
packages/slides/src/views/render/adaptors/slide-adaptor.ts
Normal file
183
packages/slides/src/views/render/adaptors/slide-adaptor.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IColorStyle } from '@univerjs/core';
|
||||
import type { Engine } from '@univerjs/engine-render';
|
||||
import type { IPageElement, ISlidePage } from '../../../types/interfaces/i-slide-data';
|
||||
import { getColorStyle, Inject, Injector } from '@univerjs/core';
|
||||
import { Rect, Scene, Slide, Viewport } from '@univerjs/engine-render';
|
||||
import { SlideDataModel } from '../../../data-model/slide-data-model';
|
||||
import { PageElementType } from '../../../types/interfaces/i-slide-data';
|
||||
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../adaptor';
|
||||
import { ObjectProvider } from '../object-provider';
|
||||
|
||||
// import { DocsAdaptor, ImageAdaptor, RichTextAdaptor, ShapeAdaptor } from './';
|
||||
|
||||
export enum SLIDE_VIEW_KEY {
|
||||
MAIN = '__SLIDERender__',
|
||||
SCENE_VIEWER = '__SLIDEViewer__',
|
||||
SCENE = '__SLIDEScene__',
|
||||
VIEWPORT = '__SLIDEViewPort_',
|
||||
}
|
||||
|
||||
export class SlideAdaptor extends ObjectAdaptor {
|
||||
override zIndex = 6;
|
||||
|
||||
override viewKey = PageElementType.SLIDE;
|
||||
|
||||
private _ObjectProvider: ObjectProvider | null = null;
|
||||
|
||||
constructor(@Inject(Injector) private _injector: Injector) {
|
||||
super();
|
||||
}
|
||||
|
||||
override check(type: PageElementType) {
|
||||
if (type !== this.viewKey) {
|
||||
return;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
override convert(pageElement: IPageElement, mainScene: Scene) {
|
||||
const {
|
||||
id,
|
||||
zIndex,
|
||||
left = 0,
|
||||
top = 0,
|
||||
width,
|
||||
height,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
title,
|
||||
description,
|
||||
slide: slideData,
|
||||
} = pageElement;
|
||||
if (slideData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = new SlideDataModel(slideData);
|
||||
|
||||
const slideComponent = new Slide(SLIDE_VIEW_KEY.MAIN + id, {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height,
|
||||
zIndex,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
forceRender: true,
|
||||
});
|
||||
|
||||
slideComponent.enableNav();
|
||||
|
||||
slideComponent.enableSelectedClipElement();
|
||||
|
||||
const pageOrder = model.getPageOrder();
|
||||
|
||||
const pages = model.getPages();
|
||||
|
||||
if (!pageOrder || !pages) {
|
||||
return slideComponent;
|
||||
}
|
||||
|
||||
this._ObjectProvider = new ObjectProvider(this._injector);
|
||||
|
||||
for (let i = 0, len = pageOrder.length; i < len; i++) {
|
||||
const page = pages[pageOrder[i]];
|
||||
const { id } = page;
|
||||
slideComponent.addPageScene(this._createScene(id, slideComponent, page, mainScene, model));
|
||||
}
|
||||
|
||||
slideComponent.activeFirstPage();
|
||||
|
||||
return slideComponent;
|
||||
}
|
||||
|
||||
private _createScene(pageId: string, parent: Engine | Slide, page: ISlidePage, mainScene: Scene, model: SlideDataModel) {
|
||||
const { width, height } = parent;
|
||||
|
||||
const scene = new Scene(pageId, parent, {
|
||||
width,
|
||||
height,
|
||||
});
|
||||
|
||||
const viewMain = new Viewport(`PageViewer_${pageId}`, scene, {
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
explicitViewportWidthSet: false,
|
||||
explicitViewportHeightSet: false,
|
||||
});
|
||||
|
||||
viewMain.closeClip();
|
||||
|
||||
const { pageElements, pageBackgroundFill } = page;
|
||||
|
||||
const objects = this._ObjectProvider?.convertToRenderObjects(pageElements, mainScene);
|
||||
|
||||
this._addBackgroundRect(scene, pageBackgroundFill, model);
|
||||
|
||||
scene.addObjects(objects!);
|
||||
|
||||
objects?.forEach((object) => {
|
||||
scene.attachTransformerTo(object);
|
||||
});
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
private _addBackgroundRect(scene: Scene, fill: IColorStyle, model: SlideDataModel) {
|
||||
const pageSize = model.getPageSize();
|
||||
|
||||
const { width: pageWidth = 0, height: pageHeight = 0 } = pageSize;
|
||||
|
||||
const page = new Rect('canvas', {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: pageWidth,
|
||||
height: pageHeight,
|
||||
strokeWidth: 1,
|
||||
stroke: 'rgba(198,198,198, 1)',
|
||||
fill: getColorStyle(fill) || 'rgba(255,255,255, 1)',
|
||||
zIndex: 0,
|
||||
evented: false,
|
||||
});
|
||||
scene.addObject(page, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export class SlideAdaptorFactory {
|
||||
readonly zIndex = 6;
|
||||
|
||||
create(injector: Injector): SlideAdaptor {
|
||||
const slideAdaptor = injector.createInstance(SlideAdaptor);
|
||||
return slideAdaptor;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasObjectProviderRegistry.add(new SlideAdaptorFactory());
|
||||
255
packages/slides/src/views/render/adaptors/spreadsheet-adaptor.ts
Normal file
255
packages/slides/src/views/render/adaptors/spreadsheet-adaptor.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { EventState } from '@univerjs/core';
|
||||
import type { IScrollObserverParam, IWheelEvent } from '@univerjs/engine-render';
|
||||
import type { IPageElement } from '../../../types/interfaces/i-slide-data';
|
||||
import { IConfigService, IContextService, Inject, Injector, LocaleService, Styles, Worksheet } from '@univerjs/core';
|
||||
import {
|
||||
getColor,
|
||||
Rect,
|
||||
Scene,
|
||||
SceneViewer,
|
||||
ScrollBar,
|
||||
Spreadsheet,
|
||||
SpreadsheetColumnHeader,
|
||||
SpreadsheetRowHeader,
|
||||
SpreadsheetSkeleton,
|
||||
Viewport,
|
||||
} from '@univerjs/engine-render';
|
||||
import { PageElementType } from '../../../types/interfaces/i-slide-data';
|
||||
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../adaptor';
|
||||
|
||||
enum SHEET_VIEW_KEY {
|
||||
MAIN = 'spreadInSlide',
|
||||
SCENE = 'spreadInSlideScene',
|
||||
SCENE_VIEWER = 'spreadInSlideSceneViewer',
|
||||
ROW = 'spreadInSlideRow',
|
||||
COLUMN = 'spreadInSlideColumn',
|
||||
LEFT_TOP = 'spreadInSlideLeftTop',
|
||||
VIEW_MAIN = 'spreadInSlideViewMain',
|
||||
VIEW_TOP = 'spreadInSlideViewTop',
|
||||
VIEW_LEFT = 'spreadInSlideViewLeft',
|
||||
VIEW_LEFT_TOP = 'spreadInSlideViewLeftTop',
|
||||
}
|
||||
|
||||
export class SpreadsheetAdaptor extends ObjectAdaptor {
|
||||
override zIndex = 4;
|
||||
|
||||
override viewKey = PageElementType.SPREADSHEET;
|
||||
|
||||
constructor(
|
||||
@Inject(LocaleService) private readonly _localeService: LocaleService,
|
||||
@IContextService private readonly _contextService: IContextService,
|
||||
@IConfigService private readonly _configService: IConfigService,
|
||||
@Inject(Injector) private readonly _injector: Injector
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
override check(type: PageElementType) {
|
||||
if (type !== this.viewKey) {
|
||||
return;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
override convert(pageElement: IPageElement, mainScene: Scene) {
|
||||
const {
|
||||
id,
|
||||
zIndex,
|
||||
left = 0,
|
||||
top = 0,
|
||||
width,
|
||||
height,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
spreadsheet: spreadsheetModel,
|
||||
} = pageElement;
|
||||
|
||||
if (spreadsheetModel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { worksheet, styles } = spreadsheetModel;
|
||||
|
||||
const styleModel = new Styles(styles);
|
||||
const spreadsheetSkeleton = new SpreadsheetSkeleton(
|
||||
new Worksheet(id, worksheet, styleModel), // FIXME: worksheet in slide doesn't has a Worksheet object
|
||||
styleModel,
|
||||
this._localeService,
|
||||
this._contextService,
|
||||
this._configService,
|
||||
this._injector
|
||||
);
|
||||
|
||||
const { rowTotalHeight, columnTotalWidth, rowHeaderWidth, columnHeaderHeight } = spreadsheetSkeleton;
|
||||
const allWidth = columnTotalWidth + worksheet.rowHeader.width || 0;
|
||||
const allHeight = rowTotalHeight + worksheet.columnHeader.height || 0;
|
||||
|
||||
const sv = new SceneViewer(SHEET_VIEW_KEY.SCENE_VIEWER + id, {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height,
|
||||
zIndex,
|
||||
angle,
|
||||
scaleX,
|
||||
scaleY,
|
||||
skewX,
|
||||
skewY,
|
||||
flipX,
|
||||
flipY,
|
||||
forceRender: true,
|
||||
});
|
||||
const scene = new Scene(SHEET_VIEW_KEY.SCENE + id, sv, {
|
||||
width: allWidth,
|
||||
height: allHeight,
|
||||
});
|
||||
spreadsheetSkeleton.setScene(scene);
|
||||
scene.disposeWithMe(spreadsheetSkeleton);
|
||||
|
||||
this._updateViewport(id, rowHeaderWidth, columnHeaderHeight, scene, mainScene);
|
||||
const spreadsheet = new Spreadsheet('testSheetViewer', spreadsheetSkeleton, false);
|
||||
const spreadsheetRowHeader = new SpreadsheetRowHeader(SHEET_VIEW_KEY.ROW, spreadsheetSkeleton);
|
||||
const spreadsheetColumnHeader = new SpreadsheetColumnHeader(SHEET_VIEW_KEY.COLUMN, spreadsheetSkeleton);
|
||||
const SpreadsheetLeftTopPlaceholder = new Rect(SHEET_VIEW_KEY.LEFT_TOP, {
|
||||
zIndex: 2,
|
||||
left: -1,
|
||||
top: -1,
|
||||
width: rowHeaderWidth,
|
||||
height: columnHeaderHeight,
|
||||
fill: getColor([248, 249, 250]),
|
||||
stroke: getColor([217, 217, 217]),
|
||||
strokeWidth: 1,
|
||||
});
|
||||
|
||||
spreadsheet.zIndex = 10;
|
||||
scene.addObjects([spreadsheet], 1);
|
||||
scene.addObjects([spreadsheetRowHeader, spreadsheetColumnHeader, SpreadsheetLeftTopPlaceholder], 2);
|
||||
return sv;
|
||||
}
|
||||
|
||||
private _updateViewport(
|
||||
id: string,
|
||||
rowHeaderWidth: number,
|
||||
columnHeaderHeight: number,
|
||||
scene: Scene,
|
||||
mainScene: Scene
|
||||
) {
|
||||
if (mainScene == null) {
|
||||
return;
|
||||
}
|
||||
const rowHeaderWidthScale = rowHeaderWidth * scene.scaleX;
|
||||
const columnHeaderHeightScale = columnHeaderHeight * scene.scaleY;
|
||||
|
||||
const viewMain = new Viewport(SHEET_VIEW_KEY.VIEW_MAIN + id, scene, {
|
||||
left: rowHeaderWidthScale,
|
||||
top: columnHeaderHeightScale,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
explicitViewportWidthSet: false,
|
||||
explicitViewportHeightSet: false,
|
||||
isWheelPreventDefaultX: true,
|
||||
});
|
||||
const viewTop = new Viewport(SHEET_VIEW_KEY.VIEW_TOP + id, scene, {
|
||||
left: rowHeaderWidthScale,
|
||||
height: columnHeaderHeightScale,
|
||||
top: 0,
|
||||
right: 0,
|
||||
explicitViewportWidthSet: false,
|
||||
isWheelPreventDefaultX: true,
|
||||
});
|
||||
const viewLeft = new Viewport(SHEET_VIEW_KEY.VIEW_LEFT + id, scene, {
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
top: columnHeaderHeightScale,
|
||||
width: rowHeaderWidthScale,
|
||||
explicitViewportHeightSet: false,
|
||||
isWheelPreventDefaultX: true,
|
||||
});
|
||||
|
||||
const _viewLeftTop = new Viewport(SHEET_VIEW_KEY.VIEW_LEFT_TOP + id, scene, {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: rowHeaderWidthScale,
|
||||
height: columnHeaderHeightScale,
|
||||
isWheelPreventDefaultX: true,
|
||||
});
|
||||
viewMain.onScrollAfter$.subscribeEvent((param: IScrollObserverParam) => {
|
||||
const { scrollX, scrollY, viewportScrollX, viewportScrollY } = param;
|
||||
|
||||
viewTop
|
||||
.updateScrollVal({
|
||||
scrollX,
|
||||
viewportScrollX,
|
||||
});
|
||||
|
||||
viewLeft
|
||||
.updateScrollVal({
|
||||
scrollY,
|
||||
viewportScrollY,
|
||||
});
|
||||
});
|
||||
|
||||
scene.attachControl();
|
||||
const _scrollbar = new ScrollBar(viewMain, {
|
||||
mainScene,
|
||||
});
|
||||
this._bindWheel(scene, viewMain);
|
||||
}
|
||||
|
||||
private _bindWheel(scene: Scene, viewMain: Viewport): void {
|
||||
scene.onMouseWheel$.subscribeEvent((evt: unknown, state: EventState) => {
|
||||
const e = evt as IWheelEvent;
|
||||
if (e.ctrlKey) {
|
||||
const deltaFactor = Math.abs(e.deltaX);
|
||||
let scrollNum = deltaFactor < 40 ? 0.05 : deltaFactor < 80 ? 0.02 : 0.01;
|
||||
scrollNum *= e.deltaY > 0 ? -1 : 1;
|
||||
if (scene.scaleX < 1) {
|
||||
scrollNum /= 2;
|
||||
}
|
||||
|
||||
if (scene.scaleX + scrollNum > 4) {
|
||||
scene.scale(4, 4);
|
||||
} else if (scene.scaleX + scrollNum < 0.1) {
|
||||
scene.scale(0.1, 0.1);
|
||||
} else {
|
||||
scene.scaleBy(scrollNum, scrollNum);
|
||||
e.preventDefault();
|
||||
}
|
||||
} else {
|
||||
viewMain.onMouseWheel(e, state);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class SpreadsheetAdaptorFactory {
|
||||
readonly zIndex = 4;
|
||||
|
||||
create(injector: Injector): SpreadsheetAdaptor {
|
||||
const spreadsheetAdaptor = injector.createInstance(SpreadsheetAdaptor);
|
||||
return spreadsheetAdaptor;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasObjectProviderRegistry.add(new SpreadsheetAdaptorFactory());
|
||||
28
packages/slides/src/views/render/index.ts
Normal file
28
packages/slides/src/views/render/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// export * from '../../../../slides-ui/src/controllers/canvas-view';
|
||||
export * from './adaptor';
|
||||
|
||||
export { SLIDE_VIEW_KEY } from './adaptors';
|
||||
export * from './object-provider';
|
||||
|
||||
export enum SLIDE_KEY {
|
||||
COMPONENT = '__slideRender__',
|
||||
SCENE = '__mainScene__',
|
||||
VIEW = '__mainView__',
|
||||
}
|
||||
export type PageID = string;
|
||||
66
packages/slides/src/views/render/object-provider.ts
Normal file
66
packages/slides/src/views/render/object-provider.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { BaseObject, Scene } from '@univerjs/engine-render';
|
||||
import type { IPageElement } from '../../types/interfaces/i-slide-data';
|
||||
import type { ObjectAdaptor } from './adaptor';
|
||||
import { Inject, Injector, sortRules } from '@univerjs/core';
|
||||
import { CanvasObjectProviderRegistry } from './adaptor';
|
||||
import './adaptors';
|
||||
|
||||
export class ObjectProvider {
|
||||
private _adaptors: ObjectAdaptor[] = [];
|
||||
|
||||
constructor(@Inject(Injector) private readonly _injector: Injector) {
|
||||
this._adaptorLoader();
|
||||
}
|
||||
|
||||
convertToRenderObjects(pageElements: { [elementId: string]: IPageElement }, mainScene: Scene) {
|
||||
const pageKeys = Object.keys(pageElements);
|
||||
const objects: BaseObject[] = [];
|
||||
pageKeys.forEach((key) => {
|
||||
const pageElement = pageElements[key];
|
||||
const o = this._executor(pageElement, mainScene);
|
||||
if (o != null) {
|
||||
objects.push(o);
|
||||
}
|
||||
});
|
||||
return objects;
|
||||
}
|
||||
|
||||
convertToRenderObject(pageElement: IPageElement, mainScene: Scene) {
|
||||
return this._executor(pageElement, mainScene);
|
||||
}
|
||||
|
||||
private _executor(pageElement: IPageElement, mainScene: Scene) {
|
||||
const { id: pageElementId, type } = pageElement;
|
||||
|
||||
for (const adaptor of this._adaptors) {
|
||||
const o = adaptor.check(type)?.convert(pageElement, mainScene);
|
||||
if (o != null) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _adaptorLoader() {
|
||||
CanvasObjectProviderRegistry.getData()
|
||||
.sort(sortRules)
|
||||
.forEach((adaptorFactory: ObjectAdaptor) => {
|
||||
this._adaptors.push(adaptorFactory.create(this._injector) as unknown as ObjectAdaptor);
|
||||
});
|
||||
}
|
||||
}
|
||||
8
packages/slides/tsconfig.json
Normal file
8
packages/slides/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@univerjs-infra/shared/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
18
packages/slides/tsconfig.node.json
Normal file
18
packages/slides/tsconfig.node.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "@univerjs-infra/shared/tsconfigs/node",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"noEmit": false,
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/__tests__/**",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts"
|
||||
]
|
||||
}
|
||||
3
packages/slides/vitest.config.ts
Normal file
3
packages/slides/vitest.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import createConfig from '@univerjs-infra/shared/vitest';
|
||||
|
||||
export default createConfig();
|
||||
Reference in New Issue
Block a user