@remotion/promo-pages 5.0.0-canary.3 → 5.0.1-canary

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.
Files changed (130) hide show
  1. package/.turbo/turbo-make.log +8 -0
  2. package/bundle.ts +38 -0
  3. package/dist/Homepage.js +1 -1
  4. package/dist/homepage/Pricing.js +1 -1
  5. package/eslint.config.mjs +7 -0
  6. package/index.html +13 -0
  7. package/package.json +3 -6
  8. package/public/fire.mp3 +0 -0
  9. package/public/img/cluster.png +0 -0
  10. package/public/img/code-sample-new.png +0 -0
  11. package/public/img/freelancers/alex.jpeg +0 -0
  12. package/public/img/freelancers/antoine.jpeg +0 -0
  13. package/public/img/freelancers/ayush.png +0 -0
  14. package/public/img/freelancers/benjamin.jpeg +0 -0
  15. package/public/img/freelancers/default.png +0 -0
  16. package/public/img/freelancers/florent.jpeg +0 -0
  17. package/public/img/freelancers/karel.jpeg +0 -0
  18. package/public/img/freelancers/lorenzo.jpeg +0 -0
  19. package/public/img/freelancers/mickael.jpeg +0 -0
  20. package/public/img/freelancers/mohit.jpeg +0 -0
  21. package/public/img/freelancers/pramod.jpg +0 -0
  22. package/public/img/freelancers/pranav.jpg +0 -0
  23. package/public/img/freelancers/rahul.png +0 -0
  24. package/public/img/freelancers/ray.jpeg +0 -0
  25. package/public/img/freelancers/stephen.png +0 -0
  26. package/public/img/freelancers/umungo.png +0 -0
  27. package/public/img/freelancers/yehor.jpeg +0 -0
  28. package/public/img/gt-planar-black.woff2 +0 -0
  29. package/public/img/gt-planar-bold.woff2 +0 -0
  30. package/public/img/gt-planar-medium.woff2 +0 -0
  31. package/public/img/gt-planar-regular.woff2 +0 -0
  32. package/public/img/logo-small.png +0 -0
  33. package/public/img/mp4.png +0 -0
  34. package/public/img/player-demo.mp4 +0 -0
  35. package/public/img/player-example-dark.png +0 -0
  36. package/public/img/player-example.png +0 -0
  37. package/public/img/writeinreact.png +0 -0
  38. package/public/partyhorn.mp3 +0 -0
  39. package/public/sad.mp3 +0 -0
  40. package/src/cn.ts +6 -0
  41. package/src/components/Homepage.tsx +88 -0
  42. package/src/components/homepage/BackgroundAnimation.tsx +108 -0
  43. package/src/components/homepage/ChooseTemplate.tsx +57 -0
  44. package/src/components/homepage/CodeExample.tsx +89 -0
  45. package/src/components/homepage/CommunityStats.tsx +54 -0
  46. package/src/components/homepage/CommunityStatsItems.tsx +304 -0
  47. package/src/components/homepage/Counter.tsx +110 -0
  48. package/src/components/homepage/Demo/Card.tsx +273 -0
  49. package/src/components/homepage/Demo/Cards.tsx +129 -0
  50. package/src/components/homepage/Demo/Comp.tsx +157 -0
  51. package/src/components/homepage/Demo/CurrentCountry.tsx +97 -0
  52. package/src/components/homepage/Demo/DemoError.tsx +18 -0
  53. package/src/components/homepage/Demo/DemoErrorIcon.tsx +32 -0
  54. package/src/components/homepage/Demo/DemoRender.tsx +166 -0
  55. package/src/components/homepage/Demo/DigitWheel.tsx +179 -0
  56. package/src/components/homepage/Demo/DisplayedEmoji.tsx +81 -0
  57. package/src/components/homepage/Demo/DoneCheckmark.tsx +39 -0
  58. package/src/components/homepage/Demo/DownloadNudge.tsx +62 -0
  59. package/src/components/homepage/Demo/DragAndDropNudge.tsx +57 -0
  60. package/src/components/homepage/Demo/EmojiCard.tsx +198 -0
  61. package/src/components/homepage/Demo/Minus.tsx +21 -0
  62. package/src/components/homepage/Demo/PlayPauseButton.tsx +66 -0
  63. package/src/components/homepage/Demo/PlayerControls.tsx +48 -0
  64. package/src/components/homepage/Demo/PlayerSeekBar.tsx +325 -0
  65. package/src/components/homepage/Demo/PlayerVolume.tsx +83 -0
  66. package/src/components/homepage/Demo/Progress.tsx +38 -0
  67. package/src/components/homepage/Demo/Spinner.tsx +60 -0
  68. package/src/components/homepage/Demo/Switcher.tsx +54 -0
  69. package/src/components/homepage/Demo/Temperature.tsx +44 -0
  70. package/src/components/homepage/Demo/TemperatureNumber.tsx +68 -0
  71. package/src/components/homepage/Demo/ThemeNudge.tsx +72 -0
  72. package/src/components/homepage/Demo/TimeDisplay.tsx +43 -0
  73. package/src/components/homepage/Demo/TrendingRepos.tsx +106 -0
  74. package/src/components/homepage/Demo/icons.tsx +114 -0
  75. package/src/components/homepage/Demo/index.tsx +158 -0
  76. package/src/components/homepage/Demo/math.ts +43 -0
  77. package/src/components/homepage/Demo/types.ts +6 -0
  78. package/src/components/homepage/Editor.tsx +67 -0
  79. package/src/components/homepage/EvaluateRemotion.tsx +92 -0
  80. package/src/components/homepage/FreePricing.tsx +295 -0
  81. package/src/components/homepage/GetStartedStrip.tsx +77 -0
  82. package/src/components/homepage/GitHubButton.tsx +23 -0
  83. package/src/components/homepage/IconForTemplate.tsx +154 -0
  84. package/src/components/homepage/IfYouKnowReact.tsx +29 -0
  85. package/src/components/homepage/InfoTooltip.tsx +25 -0
  86. package/src/components/homepage/MoreTemplatesButton.tsx +29 -0
  87. package/src/components/homepage/MuxVideo.tsx +68 -0
  88. package/src/components/homepage/NewsletterButton.tsx +88 -0
  89. package/src/components/homepage/Pricing.tsx +49 -0
  90. package/src/components/homepage/PricingBulletPoint.tsx +50 -0
  91. package/src/components/homepage/RealMp4Videos.tsx +50 -0
  92. package/src/components/homepage/Spacer.tsx +5 -0
  93. package/src/components/homepage/TemplateIcon.tsx +36 -0
  94. package/src/components/homepage/TextInput.tsx +62 -0
  95. package/src/components/homepage/TrustedByBanner.tsx +194 -0
  96. package/src/components/homepage/VideoApps.tsx +231 -0
  97. package/src/components/homepage/VideoAppsShowcase.tsx +276 -0
  98. package/src/components/homepage/VideoAppsTitle.tsx +24 -0
  99. package/src/components/homepage/VideoPlayerWithControls.tsx +188 -0
  100. package/src/components/homepage/WriteInReact.tsx +34 -0
  101. package/src/components/homepage/YouAreHere.tsx +30 -0
  102. package/src/components/homepage/custom.css +57 -0
  103. package/src/components/homepage/layout/Button.tsx +93 -0
  104. package/src/components/homepage/layout/colors.ts +17 -0
  105. package/src/components/homepage/layout/use-color-mode.tsx +44 -0
  106. package/src/components/homepage/layout/use-el-size.ts +51 -0
  107. package/src/components/homepage/layout/use-mobile-layout.ts +8 -0
  108. package/src/components/homepage/video-player.css +24 -0
  109. package/src/components/icons/blank.tsx +13 -0
  110. package/src/components/icons/clone.tsx +10 -0
  111. package/src/components/icons/code-hike.tsx +15 -0
  112. package/src/components/icons/cubes.tsx +13 -0
  113. package/src/components/icons/js.tsx +17 -0
  114. package/src/components/icons/next.tsx +64 -0
  115. package/src/components/icons/overlay.tsx +24 -0
  116. package/src/components/icons/remix.tsx +24 -0
  117. package/src/components/icons/skia.tsx +13 -0
  118. package/src/components/icons/stargazer.tsx +13 -0
  119. package/src/components/icons/still.tsx +13 -0
  120. package/src/components/icons/tailwind.tsx +22 -0
  121. package/src/components/icons/tiktok.tsx +13 -0
  122. package/src/components/icons/ts.tsx +18 -0
  123. package/src/components/icons/tts.tsx +13 -0
  124. package/src/components/icons/undo.tsx +11 -0
  125. package/src/components/icons/waveform.tsx +13 -0
  126. package/src/fonts.css +30 -0
  127. package/src/index.css +74 -0
  128. package/src/main.tsx +12 -0
  129. package/tsconfig.json +15 -0
  130. package/vite.config.ts +9 -0
@@ -0,0 +1,179 @@
1
+ import React from 'react';
2
+ import type {SpringConfig} from 'remotion';
3
+ import {
4
+ AbsoluteFill,
5
+ interpolate,
6
+ spring,
7
+ useCurrentFrame,
8
+ useVideoConfig,
9
+ } from 'remotion';
10
+ import {Minus} from './Minus';
11
+
12
+ const DURATION = 25;
13
+
14
+ const items = 10;
15
+
16
+ export const NUM_WIDTH = 36;
17
+
18
+ export const Wheel: React.FC<{
19
+ readonly delay: number;
20
+ readonly digits: number[];
21
+ readonly renderDigit: (i: number) => React.ReactNode;
22
+ readonly isLeadingDigit: boolean;
23
+ readonly isNegative: boolean[];
24
+ }> = ({delay, digits, renderDigit, isLeadingDigit, isNegative}) => {
25
+ const frame = useCurrentFrame();
26
+ const {fps} = useVideoConfig();
27
+
28
+ const singleDur = DURATION;
29
+
30
+ const progressesMaker = (
31
+ springConfig: Partial<SpringConfig>,
32
+ durationInFrames: number,
33
+ ) =>
34
+ new Array(digits.length - 1).fill(true).map((_, i) => {
35
+ const current = digits[i];
36
+ const next = digits[i + 1];
37
+ if (current === next) {
38
+ return 1;
39
+ }
40
+
41
+ return spring({
42
+ fps,
43
+ frame,
44
+ config: springConfig,
45
+ durationInFrames,
46
+ delay: i * 50 + delay,
47
+ });
48
+ });
49
+
50
+ const progresses = progressesMaker(
51
+ {
52
+ mass: 0.7,
53
+ damping: 12,
54
+ },
55
+ singleDur,
56
+ );
57
+ const softProgresses = progressesMaker(
58
+ {
59
+ damping: 200,
60
+ },
61
+ singleDur / 2,
62
+ );
63
+
64
+ const rotations = digits.map((d) => 1 / items + d / 10);
65
+
66
+ const offsets = rotations.map((r, i) => {
67
+ const next = rotations[i + 1];
68
+ if (next === undefined) {
69
+ return 0;
70
+ }
71
+
72
+ return next - r;
73
+ });
74
+
75
+ const opacity = isLeadingDigit
76
+ ? interpolate(
77
+ softProgresses.reduce((a, b) => a + b, 0),
78
+ new Array(digits.length).fill(true).map((_, i) => i),
79
+ digits.map((digit) => (digit === 0 ? 0 : 1)),
80
+ )
81
+ : 1;
82
+
83
+ const minusSignOpacity = isLeadingDigit
84
+ ? interpolate(
85
+ softProgresses.reduce((a, b) => a + b, 0),
86
+ new Array(digits.length).fill(true).map((_, i) => i),
87
+ isNegative.map((negative) => (negative ? 1 : 0)),
88
+ )
89
+ : 1;
90
+
91
+ const minusSignMarginLeft = interpolate(minusSignOpacity, [0, 1], [10, 0]);
92
+
93
+ const shiftLeft = isLeadingDigit
94
+ ? interpolate(
95
+ softProgresses.reduce((a, b) => a + b, 0),
96
+ new Array(digits.length).fill(true).map((_, i) => i),
97
+ digits.map((digit) => (digit === 0 ? -40 : 0)),
98
+ )
99
+ : 1;
100
+
101
+ const rotation =
102
+ progresses
103
+ .map((p, i) => {
104
+ return p * offsets[i];
105
+ })
106
+ .reduce((a, b) => a + b, 0) + rotations[0];
107
+
108
+ return (
109
+ <div
110
+ style={{
111
+ position: 'relative',
112
+ width: NUM_WIDTH,
113
+ display: 'inline-block',
114
+ height: 80,
115
+ marginLeft: shiftLeft,
116
+ }}
117
+ >
118
+ {isLeadingDigit ? (
119
+ <Minus
120
+ minusSignOpacity={minusSignOpacity}
121
+ leftOffset={shiftLeft + minusSignMarginLeft}
122
+ />
123
+ ) : null}
124
+ <AbsoluteFill
125
+ style={{
126
+ perspective: 5000,
127
+ maskImage: `linear-gradient(to bottom, transparent 0%, #000 28%, #000 80%, transparent 100%)`,
128
+ WebkitMaskImage: `linear-gradient(to bottom, transparent 0%, #000 28%, #000 80%, transparent 100%)`,
129
+ }}
130
+ >
131
+ {new Array(items).fill(true).map((_, i) => {
132
+ const index = i / items + rotation;
133
+
134
+ const z = Math.cos(index * -Math.PI * 2) * 120;
135
+ const y = Math.sin(index * Math.PI * 2) * -120;
136
+ const r = interpolate(index, [0, 1], [0, Math.PI * 2]);
137
+
138
+ return (
139
+ <AbsoluteFill
140
+ // eslint-disable-next-line react/no-array-index-key
141
+ key={i}
142
+ style={{
143
+ justifyContent: 'center',
144
+ alignItems: 'center',
145
+ fontSize: 60,
146
+ fontVariationSettings: `"wght" 400`,
147
+ transform: `translateZ(${z}px) translateY(${y}px) rotateX(${r}deg)`,
148
+ backfaceVisibility: 'hidden',
149
+ perspective: 1000,
150
+ }}
151
+ >
152
+ <div
153
+ style={{
154
+ transform: `rotateX(-${r}rad)`,
155
+ backfaceVisibility: 'hidden',
156
+ display: 'flex',
157
+ flexDirection: 'row',
158
+ alignItems: 'center',
159
+ justifyContent: 'center',
160
+ opacity,
161
+ }}
162
+ >
163
+ <div
164
+ style={{
165
+ lineHeight: 1,
166
+ display: 'inline',
167
+ fontFeatureSettings: "'ss03' on",
168
+ }}
169
+ >
170
+ {renderDigit(i)}
171
+ </div>
172
+ </div>
173
+ </AbsoluteFill>
174
+ );
175
+ })}
176
+ </AbsoluteFill>
177
+ </div>
178
+ );
179
+ };
@@ -0,0 +1,81 @@
1
+ import type {EmojiName} from '@remotion/animated-emoji';
2
+ import type {Lottie, LottieAnimationData} from '@remotion/lottie';
3
+ import React, {useEffect, useMemo, useState} from 'react';
4
+ import {
5
+ cancelRender,
6
+ continueRender,
7
+ delayRender,
8
+ useVideoConfig,
9
+ } from 'remotion';
10
+
11
+ type Data = {
12
+ Lottie: typeof Lottie;
13
+ duration: number;
14
+ data: LottieAnimationData;
15
+ };
16
+
17
+ export const DisplayedEmoji: React.FC<{
18
+ readonly emoji: EmojiName;
19
+ }> = ({emoji}) => {
20
+ const [data, setData] = useState<Data | null>(null);
21
+ const {durationInFrames, fps} = useVideoConfig();
22
+
23
+ const src = useMemo(() => {
24
+ if (emoji === 'melting') {
25
+ return 'https://fonts.gstatic.com/s/e/notoemoji/latest/1fae0/lottie.json';
26
+ }
27
+
28
+ if (emoji === 'partying-face') {
29
+ return 'https://fonts.gstatic.com/s/e/notoemoji/latest/1f973/lottie.json';
30
+ }
31
+
32
+ if (emoji === 'fire') {
33
+ return 'https://fonts.gstatic.com/s/e/notoemoji/latest/1f525/lottie.json';
34
+ }
35
+
36
+ throw new Error('Unknown emoji');
37
+ }, [emoji]);
38
+
39
+ const [handle] = useState(() => delayRender());
40
+
41
+ useEffect(() => {
42
+ Promise.all([
43
+ import('@remotion/lottie'),
44
+ fetch(src).then((res) => res.json()),
45
+ ])
46
+ .then(([{Lottie, getLottieMetadata}, json]) => {
47
+ setData({
48
+ Lottie,
49
+ duration: getLottieMetadata(json)?.durationInSeconds as number,
50
+ data: json,
51
+ });
52
+ continueRender(handle);
53
+ })
54
+ .catch((err) => {
55
+ cancelRender(err);
56
+ });
57
+ }, [handle, src]);
58
+
59
+ if (!data) {
60
+ return null;
61
+ }
62
+
63
+ const animDurtion = data.duration;
64
+ const ratio = durationInFrames / fps / animDurtion;
65
+ const closestInteger = ratio;
66
+ const closestRatio = closestInteger / ratio;
67
+
68
+ return (
69
+ <data.Lottie
70
+ style={{
71
+ height: 100,
72
+ width: '100%',
73
+ display: 'flex',
74
+ justifyContent: 'center',
75
+ }}
76
+ loop
77
+ animationData={data.data}
78
+ playbackRate={closestRatio}
79
+ />
80
+ );
81
+ };
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import {PALETTE} from '../layout/colors';
3
+
4
+ export const DoneCheckmark: React.FC = () => {
5
+ return (
6
+ <div
7
+ style={{
8
+ backgroundColor: PALETTE.BRAND,
9
+ height: 26,
10
+ width: 26,
11
+ borderRadius: 13,
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ justifyContent: 'center',
15
+ animation: 'jump 0.2s ease-out',
16
+ }}
17
+ >
18
+ <svg
19
+ viewBox="0 0 20 20"
20
+ fill="none"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ style={{
23
+ flexShrink: 0,
24
+ width: 26,
25
+ }}
26
+ >
27
+ <circle cx="10" cy="10" r="10" fill="#0B84F3" />
28
+ <path
29
+ d="M14.7908 7.20505C15.0697 7.47844 15.0697 7.92243 14.7908 8.19583L9.07711 13.795C8.79813 14.0683 8.34505 14.0683 8.06606 13.795L5.20924 10.9954C4.93025 10.722 4.93025 10.278 5.20924 10.0046C5.48823 9.73121 5.9413 9.73121 6.22029 10.0046L8.5727 12.3077L13.7819 7.20505C14.0609 6.93165 14.514 6.93165 14.793 7.20505H14.7908Z"
30
+ fill="white"
31
+ style={{
32
+ transform: 'scale(1.2)',
33
+ transformOrigin: 'center',
34
+ }}
35
+ />
36
+ </svg>
37
+ </div>
38
+ );
39
+ };
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import {FONTS} from '../layout/colors';
3
+
4
+ const origWidth = 77;
5
+ const scale = 0.4;
6
+
7
+ const Icon: React.FC = () => {
8
+ return (
9
+ <svg
10
+ style={{
11
+ width: origWidth * scale,
12
+ }}
13
+ viewBox="0 0 77 160"
14
+ fill="none"
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ >
17
+ <path
18
+ d="M5 154.5C51 121 79 81 69 5"
19
+ stroke="currentColor"
20
+ strokeWidth="8"
21
+ strokeLinecap="round"
22
+ />
23
+ </svg>
24
+ );
25
+ };
26
+
27
+ export const DownloadNudge: React.FC = () => {
28
+ return (
29
+ <div
30
+ style={{
31
+ position: 'relative',
32
+ flexDirection: 'row',
33
+ display: 'flex',
34
+ justifyContent: 'flex-end',
35
+ textAlign: 'right',
36
+ paddingRight: 22,
37
+ paddingTop: 20,
38
+ }}
39
+ >
40
+ <div>
41
+ <Icon />
42
+ <div
43
+ style={{
44
+ fontFamily: FONTS.GTPLANAR,
45
+ fontSize: 15,
46
+ width: 280,
47
+ position: 'absolute',
48
+ right: 70,
49
+ top: 60,
50
+ }}
51
+ >
52
+ Export the video using
53
+ <br />{' '}
54
+ <a href="/lambda" className="bluelink">
55
+ Remotion Lambda
56
+ </a>
57
+ .
58
+ </div>
59
+ </div>
60
+ </div>
61
+ );
62
+ };
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import {FONTS, PALETTE} from '../layout/colors';
3
+
4
+ const origWidth = 37;
5
+ const scale = 0.4;
6
+
7
+ export const Icon: React.FC = () => {
8
+ return (
9
+ <svg
10
+ style={{
11
+ width: origWidth * scale,
12
+ overflow: 'visible',
13
+ }}
14
+ viewBox="0 0 37 59"
15
+ fill="none"
16
+ >
17
+ <path
18
+ d="M5.00003 5C5.00002 36.5 16 44 32.0002 54"
19
+ stroke={PALETTE.TEXT_COLOR}
20
+ strokeWidth="8"
21
+ strokeLinecap="round"
22
+ />
23
+ </svg>
24
+ );
25
+ };
26
+
27
+ export const DragAndDropNudge: React.FC = () => {
28
+ return (
29
+ <div
30
+ style={{
31
+ position: 'absolute',
32
+ flexDirection: 'row',
33
+ display: 'flex',
34
+ justifyContent: 'flex-start',
35
+ paddingBottom: 5,
36
+ marginTop: 50,
37
+ }}
38
+ className={'ml-4 lg:-ml-5'}
39
+ >
40
+ <div>
41
+ <div
42
+ style={{
43
+ fontFamily: FONTS.GTPLANAR,
44
+ fontSize: 15,
45
+ width: 280,
46
+ paddingBottom: 8,
47
+ textWrap: 'balance',
48
+ }}
49
+ className={'-ml-3 lg:-ml-5'}
50
+ >
51
+ Drag and drop the cards to reorder them.
52
+ </div>
53
+ <Icon />
54
+ </div>
55
+ </div>
56
+ );
57
+ };
@@ -0,0 +1,198 @@
1
+ import type {EmojiName} from '@remotion/animated-emoji';
2
+ import React, {
3
+ forwardRef,
4
+ useCallback,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useRef,
8
+ } from 'react';
9
+ import {AbsoluteFill, getRemotionEnvironment} from 'remotion';
10
+ import {DisplayedEmoji} from './DisplayedEmoji';
11
+
12
+ export type EmojiCardRef = {
13
+ onLeft: () => void;
14
+ onRight: () => void;
15
+ };
16
+
17
+ type Transforms = [number, number, number];
18
+
19
+ type Anim = {
20
+ ref1: HTMLDivElement;
21
+ ref2: HTMLDivElement;
22
+ ref3: HTMLDivElement;
23
+ transforms: Transforms;
24
+ };
25
+
26
+ const applyTransforms = ({ref1, ref2, ref3, transforms}: Anim) => {
27
+ ref1.style.transform = `translateX(${transforms[0]}%)`;
28
+ ref2.style.transform = `translateX(${transforms[1]}%)`;
29
+ ref3.style.transform = `translateX(${transforms[2]}%)`;
30
+ };
31
+
32
+ const waitForRaq = async () => {
33
+ await new Promise((resolve) => {
34
+ requestAnimationFrame(resolve);
35
+ });
36
+ };
37
+
38
+ const moveLeft = async ({ref1, ref2, ref3, transforms}: Anim) => {
39
+ for (let i = 0; i < 20; i++) {
40
+ for (const idx in transforms) {
41
+ transforms[idx] -= 5;
42
+ if (transforms[idx] <= -200) {
43
+ transforms[idx] += 300;
44
+ }
45
+ }
46
+
47
+ applyTransforms({ref1, ref2, ref3, transforms});
48
+ await waitForRaq();
49
+ }
50
+ };
51
+
52
+ const moveRight = async ({ref1, ref2, ref3, transforms}: Anim) => {
53
+ for (let i = 0; i < 20; i++) {
54
+ for (const idx in transforms) {
55
+ transforms[idx] += 5;
56
+ if (transforms[idx] >= 100) {
57
+ transforms[idx] -= 300;
58
+ }
59
+ }
60
+
61
+ applyTransforms({ref1, ref2, ref3, transforms});
62
+ await waitForRaq();
63
+ }
64
+ };
65
+
66
+ const emojiStyle: React.CSSProperties = {
67
+ width: '100%',
68
+ position: 'absolute',
69
+ top: 'calc(50% - 50px)',
70
+ };
71
+
72
+ export type EmojiCardProps = {
73
+ readonly emojiIndex: EmojiName;
74
+ };
75
+
76
+ const EmojiCardRefFn: React.ForwardRefRenderFunction<
77
+ EmojiCardRef,
78
+ EmojiCardProps
79
+ > = ({emojiIndex}, ref) => {
80
+ const ref1 = useRef<HTMLDivElement>(null);
81
+ const ref2 = useRef<HTMLDivElement>(null);
82
+ const ref3 = useRef<HTMLDivElement>(null);
83
+ const transforms = useRef<Transforms>([-100, 0, 100]);
84
+
85
+ const onLeft = useCallback(() => {
86
+ if (!ref1.current || !ref2.current || !ref3.current) {
87
+ return;
88
+ }
89
+
90
+ moveLeft({
91
+ ref1: ref1.current,
92
+ ref2: ref2.current,
93
+ ref3: ref3.current,
94
+ transforms: transforms.current,
95
+ });
96
+ }, []);
97
+
98
+ const onRight = useCallback(() => {
99
+ if (!ref1.current || !ref2.current || !ref3.current) {
100
+ return;
101
+ }
102
+
103
+ moveRight({
104
+ ref1: ref1.current,
105
+ ref2: ref2.current,
106
+ ref3: ref3.current,
107
+ transforms: transforms.current,
108
+ });
109
+ }, []);
110
+
111
+ useEffect(() => {
112
+ if (!ref1.current || !ref2.current || !ref3.current) {
113
+ return;
114
+ }
115
+
116
+ applyTransforms({
117
+ ref1: ref1.current,
118
+ ref2: ref2.current,
119
+ ref3: ref3.current,
120
+ transforms: transforms.current,
121
+ });
122
+ }, []);
123
+
124
+ useImperativeHandle(ref, () => {
125
+ return {
126
+ onLeft,
127
+ onRight,
128
+ };
129
+ }, [onLeft, onRight]);
130
+
131
+ useEffect(() => {
132
+ if (!ref1.current || !ref2.current || !ref3.current) {
133
+ return;
134
+ }
135
+
136
+ applyTransforms({
137
+ ref1: ref1.current,
138
+ ref2: ref2.current,
139
+ ref3: ref3.current,
140
+ transforms: transforms.current,
141
+ });
142
+ }, []);
143
+
144
+ return (
145
+ <AbsoluteFill
146
+ style={{
147
+ justifyContent: 'center',
148
+ alignItems: 'center',
149
+ overflow: 'hidden',
150
+ }}
151
+ >
152
+ <div
153
+ style={{
154
+ color: '#0b84f3',
155
+ fontFamily: 'GTPlanar',
156
+ fontWeight: '500',
157
+ fontSize: 13,
158
+ textAlign: 'center',
159
+ position: 'absolute',
160
+ marginTop: -90,
161
+ }}
162
+ >
163
+ Choose an emoji
164
+ </div>
165
+ <div
166
+ style={{
167
+ position: 'absolute',
168
+ width: '100%',
169
+ display: 'flex',
170
+ whiteSpace: 'nowrap',
171
+ height: '100%',
172
+ }}
173
+ >
174
+ {getRemotionEnvironment().isRendering ? (
175
+ <div style={emojiStyle}>
176
+ <DisplayedEmoji emoji={emojiIndex} />
177
+ </div>
178
+ ) : (
179
+ <>
180
+ <div ref={ref1} style={emojiStyle}>
181
+ <DisplayedEmoji emoji={'melting'} />
182
+ </div>
183
+ <div ref={ref2} style={emojiStyle}>
184
+ <DisplayedEmoji emoji={'partying-face'} />
185
+ </div>
186
+ <div ref={ref3} style={emojiStyle}>
187
+ <DisplayedEmoji emoji={'fire'} />
188
+ </div>
189
+ </>
190
+ )}
191
+ </div>
192
+ </AbsoluteFill>
193
+ );
194
+ };
195
+
196
+ export const EmojiCard = forwardRef<EmojiCardRef, EmojiCardProps>(
197
+ EmojiCardRefFn,
198
+ );
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+
3
+ export const Minus: React.FC<{
4
+ readonly leftOffset: number;
5
+ readonly minusSignOpacity: number;
6
+ }> = ({leftOffset, minusSignOpacity}) => {
7
+ return (
8
+ <div
9
+ style={{
10
+ position: 'relative',
11
+ marginLeft: -40 - leftOffset,
12
+ marginTop: 5,
13
+ width: 40,
14
+ opacity: minusSignOpacity,
15
+ textAlign: 'right',
16
+ }}
17
+ >
18
+ -
19
+ </div>
20
+ );
21
+ };
@@ -0,0 +1,66 @@
1
+ import type {PlayerRef} from '@remotion/player';
2
+ import React, {useCallback, useEffect} from 'react';
3
+ import {PALETTE} from '../layout/colors';
4
+ import {PausedIcon, PlayingIcon} from './icons';
5
+
6
+ const playerButtonStyle: React.CSSProperties = {
7
+ appearance: 'none',
8
+ border: 'none',
9
+ borderRadius: 0,
10
+ background: 'none',
11
+ display: 'flex',
12
+ justifyContent: 'center',
13
+ alignItems: 'center',
14
+ paddingRight: 20,
15
+ paddingLeft: 20,
16
+ cursor: 'pointer',
17
+ height: 50,
18
+ color: PALETTE.TEXT_COLOR,
19
+ };
20
+
21
+ export const PlayPauseButton: React.FC<{
22
+ readonly playerRef: React.RefObject<PlayerRef | null>;
23
+ }> = ({playerRef}) => {
24
+ const [playing, setPlaying] = React.useState(true);
25
+
26
+ useEffect(() => {
27
+ const {current} = playerRef;
28
+ if (!current) {
29
+ return;
30
+ }
31
+
32
+ const onPlay = () => {
33
+ setPlaying(true);
34
+ };
35
+
36
+ const onPause = () => {
37
+ setPlaying(false);
38
+ };
39
+
40
+ current.addEventListener('play', onPlay);
41
+ current.addEventListener('pause', onPause);
42
+
43
+ return () => {
44
+ current.removeEventListener('play', onPlay);
45
+ current.removeEventListener('pause', onPause);
46
+ };
47
+ }, [playerRef]);
48
+
49
+ const onToggle = useCallback(() => {
50
+ playerRef.current?.toggle();
51
+ }, [playerRef]);
52
+
53
+ const playPauseIconStyle: React.CSSProperties = {
54
+ width: 15,
55
+ };
56
+
57
+ return (
58
+ <button type="button" style={playerButtonStyle} onClick={onToggle}>
59
+ {playing ? (
60
+ <PlayingIcon style={playPauseIconStyle} />
61
+ ) : (
62
+ <PausedIcon style={playPauseIconStyle} />
63
+ )}
64
+ </button>
65
+ );
66
+ };