@livepreso/content-react 0.7.3 → 1.0.1

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "../../content/content-react": "../../content/content-react:sVKowAAy4A1Ch2RDVit/Vz9vWyB4NwGiVz9KXkm7taw=:",
2
+ "../../content/content-react": "../../content/content-react:LI4QvLCR+A7lRqOhNBgyXxl132hAY0Q+y0icexYILvQ=:",
3
3
  "/@ampproject/remapping@2.3.0": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
4
4
  "/@babel/code-frame@7.26.2": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
5
5
  "/@babel/compat-data@7.26.2": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==",
@@ -106,6 +106,7 @@
106
106
  "/levn@0.4.1": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
107
107
  "/locate-path@6.0.0": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
108
108
  "/lodash.get@4.4.2": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
109
+ "/lodash.isequal@4.5.0": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
109
110
  "/lodash.isplainobject@4.0.6": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
110
111
  "/lodash.merge@4.6.2": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
111
112
  "/lodash.set@4.3.2": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==",
package/CHANGELOG.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "@livepreso/content-react",
3
3
  "entries": [
4
+ {
5
+ "version": "1.0.1",
6
+ "tag": "@livepreso/content-react_v1.0.1",
7
+ "date": "Thu, 12 Jun 2025 03:07:46 GMT",
8
+ "comments": {
9
+ "patch": [
10
+ {
11
+ "comment": "Update content-react useCWEEditableInfo hook to allow saved value to be an empty string."
12
+ },
13
+ {
14
+ "comment": "Add event listener for context changes to useSlideContext."
15
+ }
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "version": "1.0.0",
21
+ "tag": "@livepreso/content-react_v1.0.0",
22
+ "date": "Thu, 08 May 2025 06:12:34 GMT",
23
+ "comments": {
24
+ "patch": [
25
+ {
26
+ "comment": "Add warning to companywide editable docs about repeated keys"
27
+ }
28
+ ],
29
+ "major": [
30
+ {
31
+ "comment": "Replace usePrepEditable() hook with usePrepEditableInfo(), add useCWEEditableInfo(), improve editables interactions with new utils."
32
+ }
33
+ ]
34
+ }
35
+ },
4
36
  {
5
37
  "version": "0.7.3",
6
38
  "tag": "@livepreso/content-react_v0.7.3",
package/CHANGELOG.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # Change Log - @livepreso/content-react
2
2
 
3
- This log was last generated on Wed, 23 Apr 2025 22:55:14 GMT and should not be manually modified.
3
+ This log was last generated on Thu, 12 Jun 2025 03:07:46 GMT and should not be manually modified.
4
+
5
+ ## 1.0.1
6
+ Thu, 12 Jun 2025 03:07:46 GMT
7
+
8
+ ### Patches
9
+
10
+ - Update content-react useCWEEditableInfo hook to allow saved value to be an empty string.
11
+ - Add event listener for context changes to useSlideContext.
12
+
13
+ ## 1.0.0
14
+ Thu, 08 May 2025 06:12:34 GMT
15
+
16
+ ### Breaking changes
17
+
18
+ - Replace usePrepEditable() hook with usePrepEditableInfo(), add useCWEEditableInfo(), improve editables interactions with new utils.
19
+
20
+ ### Patches
21
+
22
+ - Add warning to companywide editable docs about repeated keys
4
23
 
5
24
  ## 0.7.3
6
25
  Wed, 23 Apr 2025 22:55:14 GMT
@@ -1,20 +1,30 @@
1
1
  import React, { useEffect, useMemo, useRef, useState } from "react";
2
2
 
3
- import { CWE_JSON_KEY } from "../constants.js";
4
3
  import { CompanywideJSONContext } from "../contexts.js";
5
4
  import set from "lodash.set";
6
5
  import unescape from "lodash.unescape";
7
6
  import { useEditableInitialState } from "../hooks/use-editable-initial-state.js";
8
7
  import { useSlide } from "../hooks/use-slide.js";
8
+ import { getCWEJSONKey, testCWEJSONExists } from "../utils.js";
9
9
 
10
+ /**
11
+ * IMPORTANT: This component must always wrap RootApp in index.js.
12
+ * We attempted to limit its use to the on-stage / active slide,
13
+ * but that resulted in off-stage slides (ie. previous and next)
14
+ * getting default values instead of latest, resulting in janky animations
15
+ * (and probably more problems too...)
16
+ */
10
17
  export function CompanywideJSON({ children }) {
11
- const { slideKey } = useSlide();
18
+ const slideState = useSlide();
19
+ const { slideKey } = slideState;
12
20
  const editableRef = useRef();
13
21
  const [editableValue, editableKey] = useEditableInitialState(
14
- `${slideKey}-${CWE_JSON_KEY}`,
22
+ getCWEJSONKey(slideKey),
15
23
  "{}",
16
24
  );
17
25
 
26
+ const cweJsonExists = testCWEJSONExists(slideState);
27
+
18
28
  let initialState = {};
19
29
 
20
30
  try {
@@ -43,15 +53,15 @@ export function CompanywideJSON({ children }) {
43
53
  const providerValues = useMemo(
44
54
  () => ({
45
55
  companyWide: state,
46
- // If not editable, we just pass a no-op function
47
- updateCompanywide: editableKey ? updateField : () => {},
56
+ // Returns no-op if CWE JSON is not in use on the current slide
57
+ updateCompanywide: cweJsonExists ? updateField : () => {},
48
58
  }),
49
- [state],
59
+ [cweJsonExists, state],
50
60
  );
51
61
 
52
62
  return (
53
63
  <CompanywideJSONContext.Provider value={providerValues}>
54
- {editableKey && (
64
+ {cweJsonExists && (
55
65
  <div
56
66
  data-companywide-editable={editableKey}
57
67
  ref={editableRef}
@@ -0,0 +1,82 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+
3
+ import { useModes } from "./use-modes";
4
+ import { useSlide } from "./use-slide";
5
+ import { useEditableFieldKey } from "./use-editable-field-key";
6
+ import { getCWEData, getUserTemplateData } from "../utils";
7
+
8
+ /**
9
+ * Returns information about a CWE, PresoManager template or user template editable
10
+ * on the current slide. Returns a modified key when the requested editable is found
11
+ * on a user or PM template.
12
+ *
13
+ * Example:
14
+ *
15
+ * useCWEEditable("test");
16
+ * => {
17
+ * key: "test-adjunct-1168",
18
+ * value: "User edited text",
19
+ * hasValue: true
20
+ * }
21
+ */
22
+ export function useCWEEditableInfo(id) {
23
+ const { isPresomanager } = useModes();
24
+ const { slideID, isUserTemplate } = useSlide();
25
+
26
+ const key = useEditableFieldKey(id);
27
+
28
+ const getValue = useCallback(() => {
29
+ let data;
30
+
31
+ if (isUserTemplate) {
32
+ data = getUserTemplateData(key, slideID);
33
+ } else {
34
+ data = getCWEData(key);
35
+ }
36
+
37
+ return data?.value ?? data?.accepted?.value ?? data?.saved?.value ?? null;
38
+ }, [key, slideID, isUserTemplate]);
39
+
40
+ const [value, setValue] = useState(getValue);
41
+
42
+ function updateValue() {
43
+ const newValue = getValue();
44
+
45
+ if (value === newValue) {
46
+ return;
47
+ }
48
+
49
+ setValue(newValue);
50
+ }
51
+
52
+ useEffect(() => {
53
+ if (isPresomanager) return;
54
+
55
+ function onContextChange() {
56
+ updateValue();
57
+ }
58
+
59
+ Bridge.presenter.getPresentation().on("change:context", onContextChange);
60
+
61
+ return () => {
62
+ Bridge.presenter.getPresentation().off("change:context", onContextChange);
63
+ };
64
+ }, [isPresomanager, value, getValue]);
65
+
66
+ // When in Presomanager we cannot use a state (nothing we can watch - yet),
67
+ // in this mode, the latest saved value can be fetched on re-render.
68
+ return {
69
+ key,
70
+ value: isPresomanager ? getValue() : value,
71
+ hasValue: (isPresomanager ? getValue() : value) !== null,
72
+ updateValue,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Returns the current value of a CWE editable on the current slide.
78
+ * (standard slide, PresoManager template or user template)
79
+ */
80
+ export function useCWEEditableValue(id, defaultValue = null) {
81
+ return useCWEEditableInfo(id).value ?? defaultValue;
82
+ }
@@ -7,6 +7,8 @@ export { useCompanywide } from "./use-companywide.js";
7
7
  export { useSlide } from "./use-slide.js";
8
8
  export { useTransitions } from "./use-transitions.js";
9
9
  export { useSubslides } from "./use-subslides.js";
10
- export { usePrepEditable } from "./use-prep-editable.js";
11
10
  export { useSlideDone } from "./use-slide-done.js";
11
+ export { useEditableFieldKey } from "./use-editable-field-key.js";
12
12
  export { useEditableInitialState } from "./use-editable-initial-state.js";
13
+ export { usePrepEditableInfo, usePrepEditableValue } from "./prep-editable.js";
14
+ export { useCWEEditableInfo, useCWEEditableValue } from "./cwe-editable.js";
@@ -0,0 +1,74 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+
3
+ import { useModes } from "./use-modes";
4
+ import { useSlide } from "./use-slide";
5
+ import { useEditableFieldKey } from "./use-editable-field-key";
6
+
7
+ /**
8
+ * Returns information about a prep editable on the current slide.
9
+ *
10
+ * If the prep editable field is found on a user or PM template, the key
11
+ * will be adjusted to match the required format.
12
+ *
13
+ * Example:
14
+ *
15
+ * usePrepEditable("test");
16
+ * => {
17
+ * key: "test",
18
+ * value: "User edited text",
19
+ * hasValue: true
20
+ * }
21
+ */
22
+ export function usePrepEditableInfo(id) {
23
+ const { isPresomanager } = useModes();
24
+ const { slideID } = useSlide();
25
+
26
+ const key = useEditableFieldKey(id, { isPrep: true });
27
+
28
+ const prepKey = `${slideID}--${key}`;
29
+ const keyPath = `_editableContent.${prepKey}.value`;
30
+
31
+ const getValue = useCallback(() => {
32
+ return Bridge.Context.get(keyPath, null);
33
+ }, [keyPath]);
34
+
35
+ const [value, setValue] = useState(getValue());
36
+
37
+ function updateValue() {
38
+ const newValue = getValue();
39
+
40
+ if (value === newValue) {
41
+ return;
42
+ }
43
+
44
+ setValue(newValue);
45
+ }
46
+
47
+ useEffect(() => {
48
+ if (isPresomanager) return;
49
+
50
+ function onContextChange() {
51
+ updateValue();
52
+ }
53
+
54
+ Bridge.presenter.getPresentation().on("change:context", onContextChange);
55
+
56
+ return () => {
57
+ Bridge.presenter.getPresentation().off("change:context", onContextChange);
58
+ };
59
+ }, [isPresomanager, value, getValue]);
60
+
61
+ return {
62
+ key,
63
+ value: value,
64
+ hasValue: value !== null,
65
+ updateValue,
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Returns the current value of a prep editable on the current slide.
71
+ */
72
+ export function usePrepEditableValue(id, defaultValue = null) {
73
+ return usePrepEditableInfo(id).value ?? defaultValue;
74
+ }
@@ -0,0 +1,19 @@
1
+ import { useSlide } from "./use-slide.js";
2
+ import { getPMTemplateFieldKey, getUserTemplateFieldKey } from "../utils.js";
3
+
4
+ /**
5
+ * Takes a base field key, and returns a modified field key depending on the slide type
6
+ * (ie. standard, user template or presomanager template).
7
+ *
8
+ * @param {string} key - The base key of the target field.
9
+ * @returns {string} - The field key modified depending on the slide type.
10
+ */
11
+ export function useEditableFieldKey(key) {
12
+ const { slideID, isPMTemplate, isUserTemplate } = useSlide();
13
+ const presomanagerTemplateKey = getPMTemplateFieldKey(key, slideID);
14
+ const userTemplateKey = getUserTemplateFieldKey(key, slideID);
15
+
16
+ if (isUserTemplate) return userTemplateKey;
17
+ if (isPMTemplate) return presomanagerTemplateKey;
18
+ return key;
19
+ }
@@ -1,128 +1,33 @@
1
- import { CWE_JSON_KEY } from "../constants.js";
2
1
  import { useSlide } from "./use-slide.js";
3
-
4
- // captures all the digits within the slide name, prefaced by a hyphen and optional trailed by -temp
5
- function getSlideNumericID() {
6
- const { slideID } = useSlide();
7
- const numericRegex = /-(\d+)(?:-temp)*$/;
8
- return slideID.match(numericRegex)?.[1];
9
- }
10
-
11
- /**
12
- * Constructs a user template field key based on the provided key and slide details.
13
- *
14
- * Note: field keys are prefixed with the slide ID when saved to the Context (_userTemplateCMSvals)
15
- * however they are not prefixed when used for the data-companywide-editable attribute.
16
- *
17
- * Slide ID variations:
18
- * saved user template: adjunct-template-slide-415
19
- * unsaved user template: adjunct-template-slide-tempkey-1168
20
- *
21
- * Constructed field keys:
22
- * saved user template: slideID--key-tempkey-415
23
- * unsaved user template: slideID--key-adjunct-1168
24
- *
25
- * @param {string} key - The base key of the target field.
26
- * @param {boolean} options.isUserCMSVals - If true, prefix the returned key with the slide ID.
27
- * @returns {string} - The constructed field key for the user template.
28
- */
29
- function getUserTemplateFieldKey(key, { isUserCMSVals = false } = {}) {
30
- const { slideID } = useSlide();
31
- const slideNumericID = getSlideNumericID();
32
- const isUnsavedTemplate = slideID.indexOf("-tempkey") > -1;
33
- let fieldKey = key;
34
-
35
- // Add slideID prefix if requested and not included in key
36
- if (isUserCMSVals && !key.startsWith(`${slideID}--`)) {
37
- fieldKey = `${slideID}--${key}`;
38
- }
39
-
40
- // unsaved user template adds all this on the end
41
- if (isUnsavedTemplate) {
42
- return `${fieldKey}-tempkey-${slideNumericID}`;
43
- }
44
-
45
- return `${fieldKey}-adjunct-${slideNumericID}`;
46
- }
47
-
48
- function getUserTemplateData(fieldKey) {
49
- const userValues = Bridge.Context.getDict()._userTemplateCMSvals;
50
- return (
51
- Object.values(userValues || {})?.find?.((vals) => vals.key === fieldKey) ||
52
- {}
53
- );
54
- }
55
-
56
- /**
57
- * Constructs a PM template field key based on the provided key and slide details.
58
- *
59
- * Slide ID variations:
60
- * saved PM template: template-slide-11
61
- * unsaved PM template: template-slide-1120-temp
62
- *
63
- * Constructed field keys:
64
- * saved PM template: key-autoadjunct-11
65
- * unsaved PM template: key-autoadjunct-1120-temp
66
- *
67
- * @param {string} key - The base key of the target field.
68
- * @returns {string} - The constructed field key for the PM template.
69
- */
70
- function getPMTemplateFieldKey(key) {
71
- const { slideID, slideKey } = useSlide();
72
- const isCWEJSON = key.endsWith(CWE_JSON_KEY);
73
- const slideNumericID = getSlideNumericID();
74
- // example: image_plus_text_columns-options-autoadjunct-767-temp
75
- const isUnsavedTemplate = slideID.indexOf("-temp") > -1;
76
- // "template-" is added to the start of the slideKey of PM templates
77
- // this is not included in CWE field keys and needs to be removed if
78
- // slideKey was used to generate the field key request.
79
- // NOTE: CWE JSON field keys require the "template-" prefix
80
- const fieldKey =
81
- !isCWEJSON && key.startsWith(slideKey) && key.startsWith("template-")
82
- ? key.replace("template-", "")
83
- : key;
84
-
85
- if (isUnsavedTemplate) {
86
- return `${fieldKey}-autoadjunct-${slideNumericID}-temp`;
87
- }
88
-
89
- return `${fieldKey}-autoadjunct-${slideNumericID}`;
90
- }
91
-
92
- function getCWEData(fieldKey) {
93
- // PM template or standard slide companywide editable field
94
- const cweVals = Bridge.Companywide.getWithKeys([fieldKey]);
95
- return cweVals.find((vals) => vals.key === fieldKey) || {};
96
- }
2
+ import { useEditableFieldKey } from "./use-editable-field-key.js";
3
+ import { getCWEData, getUserTemplateData } from "../utils.js";
97
4
 
98
5
  /**
99
6
  * Retrieves the key and value of a requested slide CWE, PM template or user template field.
100
7
  * Adjusts the field key as required for user and PM templates.
101
8
  *
9
+ * Note: This hook is not tied to a state, as such it will not update when changes are made
10
+ * to the requested editable field. This hook will only return a new value when re-run.
11
+ *
102
12
  * @param {string} key - The base key of the field to retrieve.
103
13
  * @param {string} [defaultValue=''] - The default value to use if the field is not found.
104
14
  * @returns {Array} - An array containing the value of the field and the adjusted field key.
105
15
  */
106
- export function useEditableInitialState(key, defaultValue = "") {
107
- const { isUserTemplate, isPMTemplate } = useSlide();
108
- let fieldKey = key;
16
+ export function useEditableInitialState(id, defaultValue = "") {
17
+ const { slideID, isUserTemplate } = useSlide();
18
+
19
+ const key = useEditableFieldKey(id);
20
+
109
21
  let data;
110
22
 
111
23
  if (isUserTemplate) {
112
- fieldKey = getUserTemplateFieldKey(key);
113
- const dataKey = getUserTemplateFieldKey(key, {
114
- isUserCMSVals: true,
115
- });
116
- data = getUserTemplateData(dataKey);
117
- } else if (isPMTemplate) {
118
- fieldKey = getPMTemplateFieldKey(key);
119
- data = getCWEData(fieldKey);
24
+ data = getUserTemplateData(key, slideID);
120
25
  } else {
121
- data = getCWEData(fieldKey);
26
+ data = getCWEData(key);
122
27
  }
123
28
 
124
29
  const value =
125
30
  data?.value || data?.accepted?.value || data?.saved?.value || defaultValue;
126
31
 
127
- return [value, fieldKey];
32
+ return [value, key];
128
33
  }
@@ -12,7 +12,10 @@ export function useModes() {
12
12
  const isPreview = bodyClasses.includes("preview");
13
13
  const isPresent = bodyClasses.includes("present");
14
14
  const isReview = bodyClasses.includes("review");
15
- const isPresomanager = bodyClasses.includes("edit-mode");
15
+ // Editing user templates in prep results in both the 'preview' and 'edit-mode'
16
+ // classes being added to the body. In order to detect if we are truly on the
17
+ // PresoManager page in the app, we need to ignore the 'preview' class.
18
+ const isPresomanager = bodyClasses.includes("edit-mode") && !isPreview;
16
19
  const isEmptyPreview = bodyClasses.includes("empty-preview");
17
20
  const isRemote = isOnline && isClient && !isShareOnline;
18
21
  const isCdk = typeof window.__cdk_version__ !== "undefined";
@@ -1,6 +1,9 @@
1
- import { useEffect, useState } from "react";
1
+ import { useEffect, useState, useCallback } from "react";
2
+
3
+ import { useModes } from "./use-modes";
2
4
 
3
5
  import isPlainObject from "lodash.isplainobject";
6
+ import isEqual from "lodash.isequal";
4
7
 
5
8
  function getValueFromContext(context, path, defaultValue) {
6
9
  if (path) {
@@ -14,12 +17,17 @@ export function useSlideContext(contextPath = null, defaultValue = null) {
14
17
  let persist = false;
15
18
  let path = contextPath;
16
19
 
17
- const value = getValueFromContext(
18
- window.Bridge.Context.getDict(),
19
- path,
20
- defaultValue,
21
- );
22
- const [context, setContextState] = useState(value);
20
+ const { isPresomanager } = useModes();
21
+
22
+ const getValue = useCallback(() => {
23
+ return getValueFromContext(
24
+ window.Bridge.Context.getDict(),
25
+ path,
26
+ defaultValue,
27
+ );
28
+ }, [path, defaultValue]);
29
+
30
+ const [context, setContextState] = useState(getValue);
23
31
 
24
32
  // You could pass in an object, so that you can set whether
25
33
  // the saves are persistent or not.
@@ -42,20 +50,23 @@ export function useSlideContext(contextPath = null, defaultValue = null) {
42
50
  }
43
51
 
44
52
  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
  }
53
54
 
54
55
  useEffect(() => {
55
- if (context !== value) {
56
- Bridge.Context.set(path, context, persist);
56
+ if (isPresomanager) return;
57
+
58
+ function onContextChange() {
59
+ const newValue = getValue();
60
+ if (isEqual(context, newValue)) return;
61
+ setContextState(newValue);
57
62
  }
58
- }, [context]);
63
+
64
+ Bridge.presenter.getPresentation().on("change:context", onContextChange);
65
+
66
+ return () => {
67
+ Bridge.presenter.getPresentation().off("change:context", onContextChange);
68
+ };
69
+ }, [isPresomanager, context, getValue]);
59
70
 
60
71
  return [context, setContext];
61
72
  }
package/lib/utils.js ADDED
@@ -0,0 +1,152 @@
1
+ import { CWE_JSON_KEY } from "./constants";
2
+
3
+ /**
4
+ * Creates the base key for a CWE JSON field.
5
+ * Note: This does not apply any modifications required by the PM template
6
+ * and user template uses of CWE JSON. For those, you will need to run the
7
+ * return value of this key through `getPMTemplateFieldKey` or `getUserTemplateFieldKey`.
8
+ *
9
+ * @param {String} slideKey
10
+ * @returns {String} - Composed base key used for CWE JSON (aka. Companywide JSON)
11
+ */
12
+ export function getCWEJSONKey(slideKey) {
13
+ if (!slideKey) {
14
+ throw new Error("'slideKey' is required");
15
+ }
16
+
17
+ return `${slideKey}-${CWE_JSON_KEY}`;
18
+ }
19
+
20
+ /**
21
+ * Checks CWE keys for the current slide for the relevant CWE key.
22
+ * CWE JSON key is modified as required for PM template and user template.
23
+ *
24
+ * @param {Object} slideState - slide details returned by `useSlide()` hook.
25
+ * @param {String} slideState.slideID
26
+ * @param {String} slideState.slideKey
27
+ * @param {Boolean} slideState.isPMTemplate
28
+ * @param {Boolean} slideState.isUserTemplate
29
+ * @returns
30
+ */
31
+ export function testCWEJSONExists({
32
+ slideID,
33
+ slideKey,
34
+ isPMTemplate,
35
+ isUserTemplate,
36
+ } = {}) {
37
+ const key = getCWEJSONKey(slideKey);
38
+ const cweKeys = window.Bridge.Companywide?.getSlideKeys?.() || [];
39
+
40
+ const presomanagerTemplateKey = getPMTemplateFieldKey(key, slideID);
41
+ const userTemplateKey = getUserTemplateFieldKey(key, slideID);
42
+
43
+ if (isUserTemplate) return cweKeys.includes(userTemplateKey);
44
+ if (isPMTemplate) return cweKeys.includes(presomanagerTemplateKey);
45
+ return cweKeys.includes(key);
46
+ }
47
+
48
+ /**
49
+ * Captures all the digits within the slide name,
50
+ * prefaced by a hyphen and optional trailed by -temp.
51
+ *
52
+ * Results are used when composing field keys for PM templates
53
+ * and user templates, eg. getPMTemplateFieldKey() and getUserTemplateFieldKey().
54
+ */
55
+ function getSlideNumericID(slideID) {
56
+ const numericRegex = /-(\d+)(?:-temp)*$/;
57
+ return slideID.match(numericRegex)?.[1];
58
+ }
59
+
60
+ /**
61
+ * Constructs a PM template field key based on the provided key and slide details.
62
+ *
63
+ * Slide ID variations:
64
+ * saved PM template: template-slide-11
65
+ * unsaved PM template: template-slide-1120-temp
66
+ *
67
+ * Constructed field keys:
68
+ * saved PM template: key-autoadjunct-11
69
+ * unsaved PM template: key-autoadjunct-1120-temp
70
+ *
71
+ * @param {string} key - The base key of the target field.
72
+ * @returns {string} - The constructed field key for the PM template.
73
+ */
74
+ export function getPMTemplateFieldKey(key, slideID) {
75
+ const slideNumericID = getSlideNumericID(slideID);
76
+ // example: image_plus_text_columns-options-autoadjunct-767-temp
77
+ const isUnsavedTemplate = slideID.indexOf("-temp") > -1;
78
+ const templateFieldKey = `${key}-autoadjunct-${slideNumericID}`;
79
+
80
+ if (isUnsavedTemplate) {
81
+ return `${templateFieldKey}-temp`;
82
+ }
83
+
84
+ return templateFieldKey;
85
+ }
86
+
87
+ /**
88
+ * Constructs a user template field key based on the provided key and slide details.
89
+ *
90
+ * Slide ID variations:
91
+ * saved user template: adjunct-template-slide-415
92
+ * unsaved user template: adjunct-template-slide-tempkey-1168
93
+ *
94
+ * Constructed field keys:
95
+ * saved user template: key-tempkey-11
96
+ * unsaved user template: key-adjunct-1120
97
+ *
98
+ * @param {string} key - The base key of the target field.
99
+ * @returns {string} - The constructed field key for the user template.
100
+ */
101
+ export function getUserTemplateFieldKey(key, slideID) {
102
+ const slideNumericID = getSlideNumericID(slideID);
103
+ const isUnsavedTemplate = slideID.indexOf("-tempkey") > -1;
104
+
105
+ if (isUnsavedTemplate) {
106
+ return `${key}-tempkey-${slideNumericID}`;
107
+ }
108
+
109
+ return `${key}-adjunct-${slideNumericID}`;
110
+ }
111
+
112
+ /**
113
+ * Returns PM template or standard slide companywide editable data.
114
+ *
115
+ * @param {string} fieldKey
116
+ * @returns {object} - Saved PM editable field data
117
+ */
118
+ export function getCWEData(fieldKey) {
119
+ const cweVals = window.Bridge.Companywide?.getWithKeys([fieldKey]) || [];
120
+ return cweVals.find((vals) => vals.key === fieldKey) || {};
121
+ }
122
+
123
+ /**
124
+ * Finds value saved to requested user template field. This function expects the supplied
125
+ * key to have been generated by the useEditableFieldKey() hook, this is to ensure the correct
126
+ * parent slide information has been included in the key.
127
+ *
128
+ * Note: field keys are prefixed with the slide ID when saved to the Context (_userTemplateCMSvals)
129
+ * however they are not prefixed when used for the data-companywide-editable attribute.
130
+ *
131
+ * Constructed field keys:
132
+ * saved user template: slideID--key-tempkey-415
133
+ * unsaved user template: slideID--key-adjunct-1168
134
+ *
135
+ * @param {string} key - The base key of the target field.
136
+ * @returns {object} - information saved to the requested user template field.
137
+ */
138
+ export function getUserTemplateData(key, slideID) {
139
+ let fieldKey = key;
140
+
141
+ // Add slideID prefix if not included in key
142
+ if (!fieldKey.startsWith(`${slideID}--`)) {
143
+ fieldKey = `${slideID}--${key}`;
144
+ }
145
+
146
+ const userValues =
147
+ window.Bridge.Context?.getDict()._userTemplateCMSvals || {};
148
+
149
+ return (
150
+ Object.values(userValues)?.find?.((vals) => vals.key === fieldKey) || {}
151
+ );
152
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livepreso/content-react",
3
- "version": "0.7.3",
3
+ "version": "1.0.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
@@ -22,7 +22,8 @@
22
22
  "lodash.get": "~4.4.2",
23
23
  "lodash.isplainobject": "~4.0.6",
24
24
  "lodash.set": "~4.3.2",
25
- "lodash.unescape": "~4.0.1"
25
+ "lodash.unescape": "~4.0.1",
26
+ "lodash.isequal": "~4.5.0"
26
27
  },
27
28
  "scripts": {
28
29
  "build": ""
@@ -1,50 +0,0 @@
1
- import { useCallback, useEffect, useState } from "react";
2
-
3
- import { useSlide } from "../hooks/use-slide";
4
-
5
- /**
6
- * Returns information about a prep editable on the current slide.
7
- *
8
- * Example:
9
- *
10
- * usePrepEditable("test");
11
- * => {
12
- * key: "myslide--myslide-test,
13
- * value: "User edited text,
14
- * hasValue: true
15
- * }
16
- */
17
- export function usePrepEditable(id) {
18
- const { slideKey } = useSlide();
19
-
20
- // TODO: This won't work with user templates, and might
21
- // have problems with PM templates too. Fix this.
22
- const prepKey = `${slideKey}--${slideKey}-${id}`;
23
- const keyPath = `_editableContent.${prepKey}.value`;
24
-
25
- const getValue = useCallback(() => {
26
- return Bridge.Context.get(keyPath, null);
27
- }, [keyPath]);
28
-
29
- const [value, setValue] = useState(getValue());
30
-
31
- useEffect(() => {
32
- function onContextChange() {
33
- const newValue = getValue();
34
-
35
- if (value === newValue) {
36
- return;
37
- }
38
-
39
- setValue(newValue);
40
- }
41
-
42
- Bridge.presenter.getPresentation().on("change:context", onContextChange);
43
-
44
- return () => {
45
- Bridge.presenter.getPresentation().off("change:context", onContextChange);
46
- };
47
- }, [value, getValue]);
48
-
49
- return { key: prepKey, value, hasValue: value !== null };
50
- }