presenter 0.1.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +39 -1
- package/dist/presenter.js +1 -1
- package/dist/src/index.d.ts +15 -0
- package/dist/src/objects/arrow.d.ts +36 -0
- package/dist/src/objects/group.d.ts +16 -0
- package/dist/src/objects/image.d.ts +11 -0
- package/dist/src/objects/line.d.ts +13 -0
- package/dist/src/objects/paragraph.d.ts +17 -0
- package/dist/src/objects/polygon.d.ts +13 -0
- package/dist/src/objects/rectangle.d.ts +14 -0
- package/dist/src/objects/text.d.ts +29 -0
- package/dist/src/objects/vectorGraphic.d.ts +14 -0
- package/dist/src/presentation/object.d.ts +122 -0
- package/dist/src/presentation/presentation.d.ts +99 -0
- package/dist/src/presentation/slide.d.ts +23 -0
- package/dist/src/util/animation.d.ts +37 -0
- package/dist/src/util/position.d.ts +11 -0
- package/dist/src/util/richText.d.ts +18 -0
- package/dist/webpack.config.d.ts +22 -0
- package/package.json +8 -5
- package/.husky/pre-commit +0 -1
- package/.prettierrc +0 -1
- package/examples/README.md +0 -23
- package/examples/simple/dist/index.html +0 -8
- package/examples/simple/package-lock.json +0 -3754
- package/examples/simple/package.json +0 -18
- package/examples/simple/src/index.ts +0 -20
- package/examples/simple/tsconfig.json +0 -10
- package/examples/simple/webpack.config.js +0 -25
- package/src/index.ts +0 -16
- package/src/objects/image.ts +0 -55
- package/src/objects/rectangle.ts +0 -53
- package/src/objects/text.ts +0 -79
- package/src/presentation/object.ts +0 -190
- package/src/presentation/presentation.ts +0 -299
- package/src/presentation/slide.ts +0 -52
- package/src/presentation/theme.ts +0 -3
- package/src/themes/default.ts +0 -7
- package/src/themes/index.ts +0 -1
- package/src/util/position.ts +0 -21
- package/tsconfig.json +0 -12
- package/webpack.config.js +0 -32
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
import DefaultTheme from "../themes/default";
|
|
2
|
-
import { BoundingBox } from "../util/position";
|
|
3
|
-
import { Slide } from "./slide";
|
|
4
|
-
import { Theme } from "./theme";
|
|
5
|
-
|
|
6
|
-
export interface PresentationOptions {
|
|
7
|
-
/**
|
|
8
|
-
* Width of the presentation.
|
|
9
|
-
*/
|
|
10
|
-
width: number;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Height of the presentation.
|
|
14
|
-
*/
|
|
15
|
-
height: number;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Presentation theme to use.
|
|
19
|
-
*/
|
|
20
|
-
theme: Theme;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface PresentationState {
|
|
24
|
-
currentSlide: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class Presentation {
|
|
28
|
-
/**
|
|
29
|
-
* Title of the presentation.
|
|
30
|
-
*/
|
|
31
|
-
title: string;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Element where presentation should be mounted.
|
|
35
|
-
*/
|
|
36
|
-
element: HTMLElement;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* SVG container element.
|
|
40
|
-
*/
|
|
41
|
-
container: HTMLElement;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Parent SVG element of presentation.
|
|
45
|
-
*/
|
|
46
|
-
svg: SVGSVGElement | null;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Shadow SVG element, not displayed and used for calculating sizes.
|
|
50
|
-
*/
|
|
51
|
-
shadow: SVGSVGElement | null;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Presentation settings.
|
|
55
|
-
*/
|
|
56
|
-
options: PresentationOptions;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Bounds of presentation.
|
|
60
|
-
*/
|
|
61
|
-
boundingBox: BoundingBox;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Presentation slides.
|
|
65
|
-
*/
|
|
66
|
-
slides: Slide[];
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Presentation state.
|
|
70
|
-
*/
|
|
71
|
-
presentationState: PresentationState;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
*
|
|
75
|
-
* @param title Title of the presentation.
|
|
76
|
-
*/
|
|
77
|
-
constructor(
|
|
78
|
-
title: string,
|
|
79
|
-
slides: Slide[],
|
|
80
|
-
element: HTMLElement,
|
|
81
|
-
options: Partial<PresentationOptions> = {},
|
|
82
|
-
) {
|
|
83
|
-
if (this.element === null) {
|
|
84
|
-
throw new Error("Presentation cannot be mounted to null element.");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
this.title = title;
|
|
88
|
-
this.element = element;
|
|
89
|
-
this.slides = slides;
|
|
90
|
-
this.options = {
|
|
91
|
-
width: 3840,
|
|
92
|
-
height: 2160,
|
|
93
|
-
theme: DefaultTheme,
|
|
94
|
-
...options,
|
|
95
|
-
};
|
|
96
|
-
this.presentationState = {
|
|
97
|
-
currentSlide: 0,
|
|
98
|
-
};
|
|
99
|
-
this.boundingBox = new BoundingBox(
|
|
100
|
-
{ x: 0, y: 0 },
|
|
101
|
-
this.options.width,
|
|
102
|
-
this.options.height,
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
this.container = null;
|
|
106
|
-
this.svg = null;
|
|
107
|
-
this.shadow = null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
present() {
|
|
111
|
-
// Create container element.
|
|
112
|
-
this.container = document.createElement("div");
|
|
113
|
-
this.container.style.width = "100%";
|
|
114
|
-
this.container.style.aspectRatio = `${this.options.width} / ${this.options.height}`;
|
|
115
|
-
|
|
116
|
-
// Set container to be vertically centered.
|
|
117
|
-
this.container.style.position = "relative";
|
|
118
|
-
this.container.style.top = "50%";
|
|
119
|
-
this.container.style.transform = "translateY(-50%)";
|
|
120
|
-
|
|
121
|
-
// Set container to be horizontally centered.
|
|
122
|
-
this.container.style.marginLeft = "auto";
|
|
123
|
-
this.container.style.marginRight = "auto";
|
|
124
|
-
|
|
125
|
-
// Create SVG element.
|
|
126
|
-
this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
127
|
-
this.svg.style.backgroundColor = this.options.theme.backgroundColor;
|
|
128
|
-
this.svg.style.cursor = "none";
|
|
129
|
-
|
|
130
|
-
// Set up keyboard commands.
|
|
131
|
-
const eventTarget = this.isFullBodyPresentation()
|
|
132
|
-
? document.body
|
|
133
|
-
: this.svg;
|
|
134
|
-
(eventTarget as HTMLElement).addEventListener("keyup", (event) => {
|
|
135
|
-
if (event.key === "ArrowRight" || event.key === " ") {
|
|
136
|
-
this.next();
|
|
137
|
-
} else if (event.key === "ArrowLeft") {
|
|
138
|
-
this.previous();
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Show cursor when the cursor moves.
|
|
143
|
-
eventTarget.addEventListener("mousemove", (event) => {
|
|
144
|
-
this.svg.style.cursor = "auto";
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// Create shadow element that's hidden from view.
|
|
148
|
-
this.shadow = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
149
|
-
this.shadow.style.visibility = "hidden";
|
|
150
|
-
|
|
151
|
-
[this.svg, this.shadow].forEach((svg) => {
|
|
152
|
-
svg.setAttribute("width", "100%");
|
|
153
|
-
svg.setAttribute(
|
|
154
|
-
"viewBox",
|
|
155
|
-
`0 0 ${this.options.width} ${this.options.height}`,
|
|
156
|
-
);
|
|
157
|
-
svg.style.position = "absolute";
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
if (this.isFullBodyPresentation()) {
|
|
161
|
-
// Set document title.
|
|
162
|
-
document.title = this.title;
|
|
163
|
-
|
|
164
|
-
// Set root element styles.
|
|
165
|
-
document.documentElement.style.height = "100%";
|
|
166
|
-
|
|
167
|
-
// Set body styles.
|
|
168
|
-
document.body.style.height = "100%";
|
|
169
|
-
document.body.style.width = "100%";
|
|
170
|
-
document.body.style.margin = "0";
|
|
171
|
-
document.body.style.backgroundColor = "#000000";
|
|
172
|
-
|
|
173
|
-
// Update SVG size when aspect ratio changes.
|
|
174
|
-
window
|
|
175
|
-
.matchMedia(
|
|
176
|
-
`(min-aspect-ratio: ${this.options.width} / ${this.options.height})`,
|
|
177
|
-
)
|
|
178
|
-
.addEventListener("change", this.updateSVGContainerSize.bind(this));
|
|
179
|
-
|
|
180
|
-
this.updateSVGContainerSize();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
this.container.appendChild(this.shadow);
|
|
184
|
-
this.container.appendChild(this.svg);
|
|
185
|
-
this.element.appendChild(this.container);
|
|
186
|
-
|
|
187
|
-
// Set up presentation state
|
|
188
|
-
this.presentationState.currentSlide = 0;
|
|
189
|
-
|
|
190
|
-
// Render slide
|
|
191
|
-
const currentSlide = this.slides[this.presentationState.currentSlide];
|
|
192
|
-
if (currentSlide === undefined) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
currentSlide.render(this);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Updates the size of the parent SVG element.
|
|
200
|
-
*
|
|
201
|
-
* The size of the parent SVG element needs to be updated.
|
|
202
|
-
*/
|
|
203
|
-
updateSVGContainerSize() {
|
|
204
|
-
if (
|
|
205
|
-
window.innerHeight / window.innerWidth >
|
|
206
|
-
this.options.height / this.options.width
|
|
207
|
-
) {
|
|
208
|
-
this.container.style.width = "100%";
|
|
209
|
-
this.container.style.height = "auto";
|
|
210
|
-
} else {
|
|
211
|
-
this.container.style.width = "auto";
|
|
212
|
-
this.container.style.height = "100%";
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Advances to next animation in slide, or next slide if there is no next animation.
|
|
218
|
-
* Returns true if we were able to successfully advance.
|
|
219
|
-
*/
|
|
220
|
-
next(): boolean {
|
|
221
|
-
this.svg.style.cursor = "none";
|
|
222
|
-
const currentSlide = this.slides[this.presentationState.currentSlide];
|
|
223
|
-
if (currentSlide === undefined) {
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (!currentSlide.nextAnimation(this)) {
|
|
228
|
-
this.presentationState.currentSlide++;
|
|
229
|
-
const nextSlide = this.slides[this.presentationState.currentSlide];
|
|
230
|
-
if (nextSlide === undefined) {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
nextSlide.render(this);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Goes back to the previous slide.
|
|
239
|
-
*/
|
|
240
|
-
previous(): boolean {
|
|
241
|
-
this.svg.style.cursor = "none";
|
|
242
|
-
|
|
243
|
-
// If we're past the end of the presentation, go to the last slide.
|
|
244
|
-
const currentSlide = this.slides[this.presentationState.currentSlide];
|
|
245
|
-
if (currentSlide === undefined) {
|
|
246
|
-
this.presentationState.currentSlide = this.slides.length - 1;
|
|
247
|
-
const lastSlide = this.slides[this.presentationState.currentSlide];
|
|
248
|
-
if (lastSlide === undefined) {
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
lastSlide.render(this);
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// If we're in the middle of a build, go back to the start of the build.
|
|
256
|
-
if (currentSlide.animationIndex > 0) {
|
|
257
|
-
currentSlide.animationIndex = 0;
|
|
258
|
-
currentSlide.render(this);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Otherwise, go back to the previous slide.
|
|
263
|
-
if (this.presentationState.currentSlide === 0) {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
this.presentationState.currentSlide--;
|
|
267
|
-
const previousSlide = this.slides[this.presentationState.currentSlide];
|
|
268
|
-
if (previousSlide === undefined) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
previousSlide.render(this);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Checks if the presentation takes the entire document body.
|
|
276
|
-
* @returns True if the presentation takes the entire document body.
|
|
277
|
-
*/
|
|
278
|
-
isFullBodyPresentation(): boolean {
|
|
279
|
-
return this.element === document.body;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
theme(): Theme {
|
|
283
|
-
return this.options.theme;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Computes the current size of an element in the DOM.
|
|
288
|
-
* @returns BoundingBox
|
|
289
|
-
*/
|
|
290
|
-
computeRenderedBoundingBox(element: SVGGraphicsElement): BoundingBox {
|
|
291
|
-
const clone = element.cloneNode(true) as SVGGraphicsElement;
|
|
292
|
-
|
|
293
|
-
this.shadow.appendChild(clone);
|
|
294
|
-
const boundingBox = BoundingBox.fromElement(clone);
|
|
295
|
-
this.shadow.removeChild(clone);
|
|
296
|
-
|
|
297
|
-
return boundingBox;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { SlideObject } from "./object";
|
|
2
|
-
import { Presentation } from "./presentation";
|
|
3
|
-
|
|
4
|
-
export interface SlideProps {}
|
|
5
|
-
|
|
6
|
-
export class Slide {
|
|
7
|
-
objects: SlideObject[];
|
|
8
|
-
|
|
9
|
-
animations: ((presentation: Presentation) => void)[];
|
|
10
|
-
|
|
11
|
-
animationIndex: number;
|
|
12
|
-
|
|
13
|
-
constructor(
|
|
14
|
-
objects: SlideObject[],
|
|
15
|
-
animations: ((presentation: Presentation) => void)[] = [],
|
|
16
|
-
) {
|
|
17
|
-
this.objects = objects.filter((object) => object !== null);
|
|
18
|
-
this.animations = animations;
|
|
19
|
-
this.animationIndex = 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
render(presentation: Presentation) {
|
|
23
|
-
// Clear SVG element
|
|
24
|
-
presentation.svg.innerHTML = "";
|
|
25
|
-
this.animationIndex = 0;
|
|
26
|
-
|
|
27
|
-
// Render objects
|
|
28
|
-
this.objects.forEach((object) => {
|
|
29
|
-
object._element = object.generate(presentation);
|
|
30
|
-
presentation.svg.appendChild(object.element());
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Runs next animation and returns true.
|
|
35
|
-
// If no more animations left to run, returns false.
|
|
36
|
-
nextAnimation(presentation: Presentation): boolean {
|
|
37
|
-
const animation = this.animations[this.animationIndex];
|
|
38
|
-
if (animation) {
|
|
39
|
-
animation(presentation);
|
|
40
|
-
this.animationIndex++;
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Sleep for a specified number of milliseconds in an animation.
|
|
48
|
-
*/
|
|
49
|
-
async sleep(ms: number) {
|
|
50
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
51
|
-
}
|
|
52
|
-
}
|
package/src/themes/default.ts
DELETED
package/src/themes/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as DefaultTheme } from "./default";
|
package/src/util/position.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export interface Position {
|
|
2
|
-
x: number;
|
|
3
|
-
y: number;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export class BoundingBox {
|
|
7
|
-
origin: Position;
|
|
8
|
-
width: number;
|
|
9
|
-
height: number;
|
|
10
|
-
|
|
11
|
-
constructor(origin: Position, width: number, height: number) {
|
|
12
|
-
this.origin = origin;
|
|
13
|
-
this.width = width;
|
|
14
|
-
this.height = height;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static fromElement(element: SVGGraphicsElement): BoundingBox {
|
|
18
|
-
const rect = element.getBBox();
|
|
19
|
-
return new BoundingBox({ x: rect.x, y: rect.y }, rect.width, rect.height);
|
|
20
|
-
}
|
|
21
|
-
}
|
package/tsconfig.json
DELETED
package/webpack.config.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
mode: process.env.NODE_ENV || "development",
|
|
5
|
-
entry: "./src/index.ts",
|
|
6
|
-
output: {
|
|
7
|
-
filename: "presenter.js",
|
|
8
|
-
path: path.resolve(__dirname, "dist"),
|
|
9
|
-
library: "Presenter",
|
|
10
|
-
libraryTarget: "umd",
|
|
11
|
-
globalObject: "this",
|
|
12
|
-
},
|
|
13
|
-
watchOptions: {
|
|
14
|
-
ignored: [
|
|
15
|
-
"**/node_modules",
|
|
16
|
-
path.resolve(__dirname, "dist"),
|
|
17
|
-
path.resolve(__dirname, "examples"),
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
resolve: {
|
|
21
|
-
extensions: [".ts", ".js"],
|
|
22
|
-
},
|
|
23
|
-
module: {
|
|
24
|
-
rules: [
|
|
25
|
-
{
|
|
26
|
-
test: /\.ts$/,
|
|
27
|
-
use: "ts-loader",
|
|
28
|
-
exclude: /node_modules/,
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
|
-
};
|