@netless/fastboard-react 0.2.13-canary.0 → 0.3.0-canary.0

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 (105) hide show
  1. package/README.md +49 -0
  2. package/dist/index.d.ts +11 -140
  3. package/dist/index.js +73 -2130
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +71 -2129
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +15 -23
  8. package/src/Fastboard.tsx +12 -0
  9. package/src/PageControl.tsx +11 -0
  10. package/src/RedoUndo.tsx +8 -0
  11. package/src/Toolbar.tsx +8 -0
  12. package/src/ZoomControl.tsx +11 -0
  13. package/src/hooks.tsx +53 -0
  14. package/src/index.tsx +19 -0
  15. package/src/style.scss +3 -26
  16. package/src/behaviors/style.ts +0 -4
  17. package/src/components/Fastboard.scss +0 -46
  18. package/src/components/Fastboard.tsx +0 -108
  19. package/src/components/PageControl/PageControl.scss +0 -84
  20. package/src/components/PageControl/PageControl.tsx +0 -101
  21. package/src/components/PageControl/hooks.ts +0 -24
  22. package/src/components/PageControl/index.ts +0 -2
  23. package/src/components/PlayerControl/PlayerControl.scss +0 -146
  24. package/src/components/PlayerControl/PlayerControl.tsx +0 -131
  25. package/src/components/PlayerControl/components/Button.tsx +0 -44
  26. package/src/components/PlayerControl/hooks.ts +0 -88
  27. package/src/components/PlayerControl/icons/Loading.tsx +0 -13
  28. package/src/components/PlayerControl/icons/Pause.tsx +0 -13
  29. package/src/components/PlayerControl/icons/Play.tsx +0 -13
  30. package/src/components/PlayerControl/icons/index.ts +0 -10
  31. package/src/components/PlayerControl/index.ts +0 -2
  32. package/src/components/RedoUndo/RedoUndo.scss +0 -56
  33. package/src/components/RedoUndo/RedoUndo.tsx +0 -76
  34. package/src/components/RedoUndo/hooks.ts +0 -18
  35. package/src/components/RedoUndo/index.ts +0 -2
  36. package/src/components/ReplayFastboard.tsx +0 -36
  37. package/src/components/Toolbar/Content.tsx +0 -89
  38. package/src/components/Toolbar/Toolbar.scss +0 -343
  39. package/src/components/Toolbar/Toolbar.tsx +0 -84
  40. package/src/components/Toolbar/components/ApplianceButtons.tsx +0 -108
  41. package/src/components/Toolbar/components/AppsButton.tsx +0 -134
  42. package/src/components/Toolbar/components/Button.tsx +0 -45
  43. package/src/components/Toolbar/components/ColorBox.tsx +0 -55
  44. package/src/components/Toolbar/components/CutLine.tsx +0 -8
  45. package/src/components/Toolbar/components/PencilButton.tsx +0 -66
  46. package/src/components/Toolbar/components/ShapesButton.tsx +0 -135
  47. package/src/components/Toolbar/components/Slider.tsx +0 -26
  48. package/src/components/Toolbar/components/TextButton.tsx +0 -62
  49. package/src/components/Toolbar/components/UpDownButtons.tsx +0 -49
  50. package/src/components/Toolbar/components/assets/cocos.png +0 -0
  51. package/src/components/Toolbar/components/assets/collapsed.png +0 -0
  52. package/src/components/Toolbar/components/assets/countdown.png +0 -0
  53. package/src/components/Toolbar/components/assets/expanded.png +0 -0
  54. package/src/components/Toolbar/components/assets/geogebra.png +0 -0
  55. package/src/components/Toolbar/components/assets/vscode.png +0 -0
  56. package/src/components/Toolbar/const.ts +0 -32
  57. package/src/components/Toolbar/hooks.ts +0 -89
  58. package/src/components/Toolbar/icons/Apps.tsx +0 -16
  59. package/src/components/Toolbar/icons/Arrow.tsx +0 -13
  60. package/src/components/Toolbar/icons/Circle.tsx +0 -13
  61. package/src/components/Toolbar/icons/Clean.tsx +0 -16
  62. package/src/components/Toolbar/icons/Clicker.tsx +0 -19
  63. package/src/components/Toolbar/icons/Collapse.tsx +0 -13
  64. package/src/components/Toolbar/icons/Diamond.tsx +0 -13
  65. package/src/components/Toolbar/icons/Down.tsx +0 -13
  66. package/src/components/Toolbar/icons/Eraser.tsx +0 -16
  67. package/src/components/Toolbar/icons/Expand.tsx +0 -13
  68. package/src/components/Toolbar/icons/Laser.tsx +0 -21
  69. package/src/components/Toolbar/icons/Line.tsx +0 -13
  70. package/src/components/Toolbar/icons/Loading.tsx +0 -13
  71. package/src/components/Toolbar/icons/Pencil.tsx +0 -16
  72. package/src/components/Toolbar/icons/Rectangle.tsx +0 -13
  73. package/src/components/Toolbar/icons/Selector.tsx +0 -13
  74. package/src/components/Toolbar/icons/SpeechBalloon.tsx +0 -17
  75. package/src/components/Toolbar/icons/Star.tsx +0 -17
  76. package/src/components/Toolbar/icons/Text.tsx +0 -13
  77. package/src/components/Toolbar/icons/Triangle.tsx +0 -13
  78. package/src/components/Toolbar/icons/Up.tsx +0 -13
  79. package/src/components/Toolbar/icons/index.ts +0 -42
  80. package/src/components/Toolbar/index.ts +0 -2
  81. package/src/components/ZoomControl/ZoomControl.scss +0 -84
  82. package/src/components/ZoomControl/ZoomControl.tsx +0 -96
  83. package/src/components/ZoomControl/hooks.ts +0 -50
  84. package/src/components/ZoomControl/index.ts +0 -2
  85. package/src/components/hooks.ts +0 -66
  86. package/src/components/tippy-util.ts +0 -8
  87. package/src/i18n/en.json +0 -31
  88. package/src/i18n/index.ts +0 -29
  89. package/src/i18n/zh-CN.json +0 -32
  90. package/src/icons/Left.tsx +0 -15
  91. package/src/icons/Minus.tsx +0 -15
  92. package/src/icons/Plus.tsx +0 -15
  93. package/src/icons/Redo.tsx +0 -19
  94. package/src/icons/Reset.tsx +0 -17
  95. package/src/icons/Right.tsx +0 -15
  96. package/src/icons/Undo.tsx +0 -19
  97. package/src/icons/WhiteboardAdd.tsx +0 -26
  98. package/src/icons/index.tsx +0 -11
  99. package/src/index.ts +0 -12
  100. package/src/internal/helpers.ts +0 -31
  101. package/src/internal/hooks.ts +0 -23
  102. package/src/internal/index.ts +0 -2
  103. package/src/theme.ts +0 -36
  104. package/src/typings.ts +0 -15
  105. package/src/vanilla/index.tsx +0 -28
@@ -1,108 +0,0 @@
1
- import type { FastboardApp } from "@netless/fastboard-core";
2
- import type { ForwardedRef } from "react";
3
- import type { Language, Theme } from "../typings";
4
-
5
- import React, { forwardRef, useCallback, useEffect } from "react";
6
- import { createI18n, I18nContext } from "../i18n";
7
- import { useAsyncValue, useForceUpdate } from "../internal";
8
- import { FastboardAppContext, ThemeContext, useFastboardApp, useHideControls } from "./hooks";
9
- import { PageControl } from "./PageControl";
10
- import { RedoUndo } from "./RedoUndo";
11
- import { Toolbar } from "./Toolbar";
12
- import { ZoomControl } from "./ZoomControl";
13
- import { hideAll } from "./tippy-util";
14
-
15
- export interface FastboardProps {
16
- app?: FastboardApp | null;
17
- theme?: Theme;
18
- layout?: { Toolbar?: boolean; RedoUndo?: boolean; ZoomControl?: boolean; PageControl?: boolean };
19
- language?: Language;
20
- }
21
-
22
- export type DivProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
23
- export type WithForwardedRef<T = HTMLDivElement> = { forwardedRef: ForwardedRef<T> };
24
-
25
- export const Fastboard = /* @__PURE__ */ forwardRef<HTMLDivElement, FastboardProps & DivProps>(
26
- function Fastboard({ app, theme, layout, language, ...restProps }, ref) {
27
- if (!app) {
28
- return <div className="fastboard-root" ref={ref} {...restProps} />;
29
- }
30
-
31
- return (
32
- <FastboardAppContext.Provider value={app}>
33
- <FastboardInternal forwardedRef={ref} {...{ theme, layout, language }} {...restProps} />
34
- </FastboardAppContext.Provider>
35
- );
36
- }
37
- );
38
-
39
- function FastboardInternal({
40
- forwardedRef,
41
- language,
42
- layout = {},
43
- theme = "light",
44
- children,
45
- ...restProps
46
- }: Omit<FastboardProps, "app"> & DivProps & WithForwardedRef) {
47
- const app = useFastboardApp();
48
- const forceUpdate = useForceUpdate();
49
- const i18n = useAsyncValue(() => createI18n({ language }));
50
-
51
- useEffect(() => {
52
- if (i18n) i18n.changeLanguage(language);
53
- forceUpdate();
54
- }, [forceUpdate, i18n, language]);
55
-
56
- useEffect(() => {
57
- app.manager.setPrefersColorScheme(theme);
58
- }, [app, theme]);
59
-
60
- const useWhiteboard = useCallback(
61
- (container: HTMLDivElement | null) => {
62
- if (container && app) app.bindContainer(container);
63
- },
64
- [app]
65
- );
66
-
67
- const hideControls = useHideControls();
68
- const showControls = !hideControls;
69
-
70
- const {
71
- Toolbar: toolbar = showControls || hideControls === "toolbar-only",
72
- RedoUndo: redo_undo = showControls,
73
- ZoomControl: zoom_control = showControls,
74
- PageControl: page_control = showControls,
75
- } = layout;
76
-
77
- return (
78
- <ThemeContext.Provider value={theme}>
79
- <I18nContext.Provider value={i18n}>
80
- <div {...restProps} className="fastboard-root" ref={forwardedRef}>
81
- <div className="fastboard-view" ref={useWhiteboard} onTouchStartCapture={hideAll} />
82
- {children ? (
83
- children
84
- ) : (
85
- <>
86
- {toolbar && (
87
- <div className="fastboard-left">
88
- <Toolbar />
89
- </div>
90
- )}
91
- {(redo_undo || zoom_control) && (
92
- <div className="fastboard-bottom-left">
93
- {redo_undo && <RedoUndo />}
94
- {zoom_control && <ZoomControl />}
95
- </div>
96
- )}
97
- {page_control && (
98
- <div className="fastboard-bottom-right">
99
- <PageControl />
100
- </div>
101
- )}
102
- </>
103
- )}
104
- </div>
105
- </I18nContext.Provider>
106
- </ThemeContext.Provider>
107
- );
108
- }
@@ -1,84 +0,0 @@
1
- $name: "fastboard-page-control";
2
-
3
- .#{$name} {
4
- display: inline-flex;
5
- align-items: center;
6
- gap: 4px;
7
- padding: 4px;
8
- border-radius: 4px;
9
- backdrop-filter: blur(5px);
10
- -webkit-backdrop-filter: blur(5px);
11
-
12
- &.light {
13
- color: #333;
14
- background-color: rgba(255, 255, 255, 0.9);
15
- border: 1px solid #e5e8f0;
16
- }
17
-
18
- &.dark {
19
- color: #ddd;
20
- background-color: #14181e;
21
- border: 1px solid #383b42;
22
- }
23
- }
24
-
25
- .#{$name}-btn {
26
- appearance: none;
27
- cursor: pointer;
28
- margin: 0;
29
- border: 0;
30
- padding: 0;
31
- width: 24px;
32
- height: 24px;
33
- background-color: transparent;
34
- border-radius: 4px;
35
- font-size: 24px;
36
- line-height: 1;
37
-
38
- svg,
39
- img {
40
- width: 100%;
41
- height: 100%;
42
- }
43
-
44
- &:disabled {
45
- opacity: 0.5;
46
- cursor: not-allowed;
47
- }
48
-
49
- &.light:not(:disabled):hover {
50
- background-color: #ebf2ff;
51
- }
52
-
53
- &.dark:not(:disabled):hover {
54
- background-color: #383b42;
55
- }
56
- }
57
-
58
- .#{$name}-cut-line {
59
- height: 24px;
60
- width: 0.5px;
61
-
62
- &.light {
63
- background-color: #e7e7e7;
64
- }
65
-
66
- &.dark {
67
- background-color: rgba(255, 255, 255, 0.15);
68
- }
69
- }
70
-
71
- .#{$name}-slash {
72
- opacity: 0.6;
73
- }
74
-
75
- .#{$name}-text {
76
- line-height: 24px;
77
- }
78
-
79
- .#{$name}-page,
80
- .#{$name}-slash,
81
- .#{$name}-page-count {
82
- font-size: 14px;
83
- font-variant-numeric: tabular-nums;
84
- }
@@ -1,101 +0,0 @@
1
- import type { CommonProps, GenericIcon } from "../../typings";
2
-
3
- import Tippy from "@tippyjs/react";
4
- import clsx from "clsx";
5
- import React from "react";
6
-
7
- import { useTranslation } from "../../i18n";
8
- import { Icon } from "../../icons";
9
- import { Left } from "../../icons/Left";
10
- import { Right } from "../../icons/Right";
11
- import { WhiteboardAdd } from "../../icons/WhiteboardAdd";
12
- import { TopOffset } from "../../theme";
13
- import { useTheme, useWritable } from "../hooks";
14
- import { usePageControl } from "./hooks";
15
-
16
- export const name = "fastboard-page-control";
17
-
18
- export type PageControlProps = CommonProps & GenericIcon<"add" | "prev" | "next">;
19
-
20
- export function PageControl({
21
- theme,
22
- addIcon,
23
- addIconDisable,
24
- prevIcon,
25
- prevIconDisable,
26
- nextIcon,
27
- nextIconDisable,
28
- }: PageControlProps) {
29
- theme = useTheme(theme);
30
- const { t } = useTranslation();
31
-
32
- const writable = useWritable();
33
- const { pageIndex, pageCount, ...actions } = usePageControl();
34
-
35
- const disabled = !writable;
36
-
37
- return (
38
- <div className={clsx(name, theme)}>
39
- {/* <span className={clsx(`${name}-cut-line`, theme)} />{" "} */}
40
- <Tippy
41
- className="fastboard-tip"
42
- content={t("prevPage")}
43
- theme={theme}
44
- disabled={disabled}
45
- placement="top"
46
- delay={[1000, 400]}
47
- duration={300}
48
- offset={TopOffset}
49
- >
50
- <button
51
- className={clsx(`${name}-btn`, "prev", theme)}
52
- disabled={disabled || pageIndex === 0}
53
- onClick={actions.prevPage}
54
- >
55
- <Icon fallback={<Left theme={theme} />} src={disabled ? prevIconDisable : prevIcon} alt="[prev]" />
56
- </button>
57
- </Tippy>
58
- <span className={clsx(`${name}-text`, theme)}>
59
- <span className={clsx(`${name}-page`, theme)}>{pageCount === 0 ? "\u2026" : pageIndex + 1}</span>
60
- <span className={clsx(`${name}-slash`, theme)}>/</span>
61
- <span className={clsx(`${name}-page-count`, theme)}>{pageCount}</span>
62
- </span>
63
- <Tippy
64
- className="fastboard-tip"
65
- content={t("nextPage")}
66
- theme={theme}
67
- disabled={disabled}
68
- placement="top"
69
- delay={[1000, 400]}
70
- duration={300}
71
- offset={TopOffset}
72
- >
73
- <button
74
- className={clsx(`${name}-btn`, "next", theme)}
75
- disabled={disabled || pageIndex === pageCount - 1}
76
- onClick={actions.nextPage}
77
- >
78
- <Icon fallback={<Right theme={theme} />} src={disabled ? nextIconDisable : nextIcon} alt="[next]" />
79
- </button>
80
- </Tippy>
81
- <Tippy
82
- className="fastboard-tip"
83
- content={t("addPage")}
84
- theme={theme}
85
- disabled={disabled}
86
- placement="top"
87
- delay={[1000, 400]}
88
- duration={300}
89
- offset={TopOffset}
90
- >
91
- <button className={clsx(`${name}-btn`, "add", theme)} disabled={disabled} onClick={actions.addPage}>
92
- <Icon
93
- fallback={<WhiteboardAdd theme={theme} />}
94
- src={disabled ? addIconDisable : addIcon}
95
- alt="[add]"
96
- />
97
- </button>
98
- </Tippy>
99
- </div>
100
- );
101
- }
@@ -1,24 +0,0 @@
1
- import { useCallback } from "react";
2
- import { useFastboardApp, useFastboardValue } from "../hooks";
3
-
4
- export function usePageControl() {
5
- const app = useFastboardApp();
6
- const pageIndex = useFastboardValue(app.sceneIndex);
7
- const pageCount = useFastboardValue(app.sceneLength);
8
-
9
- const addPage = useCallback(async () => {
10
- await app.manager.switchMainViewToWriter();
11
- app.room.putScenes(app.manager.mainViewSceneDir, [{}], pageIndex + 1);
12
- await app.manager.setMainViewSceneIndex(pageIndex + 1);
13
- }, [app, pageIndex]);
14
-
15
- const prevPage = useCallback(async () => {
16
- await app.manager.setMainViewSceneIndex(pageIndex - 1);
17
- }, [app, pageIndex]);
18
-
19
- const nextPage = useCallback(async () => {
20
- await app.manager.setMainViewSceneIndex(pageIndex + 1);
21
- }, [app, pageIndex]);
22
-
23
- return { pageIndex, pageCount, prevPage, nextPage, addPage };
24
- }
@@ -1,2 +0,0 @@
1
- export * from "./hooks";
2
- export { PageControl, type PageControlProps } from "./PageControl";
@@ -1,146 +0,0 @@
1
- $name: "fastboard-player-control";
2
-
3
- .#{$name} {
4
- width: 100%;
5
- display: inline-flex;
6
- align-items: center;
7
- gap: 4px;
8
- padding: 4px;
9
- border-radius: 4px;
10
- backdrop-filter: blur(5px);
11
- -webkit-backdrop-filter: blur(5px);
12
- z-index: 100;
13
-
14
- &.auto-hide {
15
- opacity: 0;
16
- transition: opacity 0.2s;
17
-
18
- &:hover {
19
- opacity: 1;
20
- }
21
- }
22
-
23
- .rc-slider-disabled {
24
- background: transparent;
25
- opacity: 0.5;
26
- }
27
-
28
- .rc-slider-rail,
29
- .rc-slider-track {
30
- height: 2px;
31
- }
32
-
33
- .tippy-content {
34
- padding: 8px;
35
- }
36
- .tippy-box {
37
- border: 1px solid rgba(0, 0, 0, 0.15);
38
- background-color: rgba($color: #333, $alpha: 0.95);
39
- backdrop-filter: blur(2px);
40
- -webkit-backdrop-filter: blur(2px);
41
- }
42
- .tippy-box[data-theme~="light"] {
43
- background-color: rgba($color: #fff, $alpha: 0.95);
44
- box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.25);
45
- }
46
-
47
- &.light {
48
- color: #333;
49
- background-color: rgba(255, 255, 255, 0.9);
50
- border: 1px solid #e5e8f0;
51
- }
52
-
53
- &.dark {
54
- color: #ddd;
55
- background-color: #14181e;
56
- border: 1px solid #383b42;
57
- }
58
- }
59
-
60
- .#{$name}-btn {
61
- appearance: none;
62
- cursor: pointer;
63
- margin: 0;
64
- border: 0;
65
- padding: 0;
66
- min-width: 24px;
67
- height: 24px;
68
- background-color: transparent;
69
- border-radius: 4px;
70
- font-size: 24px;
71
- line-height: 1;
72
- display: inline-flex;
73
- align-items: center;
74
- justify-content: center;
75
-
76
- svg,
77
- img {
78
- width: 100%;
79
- height: 100%;
80
- }
81
-
82
- &:disabled {
83
- opacity: 0.5;
84
- cursor: not-allowed;
85
- }
86
-
87
- &.light:not(:disabled):hover {
88
- background-color: #ebf2ff;
89
- }
90
-
91
- &.dark:not(:disabled):hover {
92
- background-color: #383b42;
93
- }
94
-
95
- &.loading {
96
- animation: fastboard-player-control-rotate 0.5s linear infinite;
97
- }
98
-
99
- @keyframes fastboard-player-control-rotate {
100
- 100% {
101
- transform: rotate(360deg);
102
- }
103
- }
104
- }
105
-
106
- .#{$name}-panel {
107
- padding: 0;
108
- display: flex;
109
- flex-flow: column nowrap;
110
- align-items: stretch;
111
- gap: 4px;
112
-
113
- .#{$name}-btn {
114
- width: initial;
115
- height: initial;
116
- user-select: none;
117
- font-size: 14px;
118
- padding: 4px;
119
- justify-content: flex-end;
120
-
121
- &.active {
122
- color: #3381ff;
123
- }
124
- }
125
- }
126
-
127
- .#{$name}-slider {
128
- width: 100%;
129
- padding: 0 7px;
130
-
131
- &.loading {
132
- cursor: not-allowed;
133
- }
134
- }
135
-
136
- .#{$name}-slash {
137
- opacity: 0.6;
138
- }
139
-
140
- .#{$name}-current,
141
- .#{$name}-slash,
142
- .#{$name}-total,
143
- .#{$name}-speed-text {
144
- font-size: 14px;
145
- font-variant-numeric: tabular-nums;
146
- }
@@ -1,131 +0,0 @@
1
- import type { FastboardPlayer } from "@netless/fastboard-core";
2
- import type { CommonProps, GenericIcon } from "../../typings";
3
-
4
- import Tippy from "@tippyjs/react";
5
- import clsx from "clsx";
6
- import RcSlider from "rc-slider";
7
- import React, { useEffect, useState } from "react";
8
- import { PlayerPhase } from "white-web-sdk";
9
-
10
- import { useTranslation } from "../../i18n";
11
- import { Icon } from "../../icons";
12
- import { themes, TopOffset } from "../../theme";
13
- import { useTheme } from "../hooks";
14
- import { Button } from "./components/Button";
15
- import { usePlayerControl } from "./hooks";
16
- import { Icons } from "./icons";
17
-
18
- export type PlayerControlProps = {
19
- autoHide?: boolean;
20
- player?: FastboardPlayer;
21
- } & Omit<CommonProps, "room"> &
22
- GenericIcon<"play" | "pause" | "loading">;
23
-
24
- export const name = "fastboard-player-control";
25
-
26
- export function PlayerControl({ theme, autoHide = false, player: player_, ...icons }: PlayerControlProps) {
27
- theme = useTheme(theme);
28
- const { t } = useTranslation();
29
-
30
- const [currentTime, setCurrentTime] = useState(0);
31
- const player = usePlayerControl(player_?.player);
32
-
33
- useEffect(() => {
34
- setCurrentTime(player.currentTime);
35
- }, [player.currentTime]);
36
-
37
- useEffect(() => {
38
- if (player.currentTime !== currentTime) {
39
- player.seekToProgressTime(currentTime);
40
- }
41
- // eslint-disable-next-line react-hooks/exhaustive-deps
42
- }, [currentTime]);
43
-
44
- const isLoading = player.phase === PlayerPhase.WaitingFirstFrame || player.phase === PlayerPhase.Buffering;
45
- const isPlaying = player.phase === PlayerPhase.Playing;
46
-
47
- const { activeColor } = themes[theme];
48
-
49
- return (
50
- <div className={clsx(name, theme, { "auto-hide": autoHide })}>
51
- <button
52
- className={clsx(`${name}-btn`, isLoading ? "loading" : isPlaying ? "pause" : "play", theme)}
53
- disabled={isLoading}
54
- onClick={player.togglePlay}
55
- >
56
- <Icon
57
- fallback={
58
- isLoading ? (
59
- <Icons.Loading theme={theme} />
60
- ) : isPlaying ? (
61
- <Icons.Pause theme={theme} />
62
- ) : (
63
- <Icons.Play theme={theme} />
64
- )
65
- }
66
- src={isLoading ? icons.loadingIcon : isPlaying ? icons.pauseIcon : icons.playIcon}
67
- alt={isLoading ? "[loading]" : isPlaying ? "[pause]" : "[play]"}
68
- />
69
- </button>
70
- <span className={clsx(`${name}-slider`, { loading: isLoading }, theme)}>
71
- <RcSlider
72
- disabled={isLoading}
73
- trackStyle={{ background: activeColor }}
74
- handleStyle={{ border: `1px solid ${activeColor}` }}
75
- value={currentTime}
76
- onChange={setCurrentTime}
77
- min={0}
78
- max={player.totalTime}
79
- step={100}
80
- />
81
- </span>
82
- <span className={clsx(`${name}-current`, theme)}>{renderTime(player.currentTime)}</span>
83
- <span className={clsx(`${name}-slash`, theme)}>/</span>
84
- <span className={clsx(`${name}-total`, theme)}>{renderTime(player.totalTime)}</span>
85
- <span className={`${name}-btn-interactive`}>
86
- <Tippy
87
- className="fastboard-tip"
88
- content={renderSpeeds(player)}
89
- theme={theme}
90
- placement="top-end"
91
- trigger="click"
92
- offset={TopOffset}
93
- arrow={false}
94
- interactive
95
- >
96
- <Button content={t("speed")} theme={theme} disabled={isLoading}>
97
- <span className={clsx(`${name}-speed-text`, theme)}>{player.speed}x</span>
98
- </Button>
99
- </Tippy>
100
- </span>
101
- </div>
102
- );
103
- }
104
-
105
- function renderTime(ms: number) {
106
- let seconds = ms / 1000;
107
- const minutes = Math.floor(seconds / 60);
108
- seconds = Math.floor(seconds) % 60;
109
-
110
- return `${String(minutes).padStart(2, "0")}` + `:${String(seconds).padStart(2, "0")}`;
111
- }
112
-
113
- const Speeds = [2.0, 1.5, 1.25, 1.0, 0.75, 0.5];
114
-
115
- function renderSpeeds({ speed: current, setSpeed }: { speed: number; setSpeed: (speed: number) => void }) {
116
- return (
117
- <div className={clsx(`${name}-panel`, "speed")}>
118
- {Speeds.map(speed => (
119
- <button
120
- className={clsx(`${name}-btn`, "speed", {
121
- active: speed === current,
122
- })}
123
- key={speed}
124
- onClick={() => setSpeed(speed)}
125
- >
126
- {speed}x
127
- </button>
128
- ))}
129
- </div>
130
- );
131
- }
@@ -1,44 +0,0 @@
1
- import type { Theme } from "../../../typings";
2
-
3
- import clsx from "clsx";
4
- import React, { forwardRef, type PropsWithChildren } from "react";
5
- import Tippy from "@tippyjs/react";
6
-
7
- import { TopOffset } from "../../../theme";
8
-
9
- type ButtonProps = PropsWithChildren<{
10
- theme: Theme;
11
- content: React.ReactNode;
12
- disabled?: boolean;
13
- active?: boolean;
14
- onClick?: () => void;
15
- interactive?: boolean;
16
- placement?: "top" | "right"; // not using tippy.js's placement to satisfy dts
17
- }>;
18
-
19
- export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
20
- const { theme, content, disabled, active, onClick, interactive, placement = "top", children } = props;
21
-
22
- return (
23
- <Tippy
24
- className="fastboard-tip"
25
- content={content}
26
- interactive={interactive}
27
- theme={theme}
28
- disabled={disabled}
29
- placement={placement}
30
- offset={TopOffset}
31
- delay={[1000, 400]}
32
- duration={300}
33
- >
34
- <button
35
- ref={ref}
36
- className={clsx("fastboard-player-control-btn", theme, { active })}
37
- onClick={onClick}
38
- disabled={disabled}
39
- >
40
- {children}
41
- </button>
42
- </Tippy>
43
- );
44
- });