@netless/fastboard-react 0.2.8 → 0.2.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/fastboard-react",
3
- "version": "0.2.8",
3
+ "version": "0.2.11",
4
4
  "description": "A UI kit built on top of @netless/fastboard.",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -11,7 +11,7 @@
11
11
  "@tippyjs/react": "^4.2.6",
12
12
  "clsx": "^1.1.1",
13
13
  "framer-motion": "^6.2.8",
14
- "i18next": "^21.6.12",
14
+ "i18next": "^21.6.13",
15
15
  "rc-slider": "^9.7.5"
16
16
  },
17
17
  "peerDependencies": {
@@ -22,13 +22,13 @@
22
22
  "white-web-sdk": ">=2.16.0"
23
23
  },
24
24
  "devDependencies": {
25
- "@netless/fastboard-core": "0.2.8",
26
- "@netless/window-manager": "^0.4.7",
25
+ "@netless/fastboard-core": "0.2.11",
26
+ "@netless/window-manager": "^0.4.10",
27
27
  "@types/react": "^17.0.39",
28
- "@types/react-dom": "^17.0.11",
29
- "sass": "^1.49.8",
28
+ "@types/react-dom": "^17.0.13",
29
+ "sass": "^1.49.9",
30
30
  "tippy.js": "^6.3.7",
31
- "tsup": "^5.11.13",
31
+ "tsup": "^5.12.0",
32
32
  "white-web-sdk": "^2.16.10"
33
33
  },
34
34
  "scripts": {
@@ -37,5 +37,5 @@
37
37
  "check": "tsc --noEmit"
38
38
  },
39
39
  "module": "dist/index.mjs",
40
- "types": "src/index.ts"
40
+ "types": "dist/index.d.ts"
41
41
  }
@@ -39,3 +39,8 @@
39
39
  left: auto;
40
40
  right: 8px;
41
41
  }
42
+
43
+ .fastboard-bottom {
44
+ @extend .fastboard-bottom-left;
45
+ right: 8px;
46
+ }
@@ -10,6 +10,7 @@ import { PageControl } from "./PageControl";
10
10
  import { RedoUndo } from "./RedoUndo";
11
11
  import { Toolbar } from "./Toolbar";
12
12
  import { ZoomControl } from "./ZoomControl";
13
+ import { hideAll } from "./tippy-util";
13
14
 
14
15
  export interface FastboardProps {
15
16
  app?: FastboardApp | null;
@@ -58,7 +59,7 @@ function FastboardInternal({
58
59
 
59
60
  const useWhiteboard = useCallback(
60
61
  (container: HTMLDivElement | null) => {
61
- if (container && app) app.manager.bindContainer(container);
62
+ if (container && app) app.bindContainer(container);
62
63
  },
63
64
  [app]
64
65
  );
@@ -77,11 +78,7 @@ function FastboardInternal({
77
78
  <ThemeContext.Provider value={theme}>
78
79
  <I18nContext.Provider value={i18n}>
79
80
  <div {...restProps} className="fastboard-root" ref={forwardedRef}>
80
- <div
81
- className="fastboard-view"
82
- ref={useWhiteboard}
83
- onPointerDownCapture={focusThisElementImmediate}
84
- />
81
+ <div className="fastboard-view" ref={useWhiteboard} onTouchStartCapture={hideAll} />
85
82
  {children ? (
86
83
  children
87
84
  ) : (
@@ -109,7 +106,3 @@ function FastboardInternal({
109
106
  </ThemeContext.Provider>
110
107
  );
111
108
  }
112
-
113
- function focusThisElementImmediate(ev: React.PointerEvent<HTMLDivElement>) {
114
- ev.currentTarget.focus();
115
- }
@@ -9,6 +9,7 @@ $name: "fastboard-player-control";
9
9
  border-radius: 4px;
10
10
  backdrop-filter: blur(5px);
11
11
  -webkit-backdrop-filter: blur(5px);
12
+ z-index: 100;
12
13
 
13
14
  &.auto-hide {
14
15
  opacity: 0;
@@ -1,4 +1,4 @@
1
- import type { Player } from "white-web-sdk";
1
+ import type { FastboardPlayer } from "@netless/fastboard-core";
2
2
  import type { CommonProps, GenericIcon } from "../../typings";
3
3
 
4
4
  import Tippy from "@tippyjs/react";
@@ -17,7 +17,7 @@ import { Icons } from "./icons";
17
17
 
18
18
  export type PlayerControlProps = {
19
19
  autoHide?: boolean;
20
- player?: Player;
20
+ player?: FastboardPlayer;
21
21
  } & Omit<CommonProps, "room"> &
22
22
  GenericIcon<"play" | "pause" | "loading">;
23
23
 
@@ -28,7 +28,7 @@ export function PlayerControl({ theme, autoHide = false, player: player_, ...ico
28
28
  const { t } = useTranslation();
29
29
 
30
30
  const [currentTime, setCurrentTime] = useState(0);
31
- const player = usePlayerControl(player_);
31
+ const player = usePlayerControl(player_?.player);
32
32
 
33
33
  useEffect(() => {
34
34
  setCurrentTime(player.currentTime);
@@ -1,4 +1,3 @@
1
- import type { Placement } from "tippy.js";
2
1
  import type { Theme } from "../../../typings";
3
2
 
4
3
  import clsx from "clsx";
@@ -14,7 +13,7 @@ type ButtonProps = PropsWithChildren<{
14
13
  active?: boolean;
15
14
  onClick?: () => void;
16
15
  interactive?: boolean;
17
- placement?: Placement;
16
+ placement?: "top" | "right"; // not using tippy.js's placement to satisfy dts
18
17
  }>;
19
18
 
20
19
  export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
@@ -0,0 +1,36 @@
1
+ import type { FastboardPlayer } from "@netless/fastboard-core";
2
+ import type { DivProps } from "./Fastboard";
3
+ import type { Theme } from "../typings";
4
+
5
+ import React, { forwardRef, useCallback } from "react";
6
+ import { PlayerControl } from "./PlayerControl";
7
+
8
+ export interface ReplayFastboardProps {
9
+ player?: FastboardPlayer | null;
10
+ theme?: Theme;
11
+ autoHideControl?: boolean;
12
+ }
13
+
14
+ export const ReplayFastboard = /* @__PURE__ */ forwardRef<HTMLDivElement, ReplayFastboardProps & DivProps>(
15
+ function ReplayFastboard({ player, theme = "light", autoHideControl = false, ...restProps }, ref) {
16
+ const useWhiteboard = useCallback(
17
+ (container: HTMLDivElement | null) => {
18
+ if (container && player) player.bindContainer(container);
19
+ },
20
+ [player]
21
+ );
22
+
23
+ if (!player) {
24
+ return <div className="fastboard-root" ref={ref} {...restProps} />;
25
+ }
26
+
27
+ return (
28
+ <div className="fastboard-root" ref={ref} {...restProps}>
29
+ <div className="fastboard-view" ref={useWhiteboard} />
30
+ <div className="fastboard-bottom">
31
+ <PlayerControl player={player} theme={theme} autoHide={autoHideControl} />
32
+ </div>
33
+ </div>
34
+ );
35
+ }
36
+ );
@@ -153,12 +153,12 @@ $name: "fastboard-toolbar";
153
153
  }
154
154
  }
155
155
 
156
- &-section + &-mask {
156
+ &-section ~ &-mask {
157
157
  opacity: 0;
158
158
  transition: 0.5s opacity 0.4s;
159
159
  }
160
160
 
161
- &-section:hover + &-mask,
161
+ &-section:hover ~ &-mask,
162
162
  &-mask:hover {
163
163
  opacity: 1;
164
164
  transition: 0.2s opacity;
@@ -173,7 +173,7 @@ $name: "fastboard-toolbar";
173
173
  gap: 8px;
174
174
 
175
175
  &.apps {
176
- width: 240px - 8px * 2;
176
+ width: 240px + 8px * 2;
177
177
  }
178
178
  }
179
179
 
@@ -207,14 +207,33 @@ $name: "fastboard-toolbar";
207
207
  }
208
208
  }
209
209
 
210
+ &-app-icon-wrapper {
211
+ position: relative;
212
+ display: flex;
213
+ align-items: center;
214
+ justify-content: center;
215
+ }
216
+
217
+ &-app-icon-mask {
218
+ position: absolute;
219
+ width: 36px;
220
+ height: 36px;
221
+ animation: fastboard-app-loading-rotate 0.5s linear infinite;
222
+ transform-origin: center;
223
+ }
224
+
210
225
  &-app-is-loading {
211
- opacity: 0.8;
212
226
  cursor: wait;
227
+ button:disabled {
228
+ cursor: wait;
229
+ }
213
230
  }
214
231
 
215
232
  &-app-is-failed {
216
- opacity: 0.5;
217
233
  cursor: not-allowed;
234
+ button:disabled {
235
+ cursor: not-allowed;
236
+ }
218
237
  }
219
238
 
220
239
  &-app-icon {
@@ -313,3 +332,12 @@ $name: "fastboard-toolbar";
313
332
  left: 0;
314
333
  }
315
334
  }
335
+
336
+ @keyframes fastboard-app-loading-rotate {
337
+ from {
338
+ transform: rotate(0deg);
339
+ }
340
+ to {
341
+ transform: rotate(360deg);
342
+ }
343
+ }
@@ -12,6 +12,7 @@ import { Icons } from "../icons";
12
12
  import { ToolbarContext } from "../Toolbar";
13
13
  import { Button } from "./Button";
14
14
  import { useFastboardApp } from "../../hooks";
15
+ import { Loading } from "../icons/Loading";
15
16
 
16
17
  export interface AppsButtonProps {
17
18
  content?: React.ReactNode;
@@ -104,22 +105,30 @@ interface AppIconProps {
104
105
  }
105
106
 
106
107
  function AppIcon({ title, src, alt, appStatus, onClick }: AppIconProps) {
108
+ const { theme } = useContext(ToolbarContext);
107
109
  const { status = "idle", reason } = appStatus || {};
108
110
  const loading = status === "loading";
109
111
  const failed = status === "failed";
110
112
  const unifiedTitle = loading ? "loading" : failed ? reason : title;
111
113
 
112
114
  return (
113
- <span
114
- className={clsx("fastboard-toolbar-app-icon", {
115
- "fastboard-toolbar-app-is-loading": loading,
116
- "fastboard-toolbar-app-is-failed": failed,
117
- })}
118
- >
119
- <Button disabled={failed} placement="top" content={unifiedTitle} onClick={onClick}>
120
- <img src={src} alt={alt} title={unifiedTitle} />
121
- </Button>
122
- <span className="fastboard-toolbar-app-icon-text">{title}</span>
123
- </span>
115
+ <div className="fastboard-toolbar-app-icon-wrapper">
116
+ <span
117
+ className={clsx("fastboard-toolbar-app-icon", {
118
+ "fastboard-toolbar-app-is-loading": loading,
119
+ "fastboard-toolbar-app-is-failed": failed,
120
+ })}
121
+ >
122
+ <Button disabled={failed || loading} placement="top" content={unifiedTitle} onClick={onClick}>
123
+ <img src={src} alt={alt} title={unifiedTitle} />
124
+ </Button>
125
+ <span className="fastboard-toolbar-app-icon-text">{title}</span>
126
+ </span>
127
+ {loading && (
128
+ <span className="fastboard-toolbar-app-icon-mask">
129
+ <Loading theme={theme} />
130
+ </span>
131
+ )}
132
+ </div>
124
133
  );
125
134
  }
@@ -1,4 +1,3 @@
1
- import type { Placement } from "tippy.js";
2
1
  import type { PropsWithChildren } from "react";
3
2
 
4
3
  import clsx from "clsx";
@@ -14,7 +13,7 @@ type ButtonProps = PropsWithChildren<{
14
13
  active?: boolean;
15
14
  onClick?: () => void;
16
15
  interactive?: boolean;
17
- placement?: Placement;
16
+ placement?: "top" | "right"; // not using tippy.js's placement to satisfy dts
18
17
  }>;
19
18
 
20
19
  export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
@@ -0,0 +1,13 @@
1
+ import type { IconProps } from "../../../typings";
2
+
3
+ import React from "react";
4
+ import { getStroke } from "../../../theme";
5
+
6
+ export const Loading = (props: IconProps) => {
7
+ const stroke = getStroke(props);
8
+ return (
9
+ <svg viewBox="0 0 24 24">
10
+ <path fill={stroke} d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8Z"></path>
11
+ </svg>
12
+ );
13
+ };
@@ -0,0 +1,8 @@
1
+ import type { Instance } from "tippy.js";
2
+
3
+ export function hideAll() {
4
+ Array.from(document.querySelectorAll("[data-tippy-root]")).forEach(e => {
5
+ const instance = (e as unknown as { _tippy?: Instance })._tippy;
6
+ if (instance) instance.hide();
7
+ });
8
+ }
package/src/index.ts CHANGED
@@ -8,4 +8,5 @@ export * from "./components/PageControl";
8
8
  export * from "./components/Toolbar";
9
9
  export * from "./components/PlayerControl";
10
10
  export * from "./components/Fastboard";
11
+ export * from "./components/ReplayFastboard";
11
12
  export * from "./vanilla";