@nascentdigital/funnel-editor 4.6.7 → 5.0.2

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 (185) hide show
  1. package/dist/EditorApp.js +1 -1
  2. package/dist/EditorRoutes.js +7 -5
  3. package/dist/changesets/screens/ChangeSetViewer.js +1 -1
  4. package/dist/changesets/screens/ChangeSetsExplorer.js +1 -2
  5. package/dist/collection/hooks/CollectionController.d.ts +5 -16
  6. package/dist/collection/hooks/CollectionController.js +18 -37
  7. package/dist/collection/hooks/ICollectionController.d.ts +24 -0
  8. package/dist/collection/hooks/ICollectionController.js +20 -0
  9. package/dist/collection/hooks/index.d.ts +1 -0
  10. package/dist/collection/hooks/index.js +1 -0
  11. package/dist/collection/hooks/useCollectionController.d.ts +3 -3
  12. package/dist/collection/screens/CollectionEditor.js +2 -2
  13. package/dist/components.d.ts +15 -3
  14. package/dist/components.js +18 -3
  15. package/dist/core/components/feedback/dialog/MinimalDialogLayout.js +1 -1
  16. package/dist/core/components/fields/BooleanField.d.ts +2 -1
  17. package/dist/core/components/fields/BooleanField.js +2 -2
  18. package/dist/core/components/fields/DateField.d.ts +2 -1
  19. package/dist/core/components/fields/DateField.js +2 -2
  20. package/dist/core/components/fields/DynamicPagesEditor.d.ts +2 -1
  21. package/dist/core/components/fields/DynamicPagesEditor.js +1 -1
  22. package/dist/core/components/fields/EnumField.d.ts +2 -1
  23. package/dist/core/components/fields/EnumField.js +2 -2
  24. package/dist/core/components/fields/FieldCard.d.ts +1 -0
  25. package/dist/core/components/fields/FieldCard.js +8 -1
  26. package/dist/core/components/fields/FieldEditor.d.ts +2 -1
  27. package/dist/core/components/fields/FieldEditor.js +11 -11
  28. package/dist/core/components/fields/FieldLayout.d.ts +2 -1
  29. package/dist/core/components/fields/FieldLayout.js +3 -3
  30. package/dist/core/components/fields/ListEditor.d.ts +2 -1
  31. package/dist/core/components/fields/ListEditor.js +5 -6
  32. package/dist/core/components/fields/OptInField.d.ts +2 -1
  33. package/dist/core/components/fields/OptInField.js +43 -3
  34. package/dist/core/components/fields/RichTextEditor.js +1 -1
  35. package/dist/core/components/fields/RichTextField.d.ts +2 -1
  36. package/dist/core/components/fields/RichTextField.js +3 -5
  37. package/dist/core/components/fields/TextField.d.ts +2 -0
  38. package/dist/core/components/fields/TextField.js +2 -2
  39. package/dist/core/components/fields/TimestampField.d.ts +2 -1
  40. package/dist/core/components/fields/TimestampField.js +3 -3
  41. package/dist/core/components/fields/asset/AssetEditor.d.ts +1 -2
  42. package/dist/core/components/fields/asset/AssetEditor.js +29 -49
  43. package/dist/core/components/fields/asset/AssetPicker.js +6 -6
  44. package/dist/core/components/fields/asset/AssetUploader.d.ts +1 -2
  45. package/dist/core/components/fields/asset/AssetUploader.js +39 -35
  46. package/dist/core/components/fields/asset/preview/VideoPreview.js +16 -11
  47. package/dist/core/components/fields/asset/useAssetFields.d.ts +2 -17
  48. package/dist/core/components/fields/asset/useAssetFields.js +0 -21
  49. package/dist/core/components/fields/collection/CollectionEditor.d.ts +2 -1
  50. package/dist/core/components/fields/collection/CollectionEditor.js +18 -4
  51. package/dist/core/components/fields/collection/CollectionPicker.d.ts +2 -2
  52. package/dist/core/components/fields/collection/CollectionPicker.js +3 -4
  53. package/dist/core/components/fields/dynamicPages/PagePriorityDialog.js +1 -1
  54. package/dist/core/components/fields/embed/EmbeddedVideoEditor.d.ts +2 -2
  55. package/dist/core/components/fields/embed/EmbeddedVideoEditor.js +50 -31
  56. package/dist/core/components/fields/factories/AssetFactory.d.ts +3 -3
  57. package/dist/core/components/fields/factories/AssetFactory.js +18 -13
  58. package/dist/core/components/fields/factories/CollectionFactory.d.ts +3 -3
  59. package/dist/core/components/fields/factories/CollectionFactory.js +20 -13
  60. package/dist/core/components/fields/factories/EmbeddedVideoFactory.d.ts +3 -3
  61. package/dist/core/components/fields/factories/EmbeddedVideoFactory.js +17 -8
  62. package/dist/core/components/fields/factories/FieldValueFactory.d.ts +7 -5
  63. package/dist/core/components/fields/factories/FieldValueFactory.js +57 -13
  64. package/dist/core/components/fields/factories/LinkFactory.d.ts +3 -3
  65. package/dist/core/components/fields/factories/LinkFactory.js +17 -8
  66. package/dist/core/components/fields/factories/MediaFactory.d.ts +13 -0
  67. package/dist/core/components/fields/factories/MediaFactory.js +212 -0
  68. package/dist/core/components/fields/factories/ObjectFactory.d.ts +3 -2
  69. package/dist/core/components/fields/factories/ObjectFactory.js +16 -2
  70. package/dist/core/components/fields/factories/TextFactory.d.ts +3 -3
  71. package/dist/core/components/fields/factories/TextFactory.js +16 -10
  72. package/dist/core/components/fields/link/LinkEditor.d.ts +2 -2
  73. package/dist/core/components/fields/link/LinkEditor.js +18 -6
  74. package/dist/core/components/fields/list/CreateFieldButton.js +1 -1
  75. package/dist/core/components/fields/media/MediaCard.d.ts +7 -0
  76. package/dist/core/components/fields/media/MediaCard.js +92 -0
  77. package/dist/core/components/fields/media/MediaEditor.d.ts +36 -0
  78. package/dist/core/components/fields/media/MediaEditor.js +264 -0
  79. package/dist/core/components/fields/media/index.d.ts +2 -0
  80. package/dist/core/components/fields/media/index.js +18 -0
  81. package/dist/core/components/fields/object/ObjectEditor.d.ts +2 -1
  82. package/dist/core/components/fields/object/ObjectEditor.js +16 -2
  83. package/dist/core/components/fields/page/PagePicker.js +1 -1
  84. package/dist/core/components/interaction/Combobox.js +1 -1
  85. package/dist/core/components/interaction/MultiSelectControls.js +2 -5
  86. package/dist/core/components/interaction/Popup.d.ts +11 -7
  87. package/dist/core/components/interaction/Popup.js +8 -6
  88. package/dist/core/components/interaction/QuickEditor.js +1 -1
  89. package/dist/core/components/interaction/QuickInput.d.ts +2 -4
  90. package/dist/core/components/interaction/QuickInput.js +6 -2
  91. package/dist/core/components/interaction/QuickPicker.js +1 -1
  92. package/dist/core/components/interaction/SideSheet.js +1 -1
  93. package/dist/core/components/interaction/centersheet/CenterSheet.js +7 -1
  94. package/dist/core/components/interaction/centersheet/CenterSheetContainer.js +3 -1
  95. package/dist/core/components/interaction/menu/Menu.d.ts +8 -19
  96. package/dist/core/components/interaction/menu/Menu.js +26 -60
  97. package/dist/core/components/interaction/menu/useMenuController.d.ts +4 -2
  98. package/dist/core/components/interaction/menu/useMenuController.js +4 -1
  99. package/dist/core/components/transitions/FadeIn.js +1 -1
  100. package/dist/core/components/visuals/Icon.d.ts +1 -1
  101. package/dist/core/components/visuals/Icon.js +3 -0
  102. package/dist/core/containers/AssetRepositoryContainer.d.ts +2 -0
  103. package/dist/core/containers/AssetRepositoryContainer.js +18 -0
  104. package/dist/core/containers/CollectionRepositoryContainer.d.ts +2 -0
  105. package/dist/core/containers/CollectionRepositoryContainer.js +18 -0
  106. package/dist/core/containers/PageRepositoryContainer.d.ts +2 -0
  107. package/dist/core/containers/PageRepositoryContainer.js +18 -0
  108. package/dist/core/containers/index.d.ts +3 -0
  109. package/dist/core/containers/index.js +19 -0
  110. package/dist/core/domain/Block.js +2 -1
  111. package/dist/core/domain/BlockConfiguration.d.ts +4 -1
  112. package/dist/core/domain/BlockConfiguration.js +59 -3
  113. package/dist/core/domain/Collection.d.ts +7 -25
  114. package/dist/core/domain/Collection.js +2 -16
  115. package/dist/core/domain/CollectionBrowser.d.ts +3 -2
  116. package/dist/core/domain/CollectionBrowser.js +3 -3
  117. package/dist/core/domain/CollectionFieldProxy.d.ts +12 -8
  118. package/dist/core/domain/CollectionFieldProxy.js +15 -9
  119. package/dist/core/domain/CollectionGroup.d.ts +0 -5
  120. package/dist/core/domain/CollectionGroup.js +0 -7
  121. package/dist/core/domain/CollectionSummary.d.ts +2 -2
  122. package/dist/core/domain/Collections.d.ts +3 -3
  123. package/dist/core/domain/Collections.js +4 -6
  124. package/dist/core/domain/ICollection.d.ts +72 -0
  125. package/dist/core/domain/ICollection.js +20 -0
  126. package/dist/core/domain/IPage.d.ts +16 -1
  127. package/dist/core/domain/Page.js +2 -2
  128. package/dist/core/domain/Pages.js +4 -6
  129. package/dist/core/domain/SectionBlock.js +2 -1
  130. package/dist/core/domain/Site.d.ts +4 -4
  131. package/dist/core/domain/Site.js +3 -5
  132. package/dist/core/domain/index.d.ts +1 -1
  133. package/dist/core/domain/index.js +1 -1
  134. package/dist/core/hooks/index.d.ts +4 -3
  135. package/dist/core/hooks/index.js +4 -3
  136. package/dist/core/hooks/useAssetRepository.d.ts +3 -0
  137. package/dist/core/hooks/useAssetRepository.js +11 -0
  138. package/dist/core/hooks/useCollection.d.ts +4 -4
  139. package/dist/core/hooks/useCollectionRepository.d.ts +3 -2
  140. package/dist/core/hooks/useCollectionRepository.js +4 -4
  141. package/dist/core/hooks/useCollections.js +4 -0
  142. package/dist/core/hooks/usePageRepository.d.ts +3 -2
  143. package/dist/core/hooks/usePageRepository.js +4 -4
  144. package/dist/core/hooks/useSite.js +4 -0
  145. package/dist/core/hooks/useTempFields.d.ts +2 -3
  146. package/dist/core/index.d.ts +1 -0
  147. package/dist/core/index.js +1 -0
  148. package/dist/core/repository/AssetRepository.d.ts +10 -0
  149. package/dist/core/repository/AssetRepository.js +22 -0
  150. package/dist/core/repository/CollectionRepository.d.ts +22 -21
  151. package/dist/core/repository/CollectionRepository.js +23 -161
  152. package/dist/core/repository/CollectionSerializer.d.ts +4 -1
  153. package/dist/core/repository/CollectionSerializer.js +3 -3
  154. package/dist/core/repository/IAssetRepository.d.ts +25 -0
  155. package/dist/core/repository/ICollectionRepository.d.ts +61 -0
  156. package/dist/core/repository/ICollectionRepository.js +2 -0
  157. package/dist/core/repository/IPageRepository.d.ts +40 -0
  158. package/dist/core/repository/IPageRepository.js +2 -0
  159. package/dist/core/repository/PageRepository.d.ts +5 -8
  160. package/dist/core/repository/PageRepository.js +7 -280
  161. package/dist/core/repository/SerializeCollectionUpdate.d.ts +8 -0
  162. package/dist/core/repository/SerializeCollectionUpdate.js +168 -0
  163. package/dist/core/repository/SerializePageUpdate.d.ts +10 -0
  164. package/dist/core/repository/SerializePageUpdate.js +292 -0
  165. package/dist/core/repository/index.d.ts +4 -0
  166. package/dist/core/repository/index.js +4 -0
  167. package/dist/core/splash/SplashContainer.d.ts +4 -2
  168. package/dist/core/splash/SplashContainer.js +3 -3
  169. package/dist/dashboard/screens/Dashboard.js +1 -1
  170. package/dist/devtools/screens/Devtools.js +1 -1
  171. package/dist/explore/components/CreatePageDialog.d.ts +2 -2
  172. package/dist/explore/screens/CollectionExplorer.js +1 -1
  173. package/dist/explore/screens/CollectionsExplorer.js +1 -1
  174. package/dist/explore/screens/PageExplorer.js +1 -1
  175. package/dist/explore/screens/SiteExplorer.js +1 -1
  176. package/dist/page/components/BlockEditor.js +1 -1
  177. package/dist/page/components/SettingsEditor.js +0 -7
  178. package/dist/page/components/config/BlockConfigButton.js +71 -14
  179. package/dist/page/screens/PageEditor.js +1 -1
  180. package/dist/styles/index.css +24 -20
  181. package/dist/styles/tailwind.js +1 -0
  182. package/dist/users/screens/UserExplorer.js +1 -1
  183. package/package.json +2 -2
  184. package/dist/core/domain/TranslateSchema.d.ts +0 -7
  185. /package/dist/core/{domain/TranslateSchema.js → repository/IAssetRepository.js} +0 -0
@@ -1,11 +1,21 @@
1
1
  "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  Object.defineProperty(exports, "__esModule", { value: true });
3
14
  exports.AssetUploader = void 0;
4
15
  const jsx_runtime_1 = require("react/jsx-runtime");
5
16
  const funnel_core_1 = require("@nascentdigital/funnel-core");
6
17
  const react_1 = require("motion/react");
7
18
  const react_2 = require("react");
8
- const client_1 = require("../../../client");
9
19
  const hooks_1 = require("../../../hooks");
10
20
  const feedback_1 = require("../../feedback");
11
21
  const interaction_1 = require("../../interaction");
@@ -21,22 +31,24 @@ exports.AssetUploader = (0, react_2.forwardRef)(function AssetUploader(_, ref) {
21
31
  // bind props + hooks
22
32
  const [deferral, setDeferral] = (0, react_2.useState)(undefined);
23
33
  const [result, setResult] = (0, react_2.useState)();
34
+ const [uploading, setUploading] = (0, react_2.useState)(false);
24
35
  const [schema, setSchema] = (0, react_2.useState)();
25
- const [anchor, setAnchor] = (0, react_2.useState)();
36
+ const [popupProps, setPopupProps] = (0, react_2.useState)();
26
37
  const [errorLevel, setErrorLevel] = (0, react_2.useState)('none');
27
38
  const [error, setError] = (0, react_2.useState)();
28
39
  const [assetInfo, setAssetInfo] = (0, react_2.useState)();
29
- const { nameField, anchorField, descriptionField, accessibleField } = (0, useAssetFields_1.useAssetFields)();
40
+ const { nameField, descriptionField } = (0, useAssetFields_1.useAssetFields)();
30
41
  const [shared, setShared] = (0, react_2.useState)(false);
31
- const editorApi = (0, client_1.useEditorApi)();
42
+ const assetRepo = (0, hooks_1.useAssetRepository)();
32
43
  const assetStore = (0, hooks_1.useAssetStore)();
33
44
  const assetOptions = (0, react_2.useMemo)(() => getAssetOptions(schema), [schema]);
34
45
  // export service
35
46
  (0, react_2.useImperativeHandle)(ref, () => ({
36
- upload: async ({ schema, anchor }) => {
47
+ upload: async (_a) => {
48
+ var { schema } = _a, popupProps = __rest(_a, ["schema"]);
37
49
  // reset state
38
50
  setSchema(schema);
39
- setAnchor(anchor);
51
+ setPopupProps(popupProps);
40
52
  setErrorLevel('none');
41
53
  setError(undefined);
42
54
  setShared(false);
@@ -56,9 +68,7 @@ exports.AssetUploader = (0, react_2.forwardRef)(function AssetUploader(_, ref) {
56
68
  // update fields if there's a file
57
69
  if (assetInfo) {
58
70
  nameField.reset(funnel_core_1.Schema.TextField.Value.toData(assetInfo.fileName));
59
- anchorField.reset(funnel_core_1.Schema.EnumField.Value.toData('center'));
60
71
  descriptionField.reset(null);
61
- accessibleField.reset(funnel_core_1.Schema.EnumField.Value.toData('system'));
62
72
  }
63
73
  };
64
74
  const onValidate = () => {
@@ -69,8 +79,7 @@ exports.AssetUploader = (0, react_2.forwardRef)(function AssetUploader(_, ref) {
69
79
  // check if level
70
80
  let valid = nameField.valid;
71
81
  if (valid && assetInfo.isVisual) {
72
- valid = anchorField.valid &&
73
- (!descriptionField.exists || descriptionField.valid);
82
+ valid = !descriptionField.exists || descriptionField.valid;
74
83
  }
75
84
  // update error level
76
85
  setErrorLevel('all');
@@ -86,11 +95,13 @@ exports.AssetUploader = (0, react_2.forwardRef)(function AssetUploader(_, ref) {
86
95
  }
87
96
  // handle upload
88
97
  try {
98
+ // start upload
99
+ setUploading(true);
89
100
  // create meta
90
- const metadata = await getAssetMetadata({ assetInfo, anchorField, accessibleField });
101
+ const metadata = await getAssetMetadata({ assetInfo });
91
102
  console.debug('[AssetUploader/onUpload] uploading asset', { assetInfo, metadata });
92
103
  // upload new file
93
- const uploadedFile = await editorApi.uploadFile({
104
+ const uploadedFile = await assetRepo.uploadFile({
94
105
  file: assetInfo.file,
95
106
  type: assetInfo.mimeType,
96
107
  size: assetInfo.size,
@@ -115,6 +126,10 @@ exports.AssetUploader = (0, react_2.forwardRef)(function AssetUploader(_, ref) {
115
126
  console.error('[AssetUploader/upload] file upload failure', e);
116
127
  setError(feedback_1.ErrorMessage.getGenericError((e instanceof Error) ? e : new funnel_core_1.ServerError()));
117
128
  }
129
+ // clear uploading flag
130
+ finally {
131
+ setUploading(false);
132
+ }
118
133
  };
119
134
  const onCancel = () => {
120
135
  // set result (triggers uploader to close)
@@ -129,12 +144,12 @@ exports.AssetUploader = (0, react_2.forwardRef)(function AssetUploader(_, ref) {
129
144
  deferral.resolve(result !== null && result !== void 0 ? result : undefined);
130
145
  };
131
146
  // skip if we don't have any params
132
- if (!deferral || !schema || !anchor) {
147
+ if (!deferral || !schema || !popupProps) {
133
148
  return null;
134
149
  }
135
150
  // render
136
- return ((0, jsx_runtime_1.jsxs)(interaction_1.MiniEditor, { className: "flex flex-col gap-l8", title: "Upload Asset", open: result === undefined, placement: "right", anchor: anchor, anchorOffset: 4, canSave: onValidate, disableSave: !(assetInfo === null || assetInfo === void 0 ? void 0 : assetInfo.isValid), renderSaveLabel: () => 'Upload', onSave: onUpload, onClose: onCancel, onClosed: onPickerClosed, children: [schema.description &&
137
- (0, jsx_runtime_1.jsx)(feedback_1.Note, { variant: "tip", children: schema.description }), (0, jsx_runtime_1.jsx)(AssetSelectorPane_1.AssetSelectorPane, { options: assetOptions, onAssetChanged: onAssetChanged }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { children: (0, jsx_runtime_1.jsx)(FieldErrorController_1.FieldErrorController, { errorLevel: errorLevel, children: renderAssetInfo({ assetInfo, nameField, anchorField, descriptionField, accessibleField, shared, setShared }) }) })] }));
151
+ return ((0, jsx_runtime_1.jsxs)(interaction_1.MiniEditor, Object.assign({ className: "flex flex-col gap-l8", title: "Upload Asset", open: result === undefined, canSave: onValidate, disableSave: !(assetInfo === null || assetInfo === void 0 ? void 0 : assetInfo.isValid), renderSaveLabel: () => 'Upload', customActions: (0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "secondary", size: "sm", iconLeft: "ImageDown", disabled: uploading, onClick: () => setResult('pick'), children: "Open Library" }), onSave: onUpload, onClose: onCancel, onClosed: onPickerClosed }, popupProps, { children: [schema.description &&
152
+ (0, jsx_runtime_1.jsx)(feedback_1.Note, { variant: "tip", children: schema.description }), (0, jsx_runtime_1.jsx)(AssetSelectorPane_1.AssetSelectorPane, { options: assetOptions, onAssetChanged: onAssetChanged }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { children: (0, jsx_runtime_1.jsx)(FieldErrorController_1.FieldErrorController, { errorLevel: errorLevel, children: renderAssetInfo({ assetInfo, nameField, descriptionField, shared, setShared, uploading }) }) })] })));
138
153
  });
139
154
  function getAssetOptions(schema) {
140
155
  // return empty options if there's no schema
@@ -158,52 +173,41 @@ function getAssetOptions(schema) {
158
173
  // normalize options
159
174
  return AssetInfo_1.AssetInfo.Options.normalize(options);
160
175
  }
161
- function renderAssetInfo({ assetInfo, nameField, anchorField, descriptionField, accessibleField, shared, setShared }) {
176
+ function renderAssetInfo({ assetInfo, nameField, descriptionField, shared, setShared, uploading }) {
162
177
  // skip if there's no asset info
163
178
  if (!assetInfo || !assetInfo.isValid) {
164
179
  return null;
165
180
  }
166
181
  // build fields
167
182
  const fields = [
168
- (0, jsx_runtime_1.jsx)(FieldEditor_1.FieldEditor, { field: nameField }, "name")
183
+ (0, jsx_runtime_1.jsx)(FieldEditor_1.FieldEditor, { field: nameField, disabled: uploading }, "name")
169
184
  ];
170
185
  // add fields related to visuals
171
186
  if (assetInfo.isVisual) {
172
- // add anchor
173
- fields.push((0, jsx_runtime_1.jsx)(FieldEditor_1.FieldEditor, { field: anchorField }, "anchor"));
174
- // add accessible flag (for videos)
175
- if (assetInfo.isVideo) {
176
- fields.push((0, jsx_runtime_1.jsx)(FieldEditor_1.FieldEditor, { field: accessibleField }, "accessible"));
177
- }
178
- // get description
179
187
  fields.push(descriptionField.exists
180
- ? (0, jsx_runtime_1.jsx)(FieldEditor_1.FieldEditor, { field: descriptionField }, "desc")
181
- : (0, jsx_runtime_1.jsx)(OptInField_1.OptInField, { field: descriptionField }, "desc.option"));
188
+ ? (0, jsx_runtime_1.jsx)(FieldEditor_1.FieldEditor, { field: descriptionField, disabled: uploading }, "desc")
189
+ : (0, jsx_runtime_1.jsx)(OptInField_1.OptInField, { field: descriptionField, disabled: uploading }, "desc.option"));
182
190
  }
183
191
  // render editing fields
184
- return ((0, jsx_runtime_1.jsxs)(transitions_1.FadeIn, { className: "flex flex-col gap-l8", children: [fields, (0, jsx_runtime_1.jsx)(interaction_1.Checkbox, { checked: shared, onChange: setShared, children: "Store in global media library" })] }, "fields"));
192
+ return ((0, jsx_runtime_1.jsxs)(transitions_1.FadeIn, { className: "flex flex-col gap-l8", children: [fields, (0, jsx_runtime_1.jsx)(interaction_1.Checkbox, { checked: shared, onChange: setShared, disabled: uploading, children: "Store in global media library" })] }, "fields"));
185
193
  }
186
- async function getAssetMetadata({ assetInfo, anchorField, accessibleField }) {
187
- var _a;
194
+ async function getAssetMetadata({ assetInfo }) {
188
195
  const { fileType, dimensions, blurHash } = assetInfo;
189
196
  switch (fileType) {
190
197
  case 'image':
191
198
  return {
192
- crop: funnel_core_1.Schema.EnumField.Value.fromData(anchorField.value),
199
+ crop: 'center',
193
200
  dimensions,
194
201
  blurHash
195
202
  };
196
203
  case 'icon':
197
204
  return { dimensions };
198
205
  case 'video': {
199
- const accessible = (_a = funnel_core_1.Schema.EnumField.Value.fromData(accessibleField.value)) !== null && _a !== void 0 ? _a : 'system';
200
206
  return {
201
- crop: funnel_core_1.Schema.EnumField.Value.fromData(anchorField.value),
207
+ crop: 'center',
202
208
  dimensions,
203
209
  blurHash,
204
- accessible: accessible === 'system'
205
- ? 'system'
206
- : accessible === 'true'
210
+ accessible: 'system'
207
211
  };
208
212
  }
209
213
  default:
@@ -11,11 +11,13 @@ const classnames_1 = __importDefault(require("classnames"));
11
11
  const react_1 = require("react");
12
12
  // component
13
13
  function VideoPreview({ className, asset, active }) {
14
+ var _a;
14
15
  // bind state + refs
15
16
  const { url, metadata } = asset;
16
17
  const { blurHash } = metadata;
17
18
  const [videoRef, video] = (0, client_1.useRefElement)();
18
19
  const videoProxy = (0, client_1.useVideoProxy)(video);
20
+ const videoState = (_a = videoProxy === null || videoProxy === void 0 ? void 0 : videoProxy.state) !== null && _a !== void 0 ? _a : 'uninitialized';
19
21
  const blurCanvasRef = (0, react_1.useRef)(null);
20
22
  // decode blurhash (if any)
21
23
  (0, react_1.useEffect)(() => {
@@ -44,7 +46,10 @@ function VideoPreview({ className, asset, active }) {
44
46
  });
45
47
  // cancel on unmount
46
48
  return () => {
49
+ // mark cancelled
47
50
  cancelled = true;
51
+ // stop video (if required)
52
+ videoProxy.pause().catch(console.error);
48
53
  };
49
54
  }, [videoProxy]);
50
55
  // start/stop video when active
@@ -53,22 +58,22 @@ function VideoPreview({ className, asset, active }) {
53
58
  if (!videoProxy) {
54
59
  return;
55
60
  }
56
- // start video
57
- if (active) {
61
+ // or ignore if it's not ready (playing or paused)
62
+ else if (videoState !== 'ready' && videoState !== 'playing') {
63
+ return;
64
+ }
65
+ // start video if not playing
66
+ const playbackRate = active ? 1 : 0.33;
67
+ if (videoProxy.state !== 'playing') {
58
68
  videoProxy
59
- .play({ playbackRate: 1, loop: true })
69
+ .play({ playbackRate, loop: true })
60
70
  .catch(console.error);
61
71
  }
72
+ // or adjust speed
62
73
  else {
63
- videoProxy
64
- .pause()
65
- .catch(console.error);
74
+ videoProxy.video.playbackRate = playbackRate;
66
75
  }
67
- // stop on unmount
68
- return () => {
69
- videoProxy.pause().catch(console.error);
70
- };
71
- }, [videoProxy, active]);
76
+ }, [videoProxy, videoState, active]);
72
77
  // render
73
78
  return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(className, 'relative w-full h-full'), children: [blurHash &&
74
79
  (0, jsx_runtime_1.jsx)("canvas", { ref: blurCanvasRef, className: "absolute inset-0 w-full h-full", width: 32, height: 32, draggable: false }), (0, jsx_runtime_1.jsx)("video", { ref: videoRef, className: (0, classnames_1.default)('absolute inset-0 w-full h-full object-cover cursor-pointer interpolate', {
@@ -1,20 +1,5 @@
1
- import { Position, Schema } from '@nascentdigital/funnel-core';
2
- export declare function useAssetFields(): import("../../..").TranslateSchema.Fields<{
1
+ import { Schema } from '@nascentdigital/funnel-core';
2
+ export declare function useAssetFields(): import("@nascentdigital/funnel-core").SchemaToFields<{
3
3
  nameField: Schema.TextField;
4
- anchorField: Schema.EnumField<Schema.EnumField.Options<Position>>;
5
- accessibleField: Schema.EnumField<{
6
- system: {
7
- name: string;
8
- hint: string;
9
- };
10
- true: {
11
- name: string;
12
- hint: string;
13
- };
14
- false: {
15
- name: string;
16
- hint: string;
17
- };
18
- }>;
19
4
  descriptionField: Schema.TextField;
20
5
  }>;
@@ -9,27 +9,6 @@ function useAssetFields() {
9
9
  nameField: funnel_core_1.Schema.TextField.slug({
10
10
  name: 'Name'
11
11
  }),
12
- anchorField: funnel_core_1.Schema.Field.enumeration({
13
- name: 'Cropping',
14
- options: funnel_core_1.Position.values.reduce((options, position) => {
15
- // bind position
16
- options[position] = {
17
- name: funnel_core_1.Position.getName(position)
18
- };
19
- // keep reducing
20
- return options;
21
- }, {}),
22
- defaultOption: 'center',
23
- required: true
24
- }),
25
- accessibleField: funnel_core_1.Schema.Field.enumeration({
26
- name: 'Pause Button',
27
- options: {
28
- system: { name: 'Site Default', hint: `Use the default for the site (${funnel_core_1.AppConfig.get().accessible ? 'enabled' : 'disabled'})` },
29
- true: { name: 'Enable', hint: 'A pause button will be included' },
30
- false: { name: 'Disable', hint: 'There will be no way to pause the video' }
31
- }
32
- }),
33
12
  descriptionField: funnel_core_1.Schema.TextField.text({
34
13
  name: 'Alt Text',
35
14
  hint: 'What\'s happening in this visual?',
@@ -1,4 +1,5 @@
1
1
  import { Language, Schema } from '@nascentdigital/funnel-core';
2
+ import { Popup } from '../../interaction';
2
3
  export declare const CollectionEditor: import("react").ForwardRefExoticComponent<CollectionEditor.Props & import("react").RefAttributes<CollectionEditor.Ref>>;
3
4
  export declare namespace CollectionEditor {
4
5
  type Props = {
@@ -7,7 +8,7 @@ export declare namespace CollectionEditor {
7
8
  interface Ref {
8
9
  edit(options: Options): Promise<Schema.Field.Data<Schema.CollectionField> | undefined>;
9
10
  }
10
- type Options = {
11
+ type Options = Popup.Props.Delegated & {
11
12
  schema: Schema.CollectionField;
12
13
  collectionSchema: Schema.Collection;
13
14
  value?: Schema.CollectionField.Value;
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  Object.defineProperty(exports, "__esModule", { value: true });
3
14
  exports.CollectionEditor = void 0;
4
15
  const jsx_runtime_1 = require("react/jsx-runtime");
@@ -21,6 +32,7 @@ exports.CollectionEditor = (0, react_1.forwardRef)(function CollectionEditor({ l
21
32
  const [schema, setSchema] = (0, react_1.useState)();
22
33
  const [collectionSchema, setCollectionSchema] = (0, react_1.useState)();
23
34
  const [initialValue, setInitialValue] = (0, react_1.useState)();
35
+ const [popupProps, setPopupProps] = (0, react_1.useState)();
24
36
  const [field, setField] = (0, react_1.useState)();
25
37
  const [shared, setShared] = (0, react_1.useState)(false);
26
38
  const page = (0, hooks_1.usePossiblePage)();
@@ -32,11 +44,13 @@ exports.CollectionEditor = (0, react_1.forwardRef)(function CollectionEditor({ l
32
44
  (0, hooks_1.useCollectionFieldProxy)(field);
33
45
  // export service
34
46
  (0, react_1.useImperativeHandle)(ref, () => ({
35
- edit: async ({ schema, collectionSchema, value }) => {
47
+ edit: async (_a) => {
48
+ var { schema, collectionSchema, value } = _a, popupProps = __rest(_a, ["schema", "collectionSchema", "value"]);
36
49
  // reset state
37
50
  setSchema(schema);
38
51
  setCollectionSchema(collectionSchema);
39
52
  setInitialValue(value);
53
+ setPopupProps(popupProps);
40
54
  setField(undefined);
41
55
  setShared(false);
42
56
  // create deferral
@@ -69,8 +83,8 @@ exports.CollectionEditor = (0, react_1.forwardRef)(function CollectionEditor({ l
69
83
  setShared(!field.isNew && field.isRef);
70
84
  });
71
85
  }, [language, collectionRepository, collectionStore, schema, collectionSchema, initialValue]);
72
- // skip if there's no schema
73
- if (!schema || !collectionSchema) {
86
+ // skip if not ready
87
+ if (!schema || !collectionSchema || !popupProps) {
74
88
  return null;
75
89
  }
76
90
  // helpers
@@ -147,7 +161,7 @@ exports.CollectionEditor = (0, react_1.forwardRef)(function CollectionEditor({ l
147
161
  ? () => setShared(!shared)
148
162
  : undefined, children: shared ? 'Shared' : 'Unshared' }), (0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "primary", size: "sm", disabled: !field || !field.nameField.valid || !field.modified, onClick: onSave, children: !initialValue ? 'Create' : 'Update' })] }), (0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { mode: "wait", initial: false, children: field
149
163
  ?
150
- (0, jsx_runtime_1.jsxs)(transitions_1.FadeIn, { className: "flex flex-col gap-l7", children: [(0, jsx_runtime_1.jsx)(FieldsPanel_1.FieldsPanel, { className: "flex-1 px-l7", fields: field.allFields, autoFocus: true }), field.tagGroups.length > 0 &&
164
+ (0, jsx_runtime_1.jsxs)(transitions_1.FadeIn, { className: "flex-1 flex flex-col gap-l10 px-l6 py-l10 overflow-y-scroll scrollbar-thin bg-background", children: [(0, jsx_runtime_1.jsx)(FieldsPanel_1.FieldsPanel, { className: "flex-1", fields: field.allFields, autoFocus: true }), field.tagGroups.length > 0 &&
151
165
  (0, jsx_runtime_1.jsx)("div", { className: "px-l7", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-l8 p-l8 bg-surface1 rounded-md", children: [(0, jsx_runtime_1.jsx)("h2", { className: "display-xxs flex-1", children: "Tags" }), (0, jsx_runtime_1.jsx)(TagEditor_1.TagEditor, { className: "bg-surface2 p-l6 rounded-sm", tagged: field })] }) })] }, "fields")
152
166
  :
153
167
  (0, jsx_runtime_1.jsx)(transitions_1.FadeIn, { className: "w-full h-full flex flex-col justify-center items-center", children: (0, jsx_runtime_1.jsx)(interaction_1.Loader, { size: "lg" }) }, "loader") })] }) }) }));
@@ -1,5 +1,5 @@
1
1
  import { Language, Schema } from '@nascentdigital/funnel-core';
2
- import { Collection } from '../../../domain';
2
+ import { ICollection } from '../../../domain';
3
3
  export declare const CollectionPicker: import("react").ForwardRefExoticComponent<CollectionPicker.Props & import("react").RefAttributes<CollectionPicker.Ref>>;
4
4
  export declare namespace CollectionPicker {
5
5
  type Props = {
@@ -11,5 +11,5 @@ export declare namespace CollectionPicker {
11
11
  type Options = {
12
12
  schema: Schema.CollectionField;
13
13
  };
14
- type Result = Collection.Ref | undefined;
14
+ type Result = ICollection.Ref | undefined;
15
15
  }
@@ -9,7 +9,6 @@ const funnel_core_1 = require("@nascentdigital/funnel-core");
9
9
  const classnames_1 = __importDefault(require("classnames"));
10
10
  const framer_motion_1 = require("framer-motion");
11
11
  const react_1 = require("react");
12
- const client_1 = require("../../../client");
13
12
  const hooks_1 = require("../../../hooks");
14
13
  const feedback_1 = require("../../feedback");
15
14
  const interaction_1 = require("../../interaction");
@@ -28,7 +27,7 @@ exports.CollectionPicker = (0, react_1.forwardRef)(function CollectionPicker({ l
28
27
  const [tagTooltip, setTagTooltip] = (0, react_1.useState)();
29
28
  const [fetching, setFetching] = (0, react_1.useState)(false);
30
29
  const [error, setError] = (0, react_1.useState)();
31
- const editorApi = (0, client_1.useEditorApi)();
30
+ const collectionRepo = (0, hooks_1.useCollectionRepository)();
32
31
  const siteCollections = (0, hooks_1.useCollections)();
33
32
  const collectionStore = (0, hooks_1.useCollectionStore)();
34
33
  // export service
@@ -144,7 +143,7 @@ exports.CollectionPicker = (0, react_1.forwardRef)(function CollectionPicker({ l
144
143
  setTagTooltip(undefined);
145
144
  // fetch ref (or fail)
146
145
  const { template, id, name } = collectionRef;
147
- const collection = await editorApi.getCollection({ id, version: 'draft' });
146
+ const collection = await collectionRepo.get({ id, version: 'draft' });
148
147
  if (!collection) {
149
148
  setFetching(false);
150
149
  setError({
@@ -173,7 +172,7 @@ exports.CollectionPicker = (0, react_1.forwardRef)(function CollectionPicker({ l
173
172
  deferral.resolve(result !== null && result !== void 0 ? result : undefined);
174
173
  };
175
174
  // render
176
- return ((0, jsx_runtime_1.jsxs)(interaction_1.Picker, { className: "flex", open: deferral && result === undefined, disableDismiss: fetching, onClose: onCancel, onClosed: onPickerClosed, children: [(0, jsx_runtime_1.jsxs)("section", { className: "shrink-0 w-[206px] flex flex-col gap-l8 p-l8 bg-modal-background", children: [(0, jsx_runtime_1.jsx)("h1", { className: "heading-md text-content-primary", children: "Select Collection" }), (0, jsx_runtime_1.jsx)(interaction_1.ChipFilters, { title: "Site Collections", options: collectionGroups, selectedOptions: collectionGroup && [collectionGroup], onToggle: (collectionGroup, selected) => selected && setCollectionGroup(collectionGroup), renderLabel: collectionGroup => collectionGroup.template.name, disabled: fetching })] }), (0, jsx_runtime_1.jsxs)("section", { className: "flex-1 flex flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex-1 p-l8 overflow-y-scroll", children: (0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { mode: "wait", initial: false, children: renderPicker({ language, error, collectionGroups, collectionGroup, collections, tagOptions, tagFilters, onPick, setTagTooltip, fetching }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "flex justify-end items-center p-l8 border-t border-solid border-border-l1", children: (0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "secondary", size: "sm", onClick: onCancel, disabled: fetching, children: "Cancel" }) }), (0, jsx_runtime_1.jsx)(interaction_1.Popup, { className: (0, classnames_1.default)('max-w-[200px] bg-surface2 rounded-md p-l5', 'border border-solid border-border-l1 shadow-tooltip', 'flex flex-wrap items-center gap-l4'), open: !!tagTooltip, anchor: tagTooltip === null || tagTooltip === void 0 ? void 0 : tagTooltip.anchor, anchorOffset: 4, placement: "top", disableOverlay: true, onClose: () => setTagTooltip(undefined), children: tagTooltip === null || tagTooltip === void 0 ? void 0 : tagTooltip.tags.map(tag => (0, jsx_runtime_1.jsx)(feedback_1.Badge, { variant: "outlinedTiny", status: "draft", label: tag.name }, tag.data)) })] })] }));
175
+ return ((0, jsx_runtime_1.jsxs)(interaction_1.Picker, { className: "flex", open: deferral && result === undefined, disableDismiss: fetching, onClose: onCancel, onClosed: onPickerClosed, children: [(0, jsx_runtime_1.jsxs)("section", { className: "shrink-0 w-[206px] flex flex-col gap-l8 p-l8 bg-modal-background", children: [(0, jsx_runtime_1.jsx)("h1", { className: "heading-md text-content-primary", children: "Select Collection" }), (0, jsx_runtime_1.jsx)(interaction_1.ChipFilters, { title: "Site Collections", options: collectionGroups, selectedOptions: collectionGroup && [collectionGroup], onToggle: (collectionGroup, selected) => selected && setCollectionGroup(collectionGroup), renderLabel: collectionGroup => collectionGroup.template.name, disabled: fetching })] }), (0, jsx_runtime_1.jsxs)("section", { className: "flex-1 flex flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex-1 p-l8 overflow-y-scroll", tabIndex: -1, children: (0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { mode: "wait", initial: false, children: renderPicker({ language, error, collectionGroups, collectionGroup, collections, tagOptions, tagFilters, onPick, setTagTooltip, fetching }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "flex justify-end items-center p-l8 border-t border-solid border-border-l1", children: (0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "secondary", size: "sm", onClick: onCancel, disabled: fetching, children: "Cancel" }) }), (0, jsx_runtime_1.jsx)(interaction_1.Popup, { className: (0, classnames_1.default)('max-w-[200px] bg-surface2 rounded-md p-l5', 'border border-solid border-border-l1 shadow-tooltip', 'flex flex-wrap items-center gap-l4'), open: !!tagTooltip, anchor: tagTooltip === null || tagTooltip === void 0 ? void 0 : tagTooltip.anchor, anchorOffset: 4, placement: "top", disableOverlay: true, onClose: () => setTagTooltip(undefined), children: tagTooltip === null || tagTooltip === void 0 ? void 0 : tagTooltip.tags.map(tag => (0, jsx_runtime_1.jsx)(feedback_1.Badge, { variant: "outlinedTiny", status: "draft", label: tag.name }, tag.data)) })] })] }));
177
176
  });
178
177
  function renderPicker({ language, schema, error, collectionGroups, collectionGroup, collections, tagOptions, tagFilters, onPick, setTagTooltip, fetching }) {
179
178
  // show error if we have one
@@ -50,7 +50,7 @@ exports.PagePriorityDialog = (0, react_1.forwardRef)(function CreatePageDialog({
50
50
  return;
51
51
  }
52
52
  // render
53
- return ((0, jsx_runtime_1.jsxs)(interaction_1.Picker, { open: !!deferral, onClose: onClose, className: "flex", children: [(0, jsx_runtime_1.jsxs)("section", { className: "shrink-0 w-[206px] flex flex-col gap-l8 p-l8 bg-modal-background", children: [(0, jsx_runtime_1.jsx)("h1", { className: "heading-md text-content-primary", children: "Priority Settings" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-l6", children: [(0, jsx_runtime_1.jsx)("h2", { className: "label-sm label-strong text-content-tertiary", children: "Site Pages" }), (0, jsx_runtime_1.jsx)(feedback_1.Badge, { label: currentPage.name })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "flex-1 flex flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex-1 p-l8 overflow-y-scroll", children: currentPage.tags.map(group => (0, jsx_runtime_1.jsx)(TagGroup, { page: currentPage, group: group, state: pageTagState }, group.id)) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-l4 items-center p-l8 border-t border-solid border-border-l1", children: [(0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "secondary", size: "sm", onClick: onClose, children: "Cancel" }), (0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "secondary", size: "sm", onClick: onReset, children: "Reset" }), (0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "primary", size: "sm", onClick: onSave, children: "Save" })] })] })] }));
53
+ return ((0, jsx_runtime_1.jsxs)(interaction_1.Picker, { open: !!deferral, onClose: onClose, className: "flex", children: [(0, jsx_runtime_1.jsxs)("section", { className: "shrink-0 w-[206px] flex flex-col gap-l8 p-l8 bg-modal-background", children: [(0, jsx_runtime_1.jsx)("h1", { className: "heading-md text-content-primary", children: "Priority Settings" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-l6", children: [(0, jsx_runtime_1.jsx)("h2", { className: "label-sm label-strong text-content-tertiary", children: "Site Pages" }), (0, jsx_runtime_1.jsx)(feedback_1.Badge, { label: currentPage.name })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "flex-1 flex flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex-1 p-l8 overflow-y-scroll", tabIndex: -1, children: currentPage.tags.map(group => (0, jsx_runtime_1.jsx)(TagGroup, { page: currentPage, group: group, state: pageTagState }, group.id)) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-l4 items-center p-l8 border-t border-solid border-border-l1", children: [(0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "secondary", size: "sm", onClick: onClose, children: "Cancel" }), (0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "secondary", size: "sm", onClick: onReset, children: "Reset" }), (0, jsx_runtime_1.jsx)(interaction_1.Button, { variant: "primary", size: "sm", onClick: onSave, children: "Save" })] })] })] }));
54
54
  });
55
55
  var PageTagState;
56
56
  (function (PageTagState) {
@@ -1,4 +1,5 @@
1
1
  import { Language, Schema } from '@nascentdigital/funnel-core';
2
+ import { Popup } from '../../interaction';
2
3
  export declare const EmbeddedVideoEditor: import("react").ForwardRefExoticComponent<EmbeddedVideoEditor.Props & import("react").RefAttributes<EmbeddedVideoEditor.Ref>>;
3
4
  export declare namespace EmbeddedVideoEditor {
4
5
  type Props = {
@@ -7,9 +8,8 @@ export declare namespace EmbeddedVideoEditor {
7
8
  interface Ref {
8
9
  edit(options: Options): Promise<Schema.Field.Data<Schema.EmbeddedVideoField> | undefined>;
9
10
  }
10
- type Options = {
11
+ type Options = Popup.Props.Delegated & {
11
12
  schema: Schema.EmbeddedVideoField;
12
13
  initialValue?: Schema.Field.Data<Schema.EmbeddedVideoField>;
13
- anchor: string;
14
14
  };
15
15
  }
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
15
  };
@@ -14,7 +25,6 @@ const hooks_1 = require("../../../hooks");
14
25
  const feedback_1 = require("../../feedback");
15
26
  const interaction_1 = require("../../interaction");
16
27
  const visuals_1 = require("../../visuals");
17
- const FieldEditor_1 = require("../FieldEditor");
18
28
  const FieldErrorController_1 = require("../FieldErrorController");
19
29
  const TextField_1 = require("../TextField");
20
30
  // component
@@ -23,22 +33,15 @@ exports.EmbeddedVideoEditor = (0, react_1.forwardRef)(function EmbeddedVideoEdit
23
33
  const [deferral, setDeferral] = (0, react_1.useState)(undefined);
24
34
  const [schema, setSchema] = (0, react_1.useState)();
25
35
  const [initialValue, setInitialValue] = (0, react_1.useState)();
26
- const [anchor, setAnchor] = (0, react_1.useState)();
36
+ const [popupProps, setPopupProps] = (0, react_1.useState)();
27
37
  const [state, setState] = (0, react_1.useState)('initializing');
28
38
  const touch = (0, client_1.useTouch)();
29
- const { uriField, mutedField } = (0, hooks_1.useTempFields)({
39
+ const { uriField } = (0, hooks_1.useTempFields)({
30
40
  uriField: funnel_core_1.Schema.Field.uri({
31
41
  name: 'YouTube or Vimeo URL',
32
42
  hint: 'Enter video link',
33
43
  required: true
34
44
  }),
35
- mutedField: funnel_core_1.Schema.Field.boolean({
36
- name: 'Muted',
37
- hint: 'Video starts off on mute',
38
- semantics: 'toggle',
39
- required: true,
40
- defaultValue: true
41
- })
42
45
  });
43
46
  const uri = uriField.valid
44
47
  ? funnel_core_1.Schema.TextField.Value.fromData(uriField.value)
@@ -48,12 +51,12 @@ exports.EmbeddedVideoEditor = (0, react_1.forwardRef)(function EmbeddedVideoEdit
48
51
  const video = video$.value;
49
52
  // export service
50
53
  (0, react_1.useImperativeHandle)(ref, () => ({
51
- edit: async ({ schema, initialValue, anchor }) => {
52
- var _a;
54
+ edit: async (_a) => {
55
+ var { schema, initialValue } = _a, popupProps = __rest(_a, ["schema", "initialValue"]);
53
56
  // reset state
54
57
  setSchema(schema);
55
58
  setInitialValue(initialValue);
56
- setAnchor(anchor);
59
+ setPopupProps(popupProps);
57
60
  setState('initializing');
58
61
  // setup inputs
59
62
  const video = initialValue === null || initialValue === void 0 ? void 0 : initialValue.value;
@@ -61,14 +64,13 @@ exports.EmbeddedVideoEditor = (0, react_1.forwardRef)(function EmbeddedVideoEdit
61
64
  uriField.reset(video
62
65
  ? funnel_core_1.Schema.TextField.Value.toData(funnel_core_1.Schema.EmbeddedVideoField.Value.toUrl(video).href)
63
66
  : null);
64
- mutedField.reset(funnel_core_1.Schema.BooleanField.Value.toData(schema.allowMuting ? (_a = video === null || video === void 0 ? void 0 : video.muted) !== null && _a !== void 0 ? _a : true : true));
65
67
  // create deferral
66
68
  const deferral = new funnel_core_1.Deferral();
67
69
  setDeferral(deferral);
68
70
  // wait until deferral is resolved
69
71
  return deferral.promise;
70
72
  }
71
- }), [uriField, mutedField, video$]);
73
+ }), [uriField, video$]);
72
74
  // sync uri on change
73
75
  (0, react_1.useEffect)(() => {
74
76
  var _a;
@@ -100,7 +102,7 @@ exports.EmbeddedVideoEditor = (0, react_1.forwardRef)(function EmbeddedVideoEdit
100
102
  return;
101
103
  }
102
104
  // or if there's already a thumb
103
- else if (video.thumbUrl) {
105
+ else if (video.thumbUrl && video.dimensions.width && video.dimensions.height) {
104
106
  setState('valid');
105
107
  return;
106
108
  }
@@ -113,18 +115,28 @@ exports.EmbeddedVideoEditor = (0, react_1.forwardRef)(function EmbeddedVideoEdit
113
115
  return;
114
116
  }
115
117
  // or fail if we couldn't get a thumbnail (means no video)
116
- if (!thumbUrl) {
118
+ else if (!thumbUrl) {
117
119
  setState('error');
120
+ return;
118
121
  }
119
- // otherwise, update video with valid thumb
120
- else {
121
- // update video
122
- video$.next(Object.assign(Object.assign({}, video), { thumbUrl: thumbUrl.href }));
123
- // update state
124
- setState('valid');
125
- // touch (just in case we were already valid)
126
- touch();
122
+ // get video dimensions
123
+ const dimensions = await getVideoThumbnailSize(thumbUrl.href);
124
+ console.debug('[EmbeddedVideoEditor] got video size as ', dimensions);
125
+ // skip if video has changed (debounced)
126
+ if (video$.value !== video) {
127
+ return;
127
128
  }
129
+ // or fail if we couldn't get dimensions (means no thumbnail)
130
+ else if (!dimensions || !dimensions.width || !dimensions.height) {
131
+ setState('error');
132
+ return;
133
+ }
134
+ // update video
135
+ video$.next(Object.assign(Object.assign({}, video), { dimensions, thumbUrl: thumbUrl.href }));
136
+ // update state
137
+ setState('valid');
138
+ // touch (just in case we were already valid)
139
+ touch();
128
140
  });
129
141
  // unsubscribe on unmount
130
142
  return () => subscription.unsubscribe();
@@ -134,14 +146,12 @@ exports.EmbeddedVideoEditor = (0, react_1.forwardRef)(function EmbeddedVideoEdit
134
146
  return state === 'valid';
135
147
  };
136
148
  const onSave = async () => {
137
- var _a;
138
149
  // blow up if missing uri (should never happen)
139
150
  if (!video || state !== 'valid') {
140
151
  throw new funnel_core_1.IllegalStateError('Missing URI');
141
152
  }
142
153
  // resolve embed (we're done)
143
- const muted = (_a = funnel_core_1.Schema.BooleanField.Value.fromData(mutedField.value)) !== null && _a !== void 0 ? _a : true;
144
- deferral === null || deferral === void 0 ? void 0 : deferral.resolve(funnel_core_1.Schema.EmbeddedVideoField.Value.toData(Object.assign(Object.assign({}, video), { muted })));
154
+ deferral === null || deferral === void 0 ? void 0 : deferral.resolve(funnel_core_1.Schema.EmbeddedVideoField.Value.toData(Object.assign({}, video)));
145
155
  // refresh
146
156
  touch();
147
157
  };
@@ -156,9 +166,8 @@ exports.EmbeddedVideoEditor = (0, react_1.forwardRef)(function EmbeddedVideoEdit
156
166
  touch();
157
167
  };
158
168
  // render
159
- return ((0, jsx_runtime_1.jsxs)(interaction_1.MiniEditor, { title: `${initialValue ? 'Edit' : 'Add'} ${name}`, open: !!deferral && !deferral.isComplete, placement: "right", anchor: anchor, anchorOffset: 4, canSave: canSave, onSave: onSave, onClose: onCancel, disabled: false, children: [description &&
160
- (0, jsx_runtime_1.jsx)(feedback_1.Note, { variant: "tip", children: description }), renderPreview({ state, video }), (0, jsx_runtime_1.jsxs)(FieldErrorController_1.FieldErrorController, { errorLevel: "none", children: [(0, jsx_runtime_1.jsx)(TextField_1.TextField, { field: uriField }), (schema === null || schema === void 0 ? void 0 : schema.allowMuting) &&
161
- (0, jsx_runtime_1.jsx)(FieldEditor_1.FieldEditor, { field: mutedField })] })] }));
169
+ return ((0, jsx_runtime_1.jsxs)(interaction_1.MiniEditor, Object.assign({ title: `${initialValue ? 'Edit' : 'Add'} ${name}`, open: !!deferral && !deferral.isComplete, canSave: canSave, onSave: onSave, onClose: onCancel, disabled: state === 'validating' }, popupProps, { children: [description &&
170
+ (0, jsx_runtime_1.jsx)(feedback_1.Note, { variant: "tip", children: description }), renderPreview({ state, video }), (0, jsx_runtime_1.jsx)(FieldErrorController_1.FieldErrorController, { errorLevel: "none", children: (0, jsx_runtime_1.jsx)(TextField_1.TextField, { field: uriField }) })] })));
162
171
  });
163
172
  async function getVideoThumbnailUrl(video) {
164
173
  // skip if there's no video
@@ -207,6 +216,16 @@ async function getVideoThumbnailUrl(video) {
207
216
  return null;
208
217
  }
209
218
  }
219
+ async function getVideoThumbnailSize(url) {
220
+ // get blob
221
+ const response = await fetch(url);
222
+ const blob = await response.blob();
223
+ // get image
224
+ const imageProxy = new funnel_core_1.BlobImageProxy(blob);
225
+ await imageProxy.waitUntilReady();
226
+ // return dimensions
227
+ return imageProxy.intrinsicSize;
228
+ }
210
229
  function renderPreview({ state, video }) {
211
230
  // show loader if checking
212
231
  let content;
@@ -1,10 +1,10 @@
1
1
  import { Callback, Schema } from '@nascentdigital/funnel-core';
2
- export declare function AssetFactory({ schema, assetId, anchor, onCommit, onCancel: onCancelRaw }: AssetFactory.Props): import("react/jsx-runtime").JSX.Element;
2
+ import { Popup } from '../../interaction';
3
+ export declare function AssetFactory({ schema, assetId, onCommit, onCancel: onCancelRaw, ...popupProps }: AssetFactory.Props): import("react/jsx-runtime").JSX.Element;
3
4
  export declare namespace AssetFactory {
4
- type Props = {
5
+ type Props = Popup.Props.Delegated & {
5
6
  schema: Schema.AssetField;
6
7
  assetId?: Schema.Asset.Id;
7
- anchor: string;
8
8
  onCommit: Callback<Schema.Field.Data<Schema.AssetField>>;
9
9
  onCancel: Callback.Void;
10
10
  };