@livepreso/content-react 0.3.3 → 0.3.5

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/CHANGELOG.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "@livepreso/content-react",
3
3
  "entries": [
4
+ {
5
+ "version": "0.3.5",
6
+ "tag": "@livepreso/content-react_v0.3.5",
7
+ "date": "Sun, 03 Mar 2024 23:59:09 GMT",
8
+ "comments": {
9
+ "patch": [
10
+ {
11
+ "comment": "Hot reload improvements"
12
+ }
13
+ ],
14
+ "dependency": [
15
+ {
16
+ "comment": "Updating dependency \"@livepreso/content-base\" from `~1.1.2` to `~1.1.3`"
17
+ }
18
+ ]
19
+ }
20
+ },
21
+ {
22
+ "version": "0.3.4",
23
+ "tag": "@livepreso/content-react_v0.3.4",
24
+ "date": "Sun, 14 Jan 2024 21:06:16 GMT",
25
+ "comments": {
26
+ "patch": [
27
+ {
28
+ "comment": "Remove observer in use-modes due to render loops"
29
+ }
30
+ ]
31
+ }
32
+ },
4
33
  {
5
34
  "version": "0.3.3",
6
35
  "tag": "@livepreso/content-react_v0.3.3",
package/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # Change Log - @livepreso/content-react
2
2
 
3
- This log was last generated on Thu, 21 Dec 2023 00:50:30 GMT and should not be manually modified.
3
+ This log was last generated on Sun, 03 Mar 2024 23:59:09 GMT and should not be manually modified.
4
+
5
+ ## 0.3.5
6
+ Sun, 03 Mar 2024 23:59:09 GMT
7
+
8
+ ### Patches
9
+
10
+ - Hot reload improvements
11
+
12
+ ## 0.3.4
13
+ Sun, 14 Jan 2024 21:06:16 GMT
14
+
15
+ ### Patches
16
+
17
+ - Remove observer in use-modes due to render loops
4
18
 
5
19
  ## 0.3.3
6
20
  Thu, 21 Dec 2023 00:50:30 GMT
package/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import React, { useEffect } from "react";
2
2
 
3
3
  import { AppBase } from "@livepreso/content-base";
4
- import { createRoot } from "react-dom/client";
5
4
  import { CompanywideJSON } from "./lib/components/CompanywideJSON.js";
6
5
  import { SlideContext } from "./lib/contexts.js";
6
+ import { createRoot } from "react-dom/client";
7
7
  import errorFn from "@livepreso/content-base/error.js";
8
8
  import { useRootSlide } from "./lib/hooks/use-root-slide.js";
9
9
 
@@ -26,6 +26,10 @@ class ErrorBoundary extends React.Component {
26
26
  }
27
27
  }
28
28
 
29
+ if (module.hot && typeof window.__deck_roots__ === "undefined") {
30
+ window.__deck_roots__ = {};
31
+ }
32
+
29
33
  export class App extends AppBase {
30
34
  static run(rootApp = null) {
31
35
  const base = new App();
@@ -41,8 +45,17 @@ export class App extends AppBase {
41
45
 
42
46
  defaultRenderer(Slide, article) {
43
47
  const el = article.get(0);
48
+
49
+ if (module.hot && window.__deck_roots__[el.id]) {
50
+ this.unmountRoot(el.id);
51
+ }
52
+
44
53
  const root = createRoot(el);
45
54
 
55
+ if (module.hot) {
56
+ window.__deck_roots__[el.id] = root;
57
+ }
58
+
46
59
  const RootApp = this.rootApp(Slide);
47
60
 
48
61
  function App() {
@@ -52,10 +65,17 @@ export class App extends AppBase {
52
65
  useEffect(() => {
53
66
  function slideClosed() {
54
67
  root.unmount();
68
+ if (module.hot) {
69
+ delete window.__deck_roots__[el.id];
70
+ }
55
71
  }
56
72
 
57
73
  article.on("slide:closed", slideClosed);
58
- });
74
+
75
+ return () => {
76
+ article.off("slide:closed", slideClosed);
77
+ };
78
+ }, []);
59
79
 
60
80
  return (
61
81
  <SlideContext.Provider value={slideState}>
@@ -70,6 +90,25 @@ export class App extends AppBase {
70
90
 
71
91
  root.render(<App />);
72
92
  }
93
+
94
+ unmountRoot(key) {
95
+ if (!window.__deck_roots__[key]) {
96
+ return;
97
+ }
98
+ window.__deck_roots__[key].unmount();
99
+ delete window.__deck_roots__[key];
100
+ }
101
+
102
+ refresh() {
103
+ const keys = this.getActiveSlideKeys();
104
+ for (const key of keys) {
105
+ if (window.__deck_roots__?.[key]) {
106
+ this.unmountRoot(key);
107
+ }
108
+
109
+ this.renderSlide(key);
110
+ }
111
+ }
73
112
  }
74
113
 
75
114
  export * from "./lib/hooks";
@@ -1,9 +1,11 @@
1
+ export * from "./use-slide-ready.js";
1
2
  export * from "./use-shared-state.js";
2
3
  export * from "./use-async-reference.js";
3
4
  export * from "./use-modes.js";
4
- export * from "./use-context.js";
5
+ export * from "./use-slide-context.js";
5
6
  export * from "./use-companywide.js";
6
7
  export * from "./use-slide.js";
7
8
  export * from "./use-transitions.js";
8
9
  export * from "./use-subslides.js";
9
10
  export * from "./use-prep-editable.js";
11
+ export * from "./use-slide-done.js";
@@ -1,6 +1,4 @@
1
- import { useEffect, useState } from "react";
2
-
3
- function getModes() {
1
+ export function useModes() {
4
2
  const bodyClasses = [...document.body.classList];
5
3
 
6
4
  const isMaster = bodyClasses.includes("master");
@@ -33,21 +31,3 @@ function getModes() {
33
31
  isPresomanager,
34
32
  };
35
33
  }
36
-
37
- export function useModes() {
38
- const [modes, setModes] = useState(getModes());
39
-
40
- useEffect(() => {
41
- const observer = new MutationObserver(() => {
42
- setModes(getModes());
43
- });
44
-
45
- observer.observe(document.body, {
46
- attributes: ["classList"],
47
- });
48
-
49
- return () => observer.disconnect();
50
- }, []);
51
-
52
- return modes;
53
- }
@@ -1,5 +1,5 @@
1
1
  import { useAsyncReference, useModes } from "./index.js";
2
- import { useEffect, useState } from "react";
2
+ import { useEffect, useRef, useState } from "react";
3
3
 
4
4
  import ulog from "ulog";
5
5
 
@@ -27,6 +27,7 @@ export function useRootSlide(article) {
27
27
  const [synced, setSynced] = useState(!isRemote);
28
28
  const [internalSlideState, setInternalSlideState] = useAsyncReference({});
29
29
  const [slideReady, setSlideReady] = useState(false);
30
+ const doneRegister = useRef({ promises: [] });
30
31
 
31
32
  const [transitions, setTransitions] = useState({
32
33
  ...DEFAULT_TRANSITION_STATE,
@@ -105,11 +106,21 @@ export function useRootSlide(article) {
105
106
  const slideExiting = updateState("exiting");
106
107
  const slideExited = updateState("exited");
107
108
 
109
+ article.off();
108
110
  article.on("slide:entering", slideEntering);
109
111
  article.on("slide:entered", slideEntered);
110
112
  article.on("slide:exiting", slideExiting);
111
113
  article.on("slide:exited", slideExited);
112
- article.on("slideready", (e) => {
114
+ article.on("slide:closed", () => {
115
+ doneRegister.current.promises = [];
116
+ });
117
+ article.on("slideready", (e, done) => {
118
+ Promise.all(
119
+ doneRegister.current.promises
120
+ ).then(() => {
121
+ done();
122
+ });
123
+
113
124
  setSlideReady(true);
114
125
  slideEntered(e);
115
126
  });
@@ -124,6 +135,7 @@ export function useRootSlide(article) {
124
135
  transitions,
125
136
  slideKey: key,
126
137
  isCurrentSlide: isCurrent,
138
+ doneRegister,
127
139
  rootElement: article.get(0),
128
140
 
129
141
  // For use by shared state
@@ -0,0 +1,62 @@
1
+ import isPlainObject from "lodash.isplainobject";
2
+ import { useEffect } from "react";
3
+ import { useSharedState } from "./index.js";
4
+
5
+ function getValueFromContext(context, path, defaultValue) {
6
+ if (path) {
7
+ return Bridge.Context.get(path, defaultValue);
8
+ }
9
+
10
+ return context;
11
+ }
12
+
13
+ export function useSlideContext(contextPath = null, defaultValue = null) {
14
+ let persist = false;
15
+ let path = contextPath;
16
+
17
+ const value = getValueFromContext(
18
+ window.Bridge.Context.getDict(),
19
+ path,
20
+ defaultValue
21
+ );
22
+ const [context, setContextState] = useSharedState(value);
23
+
24
+ // You could pass in an object, so that you can set whether
25
+ // the saves are persistent or not.
26
+ if (isPlainObject(contextPath)) {
27
+ path = contextPath.path || null;
28
+ persist = contextPath.save || false;
29
+ }
30
+
31
+ function setContext(key, data) {
32
+ let setPath = key;
33
+ let setData = data;
34
+
35
+ if (path) {
36
+ if (data) {
37
+ setPath = `${path}.${key}`;
38
+ } else {
39
+ setPath = path;
40
+ setData = key;
41
+ }
42
+ }
43
+
44
+ Bridge.Context.set(setPath, setData, persist);
45
+
46
+ const value = getValueFromContext(
47
+ Bridge.Context.getDict(),
48
+ path,
49
+ defaultValue
50
+ );
51
+ setContextState(value);
52
+ }
53
+
54
+ useEffect(() => {
55
+ if (context !== value) {
56
+ Bridge.Context.set(path, context, persist);
57
+ }
58
+ }, [context]);
59
+
60
+ return [context, setContext];
61
+ }
62
+
@@ -0,0 +1,27 @@
1
+ import { useEffect, useRef, useCallback } from "react";
2
+ import { useSlide } from "./use-slide";
3
+
4
+ export function useSlideDone() {
5
+ const { doneRegister } = useSlide();
6
+ const doneFn = useRef(null);
7
+
8
+ const callback = useCallback(() => {
9
+ if (!doneFn.current) {
10
+ console.warn(
11
+ "Attempted to call useSlideDone() callback before component mounted.",
12
+ );
13
+ return;
14
+ }
15
+ doneFn.current();
16
+ }, []);
17
+
18
+ useEffect(() => {
19
+ doneRegister.current.promises.push(
20
+ new Promise((resolve) => {
21
+ doneFn.current = resolve;
22
+ }),
23
+ );
24
+ }, []);
25
+
26
+ return callback;
27
+ }
@@ -0,0 +1,12 @@
1
+ import { useEffect } from "react";
2
+ import { useSlide } from "./use-slide";
3
+
4
+ export function useSlideReady(fn) {
5
+ const { isSlideReady } = useSlide();
6
+
7
+ useEffect(() => {
8
+ if (isSlideReady) {
9
+ fn();
10
+ }
11
+ }, [isSlideReady]);
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livepreso/content-react",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
@@ -17,7 +17,7 @@
17
17
  "dependencies": {
18
18
  "react": "~18.2.0",
19
19
  "react-dom": "~18.2.0",
20
- "@livepreso/content-base": "~1.1.2",
20
+ "@livepreso/content-base": "~1.1.3",
21
21
  "ulog": "~2.0.0-beta.18",
22
22
  "lodash.get": "~4.4.2",
23
23
  "lodash.isplainobject": "~4.0.6",
@@ -1,61 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- import isPlainObject from "lodash.isplainobject";
4
-
5
- function getValueFromContext(context, path, defaultValue) {
6
- if (path) {
7
- return Bridge.Context.get(path, defaultValue);
8
- }
9
-
10
- return context;
11
- }
12
-
13
- export function useContext(contextPath = null, defaultValue = null) {
14
- let persist = false;
15
- let path = contextPath;
16
-
17
- const value = getValueFromContext(
18
- window.Bridge.Context.getDict(),
19
- path,
20
- defaultValue,
21
- );
22
- const [context, setContextState] = useState(value);
23
-
24
- // You could pass in an object, so that you can set whether
25
- // the saves are persistent or not.
26
- if (isPlainObject(contextPath)) {
27
- path = contextPath.path || null;
28
- persist = contextPath.save || false;
29
- }
30
-
31
- function setContext(key, data) {
32
- let setPath = key;
33
- let setData = data;
34
-
35
- if (path) {
36
- if (data) {
37
- setPath = `${path}.${key}`;
38
- } else {
39
- setPath = path;
40
- setData = key;
41
- }
42
- }
43
-
44
- Bridge.Context.set(setPath, setData, persist);
45
-
46
- const value = getValueFromContext(
47
- Bridge.Context.getDict(),
48
- path,
49
- defaultValue,
50
- );
51
- setContextState(value);
52
- }
53
-
54
- useEffect(() => {
55
- if (context !== value) {
56
- Bridge.Context.set(path, context, persist);
57
- }
58
- }, [context]);
59
-
60
- return [context, setContext];
61
- }