Skip to content

Commit f26521a

Browse files
committed
Add slide transition support and custom layout options
1 parent 4659ec9 commit f26521a

File tree

11 files changed

+206
-19
lines changed

11 files changed

+206
-19
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
## [0.0.87] - 2025-03-xx
44

5-
- [#74](https://github.com/estruyf/vscode-demo-time/issues/74): Support for custom slide layouts
5+
- [#74](https://github.com/estruyf/vscode-demo-time/issues/74): Support for custom slide layouts with the `customLayout` property
66
- [#75](https://github.com/estruyf/vscode-demo-time/issues/75): Provide a filename for the PDF export
7+
- [#76](https://github.com/estruyf/vscode-demo-time/issues/76): Added slide transition support with the `transition` property
78

89
## [0.0.86] - 2025-03-20
910

src/constants/SlideTransition.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export enum SlideTransition {
2+
fadeIn = "fadeIn",
3+
slideRight = "slideRight",
4+
slideLeft = "slideLeft",
5+
slideUp = "slideUp",
6+
slideDown = "slideDown",
7+
zoomIn = "zoomIn",
8+
zoomOut = "zoomOut",
9+
rotateIn = "rotateIn",
10+
rotateOut = "rotateOut",
11+
}

src/constants/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ export * from "./ContextKeys";
44
export * from "./General";
55
export * from "./SlideLayout";
66
export * from "./SlideTheme";
7+
export * from "./SlideTransition";
78
export * from "./StateKeys";
89
export * from "./WebViewMessages";

src/models/SlideMetadata.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { SlideLayout, SlideTheme, SlideTransition } from "../constants";
2+
3+
export interface SlideMetadata {
4+
theme?: SlideTheme;
5+
layout?: SlideLayout;
6+
transition?: SlideTransition;
7+
customTheme?: string;
8+
customLayout?: string;
9+
10+
[key: string]: any;
11+
}

src/models/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export * from "./CommandType";
33
export * from "./DemoFileCache";
44
export * from "./Demos";
55
export * from "./ShellSetting";
6+
export * from "./SlideMetadata";
67
export * from "./Subscription";
78
export * from "./Theme";

src/preview/components/Markdown.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import { transformImageUrl, twoColumnFormatting } from '../utils';
44
import rehypePrettyCode from 'rehype-pretty-code';
55
import { usePrevious } from '../hooks/usePrevious';
66
import { messageHandler } from '@estruyf/vscode/dist/client/webview';
7-
import { WebViewMessages } from '../../constants';
7+
import { SlideTransition, WebViewMessages } from '../../constants';
88
import { renderToString } from 'react-dom/server';
99
import { convertTemplateToHtml } from '../../utils/convertTemplateToHtml';
10+
import { SlideMetadata } from '../../models';
1011

1112
export interface IMarkdownProps {
1213
content?: string;
@@ -29,6 +30,7 @@ export const Markdown: React.FunctionComponent<IMarkdownProps> = ({
2930
const [isReady, setIsReady] = React.useState(false);
3031
const [customTheme, setCustomTheme] = React.useState<string | undefined>(undefined);
3132
const [customLayout, setCustomLayout] = React.useState<string | undefined>(undefined);
33+
const [transition, setTransition] = React.useState<SlideTransition | undefined>(undefined);
3234
const [template, setTemplate] = React.useState<string | undefined>(undefined);
3335

3436
const {
@@ -55,7 +57,7 @@ export const Markdown: React.FunctionComponent<IMarkdownProps> = ({
5557

5658
const prevMatter = usePrevious(JSON.stringify(matter));
5759

58-
const updateCustomLayout = React.useCallback((layout: string, metadata: any) => {
60+
const updateCustomLayout = React.useCallback((metadata: SlideMetadata, layout?: string) => {
5961
if (layout) {
6062
messageHandler.request<string>(WebViewMessages.toVscode.getFileContents, layout).then(async (templateHtml) => {
6163
if (templateHtml) {
@@ -78,7 +80,7 @@ export const Markdown: React.FunctionComponent<IMarkdownProps> = ({
7880
}
7981
}, [content, markdown]);
8082

81-
const updateCustomThemePath = React.useCallback((customThemePath: string) => {
83+
const updateCustomThemePath = React.useCallback((customThemePath?: string) => {
8284
if (!customThemePath) {
8385
setCustomTheme(undefined);
8486
return;
@@ -108,10 +110,11 @@ export const Markdown: React.FunctionComponent<IMarkdownProps> = ({
108110
setIsReady(false);
109111
setCustomLayout(undefined);
110112
setCustomTheme(undefined);
113+
setTransition(matter?.transition || undefined);
111114
setTemplate(undefined);
112115

113116
const cLayout = matter?.customLayout || undefined;
114-
updateCustomLayout(cLayout, matter);
117+
updateCustomLayout(matter, cLayout);
115118

116119
updateTheme(matter?.theme || "default");
117120
updateLayout(cLayout || matter?.layout || "default");
@@ -151,7 +154,7 @@ export const Markdown: React.FunctionComponent<IMarkdownProps> = ({
151154
<>
152155
{customTheme && <link href={customTheme} rel="stylesheet" />}
153156

154-
<div className='slide__content__custom' dangerouslySetInnerHTML={{ __html: template }} />
157+
<div className={`slide__content__custom ${transition}`} dangerouslySetInnerHTML={{ __html: template }} />
155158
</>
156159
);
157160
}
@@ -160,7 +163,7 @@ export const Markdown: React.FunctionComponent<IMarkdownProps> = ({
160163
<>
161164
{customTheme && <link href={customTheme} rel="stylesheet" />}
162165

163-
<div className='slide__content__inner'>{markdown}</div>
166+
<div className={`slide__content__inner ${transition}`}>{markdown}</div>
164167
</>
165168
);
166169
};

src/preview/components/MarkdownPreview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const MarkdownPreview: React.FunctionComponent<IMarkdownPreviewProps> = (
7070
<div
7171
key={crntFilePath}
7272
ref={ref}
73-
className={`slide fade-in ${theme || "default"} relative w-full h-full overflow-hidden`}
73+
className={`slide ${theme || "default"} relative w-full h-full overflow-hidden`}
7474
onMouseEnter={() => setShowControls(true)}
7575
onMouseLeave={() => setShowControls(false)}
7676
onMouseMove={handleMouseMove}

src/preview/hooks/useRemark.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { type ReactElement, useCallback, useState } from 'react';
44
import { type Options as RemarkParseOptions } from 'remark-parse';
55
import { type PluggableList } from 'unified';
66
import { transformMarkdown } from '../../utils/transformMarkdown';
7+
import { SlideMetadata } from '../../models';
78

89

910
export type UseRemarkOptions = {
@@ -28,12 +29,12 @@ export const useRemark = ({
2829
markdown: null | ReactElement,
2930
processMarkdown: (source: string, customPlugins?: PluggableList) => Promise<{
3031
reactContent: ReactElement | null,
31-
metadata: any | null,
32+
metadata: SlideMetadata | null,
3233
}>,
3334
setMarkdown: (source: string, customPlugins?: PluggableList) => void,
3435
getMarkdown: (contents: string) => string,
3536
getFrontMatter: (contents: string) => string,
36-
matter: null | any,
37+
matter: null | SlideMetadata,
3738
} => {
3839
const [reactContent, setReactContent] = useState<null | ReactElement>(null);
3940
const [metadata, setMetadata] = useState<null | any>(null);
@@ -51,7 +52,7 @@ export const useRemark = ({
5152
*/
5253
const processMarkdown = async (source: string, customPlugins?: PluggableList): Promise<{
5354
reactContent: ReactElement | null,
54-
metadata: any | null,
55+
metadata: SlideMetadata | null,
5556
}> => {
5657
try {
5758
const vfile = await transformMarkdown(source, remarkParseOptions, remarRehypeOptions, remarkPlugins, [...rehypePlugins, ...(customPlugins || [])], rehypeReactOptions);

src/preview/styles.css

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
@import "tailwindcss/components";
33
@import "tailwindcss/utilities";
44

5+
:root {
6+
--demotime-transition: 0.5s;
7+
}
8+
59
html,
610
body,
711
#root {
@@ -46,6 +50,7 @@ pre code {
4650
}
4751
}
4852

53+
/* Transitions */
4954
@keyframes fadeIn {
5055
from {
5156
opacity: 0;
@@ -55,8 +60,120 @@ pre code {
5560
}
5661
}
5762

58-
.fade-in {
59-
animation: fadeIn 300ms ease-in-out;
63+
@keyframes slideLeft {
64+
from {
65+
transform: translateX(100%);
66+
}
67+
to {
68+
transform: translateX(0);
69+
}
70+
}
71+
72+
@keyframes slideRight {
73+
from {
74+
transform: translateX(-100%);
75+
}
76+
to {
77+
transform: translateX(0);
78+
}
79+
}
80+
81+
@keyframes slideUp {
82+
from {
83+
transform: translateY(100%);
84+
}
85+
to {
86+
transform: translateY(0);
87+
}
88+
}
89+
90+
@keyframes slideDown {
91+
from {
92+
transform: translateY(-100%);
93+
}
94+
to {
95+
transform: translateY(0);
96+
}
97+
}
98+
99+
@keyframes zoomIn {
100+
from {
101+
transform: scale(0.5);
102+
opacity: 0;
103+
}
104+
to {
105+
transform: scale(1);
106+
opacity: 1;
107+
}
108+
}
109+
110+
@keyframes zoomOut {
111+
from {
112+
transform: scale(1.5);
113+
opacity: 0;
114+
}
115+
to {
116+
transform: scale(1);
117+
opacity: 1;
118+
}
119+
}
120+
121+
@keyframes rotateIn {
122+
from {
123+
transform: rotate(-180deg);
124+
opacity: 0;
125+
}
126+
to {
127+
transform: rotate(0);
128+
opacity: 1;
129+
}
130+
}
131+
132+
@keyframes rotateOut {
133+
from {
134+
transform: rotate(180deg);
135+
opacity: 0;
136+
}
137+
to {
138+
transform: rotate(0);
139+
opacity: 1;
140+
}
141+
}
142+
143+
.fadeIn {
144+
animation: fadeIn var(--demotime-transition) ease-in-out;
145+
}
146+
147+
.slideLeft {
148+
animation: slideLeft var(--demotime-transition) ease-in-out;
149+
}
150+
151+
.slideRight {
152+
animation: slideRight var(--demotime-transition) ease-in-out;
153+
}
154+
155+
.slideUp {
156+
animation: slideUp var(--demotime-transition) ease-in-out;
157+
}
158+
159+
.slideDown {
160+
animation: slideDown var(--demotime-transition) ease-in-out;
161+
}
162+
163+
.zoomIn {
164+
animation: zoomIn var(--demotime-transition) ease-in-out;
165+
}
166+
167+
.zoomOut {
168+
animation: zoomOut var(--demotime-transition) ease-in-out;
169+
}
170+
171+
.rotateIn {
172+
animation: rotateIn var(--demotime-transition) ease-in-out;
173+
}
174+
175+
.rotateOut {
176+
animation: rotateOut var(--demotime-transition) ease-in-out;
60177
}
61178

62179
/* Layouts */

src/services/PdfExportService.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,32 @@ export class PdfExportService {
5353
const page = await context.newPage();
5454

5555
// Get all demo files
56-
const demoFiles = await FileProvider.getFiles();
56+
let demoFiles = await FileProvider.getFiles();
5757
if (!demoFiles) {
5858
Notifications.error("No demo files found.");
5959
return;
6060
}
6161

62+
// Sort the demo files by their paths
63+
demoFiles = Object.keys(demoFiles)
64+
.sort()
65+
.reduce((sortedFiles, key) => {
66+
if (demoFiles) {
67+
sortedFiles[key] = demoFiles[key];
68+
}
69+
return sortedFiles;
70+
}, {} as typeof demoFiles);
71+
6272
// Get all slide actions
6373
const slideActions: Step[] = [];
6474
for (const demoFile of Object.values(demoFiles)) {
65-
demoFile.demos.forEach((demo) => {
66-
demo.steps.forEach((step) => {
75+
for (const demo of demoFile.demos) {
76+
for (const step of demo.steps) {
6777
if (step.action === Action.OpenSlide && step.path) {
6878
slideActions.push(step);
6979
}
70-
});
71-
});
80+
}
81+
}
7282
}
7383

7484
// Retrieving slide contents

0 commit comments

Comments
 (0)