@webiny/app-headless-cms 6.5.0-beta.7 → 6.5.0-beta.9

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 (32) hide show
  1. package/ContentEntriesModule.js +5 -0
  2. package/ContentEntriesModule.js.map +1 -1
  3. package/admin/components/ContentEntries/Filters/RefFieldRenderer.d.ts +2 -0
  4. package/admin/components/ContentEntries/Filters/RefFieldRenderer.js +84 -0
  5. package/admin/components/ContentEntries/Filters/RefFieldRenderer.js.map +1 -0
  6. package/package.json +23 -23
  7. package/presentation/contentEntries/preview/LivePreviewPresenter.d.ts +12 -1
  8. package/presentation/contentEntries/preview/LivePreviewPresenter.js +48 -5
  9. package/presentation/contentEntries/preview/LivePreviewPresenter.js.map +1 -1
  10. package/presentation/contentEntries/preview/LivePreviewPresenter.test.d.ts +1 -0
  11. package/presentation/contentEntries/preview/LivePreviewPresenter.test.js +81 -0
  12. package/presentation/contentEntries/preview/LivePreviewPresenter.test.js.map +1 -0
  13. package/presentation/contentEntries/preview/PreviewDecorator.js +16 -3
  14. package/presentation/contentEntries/preview/PreviewDecorator.js.map +1 -1
  15. package/presentation/contentEntries/preview/PreviewPane.d.ts +5 -1
  16. package/presentation/contentEntries/preview/PreviewPane.js +2 -2
  17. package/presentation/contentEntries/preview/PreviewPane.js.map +1 -1
  18. package/presentation/contentEntries/preview/abstractions.d.ts +8 -0
  19. package/presentation/contentEntries/preview/abstractions.js.map +1 -1
  20. package/presentation/contentEntries/preview/resolvePreviewUrl.d.ts +21 -0
  21. package/presentation/contentEntries/preview/resolvePreviewUrl.js +50 -1
  22. package/presentation/contentEntries/preview/resolvePreviewUrl.js.map +1 -1
  23. package/presentation/contentEntries/preview/resolvePreviewUrl.test.d.ts +1 -0
  24. package/presentation/contentEntries/preview/resolvePreviewUrl.test.js +127 -0
  25. package/presentation/contentEntries/preview/resolvePreviewUrl.test.js.map +1 -0
  26. package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.d.ts +1 -0
  27. package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js +133 -0
  28. package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js.map +1 -0
  29. package/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js +1 -1
  30. package/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js.map +1 -1
  31. package/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js +1 -1
  32. package/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js.map +1 -1
@@ -9,6 +9,7 @@ import { ChangeEntryStatus, DeleteEntry, DuplicateEntry, EditEntry, MoveEntry }
9
9
  import { CellActions, CellAuthor, CellCreated, CellLive, CellModified, CellName, CellStatus } from "./admin/components/ContentEntries/Table/Cells/index.js";
10
10
  import { IsModelPublishable } from "./admin/components/IsModelPublishable.js";
11
11
  import { FilterByStatus } from "./admin/components/ContentEntries/FilterByStatus.js";
12
+ import { Ref } from "./admin/components/ContentEntries/Filters/RefFieldRenderer.js";
12
13
  import { CmsTrashBin } from "./presentation/contentEntries/trashBin/CmsTrashBin.js";
13
14
  import { TrashEntryConfirmDialog } from "./admin/components/Dialogs/TrashEntryConfirmDialog.js";
14
15
  import { PublishEntryConfirmDialog } from "./admin/components/Dialogs/PublishEntryConfirmDialog.js";
@@ -129,6 +130,10 @@ const ContentEntriesModule = ()=>/*#__PURE__*/ react.createElement(react.Fragmen
129
130
  hideable: false,
130
131
  truncate: false,
131
132
  className: "flex justify-center"
133
+ }), /*#__PURE__*/ react.createElement(Browser.AdvancedSearch.FieldRenderer, {
134
+ name: "ref",
135
+ type: Browser.AdvancedSearch.FieldRenderer.FieldType.REF,
136
+ element: /*#__PURE__*/ react.createElement(Ref, null)
132
137
  })), /*#__PURE__*/ react.createElement(InternalContentEntryEditorConfig, null, /*#__PURE__*/ react.createElement(IsModelPublishable, null, /*#__PURE__*/ react.createElement(Actions.ButtonAction, {
133
138
  name: "revisionSelector",
134
139
  element: /*#__PURE__*/ react.createElement(RevisionSelector, null)
@@ -1 +1 @@
1
- {"version":3,"file":"ContentEntriesModule.js","sources":["../src/ContentEntriesModule.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig } from \"@webiny/app-admin\";\nimport { AdvancedSearchConfigs } from \"@webiny/app-aco/components/AdvancedSearch/AdvancedSearchConfigs\";\nimport {\n InternalContentEntryEditorConfig,\n InternalContentEntryListConfig\n} from \"~/admin/config/contentEntries/index.js\";\n\nimport {\n ActionDelete,\n ActionMove,\n ActionPublish,\n ActionUnpublish\n} from \"~/admin/components/ContentEntries/BulkActions/index.js\";\n\nimport {\n TRASH_ENTRY_DIALOG,\n PUBLISH_ENTRY_DIALOG,\n UNPUBLISH_ENTRY_DIALOG,\n DUPLICATE_ENTRY_DIALOG\n} from \"~/presentation/contentEntries/list/ContentEntriesPresenter.js\";\nimport { DeleteFolder, EditFolder, SetFolderPermissions } from \"@webiny/app-aco\";\nimport {\n ChangeEntryStatus,\n DeleteEntry,\n DuplicateEntry,\n EditEntry,\n MoveEntry\n} from \"~/admin/components/ContentEntries/Table/Actions/index.js\";\nimport {\n CellActions,\n CellAuthor,\n CellCreated,\n CellLive,\n CellModified,\n CellName,\n CellStatus\n} from \"~/admin/components/ContentEntries/Table/Cells/index.js\";\nimport { IsModelPublishable } from \"~/admin/components/IsModelPublishable.js\";\nimport { FilterByStatus } from \"~/admin/components/ContentEntries/FilterByStatus.js\";\nimport { CmsTrashBin } from \"~/presentation/contentEntries/trashBin/CmsTrashBin.js\";\nimport { TrashEntryConfirmDialog } from \"~/admin/components/Dialogs/TrashEntryConfirmDialog.js\";\nimport { PublishEntryConfirmDialog } from \"~/admin/components/Dialogs/PublishEntryConfirmDialog.js\";\nimport { UnpublishEntryConfirmDialog } from \"~/admin/components/Dialogs/UnpublishEntryConfirmDialog.js\";\nimport { DuplicateEntryConfirmDialog } from \"~/admin/components/Dialogs/DuplicateEntryConfirmDialog.js\";\nimport { DeleteRevisionConfirmDialog } from \"~/admin/components/Dialogs/DeleteRevisionConfirmDialog.js\";\nimport { EditRevisionNoteDialog } from \"~/admin/components/Dialogs/EditRevisionNoteDialog.js\";\nimport {\n DELETE_REVISION_DIALOG,\n EDIT_REVISION_NOTE_DIALOG\n} from \"~/presentation/contentEntries/revisionsList/RevisionsListPresenter.js\";\nimport { RevisionSelector } from \"~/presentation/contentEntries/views/actions/RevisionSelector.js\";\nimport { ContentFormOptionsMenu } from \"~/presentation/contentEntries/views/actions/ContentFormOptionsMenu.js\";\nimport { SaveContentButton } from \"~/presentation/contentEntries/views/actions/SaveContentButton.js\";\nimport { SaveAndPublishButton } from \"~/presentation/contentEntries/views/actions/SaveAndPublishButton.js\";\nimport { DeleteEntryMenuItem } from \"~/presentation/contentEntries/views/actions/DeleteEntryMenuItem.js\";\nimport { ShowRevisionListMenuItem } from \"~/presentation/contentEntries/views/actions/ShowRevisionListMenuItem.js\";\nimport { CreateNewRevisionButton } from \"~/presentation/contentEntries/views/actions/CreateNewRevisionButton.js\";\nimport { PublishOnlyButton } from \"~/presentation/contentEntries/views/actions/PublishOnlyButton.js\";\n\nconst { Browser } = InternalContentEntryListConfig;\nconst { Actions } = InternalContentEntryEditorConfig;\n\nexport const ContentEntriesModule = () => {\n return (\n <>\n <AdminConfig>\n <AdminConfig.Dialog\n name={TRASH_ENTRY_DIALOG}\n element={<TrashEntryConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={PUBLISH_ENTRY_DIALOG}\n element={<PublishEntryConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={UNPUBLISH_ENTRY_DIALOG}\n element={<UnpublishEntryConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={DUPLICATE_ENTRY_DIALOG}\n element={<DuplicateEntryConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={DELETE_REVISION_DIALOG}\n element={<DeleteRevisionConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={EDIT_REVISION_NOTE_DIALOG}\n element={<EditRevisionNoteDialog />}\n />\n </AdminConfig>\n <InternalContentEntryListConfig>\n <AdvancedSearchConfigs />\n <IsModelPublishable>\n <Browser.Filter name={\"status\"} element={<FilterByStatus />} />\n <Browser.BulkAction name={\"publish\"} element={<ActionPublish />} />\n <Browser.BulkAction name={\"unpublish\"} element={<ActionUnpublish />} />\n </IsModelPublishable>\n <Browser.Sidebar.Footer name={\"trashBin\"} element={<CmsTrashBin />} />\n <Browser.BulkAction name={\"move\"} element={<ActionMove />} />\n <Browser.BulkAction name={\"delete\"} element={<ActionDelete />} />\n <Browser.Folder.Action name={\"edit\"} element={<EditFolder />} />\n <Browser.Folder.Action name={\"permissions\"} element={<SetFolderPermissions />} />\n <Browser.Folder.Action name={\"delete\"} element={<DeleteFolder />} />\n <Browser.Entry.Action name={\"edit\"} element={<EditEntry />} />\n <IsModelPublishable>\n <Browser.Entry.Action name={\"status\"} element={<ChangeEntryStatus />} />\n </IsModelPublishable>\n <Browser.Entry.Action name={\"move\"} element={<MoveEntry />} />\n <Browser.Entry.Action name={\"duplicate\"} element={<DuplicateEntry />} />\n <Browser.Entry.Action name={\"delete\"} element={<DeleteEntry />} after={\"$last\"} />\n <Browser.Table.Column\n name={\"name\"}\n header={\"Name\"}\n cell={<CellName />}\n sortable={true}\n hideable={false}\n size={200}\n />\n <Browser.Table.Column name={\"createdBy\"} header={\"Author\"} cell={<CellAuthor />} />\n <Browser.Table.Column\n name={\"createdOn\"}\n header={\"Created\"}\n cell={<CellCreated />}\n sortable={true}\n />\n <Browser.Table.Column\n name={\"savedOn\"}\n header={\"Modified\"}\n cell={<CellModified />}\n sortable={true}\n />\n <IsModelPublishable>\n <Browser.Table.Column\n name={\"status\"}\n header={\"Status\"}\n cell={<CellStatus />}\n truncate={false}\n />\n <Browser.Table.Column\n name={\"live\"}\n header={\"Live\"}\n truncate={false}\n sortable={true}\n cell={<CellLive />}\n />\n </IsModelPublishable>\n <Browser.Table.Column\n name={\"actions\"}\n header={\"\"}\n cell={<CellActions />}\n size={56}\n resizable={false}\n hideable={false}\n truncate={false}\n className={\"flex justify-center\"}\n />\n </InternalContentEntryListConfig>\n <InternalContentEntryEditorConfig>\n <IsModelPublishable>\n <Actions.ButtonAction\n name={\"revisionSelector\"}\n element={<RevisionSelector />}\n />\n </IsModelPublishable>\n <Actions.ButtonAction name={\"optionsMenu\"} element={<ContentFormOptionsMenu />} />\n <Actions.ButtonAction name={\"save\"} element={<SaveContentButton />} />\n <IsModelPublishable>\n <Actions.ButtonAction\n name={\"cms/createNewRevision\"}\n element={<CreateNewRevisionButton />}\n />\n <Actions.ButtonAction name={\"publish\"} element={<SaveAndPublishButton />} />\n <Actions.ButtonAction name={\"publishOnly\"} element={<PublishOnlyButton />} />\n </IsModelPublishable>\n <Actions.MenuItemAction\n name={\"delete\"}\n element={<DeleteEntryMenuItem />}\n after={\"$last\"}\n />\n <IsModelPublishable>\n <Actions.MenuItemAction\n name={\"showRevisionsList\"}\n element={<ShowRevisionListMenuItem />}\n />\n </IsModelPublishable>\n </InternalContentEntryEditorConfig>\n </>\n );\n};\n"],"names":["Browser","InternalContentEntryListConfig","Actions","InternalContentEntryEditorConfig","ContentEntriesModule","AdminConfig","TRASH_ENTRY_DIALOG","TrashEntryConfirmDialog","PUBLISH_ENTRY_DIALOG","PublishEntryConfirmDialog","UNPUBLISH_ENTRY_DIALOG","UnpublishEntryConfirmDialog","DUPLICATE_ENTRY_DIALOG","DuplicateEntryConfirmDialog","DELETE_REVISION_DIALOG","DeleteRevisionConfirmDialog","EDIT_REVISION_NOTE_DIALOG","EditRevisionNoteDialog","AdvancedSearchConfigs","IsModelPublishable","FilterByStatus","ActionPublish","ActionUnpublish","CmsTrashBin","ActionMove","ActionDelete","EditFolder","SetFolderPermissions","DeleteFolder","EditEntry","ChangeEntryStatus","MoveEntry","DuplicateEntry","DeleteEntry","CellName","CellAuthor","CellCreated","CellModified","CellStatus","CellLive","CellActions","RevisionSelector","ContentFormOptionsMenu","SaveContentButton","CreateNewRevisionButton","SaveAndPublishButton","PublishOnlyButton","DeleteEntryMenuItem","ShowRevisionListMenuItem"],"mappings":"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"}
1
+ {"version":3,"file":"ContentEntriesModule.js","sources":["../src/ContentEntriesModule.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig } from \"@webiny/app-admin\";\nimport { AdvancedSearchConfigs } from \"@webiny/app-aco/components/AdvancedSearch/AdvancedSearchConfigs\";\nimport {\n InternalContentEntryEditorConfig,\n InternalContentEntryListConfig\n} from \"~/admin/config/contentEntries/index.js\";\n\nimport {\n ActionDelete,\n ActionMove,\n ActionPublish,\n ActionUnpublish\n} from \"~/admin/components/ContentEntries/BulkActions/index.js\";\n\nimport {\n TRASH_ENTRY_DIALOG,\n PUBLISH_ENTRY_DIALOG,\n UNPUBLISH_ENTRY_DIALOG,\n DUPLICATE_ENTRY_DIALOG\n} from \"~/presentation/contentEntries/list/ContentEntriesPresenter.js\";\nimport { DeleteFolder, EditFolder, SetFolderPermissions } from \"@webiny/app-aco\";\nimport {\n ChangeEntryStatus,\n DeleteEntry,\n DuplicateEntry,\n EditEntry,\n MoveEntry\n} from \"~/admin/components/ContentEntries/Table/Actions/index.js\";\nimport {\n CellActions,\n CellAuthor,\n CellCreated,\n CellLive,\n CellModified,\n CellName,\n CellStatus\n} from \"~/admin/components/ContentEntries/Table/Cells/index.js\";\nimport { IsModelPublishable } from \"~/admin/components/IsModelPublishable.js\";\nimport { FilterByStatus } from \"~/admin/components/ContentEntries/FilterByStatus.js\";\nimport { Ref } from \"~/admin/components/ContentEntries/Filters/RefFieldRenderer.js\";\nimport { CmsTrashBin } from \"~/presentation/contentEntries/trashBin/CmsTrashBin.js\";\nimport { TrashEntryConfirmDialog } from \"~/admin/components/Dialogs/TrashEntryConfirmDialog.js\";\nimport { PublishEntryConfirmDialog } from \"~/admin/components/Dialogs/PublishEntryConfirmDialog.js\";\nimport { UnpublishEntryConfirmDialog } from \"~/admin/components/Dialogs/UnpublishEntryConfirmDialog.js\";\nimport { DuplicateEntryConfirmDialog } from \"~/admin/components/Dialogs/DuplicateEntryConfirmDialog.js\";\nimport { DeleteRevisionConfirmDialog } from \"~/admin/components/Dialogs/DeleteRevisionConfirmDialog.js\";\nimport { EditRevisionNoteDialog } from \"~/admin/components/Dialogs/EditRevisionNoteDialog.js\";\nimport {\n DELETE_REVISION_DIALOG,\n EDIT_REVISION_NOTE_DIALOG\n} from \"~/presentation/contentEntries/revisionsList/RevisionsListPresenter.js\";\nimport { RevisionSelector } from \"~/presentation/contentEntries/views/actions/RevisionSelector.js\";\nimport { ContentFormOptionsMenu } from \"~/presentation/contentEntries/views/actions/ContentFormOptionsMenu.js\";\nimport { SaveContentButton } from \"~/presentation/contentEntries/views/actions/SaveContentButton.js\";\nimport { SaveAndPublishButton } from \"~/presentation/contentEntries/views/actions/SaveAndPublishButton.js\";\nimport { DeleteEntryMenuItem } from \"~/presentation/contentEntries/views/actions/DeleteEntryMenuItem.js\";\nimport { ShowRevisionListMenuItem } from \"~/presentation/contentEntries/views/actions/ShowRevisionListMenuItem.js\";\nimport { CreateNewRevisionButton } from \"~/presentation/contentEntries/views/actions/CreateNewRevisionButton.js\";\nimport { PublishOnlyButton } from \"~/presentation/contentEntries/views/actions/PublishOnlyButton.js\";\n\nconst { Browser } = InternalContentEntryListConfig;\nconst { Actions } = InternalContentEntryEditorConfig;\n\nexport const ContentEntriesModule = () => {\n return (\n <>\n <AdminConfig>\n <AdminConfig.Dialog\n name={TRASH_ENTRY_DIALOG}\n element={<TrashEntryConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={PUBLISH_ENTRY_DIALOG}\n element={<PublishEntryConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={UNPUBLISH_ENTRY_DIALOG}\n element={<UnpublishEntryConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={DUPLICATE_ENTRY_DIALOG}\n element={<DuplicateEntryConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={DELETE_REVISION_DIALOG}\n element={<DeleteRevisionConfirmDialog />}\n />\n <AdminConfig.Dialog\n name={EDIT_REVISION_NOTE_DIALOG}\n element={<EditRevisionNoteDialog />}\n />\n </AdminConfig>\n <InternalContentEntryListConfig>\n <AdvancedSearchConfigs />\n <IsModelPublishable>\n <Browser.Filter name={\"status\"} element={<FilterByStatus />} />\n <Browser.BulkAction name={\"publish\"} element={<ActionPublish />} />\n <Browser.BulkAction name={\"unpublish\"} element={<ActionUnpublish />} />\n </IsModelPublishable>\n <Browser.Sidebar.Footer name={\"trashBin\"} element={<CmsTrashBin />} />\n <Browser.BulkAction name={\"move\"} element={<ActionMove />} />\n <Browser.BulkAction name={\"delete\"} element={<ActionDelete />} />\n <Browser.Folder.Action name={\"edit\"} element={<EditFolder />} />\n <Browser.Folder.Action name={\"permissions\"} element={<SetFolderPermissions />} />\n <Browser.Folder.Action name={\"delete\"} element={<DeleteFolder />} />\n <Browser.Entry.Action name={\"edit\"} element={<EditEntry />} />\n <IsModelPublishable>\n <Browser.Entry.Action name={\"status\"} element={<ChangeEntryStatus />} />\n </IsModelPublishable>\n <Browser.Entry.Action name={\"move\"} element={<MoveEntry />} />\n <Browser.Entry.Action name={\"duplicate\"} element={<DuplicateEntry />} />\n <Browser.Entry.Action name={\"delete\"} element={<DeleteEntry />} after={\"$last\"} />\n <Browser.Table.Column\n name={\"name\"}\n header={\"Name\"}\n cell={<CellName />}\n sortable={true}\n hideable={false}\n size={200}\n />\n <Browser.Table.Column name={\"createdBy\"} header={\"Author\"} cell={<CellAuthor />} />\n <Browser.Table.Column\n name={\"createdOn\"}\n header={\"Created\"}\n cell={<CellCreated />}\n sortable={true}\n />\n <Browser.Table.Column\n name={\"savedOn\"}\n header={\"Modified\"}\n cell={<CellModified />}\n sortable={true}\n />\n <IsModelPublishable>\n <Browser.Table.Column\n name={\"status\"}\n header={\"Status\"}\n cell={<CellStatus />}\n truncate={false}\n />\n <Browser.Table.Column\n name={\"live\"}\n header={\"Live\"}\n truncate={false}\n sortable={true}\n cell={<CellLive />}\n />\n </IsModelPublishable>\n <Browser.Table.Column\n name={\"actions\"}\n header={\"\"}\n cell={<CellActions />}\n size={56}\n resizable={false}\n hideable={false}\n truncate={false}\n className={\"flex justify-center\"}\n />\n <Browser.AdvancedSearch.FieldRenderer\n name={\"ref\"}\n type={Browser.AdvancedSearch.FieldRenderer.FieldType.REF}\n element={<Ref />}\n />\n </InternalContentEntryListConfig>\n <InternalContentEntryEditorConfig>\n <IsModelPublishable>\n <Actions.ButtonAction\n name={\"revisionSelector\"}\n element={<RevisionSelector />}\n />\n </IsModelPublishable>\n <Actions.ButtonAction name={\"optionsMenu\"} element={<ContentFormOptionsMenu />} />\n <Actions.ButtonAction name={\"save\"} element={<SaveContentButton />} />\n <IsModelPublishable>\n <Actions.ButtonAction\n name={\"cms/createNewRevision\"}\n element={<CreateNewRevisionButton />}\n />\n <Actions.ButtonAction name={\"publish\"} element={<SaveAndPublishButton />} />\n <Actions.ButtonAction name={\"publishOnly\"} element={<PublishOnlyButton />} />\n </IsModelPublishable>\n <Actions.MenuItemAction\n name={\"delete\"}\n element={<DeleteEntryMenuItem />}\n after={\"$last\"}\n />\n <IsModelPublishable>\n <Actions.MenuItemAction\n name={\"showRevisionsList\"}\n element={<ShowRevisionListMenuItem />}\n />\n </IsModelPublishable>\n </InternalContentEntryEditorConfig>\n </>\n );\n};\n"],"names":["Browser","InternalContentEntryListConfig","Actions","InternalContentEntryEditorConfig","ContentEntriesModule","AdminConfig","TRASH_ENTRY_DIALOG","TrashEntryConfirmDialog","PUBLISH_ENTRY_DIALOG","PublishEntryConfirmDialog","UNPUBLISH_ENTRY_DIALOG","UnpublishEntryConfirmDialog","DUPLICATE_ENTRY_DIALOG","DuplicateEntryConfirmDialog","DELETE_REVISION_DIALOG","DeleteRevisionConfirmDialog","EDIT_REVISION_NOTE_DIALOG","EditRevisionNoteDialog","AdvancedSearchConfigs","IsModelPublishable","FilterByStatus","ActionPublish","ActionUnpublish","CmsTrashBin","ActionMove","ActionDelete","EditFolder","SetFolderPermissions","DeleteFolder","EditEntry","ChangeEntryStatus","MoveEntry","DuplicateEntry","DeleteEntry","CellName","CellAuthor","CellCreated","CellModified","CellStatus","CellLive","CellActions","Ref","RevisionSelector","ContentFormOptionsMenu","SaveContentButton","CreateNewRevisionButton","SaveAndPublishButton","PublishOnlyButton","DeleteEntryMenuItem","ShowRevisionListMenuItem"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const Ref: () => React.JSX.Element;
@@ -0,0 +1,84 @@
1
+ import react, { useEffect, useMemo } from "react";
2
+ import { observer } from "mobx-react-lite";
3
+ import { Bind } from "@webiny/form";
4
+ import { AutoComplete } from "@webiny/admin-ui";
5
+ import { DiContainerProvider, useContainer, useFeature } from "@webiny/app";
6
+ import { ContentEntryListConfig } from "../../../config/contentEntries/index.js";
7
+ import { RefSingleAutocompletePresenterFeature } from "../../../../presentation/fieldRenderers/ref/autocomplete/single/feature.js";
8
+ const { useInputField: useInputField } = ContentEntryListConfig.Browser.AdvancedSearch.FieldRenderer;
9
+ const parseValue = (value)=>{
10
+ if ("string" != typeof value || !value) return null;
11
+ try {
12
+ const parsed = JSON.parse(value);
13
+ if (!parsed?.entryId || !parsed?.modelId) return null;
14
+ return {
15
+ entryId: parsed.entryId,
16
+ modelId: parsed.modelId
17
+ };
18
+ } catch {
19
+ return null;
20
+ }
21
+ };
22
+ const Ref = ()=>{
23
+ const { name, field } = useInputField();
24
+ const parentContainer = useContainer();
25
+ const scopedContainer = useMemo(()=>{
26
+ const child = parentContainer.createChildContainer();
27
+ RefSingleAutocompletePresenterFeature.register(child);
28
+ return child;
29
+ }, [
30
+ field.value
31
+ ]);
32
+ return /*#__PURE__*/ react.createElement(DiContainerProvider, {
33
+ container: scopedContainer,
34
+ key: field.value
35
+ }, /*#__PURE__*/ react.createElement(Bind, {
36
+ name: name
37
+ }, (bind)=>/*#__PURE__*/ react.createElement(RefInner, {
38
+ bind: bind,
39
+ modelIds: field.settings.modelIds
40
+ })));
41
+ };
42
+ const RefInner = observer(({ bind, modelIds })=>{
43
+ const { presenter } = useFeature(RefSingleAutocompletePresenterFeature);
44
+ useEffect(()=>{
45
+ const value = parseValue(bind.value);
46
+ presenter.init({
47
+ modelIds,
48
+ value: value ? {
49
+ id: value.entryId,
50
+ modelId: value.modelId
51
+ } : null
52
+ });
53
+ }, []);
54
+ const vm = presenter.vm;
55
+ return /*#__PURE__*/ react.createElement(AutoComplete, {
56
+ label: "Value",
57
+ size: "lg",
58
+ loading: vm.loading,
59
+ value: vm.value,
60
+ options: vm.options,
61
+ validation: bind.validation,
62
+ onValueSearch: (query)=>presenter.search(query),
63
+ onValueChange: (entryId)=>{
64
+ if (!entryId) {
65
+ presenter.clear();
66
+ bind.onChange("");
67
+ return;
68
+ }
69
+ const ref = presenter.select(entryId);
70
+ if (ref) bind.onChange(JSON.stringify({
71
+ entryId,
72
+ modelId: ref.modelId
73
+ }));
74
+ },
75
+ onValueReset: ()=>{
76
+ presenter.clear();
77
+ bind.onChange("");
78
+ },
79
+ displayResetAction: vm.canReset
80
+ });
81
+ });
82
+ export { Ref };
83
+
84
+ //# sourceMappingURL=RefFieldRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin/components/ContentEntries/Filters/RefFieldRenderer.js","sources":["../../../../../src/admin/components/ContentEntries/Filters/RefFieldRenderer.tsx"],"sourcesContent":["import React, { useEffect, useMemo } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { Bind } from \"@webiny/form\";\nimport type { BindComponentRenderProp } from \"@webiny/form\";\nimport { AutoComplete } from \"@webiny/admin-ui\";\nimport { DiContainerProvider, useContainer, useFeature } from \"@webiny/app\";\nimport { ContentEntryListConfig } from \"~/admin/config/contentEntries/index.js\";\nimport { RefSingleAutocompletePresenterFeature } from \"~/presentation/fieldRenderers/ref/autocomplete/single/feature.js\";\n\nconst { useInputField } = ContentEntryListConfig.Browser.AdvancedSearch.FieldRenderer;\n\n/**\n * The advanced search filter stores the selected reference as a JSON string\n * `{ entryId, modelId }`. `GraphQLInputMapper` reads `entryId` from it to build the\n * `<fieldId>.entryId` where condition, and `modelId` is used to load the label back\n * when a saved filter is opened.\n */\ninterface RefFilterValue {\n entryId: string;\n modelId: string;\n}\n\nconst parseValue = (value: unknown): RefFilterValue | null => {\n if (typeof value !== \"string\" || !value) {\n return null;\n }\n try {\n const parsed = JSON.parse(value);\n if (!parsed?.entryId || !parsed?.modelId) {\n return null;\n }\n return { entryId: parsed.entryId, modelId: parsed.modelId };\n } catch {\n return null;\n }\n};\n\nexport const Ref = () => {\n const { name, field } = useInputField();\n const parentContainer = useContainer();\n\n const scopedContainer = useMemo(() => {\n const child = parentContainer.createChildContainer();\n RefSingleAutocompletePresenterFeature.register(child);\n return child;\n }, [field.value]);\n\n return (\n <DiContainerProvider container={scopedContainer} key={field.value}>\n <Bind name={name}>\n {bind => <RefInner bind={bind} modelIds={field.settings.modelIds} />}\n </Bind>\n </DiContainerProvider>\n );\n};\n\ninterface RefInnerProps {\n bind: BindComponentRenderProp;\n modelIds: string[];\n}\n\nconst RefInner = observer(({ bind, modelIds }: RefInnerProps) => {\n const { presenter } = useFeature(RefSingleAutocompletePresenterFeature);\n\n useEffect(() => {\n const value = parseValue(bind.value);\n presenter.init({\n modelIds,\n value: value ? { id: value.entryId, modelId: value.modelId } : null\n });\n }, []);\n\n const vm = presenter.vm;\n\n return (\n <AutoComplete\n label={\"Value\"}\n size={\"lg\"}\n loading={vm.loading}\n value={vm.value}\n options={vm.options}\n validation={bind.validation}\n onValueSearch={query => presenter.search(query)}\n onValueChange={entryId => {\n if (!entryId) {\n presenter.clear();\n bind.onChange(\"\");\n return;\n }\n const ref = presenter.select(entryId);\n if (ref) {\n bind.onChange(JSON.stringify({ entryId, modelId: ref.modelId }));\n }\n }}\n onValueReset={() => {\n presenter.clear();\n bind.onChange(\"\");\n }}\n displayResetAction={vm.canReset}\n />\n );\n});\n"],"names":["useInputField","ContentEntryListConfig","parseValue","value","parsed","JSON","Ref","name","field","parentContainer","useContainer","scopedContainer","useMemo","child","RefSingleAutocompletePresenterFeature","DiContainerProvider","Bind","bind","RefInner","observer","modelIds","presenter","useFeature","useEffect","vm","AutoComplete","query","entryId","ref"],"mappings":";;;;;;;AASA,MAAM,EAAEA,eAAAA,aAAa,EAAE,GAAGC,uBAAuB,OAAO,CAAC,cAAc,CAAC,aAAa;AAarF,MAAMC,aAAa,CAACC;IAChB,IAAI,AAAiB,YAAjB,OAAOA,SAAsB,CAACA,OAC9B,OAAO;IAEX,IAAI;QACA,MAAMC,SAASC,KAAK,KAAK,CAACF;QAC1B,IAAI,CAACC,QAAQ,WAAW,CAACA,QAAQ,SAC7B,OAAO;QAEX,OAAO;YAAE,SAASA,OAAO,OAAO;YAAE,SAASA,OAAO,OAAO;QAAC;IAC9D,EAAE,OAAM;QACJ,OAAO;IACX;AACJ;AAEO,MAAME,MAAM;IACf,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAGR;IACxB,MAAMS,kBAAkBC;IAExB,MAAMC,kBAAkBC,QAAQ;QAC5B,MAAMC,QAAQJ,gBAAgB,oBAAoB;QAClDK,sCAAsC,QAAQ,CAACD;QAC/C,OAAOA;IACX,GAAG;QAACL,MAAM,KAAK;KAAC;IAEhB,OAAO,WAAP,GACI,oBAACO,qBAAmBA;QAAC,WAAWJ;QAAiB,KAAKH,MAAM,KAAK;qBAC7D,oBAACQ,MAAIA;QAAC,MAAMT;OACPU,CAAAA,OAAAA,WAAAA,GAAQ,oBAACC,UAAQA;YAAC,MAAMD;YAAM,UAAUT,MAAM,QAAQ,CAAC,QAAQ;;AAIhF;AAOA,MAAMU,WAAWC,SAAS,CAAC,EAAEF,IAAI,EAAEG,QAAQ,EAAiB;IACxD,MAAM,EAAEC,SAAS,EAAE,GAAGC,WAAWR;IAEjCS,UAAU;QACN,MAAMpB,QAAQD,WAAWe,KAAK,KAAK;QACnCI,UAAU,IAAI,CAAC;YACXD;YACA,OAAOjB,QAAQ;gBAAE,IAAIA,MAAM,OAAO;gBAAE,SAASA,MAAM,OAAO;YAAC,IAAI;QACnE;IACJ,GAAG,EAAE;IAEL,MAAMqB,KAAKH,UAAU,EAAE;IAEvB,OAAO,WAAP,GACI,oBAACI,cAAYA;QACT,OAAO;QACP,MAAM;QACN,SAASD,GAAG,OAAO;QACnB,OAAOA,GAAG,KAAK;QACf,SAASA,GAAG,OAAO;QACnB,YAAYP,KAAK,UAAU;QAC3B,eAAeS,CAAAA,QAASL,UAAU,MAAM,CAACK;QACzC,eAAeC,CAAAA;YACX,IAAI,CAACA,SAAS;gBACVN,UAAU,KAAK;gBACfJ,KAAK,QAAQ,CAAC;gBACd;YACJ;YACA,MAAMW,MAAMP,UAAU,MAAM,CAACM;YAC7B,IAAIC,KACAX,KAAK,QAAQ,CAACZ,KAAK,SAAS,CAAC;gBAAEsB;gBAAS,SAASC,IAAI,OAAO;YAAC;QAErE;QACA,cAAc;YACVP,UAAU,KAAK;YACfJ,KAAK,QAAQ,CAAC;QAClB;QACA,oBAAoBO,GAAG,QAAQ;;AAG3C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/app-headless-cms",
3
- "version": "6.5.0-beta.7",
3
+ "version": "6.5.0-beta.9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -26,32 +26,32 @@
26
26
  "@fortawesome/free-regular-svg-icons": "7.3.1",
27
27
  "@fortawesome/free-solid-svg-icons": "7.3.1",
28
28
  "@fortawesome/react-fontawesome": "3.5.0",
29
- "@lexical/utils": "0.51.0",
29
+ "@lexical/utils": "0.52.0",
30
30
  "@types/react": "18.3.31",
31
- "@webiny/admin-ui": "6.5.0-beta.7",
32
- "@webiny/app": "6.5.0-beta.7",
33
- "@webiny/app-aco": "6.5.0-beta.7",
34
- "@webiny/app-admin": "6.5.0-beta.7",
35
- "@webiny/app-graphql-playground": "6.5.0-beta.7",
36
- "@webiny/app-headless-cms-common": "6.5.0-beta.7",
37
- "@webiny/cms-sdk": "6.5.0-beta.7",
31
+ "@webiny/admin-ui": "6.5.0-beta.9",
32
+ "@webiny/app": "6.5.0-beta.9",
33
+ "@webiny/app-aco": "6.5.0-beta.9",
34
+ "@webiny/app-admin": "6.5.0-beta.9",
35
+ "@webiny/app-graphql-playground": "6.5.0-beta.9",
36
+ "@webiny/app-headless-cms-common": "6.5.0-beta.9",
37
+ "@webiny/cms-sdk": "6.5.0-beta.9",
38
38
  "@webiny/di": "1.1.0",
39
- "@webiny/feature": "6.5.0-beta.7",
40
- "@webiny/form": "6.5.0-beta.7",
41
- "@webiny/frontend-settings": "6.5.0-beta.7",
42
- "@webiny/icons": "6.5.0-beta.7",
43
- "@webiny/lexical-editor": "6.5.0-beta.7",
44
- "@webiny/lexical-nodes": "6.5.0-beta.7",
45
- "@webiny/plugins": "6.5.0-beta.7",
46
- "@webiny/react-composition": "6.5.0-beta.7",
47
- "@webiny/react-properties": "6.5.0-beta.7",
48
- "@webiny/utils": "6.5.0-beta.7",
49
- "@webiny/validation": "6.5.0-beta.7",
39
+ "@webiny/feature": "6.5.0-beta.9",
40
+ "@webiny/form": "6.5.0-beta.9",
41
+ "@webiny/frontend-settings": "6.5.0-beta.9",
42
+ "@webiny/icons": "6.5.0-beta.9",
43
+ "@webiny/lexical-editor": "6.5.0-beta.9",
44
+ "@webiny/lexical-nodes": "6.5.0-beta.9",
45
+ "@webiny/plugins": "6.5.0-beta.9",
46
+ "@webiny/react-composition": "6.5.0-beta.9",
47
+ "@webiny/react-properties": "6.5.0-beta.9",
48
+ "@webiny/utils": "6.5.0-beta.9",
49
+ "@webiny/validation": "6.5.0-beta.9",
50
50
  "dot-prop-immutable": "2.1.1",
51
51
  "graphql": "16.14.2",
52
52
  "graphql-tag": "2.12.7",
53
53
  "lodash": "4.18.1",
54
- "mobx": "7.0.5",
54
+ "mobx": "7.0.6",
55
55
  "mobx-react-lite": "5.1.0",
56
56
  "react": "18.3.1",
57
57
  "react-dnd": "16.0.1",
@@ -63,10 +63,10 @@
63
63
  "zod": "4.6.5"
64
64
  },
65
65
  "devDependencies": {
66
- "@webiny/build-tools": "6.5.0-beta.7",
66
+ "@webiny/build-tools": "6.5.0-beta.9",
67
67
  "rimraf": "6.1.3",
68
68
  "typescript": "7.0.2",
69
- "vitest": "5.0.2"
69
+ "vitest": "5.0.3"
70
70
  },
71
71
  "publishConfig": {
72
72
  "access": "public"
@@ -1,12 +1,23 @@
1
+ import { GetModelUseCase } from "../../../features/model/getModel/abstractions.js";
2
+ import { GetEntryUseCase } from "../../../features/contentEntry/getEntry/abstractions.js";
3
+ import type { CmsReferenceValue } from "../../../features/contentEntry/refTypes.js";
1
4
  import { type ILivePreviewPresenter, type PreviewComponent } from "./abstractions.js";
5
+ import type { RefValuesMap } from "./resolvePreviewUrl.js";
2
6
  declare class LivePreviewPresenterImpl implements ILivePreviewPresenter {
7
+ private readonly getModelUseCase;
8
+ private readonly getEntryUseCase;
3
9
  private components;
4
- constructor();
10
+ private refValues;
11
+ private requestedRefs;
12
+ constructor(getModelUseCase: GetModelUseCase.Interface, getEntryUseCase: GetEntryUseCase.Interface);
5
13
  get vm(): {
6
14
  components: PreviewComponent[];
15
+ refValues: RefValuesMap;
7
16
  };
8
17
  addComponent(component: PreviewComponent): void;
9
18
  clearComponents(): void;
19
+ loadRefValues(refs: CmsReferenceValue[]): void;
20
+ private loadRef;
10
21
  }
11
22
  export declare const LivePreviewPresenter: typeof LivePreviewPresenterImpl & {
12
23
  __abstraction: import("@webiny/di").Abstraction<ILivePreviewPresenter>;
@@ -1,15 +1,25 @@
1
- import { computed, makeAutoObservable } from "mobx";
1
+ import { computed, makeAutoObservable, runInAction } from "mobx";
2
+ import { GetModelUseCase } from "../../../features/model/getModel/abstractions.js";
3
+ import { GetEntryUseCase } from "../../../features/contentEntry/getEntry/abstractions.js";
2
4
  import { LivePreviewPresenter } from "./abstractions.js";
3
5
  class LivePreviewPresenterImpl {
4
- constructor(){
6
+ constructor(getModelUseCase, getEntryUseCase){
7
+ this.getModelUseCase = getModelUseCase;
8
+ this.getEntryUseCase = getEntryUseCase;
5
9
  this.components = [];
10
+ this.refValues = {};
11
+ this.requestedRefs = new Set();
6
12
  makeAutoObservable(this, {
7
- vm: computed
13
+ vm: computed,
14
+ getModelUseCase: false,
15
+ getEntryUseCase: false,
16
+ requestedRefs: false
8
17
  });
9
18
  }
10
19
  get vm() {
11
20
  return {
12
- components: this.components
21
+ components: this.components,
22
+ refValues: this.refValues
13
23
  };
14
24
  }
15
25
  addComponent(component) {
@@ -23,10 +33,43 @@ class LivePreviewPresenterImpl {
23
33
  clearComponents() {
24
34
  this.components = [];
25
35
  }
36
+ loadRefValues(refs) {
37
+ for (const ref of refs)if (!this.requestedRefs.has(ref.id)) {
38
+ this.requestedRefs.add(ref.id);
39
+ this.loadRef(ref);
40
+ }
41
+ }
42
+ async loadRef(ref) {
43
+ try {
44
+ const model = await this.getModelUseCase.execute({
45
+ modelId: ref.modelId
46
+ });
47
+ const entry = await this.getEntryUseCase.execute({
48
+ model,
49
+ id: ref.id
50
+ });
51
+ runInAction(()=>{
52
+ this.refValues = {
53
+ ...this.refValues,
54
+ [ref.id]: entry.values ?? {}
55
+ };
56
+ });
57
+ } catch {
58
+ runInAction(()=>{
59
+ this.refValues = {
60
+ ...this.refValues,
61
+ [ref.id]: null
62
+ };
63
+ });
64
+ }
65
+ }
26
66
  }
27
67
  const LivePreviewPresenter_LivePreviewPresenter = LivePreviewPresenter.createImplementation({
28
68
  implementation: LivePreviewPresenterImpl,
29
- dependencies: []
69
+ dependencies: [
70
+ GetModelUseCase,
71
+ GetEntryUseCase
72
+ ]
30
73
  });
31
74
  export { LivePreviewPresenter_LivePreviewPresenter as LivePreviewPresenter };
32
75
 
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/contentEntries/preview/LivePreviewPresenter.js","sources":["../../../../src/presentation/contentEntries/preview/LivePreviewPresenter.ts"],"sourcesContent":["import { makeAutoObservable, computed } from \"mobx\";\nimport {\n LivePreviewPresenter as Abstraction,\n type ILivePreviewPresenter,\n type PreviewComponent\n} from \"./abstractions.js\";\n\nclass LivePreviewPresenterImpl implements ILivePreviewPresenter {\n private components: PreviewComponent[] = [];\n\n constructor() {\n makeAutoObservable(this, { vm: computed });\n }\n\n get vm() {\n return {\n components: this.components\n };\n }\n\n addComponent(component: PreviewComponent): void {\n const exists = this.components.some(c => c.name === component.name);\n if (exists) {\n this.components = this.components.map(c => (c.name === component.name ? component : c));\n } else {\n this.components = [...this.components, component];\n }\n }\n\n clearComponents(): void {\n this.components = [];\n }\n}\n\nexport const LivePreviewPresenter = Abstraction.createImplementation({\n implementation: LivePreviewPresenterImpl,\n dependencies: []\n});\n"],"names":["LivePreviewPresenterImpl","makeAutoObservable","computed","component","exists","c","LivePreviewPresenter","Abstraction"],"mappings":";;AAOA,MAAMA;IAGF,aAAc;aAFN,UAAU,GAAuB,EAAE;QAGvCC,mBAAmB,IAAI,EAAE;YAAE,IAAIC;QAAS;IAC5C;IAEA,IAAI,KAAK;QACL,OAAO;YACH,YAAY,IAAI,CAAC,UAAU;QAC/B;IACJ;IAEA,aAAaC,SAA2B,EAAQ;QAC5C,MAAMC,SAAS,IAAI,CAAC,UAAU,CAAC,IAAI,CAACC,CAAAA,IAAKA,EAAE,IAAI,KAAKF,UAAU,IAAI;QAClE,IAAIC,QACA,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAACC,CAAAA,IAAMA,EAAE,IAAI,KAAKF,UAAU,IAAI,GAAGA,YAAYE;aAEpF,IAAI,CAAC,UAAU,GAAG;eAAI,IAAI,CAAC,UAAU;YAAEF;SAAU;IAEzD;IAEA,kBAAwB;QACpB,IAAI,CAAC,UAAU,GAAG,EAAE;IACxB;AACJ;AAEO,MAAMG,4CAAuBC,qBAAAA,oBAAgC,CAAC;IACjE,gBAAgBP;IAChB,cAAc,EAAE;AACpB"}
1
+ {"version":3,"file":"presentation/contentEntries/preview/LivePreviewPresenter.js","sources":["../../../../src/presentation/contentEntries/preview/LivePreviewPresenter.ts"],"sourcesContent":["import { makeAutoObservable, computed, runInAction } from \"mobx\";\nimport { GetModelUseCase } from \"~/features/model/getModel/abstractions.js\";\nimport { GetEntryUseCase } from \"~/features/contentEntry/getEntry/abstractions.js\";\nimport type { CmsReferenceValue } from \"~/features/contentEntry/refTypes.js\";\nimport {\n LivePreviewPresenter as Abstraction,\n type ILivePreviewPresenter,\n type PreviewComponent\n} from \"./abstractions.js\";\nimport type { RefValuesMap } from \"./resolvePreviewUrl.js\";\n\nclass LivePreviewPresenterImpl implements ILivePreviewPresenter {\n private components: PreviewComponent[] = [];\n private refValues: RefValuesMap = {};\n private requestedRefs = new Set<string>();\n\n constructor(\n private readonly getModelUseCase: GetModelUseCase.Interface,\n private readonly getEntryUseCase: GetEntryUseCase.Interface\n ) {\n makeAutoObservable<\n LivePreviewPresenterImpl,\n \"getModelUseCase\" | \"getEntryUseCase\" | \"requestedRefs\"\n >(this, {\n vm: computed,\n getModelUseCase: false,\n getEntryUseCase: false,\n requestedRefs: false\n });\n }\n\n get vm() {\n return {\n components: this.components,\n refValues: this.refValues\n };\n }\n\n addComponent(component: PreviewComponent): void {\n const exists = this.components.some(c => c.name === component.name);\n if (exists) {\n this.components = this.components.map(c => (c.name === component.name ? component : c));\n } else {\n this.components = [...this.components, component];\n }\n }\n\n clearComponents(): void {\n this.components = [];\n }\n\n loadRefValues(refs: CmsReferenceValue[]): void {\n for (const ref of refs) {\n if (this.requestedRefs.has(ref.id)) {\n continue;\n }\n this.requestedRefs.add(ref.id);\n void this.loadRef(ref);\n }\n }\n\n private async loadRef(ref: CmsReferenceValue): Promise<void> {\n try {\n const model = await this.getModelUseCase.execute({ modelId: ref.modelId });\n const entry = await this.getEntryUseCase.execute({ model, id: ref.id });\n runInAction(() => {\n this.refValues = { ...this.refValues, [ref.id]: entry.values ?? {} };\n });\n } catch {\n runInAction(() => {\n this.refValues = { ...this.refValues, [ref.id]: null };\n });\n }\n }\n}\n\nexport const LivePreviewPresenter = Abstraction.createImplementation({\n implementation: LivePreviewPresenterImpl,\n dependencies: [GetModelUseCase, GetEntryUseCase]\n});\n"],"names":["LivePreviewPresenterImpl","getModelUseCase","getEntryUseCase","Set","makeAutoObservable","computed","component","exists","c","refs","ref","model","entry","runInAction","LivePreviewPresenter","Abstraction","GetModelUseCase","GetEntryUseCase"],"mappings":";;;;AAWA,MAAMA;IAKF,YACqBC,eAA0C,EAC1CC,eAA0C,CAC7D;aAFmBD,eAAe,GAAfA;aACAC,eAAe,GAAfA;aANb,UAAU,GAAuB,EAAE;aACnC,SAAS,GAAiB,CAAC;aAC3B,aAAa,GAAG,IAAIC;QAMxBC,mBAGE,IAAI,EAAE;YACJ,IAAIC;YACJ,iBAAiB;YACjB,iBAAiB;YACjB,eAAe;QACnB;IACJ;IAEA,IAAI,KAAK;QACL,OAAO;YACH,YAAY,IAAI,CAAC,UAAU;YAC3B,WAAW,IAAI,CAAC,SAAS;QAC7B;IACJ;IAEA,aAAaC,SAA2B,EAAQ;QAC5C,MAAMC,SAAS,IAAI,CAAC,UAAU,CAAC,IAAI,CAACC,CAAAA,IAAKA,EAAE,IAAI,KAAKF,UAAU,IAAI;QAClE,IAAIC,QACA,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAACC,CAAAA,IAAMA,EAAE,IAAI,KAAKF,UAAU,IAAI,GAAGA,YAAYE;aAEpF,IAAI,CAAC,UAAU,GAAG;eAAI,IAAI,CAAC,UAAU;YAAEF;SAAU;IAEzD;IAEA,kBAAwB;QACpB,IAAI,CAAC,UAAU,GAAG,EAAE;IACxB;IAEA,cAAcG,IAAyB,EAAQ;QAC3C,KAAK,MAAMC,OAAOD,KACd,KAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAACC,IAAI,EAAE;YAGjC,IAAI,CAAC,aAAa,CAAC,GAAG,CAACA,IAAI,EAAE;YACxB,IAAI,CAAC,OAAO,CAACA;;IAE1B;IAEA,MAAc,QAAQA,GAAsB,EAAiB;QACzD,IAAI;YACA,MAAMC,QAAQ,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;gBAAE,SAASD,IAAI,OAAO;YAAC;YACxE,MAAME,QAAQ,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;gBAAED;gBAAO,IAAID,IAAI,EAAE;YAAC;YACrEG,YAAY;gBACR,IAAI,CAAC,SAAS,GAAG;oBAAE,GAAG,IAAI,CAAC,SAAS;oBAAE,CAACH,IAAI,EAAE,CAAC,EAAEE,MAAM,MAAM,IAAI,CAAC;gBAAE;YACvE;QACJ,EAAE,OAAM;YACJC,YAAY;gBACR,IAAI,CAAC,SAAS,GAAG;oBAAE,GAAG,IAAI,CAAC,SAAS;oBAAE,CAACH,IAAI,EAAE,CAAC,EAAE;gBAAK;YACzD;QACJ;IACJ;AACJ;AAEO,MAAMI,4CAAuBC,qBAAAA,oBAAgC,CAAC;IACjE,gBAAgBf;IAChB,cAAc;QAACgB;QAAiBC;KAAgB;AACpD"}
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { Container } from "@webiny/di";
3
+ import { GetModelUseCase } from "../../../features/model/getModel/abstractions.js";
4
+ import { GetEntryUseCase } from "../../../features/contentEntry/getEntry/abstractions.js";
5
+ import { LivePreviewPresenter } from "./abstractions.js";
6
+ import { LivePreviewPresenter as external_LivePreviewPresenter_js_LivePreviewPresenter } from "./LivePreviewPresenter.js";
7
+ const setup = ()=>{
8
+ const getModel = vi.fn(async ({ modelId })=>({
9
+ modelId,
10
+ fields: []
11
+ }));
12
+ const getEntry = vi.fn(async ({ id })=>{
13
+ if ("missing#0001" === id) throw new Error("Entry not found.");
14
+ return {
15
+ id,
16
+ values: {
17
+ slug: "baker"
18
+ }
19
+ };
20
+ });
21
+ const container = new Container();
22
+ container.registerInstance(GetModelUseCase, {
23
+ execute: getModel
24
+ });
25
+ container.registerInstance(GetEntryUseCase, {
26
+ execute: getEntry
27
+ });
28
+ container.register(external_LivePreviewPresenter_js_LivePreviewPresenter);
29
+ return {
30
+ presenter: container.resolve(LivePreviewPresenter),
31
+ getModel,
32
+ getEntry
33
+ };
34
+ };
35
+ describe("LivePreviewPresenter", ()=>{
36
+ it("should load each referenced entry once and expose its values", async ()=>{
37
+ const { presenter, getEntry } = setup();
38
+ const ref = {
39
+ id: "loc1#0001",
40
+ modelId: "location"
41
+ };
42
+ presenter.loadRefValues([
43
+ ref
44
+ ]);
45
+ presenter.loadRefValues([
46
+ ref
47
+ ]);
48
+ await vi.waitFor(()=>{
49
+ expect(presenter.vm.refValues[ref.id]).toEqual({
50
+ slug: "baker"
51
+ });
52
+ });
53
+ expect(getEntry).toHaveBeenCalledTimes(1);
54
+ expect(getEntry).toHaveBeenCalledWith({
55
+ model: {
56
+ modelId: "location",
57
+ fields: []
58
+ },
59
+ id: ref.id
60
+ });
61
+ });
62
+ it("should mark a ref that fails to load and not request it again", async ()=>{
63
+ const { presenter, getEntry } = setup();
64
+ const ref = {
65
+ id: "missing#0001",
66
+ modelId: "location"
67
+ };
68
+ presenter.loadRefValues([
69
+ ref
70
+ ]);
71
+ await vi.waitFor(()=>{
72
+ expect(presenter.vm.refValues[ref.id]).toBeNull();
73
+ });
74
+ presenter.loadRefValues([
75
+ ref
76
+ ]);
77
+ expect(getEntry).toHaveBeenCalledTimes(1);
78
+ });
79
+ });
80
+
81
+ //# sourceMappingURL=LivePreviewPresenter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation/contentEntries/preview/LivePreviewPresenter.test.js","sources":["../../../../src/presentation/contentEntries/preview/LivePreviewPresenter.test.ts"],"sourcesContent":["import { describe, it, expect, vi } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { GetModelUseCase } from \"~/features/model/getModel/abstractions.js\";\nimport { GetEntryUseCase } from \"~/features/contentEntry/getEntry/abstractions.js\";\nimport { LivePreviewPresenter as Abstraction } from \"./abstractions.js\";\nimport { LivePreviewPresenter } from \"./LivePreviewPresenter.js\";\n\nconst setup = () => {\n const getModel = vi.fn(async ({ modelId }: { modelId: string }) => ({ modelId, fields: [] }));\n const getEntry = vi.fn(async ({ id }: { id: string }) => {\n if (id === \"missing#0001\") {\n throw new Error(\"Entry not found.\");\n }\n return { id, values: { slug: \"baker\" } };\n });\n\n const container = new Container();\n container.registerInstance(GetModelUseCase, {\n execute: getModel\n } as unknown as GetModelUseCase.Interface);\n container.registerInstance(GetEntryUseCase, {\n execute: getEntry\n } as unknown as GetEntryUseCase.Interface);\n container.register(LivePreviewPresenter);\n\n return { presenter: container.resolve(Abstraction), getModel, getEntry };\n};\n\ndescribe(\"LivePreviewPresenter\", () => {\n it(\"should load each referenced entry once and expose its values\", async () => {\n const { presenter, getEntry } = setup();\n const ref = { id: \"loc1#0001\", modelId: \"location\" };\n\n presenter.loadRefValues([ref]);\n presenter.loadRefValues([ref]);\n\n await vi.waitFor(() => {\n expect(presenter.vm.refValues[ref.id]).toEqual({ slug: \"baker\" });\n });\n expect(getEntry).toHaveBeenCalledTimes(1);\n expect(getEntry).toHaveBeenCalledWith({\n model: { modelId: \"location\", fields: [] },\n id: ref.id\n });\n });\n\n it(\"should mark a ref that fails to load and not request it again\", async () => {\n const { presenter, getEntry } = setup();\n const ref = { id: \"missing#0001\", modelId: \"location\" };\n\n presenter.loadRefValues([ref]);\n\n await vi.waitFor(() => {\n expect(presenter.vm.refValues[ref.id]).toBeNull();\n });\n\n presenter.loadRefValues([ref]);\n expect(getEntry).toHaveBeenCalledTimes(1);\n });\n});\n"],"names":["setup","getModel","vi","modelId","getEntry","id","Error","container","Container","GetModelUseCase","GetEntryUseCase","LivePreviewPresenter","Abstraction","describe","it","presenter","ref","expect"],"mappings":";;;;;;AAOA,MAAMA,QAAQ;IACV,MAAMC,WAAWC,GAAG,EAAE,CAAC,OAAO,EAAEC,OAAO,EAAuB,GAAM;YAAEA;YAAS,QAAQ,EAAE;QAAC;IAC1F,MAAMC,WAAWF,GAAG,EAAE,CAAC,OAAO,EAAEG,EAAE,EAAkB;QAChD,IAAIA,AAAO,mBAAPA,IACA,MAAM,IAAIC,MAAM;QAEpB,OAAO;YAAED;YAAI,QAAQ;gBAAE,MAAM;YAAQ;QAAE;IAC3C;IAEA,MAAME,YAAY,IAAIC;IACtBD,UAAU,gBAAgB,CAACE,iBAAiB;QACxC,SAASR;IACb;IACAM,UAAU,gBAAgB,CAACG,iBAAiB;QACxC,SAASN;IACb;IACAG,UAAU,QAAQ,CAACI;IAEnB,OAAO;QAAE,WAAWJ,UAAU,OAAO,CAACK;QAAcX;QAAUG;IAAS;AAC3E;AAEAS,SAAS,wBAAwB;IAC7BC,GAAG,gEAAgE;QAC/D,MAAM,EAAEC,SAAS,EAAEX,QAAQ,EAAE,GAAGJ;QAChC,MAAMgB,MAAM;YAAE,IAAI;YAAa,SAAS;QAAW;QAEnDD,UAAU,aAAa,CAAC;YAACC;SAAI;QAC7BD,UAAU,aAAa,CAAC;YAACC;SAAI;QAE7B,MAAMd,GAAG,OAAO,CAAC;YACbe,OAAOF,UAAU,EAAE,CAAC,SAAS,CAACC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC;gBAAE,MAAM;YAAQ;QACnE;QACAC,OAAOb,UAAU,qBAAqB,CAAC;QACvCa,OAAOb,UAAU,oBAAoB,CAAC;YAClC,OAAO;gBAAE,SAAS;gBAAY,QAAQ,EAAE;YAAC;YACzC,IAAIY,IAAI,EAAE;QACd;IACJ;IAEAF,GAAG,iEAAiE;QAChE,MAAM,EAAEC,SAAS,EAAEX,QAAQ,EAAE,GAAGJ;QAChC,MAAMgB,MAAM;YAAE,IAAI;YAAgB,SAAS;QAAW;QAEtDD,UAAU,aAAa,CAAC;YAACC;SAAI;QAE7B,MAAMd,GAAG,OAAO,CAAC;YACbe,OAAOF,UAAU,EAAE,CAAC,SAAS,CAACC,IAAI,EAAE,CAAC,EAAE,QAAQ;QACnD;QAEAD,UAAU,aAAa,CAAC;YAACC;SAAI;QAC7BC,OAAOb,UAAU,qBAAqB,CAAC;IAC3C;AACJ"}
@@ -1,16 +1,18 @@
1
- import react from "react";
1
+ import react, { useEffect } from "react";
2
2
  import { createReactiveComponent } from "@webiny/app-admin";
3
3
  import { LeftPanel, RightPanel, SplitView } from "@webiny/app-admin/components/SplitView/SplitView.js";
4
4
  import { usePreviewDomain } from "@webiny/frontend-settings/exports/admin.js";
5
5
  import { ContentEntryFormContent } from "../views/layout/ContentEntryFormContent.js";
6
6
  import { useContentEntryFormPresenter } from "../form/useContentEntryFormPresenter.js";
7
7
  import { PreviewPane } from "./PreviewPane.js";
8
+ import { useLivePreviewPresenter } from "./useLivePreviewPresenter.js";
9
+ import { getPatternRefFieldIds, getRefValues, withRefValues } from "./resolvePreviewUrl.js";
8
10
  const PreviewDecorator = ContentEntryFormContent.createDecorator((Original)=>createReactiveComponent((props)=>{
9
11
  const presenter = useContentEntryFormPresenter();
12
+ const livePreview = useLivePreviewPresenter();
10
13
  const model = presenter.vm.model;
11
14
  const previewPath = model?.settings?.previewPath;
12
15
  const { previewDomain } = usePreviewDomain();
13
- if (!previewPath) return /*#__PURE__*/ react.createElement(Original, props);
14
16
  const form = presenter.vm.form;
15
17
  const entry = presenter.vm.entry;
16
18
  const formValues = form ? form.getData() : null;
@@ -18,6 +20,16 @@ const PreviewDecorator = ContentEntryFormContent.createDecorator((Original)=>cre
18
20
  ...entry,
19
21
  values: formValues
20
22
  } : null;
23
+ const refFieldIds = previewPath && model ? getPatternRefFieldIds(previewPath, model.fields) : [];
24
+ const refs = formValues ? getRefValues(formValues, refFieldIds) : [];
25
+ const refsKey = refs.map((ref)=>ref.id).join(",");
26
+ useEffect(()=>{
27
+ if (refs.length > 0) livePreview.loadRefValues(refs);
28
+ }, [
29
+ refsKey
30
+ ]);
31
+ if (!previewPath) return /*#__PURE__*/ react.createElement(Original, props);
32
+ const urlEntryData = entryData ? withRefValues(entryData, refFieldIds, livePreview.vm.refValues) : null;
21
33
  const entryId = entry?.id || "new";
22
34
  return /*#__PURE__*/ react.createElement(SplitView, {
23
35
  namespace: "cms-live-preview",
@@ -41,7 +53,8 @@ const PreviewDecorator = ContentEntryFormContent.createDecorator((Original)=>cre
41
53
  domain: previewDomain,
42
54
  previewPath: previewPath,
43
55
  entryId: entryId,
44
- entryData: entryData
56
+ entryData: entryData,
57
+ urlEntryData: urlEntryData
45
58
  })));
46
59
  }));
47
60
  export { PreviewDecorator };
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/contentEntries/preview/PreviewDecorator.js","sources":["../../../../src/presentation/contentEntries/preview/PreviewDecorator.tsx"],"sourcesContent":["import React from \"react\";\nimport { createReactiveComponent } from \"@webiny/app-admin\";\nimport {\n SplitView,\n LeftPanel,\n RightPanel\n} from \"@webiny/app-admin/components/SplitView/SplitView.js\";\nimport { usePreviewDomain } from \"@webiny/frontend-settings/exports/admin.js\";\nimport { ContentEntryFormContent } from \"~/presentation/contentEntries/views/layout/ContentEntryFormContent.js\";\nimport { useContentEntryFormPresenter } from \"~/presentation/contentEntries/form/useContentEntryFormPresenter.js\";\nimport { PreviewPane } from \"./PreviewPane.js\";\n\nexport const PreviewDecorator = ContentEntryFormContent.createDecorator(Original => {\n return createReactiveComponent(\n (props: React.HTMLAttributes<HTMLDivElement> & { width?: string }) => {\n const presenter = useContentEntryFormPresenter();\n const model = presenter.vm.model;\n const previewPath = model?.settings?.previewPath as string | undefined;\n const { previewDomain } = usePreviewDomain();\n\n if (!previewPath) {\n return <Original {...props} />;\n }\n\n const form = presenter.vm.form;\n const entry = presenter.vm.entry;\n const formValues = form ? form.getData() : null;\n const entryData = formValues\n ? { ...entry, values: formValues as Record<string, unknown> }\n : null;\n\n const entryId = entry?.id || \"new\";\n\n return (\n <SplitView namespace={\"cms-live-preview\"} className=\"h-full\">\n <LeftPanel span={5} minSize={20} className=\"bg-white overflow-y-auto\">\n <Original\n {...props}\n width={\"100%\"}\n className={\"!pt-0 [&>div]:!rounded-none\"}\n />\n </LeftPanel>\n <RightPanel\n span={7}\n minSize={20}\n className=\"overflow-hidden p-md\"\n style={{ overflowY: \"hidden\" }}\n >\n <PreviewPane\n domain={previewDomain}\n previewPath={previewPath}\n entryId={entryId}\n entryData={entryData}\n />\n </RightPanel>\n </SplitView>\n );\n }\n );\n});\n"],"names":["PreviewDecorator","ContentEntryFormContent","Original","createReactiveComponent","props","presenter","useContentEntryFormPresenter","model","previewPath","previewDomain","usePreviewDomain","form","entry","formValues","entryData","entryId","SplitView","LeftPanel","RightPanel","PreviewPane"],"mappings":";;;;;;;AAYO,MAAMA,mBAAmBC,wBAAwB,eAAe,CAACC,CAAAA,WAC7DC,wBACH,CAACC;QACG,MAAMC,YAAYC;QAClB,MAAMC,QAAQF,UAAU,EAAE,CAAC,KAAK;QAChC,MAAMG,cAAcD,OAAO,UAAU;QACrC,MAAM,EAAEE,aAAa,EAAE,GAAGC;QAE1B,IAAI,CAACF,aACD,OAAO,WAAP,GAAO,oBAACN,UAAaE;QAGzB,MAAMO,OAAON,UAAU,EAAE,CAAC,IAAI;QAC9B,MAAMO,QAAQP,UAAU,EAAE,CAAC,KAAK;QAChC,MAAMQ,aAAaF,OAAOA,KAAK,OAAO,KAAK;QAC3C,MAAMG,YAAYD,aACZ;YAAE,GAAGD,KAAK;YAAE,QAAQC;QAAsC,IAC1D;QAEN,MAAME,UAAUH,OAAO,MAAM;QAE7B,OAAO,WAAP,GACI,oBAACI,WAASA;YAAC,WAAW;YAAoB,WAAU;yBAChD,oBAACC,WAASA;YAAC,MAAM;YAAG,SAAS;YAAI,WAAU;yBACvC,oBAACf,UAAAA;YACI,GAAGE,KAAK;YACT,OAAO;YACP,WAAW;2BAGnB,oBAACc,YAAUA;YACP,MAAM;YACN,SAAS;YACT,WAAU;YACV,OAAO;gBAAE,WAAW;YAAS;yBAE7B,oBAACC,aAAWA;YACR,QAAQV;YACR,aAAaD;YACb,SAASO;YACT,WAAWD;;IAK/B"}
1
+ {"version":3,"file":"presentation/contentEntries/preview/PreviewDecorator.js","sources":["../../../../src/presentation/contentEntries/preview/PreviewDecorator.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\nimport { createReactiveComponent } from \"@webiny/app-admin\";\nimport {\n SplitView,\n LeftPanel,\n RightPanel\n} from \"@webiny/app-admin/components/SplitView/SplitView.js\";\nimport { usePreviewDomain } from \"@webiny/frontend-settings/exports/admin.js\";\nimport { ContentEntryFormContent } from \"~/presentation/contentEntries/views/layout/ContentEntryFormContent.js\";\nimport { useContentEntryFormPresenter } from \"~/presentation/contentEntries/form/useContentEntryFormPresenter.js\";\nimport { PreviewPane } from \"./PreviewPane.js\";\nimport { useLivePreviewPresenter } from \"./useLivePreviewPresenter.js\";\nimport { getPatternRefFieldIds, getRefValues, withRefValues } from \"./resolvePreviewUrl.js\";\n\nexport const PreviewDecorator = ContentEntryFormContent.createDecorator(Original => {\n return createReactiveComponent(\n (props: React.HTMLAttributes<HTMLDivElement> & { width?: string }) => {\n const presenter = useContentEntryFormPresenter();\n const livePreview = useLivePreviewPresenter();\n const model = presenter.vm.model;\n const previewPath = model?.settings?.previewPath as string | undefined;\n const { previewDomain } = usePreviewDomain();\n\n const form = presenter.vm.form;\n const entry = presenter.vm.entry;\n const formValues = form ? (form.getData() as Record<string, unknown>) : null;\n const entryData = formValues ? { ...entry, values: formValues } : null;\n\n const refFieldIds =\n previewPath && model ? getPatternRefFieldIds(previewPath, model.fields) : [];\n const refs = formValues ? getRefValues(formValues, refFieldIds) : [];\n const refsKey = refs.map(ref => ref.id).join(\",\");\n\n useEffect(() => {\n if (refs.length > 0) {\n livePreview.loadRefValues(refs);\n }\n }, [refsKey]);\n\n if (!previewPath) {\n return <Original {...props} />;\n }\n\n // Only the preview URL gets the referenced values; the iframe receives the raw form data.\n const urlEntryData = entryData\n ? withRefValues(entryData, refFieldIds, livePreview.vm.refValues)\n : null;\n\n const entryId = entry?.id || \"new\";\n\n return (\n <SplitView namespace={\"cms-live-preview\"} className=\"h-full\">\n <LeftPanel span={5} minSize={20} className=\"bg-white overflow-y-auto\">\n <Original\n {...props}\n width={\"100%\"}\n className={\"!pt-0 [&>div]:!rounded-none\"}\n />\n </LeftPanel>\n <RightPanel\n span={7}\n minSize={20}\n className=\"overflow-hidden p-md\"\n style={{ overflowY: \"hidden\" }}\n >\n <PreviewPane\n domain={previewDomain}\n previewPath={previewPath}\n entryId={entryId}\n entryData={entryData}\n urlEntryData={urlEntryData}\n />\n </RightPanel>\n </SplitView>\n );\n }\n );\n});\n"],"names":["PreviewDecorator","ContentEntryFormContent","Original","createReactiveComponent","props","presenter","useContentEntryFormPresenter","livePreview","useLivePreviewPresenter","model","previewPath","previewDomain","usePreviewDomain","form","entry","formValues","entryData","refFieldIds","getPatternRefFieldIds","refs","getRefValues","refsKey","ref","useEffect","urlEntryData","withRefValues","entryId","SplitView","LeftPanel","RightPanel","PreviewPane"],"mappings":";;;;;;;;;AAcO,MAAMA,mBAAmBC,wBAAwB,eAAe,CAACC,CAAAA,WAC7DC,wBACH,CAACC;QACG,MAAMC,YAAYC;QAClB,MAAMC,cAAcC;QACpB,MAAMC,QAAQJ,UAAU,EAAE,CAAC,KAAK;QAChC,MAAMK,cAAcD,OAAO,UAAU;QACrC,MAAM,EAAEE,aAAa,EAAE,GAAGC;QAE1B,MAAMC,OAAOR,UAAU,EAAE,CAAC,IAAI;QAC9B,MAAMS,QAAQT,UAAU,EAAE,CAAC,KAAK;QAChC,MAAMU,aAAaF,OAAQA,KAAK,OAAO,KAAiC;QACxE,MAAMG,YAAYD,aAAa;YAAE,GAAGD,KAAK;YAAE,QAAQC;QAAW,IAAI;QAElE,MAAME,cACFP,eAAeD,QAAQS,sBAAsBR,aAAaD,MAAM,MAAM,IAAI,EAAE;QAChF,MAAMU,OAAOJ,aAAaK,aAAaL,YAAYE,eAAe,EAAE;QACpE,MAAMI,UAAUF,KAAK,GAAG,CAACG,CAAAA,MAAOA,IAAI,EAAE,EAAE,IAAI,CAAC;QAE7CC,UAAU;YACN,IAAIJ,KAAK,MAAM,GAAG,GACdZ,YAAY,aAAa,CAACY;QAElC,GAAG;YAACE;SAAQ;QAEZ,IAAI,CAACX,aACD,OAAO,WAAP,GAAO,oBAACR,UAAaE;QAIzB,MAAMoB,eAAeR,YACfS,cAAcT,WAAWC,aAAaV,YAAY,EAAE,CAAC,SAAS,IAC9D;QAEN,MAAMmB,UAAUZ,OAAO,MAAM;QAE7B,OAAO,WAAP,GACI,oBAACa,WAASA;YAAC,WAAW;YAAoB,WAAU;yBAChD,oBAACC,WAASA;YAAC,MAAM;YAAG,SAAS;YAAI,WAAU;yBACvC,oBAAC1B,UAAAA;YACI,GAAGE,KAAK;YACT,OAAO;YACP,WAAW;2BAGnB,oBAACyB,YAAUA;YACP,MAAM;YACN,SAAS;YACT,WAAU;YACV,OAAO;gBAAE,WAAW;YAAS;yBAE7B,oBAACC,aAAWA;YACR,QAAQnB;YACR,aAAaD;YACb,SAASgB;YACT,WAAWV;YACX,cAAcQ;;IAKlC"}
@@ -4,6 +4,10 @@ interface PreviewPaneProps {
4
4
  previewPath: string;
5
5
  entryId: string;
6
6
  entryData: Record<string, unknown> | null;
7
+ /**
8
+ * Entry data used to resolve the preview path, with referenced entries' values filled in.
9
+ */
10
+ urlEntryData: Record<string, unknown> | null;
7
11
  }
8
- export declare const PreviewPane: ({ domain, previewPath, entryId, entryData }: PreviewPaneProps) => React.JSX.Element;
12
+ export declare const PreviewPane: ({ domain, previewPath, entryId, entryData, urlEntryData }: PreviewPaneProps) => React.JSX.Element;
9
13
  export {};
@@ -9,7 +9,7 @@ import { ReactComponent as smartphone_svg_ReactComponent } from "@webiny/icons/s
9
9
  import { ReactComponent as open_in_new_svg_ReactComponent } from "@webiny/icons/open_in_new.svg";
10
10
  import { useLivePreviewPresenter } from "./useLivePreviewPresenter.js";
11
11
  import { buildDisplayUrl, buildEditorUrl } from "./resolvePreviewUrl.js";
12
- const PreviewPane = ({ domain, previewPath, entryId, entryData })=>{
12
+ const PreviewPane = ({ domain, previewPath, entryId, entryData, urlEntryData })=>{
13
13
  const iframeRef = useRef(null);
14
14
  const messengerRef = useRef(null);
15
15
  const entryDataRef = useRef(entryData);
@@ -20,7 +20,7 @@ const PreviewPane = ({ domain, previewPath, entryId, entryData })=>{
20
20
  const [viewport, setViewport] = useState("desktop");
21
21
  const presenter = useLivePreviewPresenter();
22
22
  entryDataRef.current = entryData;
23
- const displayUrl = buildDisplayUrl(domain, previewPath, entryData || {});
23
+ const displayUrl = buildDisplayUrl(domain, previewPath, urlEntryData || {});
24
24
  const [address, setAddress] = useState(displayUrl);
25
25
  useEffect(()=>{
26
26
  setAddress(displayUrl);
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/contentEntries/preview/PreviewPane.js","sources":["../../../../src/presentation/contentEntries/preview/PreviewPane.tsx"],"sourcesContent":["import React, { useRef, useEffect, useCallback, useState } from \"react\";\nimport { Messenger, MessageOrigin } from \"@webiny/cms-sdk/messenger\";\nimport { jsonPatch } from \"@webiny/cms-sdk\";\nimport { IconButton, Input, OverlayLoader, SegmentedControl } from \"@webiny/admin-ui\";\nimport { ReactComponent as RefreshIcon } from \"@webiny/icons/refresh.svg\";\nimport { ReactComponent as ContentCopyIcon } from \"@webiny/icons/content_copy.svg\";\nimport { ReactComponent as LaptopIcon } from \"@webiny/icons/laptop.svg\";\nimport { ReactComponent as SmartphoneIcon } from \"@webiny/icons/smartphone.svg\";\nimport { ReactComponent as OpenInNewIcon } from \"@webiny/icons/open_in_new.svg\";\nimport { useLivePreviewPresenter } from \"./useLivePreviewPresenter.js\";\nimport { buildEditorUrl, buildDisplayUrl } from \"./resolvePreviewUrl.js\";\n\ninterface PreviewPaneProps {\n domain: string;\n previewPath: string;\n entryId: string;\n entryData: Record<string, unknown> | null;\n}\n\ntype ViewportMode = \"desktop\" | \"mobile\";\n\nexport const PreviewPane = ({ domain, previewPath, entryId, entryData }: PreviewPaneProps) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const messengerRef = useRef<Messenger | null>(null);\n const entryDataRef = useRef(entryData);\n const lastSentDataRef = useRef<Record<string, unknown> | null>(null);\n const [ready, setReady] = useState(false);\n const [loading, setLoading] = useState(true);\n const [iframeKey, setIframeKey] = useState(0);\n const [viewport, setViewport] = useState<ViewportMode>(\"desktop\");\n const presenter = useLivePreviewPresenter();\n\n entryDataRef.current = entryData;\n\n const displayUrl = buildDisplayUrl(\n domain,\n previewPath,\n (entryData as Record<string, unknown>) || {}\n );\n\n const [address, setAddress] = useState(displayUrl);\n\n useEffect(() => {\n setAddress(displayUrl);\n }, [displayUrl]);\n\n const iframeSrc = (() => {\n const editorPath = buildEditorUrl(domain, previewPath);\n const url = new URL(editorPath);\n url.searchParams.set(\"wb.editing\", \"true\");\n url.searchParams.set(\"wb.type\", \"entry\");\n url.searchParams.set(\"wb.id\", entryId);\n url.searchParams.set(\"wb.path\", url.pathname);\n url.searchParams.set(\"wb.referrer\", window.location.origin);\n return url.toString();\n })();\n\n const sendEntryData = useCallback(() => {\n const data = entryDataRef.current;\n if (!data || !messengerRef.current) {\n return;\n }\n\n const serialized = JSON.parse(JSON.stringify(data));\n messengerRef.current.send(\"document.set\", serialized);\n lastSentDataRef.current = serialized;\n }, []);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (!iframe || !iframe.contentWindow) {\n return;\n }\n\n const targetOrigin = new URL(iframe.src).origin;\n const editorOrigin = new MessageOrigin(() => window, window.location.origin);\n const previewTarget = new MessageOrigin(() => iframe.contentWindow!, targetOrigin);\n\n const messenger = new Messenger(editorOrigin, previewTarget, \"wb.editor.*\");\n messengerRef.current = messenger;\n\n messenger.on(\"preview.ready\", () => {\n setReady(true);\n setLoading(false);\n sendEntryData();\n });\n\n messenger.on(\n \"preview.component.register\",\n (manifest: { name: string; label: string; description: string }) => {\n presenter.addComponent(manifest);\n }\n );\n\n return () => {\n messenger.dispose();\n messengerRef.current = null;\n lastSentDataRef.current = null;\n setReady(false);\n };\n }, [iframeSrc, presenter, iframeKey, sendEntryData]);\n\n useEffect(() => {\n if (!ready || !entryData || !messengerRef.current) {\n return;\n }\n\n const current = JSON.parse(JSON.stringify(entryData));\n const previous = lastSentDataRef.current;\n\n if (previous) {\n const patch = jsonPatch.compare(previous, current);\n if (patch.length > 0) {\n messengerRef.current.send(\"document.patch\", patch);\n }\n } else {\n messengerRef.current.send(\"document.set\", current);\n }\n\n lastSentDataRef.current = current;\n }, [ready, entryData]);\n\n const reload = useCallback(() => {\n setLoading(true);\n setReady(false);\n lastSentDataRef.current = null;\n setIframeKey(prev => prev + 1);\n }, []);\n\n const getDraftUrl = useCallback(() => {\n const url = new URL(displayUrl);\n url.searchParams.set(\"wb.type\", \"entry\");\n url.searchParams.set(\"wb.path\", url.pathname);\n url.searchParams.set(\"wb.preview\", \"true\");\n url.searchParams.set(\"wb.id\", entryId);\n return url.toString();\n }, [displayUrl, entryId]);\n\n const copyUrl = useCallback(() => {\n navigator.clipboard.writeText(getDraftUrl());\n }, [getDraftUrl]);\n\n const openInNewTab = useCallback(() => {\n window.open(getDraftUrl(), \"_blank\");\n }, [getDraftUrl]);\n\n return (\n <div className=\"relative border border-neutral-dimmed rounded-t-lg flex flex-col flex-1 h-full overflow-hidden\">\n <div className=\"flex p-md items-center bg-white border-b border-neutral-dimmed\">\n <div className=\"w-full\">\n <Input\n value={address}\n onChange={setAddress}\n readOnly\n size=\"md\"\n variant={\"secondary\"}\n className=\"bg-gray-100\"\n />\n </div>\n\n <div className=\"flex items-center gap-xxs ml-sm\">\n <IconButton\n onClick={openInNewTab}\n icon={<OpenInNewIcon />}\n variant=\"ghost\"\n size=\"md\"\n />\n <IconButton\n onClick={copyUrl}\n icon={<ContentCopyIcon />}\n variant=\"ghost\"\n size=\"sm\"\n />\n <IconButton onClick={reload} icon={<RefreshIcon />} variant=\"ghost\" size=\"sm\" />\n <SegmentedControl\n value={viewport}\n onChange={(value: string) => setViewport(value as ViewportMode)}\n items={[\n { value: \"desktop\", label: \"\", icon: <LaptopIcon /> },\n { value: \"mobile\", label: \"\", icon: <SmartphoneIcon /> }\n ]}\n />\n </div>\n </div>\n\n <div className=\"block box-border h-full w-full overflow-auto fill-grid\">\n {loading ? <OverlayLoader text=\"Connecting to Live Preview...\" /> : null}\n <div\n className={`mx-auto h-full transition-all duration-300 ${viewport === \"mobile\" ? \"p-md\" : \"\"}`}\n style={{ width: viewport === \"mobile\" ? \"375px\" : \"100%\" }}\n >\n <iframe\n key={iframeKey}\n ref={iframeRef}\n src={iframeSrc}\n width=\"100%\"\n height=\"100%\"\n className=\"h-full\"\n sandbox=\"allow-same-origin allow-scripts allow-forms\"\n />\n </div>\n </div>\n </div>\n );\n};\n"],"names":["PreviewPane","domain","previewPath","entryId","entryData","iframeRef","useRef","messengerRef","entryDataRef","lastSentDataRef","ready","setReady","useState","loading","setLoading","iframeKey","setIframeKey","viewport","setViewport","presenter","useLivePreviewPresenter","displayUrl","buildDisplayUrl","address","setAddress","useEffect","iframeSrc","editorPath","buildEditorUrl","url","URL","window","sendEntryData","useCallback","data","serialized","JSON","iframe","targetOrigin","editorOrigin","MessageOrigin","previewTarget","messenger","Messenger","manifest","current","previous","patch","jsonPatch","reload","prev","getDraftUrl","copyUrl","navigator","openInNewTab","Input","IconButton","OpenInNewIcon","ContentCopyIcon","RefreshIcon","SegmentedControl","value","LaptopIcon","SmartphoneIcon","OverlayLoader"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/contentEntries/preview/PreviewPane.js","sources":["../../../../src/presentation/contentEntries/preview/PreviewPane.tsx"],"sourcesContent":["import React, { useRef, useEffect, useCallback, useState } from \"react\";\nimport { Messenger, MessageOrigin } from \"@webiny/cms-sdk/messenger\";\nimport { jsonPatch } from \"@webiny/cms-sdk\";\nimport { IconButton, Input, OverlayLoader, SegmentedControl } from \"@webiny/admin-ui\";\nimport { ReactComponent as RefreshIcon } from \"@webiny/icons/refresh.svg\";\nimport { ReactComponent as ContentCopyIcon } from \"@webiny/icons/content_copy.svg\";\nimport { ReactComponent as LaptopIcon } from \"@webiny/icons/laptop.svg\";\nimport { ReactComponent as SmartphoneIcon } from \"@webiny/icons/smartphone.svg\";\nimport { ReactComponent as OpenInNewIcon } from \"@webiny/icons/open_in_new.svg\";\nimport { useLivePreviewPresenter } from \"./useLivePreviewPresenter.js\";\nimport { buildEditorUrl, buildDisplayUrl } from \"./resolvePreviewUrl.js\";\n\ninterface PreviewPaneProps {\n domain: string;\n previewPath: string;\n entryId: string;\n entryData: Record<string, unknown> | null;\n /**\n * Entry data used to resolve the preview path, with referenced entries' values filled in.\n */\n urlEntryData: Record<string, unknown> | null;\n}\n\ntype ViewportMode = \"desktop\" | \"mobile\";\n\nexport const PreviewPane = ({\n domain,\n previewPath,\n entryId,\n entryData,\n urlEntryData\n}: PreviewPaneProps) => {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const messengerRef = useRef<Messenger | null>(null);\n const entryDataRef = useRef(entryData);\n const lastSentDataRef = useRef<Record<string, unknown> | null>(null);\n const [ready, setReady] = useState(false);\n const [loading, setLoading] = useState(true);\n const [iframeKey, setIframeKey] = useState(0);\n const [viewport, setViewport] = useState<ViewportMode>(\"desktop\");\n const presenter = useLivePreviewPresenter();\n\n entryDataRef.current = entryData;\n\n const displayUrl = buildDisplayUrl(domain, previewPath, urlEntryData || {});\n\n const [address, setAddress] = useState(displayUrl);\n\n useEffect(() => {\n setAddress(displayUrl);\n }, [displayUrl]);\n\n const iframeSrc = (() => {\n const editorPath = buildEditorUrl(domain, previewPath);\n const url = new URL(editorPath);\n url.searchParams.set(\"wb.editing\", \"true\");\n url.searchParams.set(\"wb.type\", \"entry\");\n url.searchParams.set(\"wb.id\", entryId);\n url.searchParams.set(\"wb.path\", url.pathname);\n url.searchParams.set(\"wb.referrer\", window.location.origin);\n return url.toString();\n })();\n\n const sendEntryData = useCallback(() => {\n const data = entryDataRef.current;\n if (!data || !messengerRef.current) {\n return;\n }\n\n const serialized = JSON.parse(JSON.stringify(data));\n messengerRef.current.send(\"document.set\", serialized);\n lastSentDataRef.current = serialized;\n }, []);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (!iframe || !iframe.contentWindow) {\n return;\n }\n\n const targetOrigin = new URL(iframe.src).origin;\n const editorOrigin = new MessageOrigin(() => window, window.location.origin);\n const previewTarget = new MessageOrigin(() => iframe.contentWindow!, targetOrigin);\n\n const messenger = new Messenger(editorOrigin, previewTarget, \"wb.editor.*\");\n messengerRef.current = messenger;\n\n messenger.on(\"preview.ready\", () => {\n setReady(true);\n setLoading(false);\n sendEntryData();\n });\n\n messenger.on(\n \"preview.component.register\",\n (manifest: { name: string; label: string; description: string }) => {\n presenter.addComponent(manifest);\n }\n );\n\n return () => {\n messenger.dispose();\n messengerRef.current = null;\n lastSentDataRef.current = null;\n setReady(false);\n };\n }, [iframeSrc, presenter, iframeKey, sendEntryData]);\n\n useEffect(() => {\n if (!ready || !entryData || !messengerRef.current) {\n return;\n }\n\n const current = JSON.parse(JSON.stringify(entryData));\n const previous = lastSentDataRef.current;\n\n if (previous) {\n const patch = jsonPatch.compare(previous, current);\n if (patch.length > 0) {\n messengerRef.current.send(\"document.patch\", patch);\n }\n } else {\n messengerRef.current.send(\"document.set\", current);\n }\n\n lastSentDataRef.current = current;\n }, [ready, entryData]);\n\n const reload = useCallback(() => {\n setLoading(true);\n setReady(false);\n lastSentDataRef.current = null;\n setIframeKey(prev => prev + 1);\n }, []);\n\n const getDraftUrl = useCallback(() => {\n const url = new URL(displayUrl);\n url.searchParams.set(\"wb.type\", \"entry\");\n url.searchParams.set(\"wb.path\", url.pathname);\n url.searchParams.set(\"wb.preview\", \"true\");\n url.searchParams.set(\"wb.id\", entryId);\n return url.toString();\n }, [displayUrl, entryId]);\n\n const copyUrl = useCallback(() => {\n navigator.clipboard.writeText(getDraftUrl());\n }, [getDraftUrl]);\n\n const openInNewTab = useCallback(() => {\n window.open(getDraftUrl(), \"_blank\");\n }, [getDraftUrl]);\n\n return (\n <div className=\"relative border border-neutral-dimmed rounded-t-lg flex flex-col flex-1 h-full overflow-hidden\">\n <div className=\"flex p-md items-center bg-white border-b border-neutral-dimmed\">\n <div className=\"w-full\">\n <Input\n value={address}\n onChange={setAddress}\n readOnly\n size=\"md\"\n variant={\"secondary\"}\n className=\"bg-gray-100\"\n />\n </div>\n\n <div className=\"flex items-center gap-xxs ml-sm\">\n <IconButton\n onClick={openInNewTab}\n icon={<OpenInNewIcon />}\n variant=\"ghost\"\n size=\"md\"\n />\n <IconButton\n onClick={copyUrl}\n icon={<ContentCopyIcon />}\n variant=\"ghost\"\n size=\"sm\"\n />\n <IconButton onClick={reload} icon={<RefreshIcon />} variant=\"ghost\" size=\"sm\" />\n <SegmentedControl\n value={viewport}\n onChange={(value: string) => setViewport(value as ViewportMode)}\n items={[\n { value: \"desktop\", label: \"\", icon: <LaptopIcon /> },\n { value: \"mobile\", label: \"\", icon: <SmartphoneIcon /> }\n ]}\n />\n </div>\n </div>\n\n <div className=\"block box-border h-full w-full overflow-auto fill-grid\">\n {loading ? <OverlayLoader text=\"Connecting to Live Preview...\" /> : null}\n <div\n className={`mx-auto h-full transition-all duration-300 ${viewport === \"mobile\" ? \"p-md\" : \"\"}`}\n style={{ width: viewport === \"mobile\" ? \"375px\" : \"100%\" }}\n >\n <iframe\n key={iframeKey}\n ref={iframeRef}\n src={iframeSrc}\n width=\"100%\"\n height=\"100%\"\n className=\"h-full\"\n sandbox=\"allow-same-origin allow-scripts allow-forms\"\n />\n </div>\n </div>\n </div>\n );\n};\n"],"names":["PreviewPane","domain","previewPath","entryId","entryData","urlEntryData","iframeRef","useRef","messengerRef","entryDataRef","lastSentDataRef","ready","setReady","useState","loading","setLoading","iframeKey","setIframeKey","viewport","setViewport","presenter","useLivePreviewPresenter","displayUrl","buildDisplayUrl","address","setAddress","useEffect","iframeSrc","editorPath","buildEditorUrl","url","URL","window","sendEntryData","useCallback","data","serialized","JSON","iframe","targetOrigin","editorOrigin","MessageOrigin","previewTarget","messenger","Messenger","manifest","current","previous","patch","jsonPatch","reload","prev","getDraftUrl","copyUrl","navigator","openInNewTab","Input","IconButton","OpenInNewIcon","ContentCopyIcon","RefreshIcon","SegmentedControl","value","LaptopIcon","SmartphoneIcon","OverlayLoader"],"mappings":"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"}
@@ -1,3 +1,5 @@
1
+ import type { CmsReferenceValue } from "../../../features/contentEntry/refTypes.js";
2
+ import type { RefValuesMap } from "./resolvePreviewUrl.js";
1
3
  export interface PreviewComponent {
2
4
  name: string;
3
5
  label: string;
@@ -6,9 +8,15 @@ export interface PreviewComponent {
6
8
  export interface ILivePreviewPresenter {
7
9
  vm: {
8
10
  components: PreviewComponent[];
11
+ refValues: RefValuesMap;
9
12
  };
10
13
  addComponent(component: PreviewComponent): void;
11
14
  clearComponents(): void;
15
+ /**
16
+ * Loads referenced entries that the preview path reads through, e.g. `{values.location.slug}`.
17
+ * Each ref is requested once; results land in `vm.refValues`.
18
+ */
19
+ loadRefValues(refs: CmsReferenceValue[]): void;
12
20
  }
13
21
  export declare const LivePreviewPresenter: import("@webiny/di").Abstraction<ILivePreviewPresenter>;
14
22
  export declare namespace LivePreviewPresenter {
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/contentEntries/preview/abstractions.js","sources":["../../../../src/presentation/contentEntries/preview/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\n\nexport interface PreviewComponent {\n name: string;\n label: string;\n description: string;\n}\n\nexport interface ILivePreviewPresenter {\n vm: {\n components: PreviewComponent[];\n };\n addComponent(component: PreviewComponent): void;\n clearComponents(): void;\n}\n\nexport const LivePreviewPresenter = createAbstraction<ILivePreviewPresenter>(\n \"CmsContentEntries/LivePreviewPresenter\"\n);\n\nexport namespace LivePreviewPresenter {\n export type Interface = ILivePreviewPresenter;\n}\n"],"names":["LivePreviewPresenter","createAbstraction"],"mappings":";AAgBO,MAAMA,uBAAuBC,kBAChC"}
1
+ {"version":3,"file":"presentation/contentEntries/preview/abstractions.js","sources":["../../../../src/presentation/contentEntries/preview/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { CmsReferenceValue } from \"~/features/contentEntry/refTypes.js\";\nimport type { RefValuesMap } from \"./resolvePreviewUrl.js\";\n\nexport interface PreviewComponent {\n name: string;\n label: string;\n description: string;\n}\n\nexport interface ILivePreviewPresenter {\n vm: {\n components: PreviewComponent[];\n refValues: RefValuesMap;\n };\n addComponent(component: PreviewComponent): void;\n clearComponents(): void;\n /**\n * Loads referenced entries that the preview path reads through, e.g. `{values.location.slug}`.\n * Each ref is requested once; results land in `vm.refValues`.\n */\n loadRefValues(refs: CmsReferenceValue[]): void;\n}\n\nexport const LivePreviewPresenter = createAbstraction<ILivePreviewPresenter>(\n \"CmsContentEntries/LivePreviewPresenter\"\n);\n\nexport namespace LivePreviewPresenter {\n export type Interface = ILivePreviewPresenter;\n}\n"],"names":["LivePreviewPresenter","createAbstraction"],"mappings":";AAwBO,MAAMA,uBAAuBC,kBAChC"}
@@ -1,4 +1,25 @@
1
+ import type { CmsModelField } from "../../../types.js";
2
+ import type { CmsReferenceValue } from "../../../features/contentEntry/refTypes.js";
1
3
  type EntryData = Record<string, unknown>;
4
+ /**
5
+ * Values of loaded referenced entries, keyed by the ref `id`.
6
+ * `null` marks a ref that failed to load, so it isn't requested again.
7
+ */
8
+ export type RefValuesMap = Record<string, Record<string, unknown> | null>;
9
+ /**
10
+ * Returns the ids of ref fields that the pattern reads through,
11
+ * e.g. `location` for `/{values.location.slug}/{values.slug}`.
12
+ */
13
+ export declare function getPatternRefFieldIds(pattern: string, fields: Pick<CmsModelField, "fieldId" | "type">[]): string[];
14
+ /**
15
+ * Collects the ref values held by the given fields, for both single and multiple-value fields.
16
+ */
17
+ export declare function getRefValues(values: EntryData, refFieldIds: string[]): CmsReferenceValue[];
18
+ /**
19
+ * Spreads loaded referenced entry values into the ref values, so `{values.location.slug}`
20
+ * resolves against the referenced entry. Refs that aren't loaded yet are left as they are.
21
+ */
22
+ export declare function withRefValues(entry: EntryData, refFieldIds: string[], refValues: RefValuesMap): EntryData;
2
23
  export declare function resolveSlugPattern(pattern: string, entry: EntryData): string;
3
24
  export declare function buildEditorUrl(domain: string, previewPath: string): string;
4
25
  export declare function buildDisplayUrl(domain: string, path: string, entry: EntryData): string;
@@ -1,3 +1,52 @@
1
+ function isRefValue(value) {
2
+ if (!value || "object" != typeof value) return false;
3
+ const ref = value;
4
+ return "string" == typeof ref.id && "string" == typeof ref.modelId;
5
+ }
6
+ function getPatternRefFieldIds(pattern, fields) {
7
+ const refFieldIds = new Set(fields.filter((f)=>"ref" === f.type).map((f)=>f.fieldId));
8
+ const result = new Set();
9
+ for (const [, path] of pattern.matchAll(/\{([^}]+)\}/g)){
10
+ const [root, fieldId, ...rest] = path.split(".");
11
+ if ("values" === root && rest.length > 0 && refFieldIds.has(fieldId)) result.add(fieldId);
12
+ }
13
+ return [
14
+ ...result
15
+ ];
16
+ }
17
+ function getRefValues(values, refFieldIds) {
18
+ const refs = [];
19
+ for (const fieldId of refFieldIds){
20
+ const value = values[fieldId];
21
+ const items = Array.isArray(value) ? value : [
22
+ value
23
+ ];
24
+ refs.push(...items.filter(isRefValue));
25
+ }
26
+ return refs;
27
+ }
28
+ function withRefValues(entry, refFieldIds, refValues) {
29
+ const values = entry.values;
30
+ if (!values || 0 === refFieldIds.length) return entry;
31
+ const expand = (value)=>{
32
+ if (!isRefValue(value) || !refValues[value.id]) return value;
33
+ return {
34
+ ...refValues[value.id],
35
+ ...value
36
+ };
37
+ };
38
+ const expanded = {
39
+ ...values
40
+ };
41
+ for (const fieldId of refFieldIds){
42
+ const value = values[fieldId];
43
+ expanded[fieldId] = Array.isArray(value) ? value.map(expand) : expand(value);
44
+ }
45
+ return {
46
+ ...entry,
47
+ values: expanded
48
+ };
49
+ }
1
50
  function getNestedValue(obj, path) {
2
51
  const parts = path.split(".");
3
52
  let current = obj;
@@ -27,6 +76,6 @@ function buildDisplayUrl(domain, path, entry) {
27
76
  const normalizedPath = resolvedPath.startsWith("/") ? resolvedPath : `/${resolvedPath}`;
28
77
  return `${base}${normalizedPath}`;
29
78
  }
30
- export { buildDisplayUrl, buildEditorUrl, resolveSlugPattern };
79
+ export { buildDisplayUrl, buildEditorUrl, getPatternRefFieldIds, getRefValues, resolveSlugPattern, withRefValues };
31
80
 
32
81
  //# sourceMappingURL=resolvePreviewUrl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/contentEntries/preview/resolvePreviewUrl.js","sources":["../../../../src/presentation/contentEntries/preview/resolvePreviewUrl.ts"],"sourcesContent":["type EntryData = Record<string, unknown>;\n\nfunction getNestedValue(obj: EntryData, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = obj;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\nexport function resolveSlugPattern(pattern: string, entry: EntryData): string {\n return pattern.replace(/\\{([^}]+)\\}/g, (_match, path: string) => {\n const value = getNestedValue(entry, path);\n if (value !== null && value !== undefined && value !== \"\") {\n return String(value);\n }\n return \"new\";\n });\n}\n\nexport function buildEditorUrl(domain: string, previewPath: string): string {\n const base = domain.endsWith(\"/\") ? domain.slice(0, -1) : domain;\n const bracketIndex = previewPath.indexOf(\"{\");\n const staticPrefix = bracketIndex >= 0 ? previewPath.substring(0, bracketIndex) : previewPath;\n const trimmed = staticPrefix.endsWith(\"/\") ? staticPrefix.slice(0, -1) : staticPrefix;\n return `${base}${trimmed}/preview`;\n}\n\nexport function buildDisplayUrl(domain: string, path: string, entry: EntryData): string {\n const base = domain.endsWith(\"/\") ? domain.slice(0, -1) : domain;\n const resolvedPath = resolveSlugPattern(path, entry);\n const normalizedPath = resolvedPath.startsWith(\"/\") ? resolvedPath : `/${resolvedPath}`;\n return `${base}${normalizedPath}`;\n}\n"],"names":["getNestedValue","obj","path","parts","current","part","resolveSlugPattern","pattern","entry","_match","value","String","buildEditorUrl","domain","previewPath","base","bracketIndex","staticPrefix","trimmed","buildDisplayUrl","resolvedPath","normalizedPath"],"mappings":"AAEA,SAASA,eAAeC,GAAc,EAAEC,IAAY;IAChD,MAAMC,QAAQD,KAAK,KAAK,CAAC;IACzB,IAAIE,UAAmBH;IACvB,KAAK,MAAMI,QAAQF,MAAO;QACtB,IAAIC,QAAAA,WAA6C,AAAmB,YAAnB,OAAOA,SACpD;QAEJA,UAAWA,OAAmC,CAACC,KAAK;IACxD;IACA,OAAOD;AACX;AAEO,SAASE,mBAAmBC,OAAe,EAAEC,KAAgB;IAChE,OAAOD,QAAQ,OAAO,CAAC,gBAAgB,CAACE,QAAQP;QAC5C,MAAMQ,QAAQV,eAAeQ,OAAON;QACpC,IAAIQ,QAAAA,SAAyCA,AAAU,OAAVA,OACzC,OAAOC,OAAOD;QAElB,OAAO;IACX;AACJ;AAEO,SAASE,eAAeC,MAAc,EAAEC,WAAmB;IAC9D,MAAMC,OAAOF,OAAO,QAAQ,CAAC,OAAOA,OAAO,KAAK,CAAC,GAAG,MAAMA;IAC1D,MAAMG,eAAeF,YAAY,OAAO,CAAC;IACzC,MAAMG,eAAeD,gBAAgB,IAAIF,YAAY,SAAS,CAAC,GAAGE,gBAAgBF;IAClF,MAAMI,UAAUD,aAAa,QAAQ,CAAC,OAAOA,aAAa,KAAK,CAAC,GAAG,MAAMA;IACzE,OAAO,GAAGF,OAAOG,QAAQ,QAAQ,CAAC;AACtC;AAEO,SAASC,gBAAgBN,MAAc,EAAEX,IAAY,EAAEM,KAAgB;IAC1E,MAAMO,OAAOF,OAAO,QAAQ,CAAC,OAAOA,OAAO,KAAK,CAAC,GAAG,MAAMA;IAC1D,MAAMO,eAAed,mBAAmBJ,MAAMM;IAC9C,MAAMa,iBAAiBD,aAAa,UAAU,CAAC,OAAOA,eAAe,CAAC,CAAC,EAAEA,cAAc;IACvF,OAAO,GAAGL,OAAOM,gBAAgB;AACrC"}
1
+ {"version":3,"file":"presentation/contentEntries/preview/resolvePreviewUrl.js","sources":["../../../../src/presentation/contentEntries/preview/resolvePreviewUrl.ts"],"sourcesContent":["import type { CmsModelField } from \"~/types.js\";\nimport type { CmsReferenceValue } from \"~/features/contentEntry/refTypes.js\";\n\ntype EntryData = Record<string, unknown>;\n\n/**\n * Values of loaded referenced entries, keyed by the ref `id`.\n * `null` marks a ref that failed to load, so it isn't requested again.\n */\nexport type RefValuesMap = Record<string, Record<string, unknown> | null>;\n\nfunction isRefValue(value: unknown): value is CmsReferenceValue {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const ref = value as Partial<CmsReferenceValue>;\n return typeof ref.id === \"string\" && typeof ref.modelId === \"string\";\n}\n\n/**\n * Returns the ids of ref fields that the pattern reads through,\n * e.g. `location` for `/{values.location.slug}/{values.slug}`.\n */\nexport function getPatternRefFieldIds(\n pattern: string,\n fields: Pick<CmsModelField, \"fieldId\" | \"type\">[]\n): string[] {\n const refFieldIds = new Set(fields.filter(f => f.type === \"ref\").map(f => f.fieldId));\n const result = new Set<string>();\n for (const [, path] of pattern.matchAll(/\\{([^}]+)\\}/g)) {\n const [root, fieldId, ...rest] = path.split(\".\");\n if (root === \"values\" && rest.length > 0 && refFieldIds.has(fieldId)) {\n result.add(fieldId);\n }\n }\n return [...result];\n}\n\n/**\n * Collects the ref values held by the given fields, for both single and multiple-value fields.\n */\nexport function getRefValues(values: EntryData, refFieldIds: string[]): CmsReferenceValue[] {\n const refs: CmsReferenceValue[] = [];\n for (const fieldId of refFieldIds) {\n const value = values[fieldId];\n const items = Array.isArray(value) ? value : [value];\n refs.push(...items.filter(isRefValue));\n }\n return refs;\n}\n\n/**\n * Spreads loaded referenced entry values into the ref values, so `{values.location.slug}`\n * resolves against the referenced entry. Refs that aren't loaded yet are left as they are.\n */\nexport function withRefValues(\n entry: EntryData,\n refFieldIds: string[],\n refValues: RefValuesMap\n): EntryData {\n const values = entry.values as EntryData | undefined;\n if (!values || refFieldIds.length === 0) {\n return entry;\n }\n\n const expand = (value: unknown) => {\n if (!isRefValue(value) || !refValues[value.id]) {\n return value;\n }\n return { ...refValues[value.id], ...value };\n };\n\n const expanded = { ...values };\n for (const fieldId of refFieldIds) {\n const value = values[fieldId];\n expanded[fieldId] = Array.isArray(value) ? value.map(expand) : expand(value);\n }\n return { ...entry, values: expanded };\n}\n\nfunction getNestedValue(obj: EntryData, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = obj;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\nexport function resolveSlugPattern(pattern: string, entry: EntryData): string {\n return pattern.replace(/\\{([^}]+)\\}/g, (_match, path: string) => {\n const value = getNestedValue(entry, path);\n if (value !== null && value !== undefined && value !== \"\") {\n return String(value);\n }\n return \"new\";\n });\n}\n\nexport function buildEditorUrl(domain: string, previewPath: string): string {\n const base = domain.endsWith(\"/\") ? domain.slice(0, -1) : domain;\n const bracketIndex = previewPath.indexOf(\"{\");\n const staticPrefix = bracketIndex >= 0 ? previewPath.substring(0, bracketIndex) : previewPath;\n const trimmed = staticPrefix.endsWith(\"/\") ? staticPrefix.slice(0, -1) : staticPrefix;\n return `${base}${trimmed}/preview`;\n}\n\nexport function buildDisplayUrl(domain: string, path: string, entry: EntryData): string {\n const base = domain.endsWith(\"/\") ? domain.slice(0, -1) : domain;\n const resolvedPath = resolveSlugPattern(path, entry);\n const normalizedPath = resolvedPath.startsWith(\"/\") ? resolvedPath : `/${resolvedPath}`;\n return `${base}${normalizedPath}`;\n}\n"],"names":["isRefValue","value","ref","getPatternRefFieldIds","pattern","fields","refFieldIds","Set","f","result","path","root","fieldId","rest","getRefValues","values","refs","items","Array","withRefValues","entry","refValues","expand","expanded","getNestedValue","obj","parts","current","part","resolveSlugPattern","_match","String","buildEditorUrl","domain","previewPath","base","bracketIndex","staticPrefix","trimmed","buildDisplayUrl","resolvedPath","normalizedPath"],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildDisplayUrl, buildEditorUrl, getPatternRefFieldIds, getRefValues, resolveSlugPattern, withRefValues } from "./resolvePreviewUrl.js";
3
+ const fields = [
4
+ {
5
+ fieldId: "slug",
6
+ type: "text"
7
+ },
8
+ {
9
+ fieldId: "location",
10
+ type: "ref"
11
+ },
12
+ {
13
+ fieldId: "tags",
14
+ type: "ref"
15
+ }
16
+ ];
17
+ const resolvePreviewUrl_test_location = {
18
+ id: "loc1#0001",
19
+ modelId: "location"
20
+ };
21
+ describe("resolvePreviewUrl", ()=>{
22
+ it("should resolve plain values and fall back to `new` for missing ones", ()=>{
23
+ expect(resolveSlugPattern("/articles/{values.slug}", {
24
+ values: {
25
+ slug: "hello"
26
+ }
27
+ })).toBe("/articles/hello");
28
+ expect(resolveSlugPattern("/articles/{values.slug}", {
29
+ values: {}
30
+ })).toBe("/articles/new");
31
+ });
32
+ it("should build the iframe url from the static prefix only", ()=>{
33
+ expect(buildEditorUrl("https://site.com/", "/articles/{values.slug}")).toBe("https://site.com/articles/preview");
34
+ expect(buildEditorUrl("https://site.com", "/{values.location.slug}/{values.slug}")).toBe("https://site.com/preview");
35
+ });
36
+ it("should find ref fields the pattern reads through", ()=>{
37
+ expect(getPatternRefFieldIds("/{values.location.slug}/{values.slug}/{values.tags}", fields)).toEqual([
38
+ "location"
39
+ ]);
40
+ expect(getPatternRefFieldIds("/articles/{values.slug}", fields)).toEqual([]);
41
+ });
42
+ it("should collect ref values from single and multiple-value fields", ()=>{
43
+ const tag = {
44
+ id: "tag1#0001",
45
+ modelId: "tag"
46
+ };
47
+ expect(getRefValues({
48
+ location: resolvePreviewUrl_test_location,
49
+ tags: [
50
+ tag,
51
+ null
52
+ ],
53
+ slug: "x"
54
+ }, [
55
+ "location",
56
+ "tags"
57
+ ])).toEqual([
58
+ resolvePreviewUrl_test_location,
59
+ tag
60
+ ]);
61
+ expect(getRefValues({
62
+ location: null
63
+ }, [
64
+ "location"
65
+ ])).toEqual([]);
66
+ });
67
+ it("should resolve a ref field value once the referenced entry is loaded", ()=>{
68
+ const entry = {
69
+ id: "page1#0001",
70
+ values: {
71
+ slug: "climbing",
72
+ location: resolvePreviewUrl_test_location
73
+ }
74
+ };
75
+ const pattern = "/{values.location.slug}/{values.slug}";
76
+ const notLoaded = withRefValues(entry, [
77
+ "location"
78
+ ], {});
79
+ expect(buildDisplayUrl("https://site.com", pattern, notLoaded)).toBe("https://site.com/new/climbing");
80
+ const loaded = withRefValues(entry, [
81
+ "location"
82
+ ], {
83
+ [resolvePreviewUrl_test_location.id]: {
84
+ slug: "baker",
85
+ id: "should-not-win"
86
+ }
87
+ });
88
+ expect(buildDisplayUrl("https://site.com", pattern, loaded)).toBe("https://site.com/baker/climbing");
89
+ expect(loaded.values.location.id).toBe(resolvePreviewUrl_test_location.id);
90
+ });
91
+ it("should support indexing into multiple-value ref fields", ()=>{
92
+ const tag = {
93
+ id: "tag1#0001",
94
+ modelId: "tag"
95
+ };
96
+ const entry = {
97
+ values: {
98
+ tags: [
99
+ tag
100
+ ]
101
+ }
102
+ };
103
+ const resolved = withRefValues(entry, [
104
+ "tags"
105
+ ], {
106
+ [tag.id]: {
107
+ slug: "news"
108
+ }
109
+ });
110
+ expect(resolveSlugPattern("/{values.tags.0.slug}", resolved)).toBe("/news");
111
+ });
112
+ it("should leave the entry untouched when there are no ref fields to fill in", ()=>{
113
+ const entry = {
114
+ values: {
115
+ slug: "x",
116
+ location: resolvePreviewUrl_test_location
117
+ }
118
+ };
119
+ expect(withRefValues(entry, [], {
120
+ [resolvePreviewUrl_test_location.id]: {
121
+ slug: "baker"
122
+ }
123
+ })).toBe(entry);
124
+ });
125
+ });
126
+
127
+ //# sourceMappingURL=resolvePreviewUrl.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation/contentEntries/preview/resolvePreviewUrl.test.js","sources":["../../../../src/presentation/contentEntries/preview/resolvePreviewUrl.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport {\n buildDisplayUrl,\n buildEditorUrl,\n getPatternRefFieldIds,\n getRefValues,\n resolveSlugPattern,\n withRefValues\n} from \"./resolvePreviewUrl.js\";\n\nconst fields = [\n { fieldId: \"slug\", type: \"text\" },\n { fieldId: \"location\", type: \"ref\" },\n { fieldId: \"tags\", type: \"ref\" }\n];\n\nconst location = { id: \"loc1#0001\", modelId: \"location\" };\n\ndescribe(\"resolvePreviewUrl\", () => {\n it(\"should resolve plain values and fall back to `new` for missing ones\", () => {\n expect(resolveSlugPattern(\"/articles/{values.slug}\", { values: { slug: \"hello\" } })).toBe(\n \"/articles/hello\"\n );\n expect(resolveSlugPattern(\"/articles/{values.slug}\", { values: {} })).toBe(\"/articles/new\");\n });\n\n it(\"should build the iframe url from the static prefix only\", () => {\n expect(buildEditorUrl(\"https://site.com/\", \"/articles/{values.slug}\")).toBe(\n \"https://site.com/articles/preview\"\n );\n expect(buildEditorUrl(\"https://site.com\", \"/{values.location.slug}/{values.slug}\")).toBe(\n \"https://site.com/preview\"\n );\n });\n\n it(\"should find ref fields the pattern reads through\", () => {\n expect(\n getPatternRefFieldIds(\"/{values.location.slug}/{values.slug}/{values.tags}\", fields)\n ).toEqual([\"location\"]);\n expect(getPatternRefFieldIds(\"/articles/{values.slug}\", fields)).toEqual([]);\n });\n\n it(\"should collect ref values from single and multiple-value fields\", () => {\n const tag = { id: \"tag1#0001\", modelId: \"tag\" };\n expect(\n getRefValues({ location, tags: [tag, null], slug: \"x\" }, [\"location\", \"tags\"])\n ).toEqual([location, tag]);\n expect(getRefValues({ location: null }, [\"location\"])).toEqual([]);\n });\n\n it(\"should resolve a ref field value once the referenced entry is loaded\", () => {\n const entry = { id: \"page1#0001\", values: { slug: \"climbing\", location } };\n const pattern = \"/{values.location.slug}/{values.slug}\";\n\n const notLoaded = withRefValues(entry, [\"location\"], {});\n expect(buildDisplayUrl(\"https://site.com\", pattern, notLoaded)).toBe(\n \"https://site.com/new/climbing\"\n );\n\n const loaded = withRefValues(entry, [\"location\"], {\n [location.id]: { slug: \"baker\", id: \"should-not-win\" }\n });\n expect(buildDisplayUrl(\"https://site.com\", pattern, loaded)).toBe(\n \"https://site.com/baker/climbing\"\n );\n expect((loaded.values as any).location.id).toBe(location.id);\n });\n\n it(\"should support indexing into multiple-value ref fields\", () => {\n const tag = { id: \"tag1#0001\", modelId: \"tag\" };\n const entry = { values: { tags: [tag] } };\n const resolved = withRefValues(entry, [\"tags\"], { [tag.id]: { slug: \"news\" } });\n expect(resolveSlugPattern(\"/{values.tags.0.slug}\", resolved)).toBe(\"/news\");\n });\n\n it(\"should leave the entry untouched when there are no ref fields to fill in\", () => {\n const entry = { values: { slug: \"x\", location } };\n expect(withRefValues(entry, [], { [location.id]: { slug: \"baker\" } })).toBe(entry);\n });\n});\n"],"names":["fields","location","describe","it","expect","resolveSlugPattern","buildEditorUrl","getPatternRefFieldIds","tag","getRefValues","entry","pattern","notLoaded","withRefValues","buildDisplayUrl","loaded","resolved"],"mappings":";;AAUA,MAAMA,SAAS;IACX;QAAE,SAAS;QAAQ,MAAM;IAAO;IAChC;QAAE,SAAS;QAAY,MAAM;IAAM;IACnC;QAAE,SAAS;QAAQ,MAAM;IAAM;CAClC;AAED,MAAMC,kCAAW;IAAE,IAAI;IAAa,SAAS;AAAW;AAExDC,SAAS,qBAAqB;IAC1BC,GAAG,uEAAuE;QACtEC,OAAOC,mBAAmB,2BAA2B;YAAE,QAAQ;gBAAE,MAAM;YAAQ;QAAE,IAAI,IAAI,CACrF;QAEJD,OAAOC,mBAAmB,2BAA2B;YAAE,QAAQ,CAAC;QAAE,IAAI,IAAI,CAAC;IAC/E;IAEAF,GAAG,2DAA2D;QAC1DC,OAAOE,eAAe,qBAAqB,4BAA4B,IAAI,CACvE;QAEJF,OAAOE,eAAe,oBAAoB,0CAA0C,IAAI,CACpF;IAER;IAEAH,GAAG,oDAAoD;QACnDC,OACIG,sBAAsB,uDAAuDP,SAC/E,OAAO,CAAC;YAAC;SAAW;QACtBI,OAAOG,sBAAsB,2BAA2BP,SAAS,OAAO,CAAC,EAAE;IAC/E;IAEAG,GAAG,mEAAmE;QAClE,MAAMK,MAAM;YAAE,IAAI;YAAa,SAAS;QAAM;QAC9CJ,OACIK,aAAa;YAAER,UAAAA;YAAU,MAAM;gBAACO;gBAAK;aAAK;YAAE,MAAM;QAAI,GAAG;YAAC;YAAY;SAAO,GAC/E,OAAO,CAAC;YAACP;YAAUO;SAAI;QACzBJ,OAAOK,aAAa;YAAE,UAAU;QAAK,GAAG;YAAC;SAAW,GAAG,OAAO,CAAC,EAAE;IACrE;IAEAN,GAAG,wEAAwE;QACvE,MAAMO,QAAQ;YAAE,IAAI;YAAc,QAAQ;gBAAE,MAAM;gBAAYT,UAAAA;YAAS;QAAE;QACzE,MAAMU,UAAU;QAEhB,MAAMC,YAAYC,cAAcH,OAAO;YAAC;SAAW,EAAE,CAAC;QACtDN,OAAOU,gBAAgB,oBAAoBH,SAASC,YAAY,IAAI,CAChE;QAGJ,MAAMG,SAASF,cAAcH,OAAO;YAAC;SAAW,EAAE;YAC9C,CAACT,gCAAS,EAAE,CAAC,EAAE;gBAAE,MAAM;gBAAS,IAAI;YAAiB;QACzD;QACAG,OAAOU,gBAAgB,oBAAoBH,SAASI,SAAS,IAAI,CAC7D;QAEJX,OAAQW,OAAO,MAAM,CAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAACd,gCAAS,EAAE;IAC/D;IAEAE,GAAG,0DAA0D;QACzD,MAAMK,MAAM;YAAE,IAAI;YAAa,SAAS;QAAM;QAC9C,MAAME,QAAQ;YAAE,QAAQ;gBAAE,MAAM;oBAACF;iBAAI;YAAC;QAAE;QACxC,MAAMQ,WAAWH,cAAcH,OAAO;YAAC;SAAO,EAAE;YAAE,CAACF,IAAI,EAAE,CAAC,EAAE;gBAAE,MAAM;YAAO;QAAE;QAC7EJ,OAAOC,mBAAmB,yBAAyBW,WAAW,IAAI,CAAC;IACvE;IAEAb,GAAG,4EAA4E;QAC3E,MAAMO,QAAQ;YAAE,QAAQ;gBAAE,MAAM;gBAAKT,UAAAA;YAAS;QAAE;QAChDG,OAAOS,cAAcH,OAAO,EAAE,EAAE;YAAE,CAACT,gCAAS,EAAE,CAAC,EAAE;gBAAE,MAAM;YAAQ;QAAE,IAAI,IAAI,CAACS;IAChF;AACJ"}
@@ -0,0 +1,133 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { Container } from "@webiny/di";
3
+ import { SearchContentEntriesUseCase } from "../../../../features/contentEntry/searchContentEntries/abstractions.js";
4
+ import { GetContentEntriesUseCase } from "../../../../features/contentEntry/getContentEntries/abstractions.js";
5
+ import { RefMultiAutocompletePresenter } from "./multi/abstractions.js";
6
+ import { RefMultiAutocompletePresenter as RefMultiAutocompletePresenter_js_RefMultiAutocompletePresenter } from "./multi/RefMultiAutocompletePresenter.js";
7
+ import { RefSingleAutocompletePresenter } from "./single/abstractions.js";
8
+ import { RefSingleAutocompletePresenter as RefSingleAutocompletePresenter_js_RefSingleAutocompletePresenter } from "./single/RefSingleAutocompletePresenter.js";
9
+ const createEntry = (entryId, title)=>({
10
+ id: `${entryId}#0001`,
11
+ entryId,
12
+ status: "draft",
13
+ title,
14
+ description: null,
15
+ image: null,
16
+ createdOn: "2026-01-01T00:00:00.000Z",
17
+ savedOn: "2026-01-01T00:00:00.000Z",
18
+ createdBy: {
19
+ id: "1",
20
+ type: "admin",
21
+ displayName: "Admin"
22
+ },
23
+ modifiedBy: null,
24
+ model: {
25
+ modelId: "location",
26
+ name: "Location"
27
+ },
28
+ published: null,
29
+ wbyAco_location: null
30
+ });
31
+ const defaultEntry = createEntry("first", "First");
32
+ const searchedEntry = createEntry("second", "Second");
33
+ const createContainer = ()=>{
34
+ const container = new Container();
35
+ container.registerInstance(SearchContentEntriesUseCase, {
36
+ execute: async ({ query })=>({
37
+ data: query ? [
38
+ searchedEntry
39
+ ] : [
40
+ defaultEntry
41
+ ]
42
+ })
43
+ });
44
+ container.registerInstance(GetContentEntriesUseCase, {
45
+ execute: async ()=>({
46
+ latest: [],
47
+ published: []
48
+ })
49
+ });
50
+ return container;
51
+ };
52
+ describe("RefMultiAutocompletePresenter", ()=>{
53
+ it("keeps an entry picked from the search results", async ()=>{
54
+ const container = createContainer();
55
+ container.register(RefMultiAutocompletePresenter_js_RefMultiAutocompletePresenter);
56
+ const presenter = container.resolve(RefMultiAutocompletePresenter);
57
+ await presenter.init({
58
+ modelIds: [
59
+ "location"
60
+ ]
61
+ });
62
+ await presenter.search("sec");
63
+ const refs = presenter.select([
64
+ "second"
65
+ ]);
66
+ expect(refs).toEqual([
67
+ {
68
+ id: "second#0001",
69
+ modelId: "location"
70
+ }
71
+ ]);
72
+ expect(presenter.vm.values).toEqual([
73
+ "second"
74
+ ]);
75
+ expect(presenter.vm.options).toContainEqual({
76
+ label: "Second",
77
+ value: "second"
78
+ });
79
+ });
80
+ it("keeps earlier picks when another entry is picked from the search results", async ()=>{
81
+ const container = createContainer();
82
+ container.register(RefMultiAutocompletePresenter_js_RefMultiAutocompletePresenter);
83
+ const presenter = container.resolve(RefMultiAutocompletePresenter);
84
+ await presenter.init({
85
+ modelIds: [
86
+ "location"
87
+ ]
88
+ });
89
+ presenter.select([
90
+ "first"
91
+ ]);
92
+ await presenter.search("sec");
93
+ const refs = presenter.select([
94
+ "first",
95
+ "second"
96
+ ]);
97
+ expect(refs).toEqual([
98
+ {
99
+ id: "first#0001",
100
+ modelId: "location"
101
+ },
102
+ {
103
+ id: "second#0001",
104
+ modelId: "location"
105
+ }
106
+ ]);
107
+ expect(presenter.vm.values).toEqual([
108
+ "first",
109
+ "second"
110
+ ]);
111
+ });
112
+ });
113
+ describe("RefSingleAutocompletePresenter", ()=>{
114
+ it("keeps an entry picked from the search results", async ()=>{
115
+ const container = createContainer();
116
+ container.register(RefSingleAutocompletePresenter_js_RefSingleAutocompletePresenter);
117
+ const presenter = container.resolve(RefSingleAutocompletePresenter);
118
+ await presenter.init({
119
+ modelIds: [
120
+ "location"
121
+ ]
122
+ });
123
+ await presenter.search("sec");
124
+ const ref = presenter.select("second");
125
+ expect(ref).toEqual({
126
+ id: "second#0001",
127
+ modelId: "location"
128
+ });
129
+ expect(presenter.vm.value).toBe("second");
130
+ });
131
+ });
132
+
133
+ //# sourceMappingURL=RefAutocompletePresenters.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js","sources":["../../../../../src/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { CmsReferenceEntry } from \"~/features/contentEntry/refTypes.js\";\nimport { RefMultiAutocompletePresenter as MultiAbstraction } from \"./multi/abstractions.js\";\nimport { RefMultiAutocompletePresenter } from \"./multi/RefMultiAutocompletePresenter.js\";\nimport { RefSingleAutocompletePresenter as SingleAbstraction } from \"./single/abstractions.js\";\nimport { RefSingleAutocompletePresenter } from \"./single/RefSingleAutocompletePresenter.js\";\n\nconst createEntry = (entryId: string, title: string): CmsReferenceEntry => ({\n id: `${entryId}#0001`,\n entryId,\n status: \"draft\",\n title,\n description: null,\n image: null,\n createdOn: \"2026-01-01T00:00:00.000Z\",\n savedOn: \"2026-01-01T00:00:00.000Z\",\n createdBy: { id: \"1\", type: \"admin\", displayName: \"Admin\" },\n modifiedBy: null,\n model: { modelId: \"location\", name: \"Location\" },\n published: null,\n wbyAco_location: null\n});\n\n// The default list holds only the first entry, so the second one can only be found by searching.\nconst defaultEntry = createEntry(\"first\", \"First\");\nconst searchedEntry = createEntry(\"second\", \"Second\");\n\nconst createContainer = () => {\n const container = new Container();\n container.registerInstance(SearchContentEntriesUseCase, {\n execute: async ({ query }) => ({ data: query ? [searchedEntry] : [defaultEntry] })\n });\n container.registerInstance(GetContentEntriesUseCase, {\n execute: async () => ({ latest: [], published: [] })\n });\n return container;\n};\n\ndescribe(\"RefMultiAutocompletePresenter\", () => {\n it(\"keeps an entry picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n await presenter.search(\"sec\");\n\n const refs = presenter.select([\"second\"]);\n\n expect(refs).toEqual([{ id: \"second#0001\", modelId: \"location\" }]);\n expect(presenter.vm.values).toEqual([\"second\"]);\n expect(presenter.vm.options).toContainEqual({ label: \"Second\", value: \"second\" });\n });\n\n it(\"keeps earlier picks when another entry is picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n presenter.select([\"first\"]);\n await presenter.search(\"sec\");\n\n const refs = presenter.select([\"first\", \"second\"]);\n\n expect(refs).toEqual([\n { id: \"first#0001\", modelId: \"location\" },\n { id: \"second#0001\", modelId: \"location\" }\n ]);\n expect(presenter.vm.values).toEqual([\"first\", \"second\"]);\n });\n});\n\ndescribe(\"RefSingleAutocompletePresenter\", () => {\n it(\"keeps an entry picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefSingleAutocompletePresenter);\n const presenter = container.resolve(SingleAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n await presenter.search(\"sec\");\n\n const ref = presenter.select(\"second\");\n\n expect(ref).toEqual({ id: \"second#0001\", modelId: \"location\" });\n expect(presenter.vm.value).toBe(\"second\");\n });\n});\n"],"names":["createEntry","entryId","title","defaultEntry","searchedEntry","createContainer","container","Container","SearchContentEntriesUseCase","query","GetContentEntriesUseCase","describe","it","RefMultiAutocompletePresenter","presenter","MultiAbstraction","refs","expect","RefSingleAutocompletePresenter","SingleAbstraction","ref"],"mappings":";;;;;;;;AAUA,MAAMA,cAAc,CAACC,SAAiBC,QAAsC;QACxE,IAAI,GAAGD,QAAQ,KAAK,CAAC;QACrBA;QACA,QAAQ;QACRC;QACA,aAAa;QACb,OAAO;QACP,WAAW;QACX,SAAS;QACT,WAAW;YAAE,IAAI;YAAK,MAAM;YAAS,aAAa;QAAQ;QAC1D,YAAY;QACZ,OAAO;YAAE,SAAS;YAAY,MAAM;QAAW;QAC/C,WAAW;QACX,iBAAiB;IACrB;AAGA,MAAMC,eAAeH,YAAY,SAAS;AAC1C,MAAMI,gBAAgBJ,YAAY,UAAU;AAE5C,MAAMK,kBAAkB;IACpB,MAAMC,YAAY,IAAIC;IACtBD,UAAU,gBAAgB,CAACE,6BAA6B;QACpD,SAAS,OAAO,EAAEC,KAAK,EAAE,GAAM;gBAAE,MAAMA,QAAQ;oBAACL;iBAAc,GAAG;oBAACD;iBAAa;YAAC;IACpF;IACAG,UAAU,gBAAgB,CAACI,0BAA0B;QACjD,SAAS,UAAa;gBAAE,QAAQ,EAAE;gBAAE,WAAW,EAAE;YAAC;IACtD;IACA,OAAOJ;AACX;AAEAK,SAAS,iCAAiC;IACtCC,GAAG,iDAAiD;QAChD,MAAMN,YAAYD;QAClBC,UAAU,QAAQ,CAACO;QACnB,MAAMC,YAAYR,UAAU,OAAO,CAACS;QAEpC,MAAMD,UAAU,IAAI,CAAC;YAAE,UAAU;gBAAC;aAAW;QAAC;QAC9C,MAAMA,UAAU,MAAM,CAAC;QAEvB,MAAME,OAAOF,UAAU,MAAM,CAAC;YAAC;SAAS;QAExCG,OAAOD,MAAM,OAAO,CAAC;YAAC;gBAAE,IAAI;gBAAe,SAAS;YAAW;SAAE;QACjEC,OAAOH,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;YAAC;SAAS;QAC9CG,OAAOH,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC;YAAE,OAAO;YAAU,OAAO;QAAS;IACnF;IAEAF,GAAG,4EAA4E;QAC3E,MAAMN,YAAYD;QAClBC,UAAU,QAAQ,CAACO;QACnB,MAAMC,YAAYR,UAAU,OAAO,CAACS;QAEpC,MAAMD,UAAU,IAAI,CAAC;YAAE,UAAU;gBAAC;aAAW;QAAC;QAC9CA,UAAU,MAAM,CAAC;YAAC;SAAQ;QAC1B,MAAMA,UAAU,MAAM,CAAC;QAEvB,MAAME,OAAOF,UAAU,MAAM,CAAC;YAAC;YAAS;SAAS;QAEjDG,OAAOD,MAAM,OAAO,CAAC;YACjB;gBAAE,IAAI;gBAAc,SAAS;YAAW;YACxC;gBAAE,IAAI;gBAAe,SAAS;YAAW;SAC5C;QACDC,OAAOH,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;YAAC;YAAS;SAAS;IAC3D;AACJ;AAEAH,SAAS,kCAAkC;IACvCC,GAAG,iDAAiD;QAChD,MAAMN,YAAYD;QAClBC,UAAU,QAAQ,CAACY;QACnB,MAAMJ,YAAYR,UAAU,OAAO,CAACa;QAEpC,MAAML,UAAU,IAAI,CAAC;YAAE,UAAU;gBAAC;aAAW;QAAC;QAC9C,MAAMA,UAAU,MAAM,CAAC;QAEvB,MAAMM,MAAMN,UAAU,MAAM,CAAC;QAE7BG,OAAOG,KAAK,OAAO,CAAC;YAAE,IAAI;YAAe,SAAS;QAAW;QAC7DH,OAAOH,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;IACpC;AACJ"}
@@ -47,9 +47,9 @@ class RefMultiAutocompletePresenterImpl {
47
47
  await this.base.search(query);
48
48
  }
49
49
  select(entryIds) {
50
- this.base.clearSearch();
51
50
  const allOptions = this.getAllKnownOptions();
52
51
  const selected = entryIds.map((entryId)=>allOptions.find((o)=>o.entryId === entryId)).filter((o)=>null != o);
52
+ this.base.clearSearch();
53
53
  this.resolved = selected;
54
54
  return selected.map((opt)=>({
55
55
  id: opt.id,
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js","sources":["../../../../../../src/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, runInAction } from \"mobx\";\nimport { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { CmsReferenceValue } from \"~/features/contentEntry/refTypes.js\";\nimport type { IRefEntryOption, IDropdownOption } from \"../abstractions.js\";\nimport { BaseAutocompletePresenter } from \"../BaseAutocompletePresenter.js\";\nimport {\n RefMultiAutocompletePresenter as Abstraction,\n type IRefMultiAutocompleteInitConfig,\n type IRefMultiAutocompleteViewModel\n} from \"./abstractions.js\";\n\nclass RefMultiAutocompletePresenterImpl implements Abstraction.Interface {\n private base: BaseAutocompletePresenter;\n private resolved: IRefEntryOption[] = [];\n\n constructor(\n searchUseCase: SearchContentEntriesUseCase.Interface,\n getEntriesUseCase: GetContentEntriesUseCase.Interface\n ) {\n this.base = new BaseAutocompletePresenter(searchUseCase, getEntriesUseCase);\n makeAutoObservable(this, { vm: computed });\n }\n\n get vm(): IRefMultiAutocompleteViewModel {\n const baseOptions = this.base.activeOptions;\n\n const seen = new Set<string>();\n const merged: IDropdownOption[] = [];\n\n for (const opt of this.resolved) {\n if (!seen.has(opt.entryId)) {\n seen.add(opt.entryId);\n merged.push({ label: opt.name, value: opt.entryId });\n }\n }\n\n for (const opt of baseOptions) {\n if (!seen.has(opt.entryId)) {\n seen.add(opt.entryId);\n merged.push({ label: opt.name, value: opt.entryId });\n }\n }\n\n const values = this.resolved.map(v => v.entryId);\n const ready = this.resolved.length > 0;\n\n return {\n loading: this.base.loading,\n options: merged,\n values: ready ? values : [],\n ready\n };\n }\n\n async init(config: IRefMultiAutocompleteInitConfig): Promise<void> {\n this.base.modelIds = config.modelIds;\n await this.base.loadDefaults();\n\n if (config.values && config.values.length > 0) {\n await this.resolve(config.values);\n }\n }\n\n async search(query: string): Promise<void> {\n await this.base.search(query);\n }\n\n select(entryIds: string[]): CmsReferenceValue[] {\n this.base.clearSearch();\n\n const allOptions = this.getAllKnownOptions();\n const selected = entryIds\n .map(entryId => allOptions.find(o => o.entryId === entryId))\n .filter((o): o is IRefEntryOption => o != null);\n\n this.resolved = selected;\n return selected.map(opt => ({ id: opt.id, modelId: opt.modelId }));\n }\n\n clear(): void {\n this.base.clearSearch();\n this.resolved = [];\n }\n\n dispose(): void {}\n\n private async resolve(values: CmsReferenceValue[]): Promise<void> {\n const options = await this.base.resolveEntries(values);\n runInAction(() => {\n this.resolved = options;\n });\n }\n\n private getAllKnownOptions(): IRefEntryOption[] {\n const seen = new Set<string>();\n const result: IRefEntryOption[] = [];\n\n for (const source of [this.resolved, this.base.searchOptions, this.base.defaultOptions]) {\n for (const opt of source) {\n if (!seen.has(opt.entryId)) {\n seen.add(opt.entryId);\n result.push(opt);\n }\n }\n }\n\n return result;\n }\n}\n\nexport const RefMultiAutocompletePresenter = Abstraction.createImplementation({\n implementation: RefMultiAutocompletePresenterImpl,\n dependencies: [SearchContentEntriesUseCase, GetContentEntriesUseCase]\n});\n"],"names":["RefMultiAutocompletePresenterImpl","searchUseCase","getEntriesUseCase","BaseAutocompletePresenter","makeAutoObservable","computed","baseOptions","seen","Set","merged","opt","values","v","ready","config","query","entryIds","allOptions","selected","entryId","o","options","runInAction","result","source","RefMultiAutocompletePresenter","Abstraction","SearchContentEntriesUseCase","GetContentEntriesUseCase"],"mappings":";;;;;AAYA,MAAMA;IAIF,YACIC,aAAoD,EACpDC,iBAAqD,CACvD;aALM,QAAQ,GAAsB,EAAE;QAMpC,IAAI,CAAC,IAAI,GAAG,IAAIC,0BAA0BF,eAAeC;QACzDE,mBAAmB,IAAI,EAAE;YAAE,IAAIC;QAAS;IAC5C;IAEA,IAAI,KAAqC;QACrC,MAAMC,cAAc,IAAI,CAAC,IAAI,CAAC,aAAa;QAE3C,MAAMC,OAAO,IAAIC;QACjB,MAAMC,SAA4B,EAAE;QAEpC,KAAK,MAAMC,OAAO,IAAI,CAAC,QAAQ,CAC3B,IAAI,CAACH,KAAK,GAAG,CAACG,IAAI,OAAO,GAAG;YACxBH,KAAK,GAAG,CAACG,IAAI,OAAO;YACpBD,OAAO,IAAI,CAAC;gBAAE,OAAOC,IAAI,IAAI;gBAAE,OAAOA,IAAI,OAAO;YAAC;QACtD;QAGJ,KAAK,MAAMA,OAAOJ,YACd,IAAI,CAACC,KAAK,GAAG,CAACG,IAAI,OAAO,GAAG;YACxBH,KAAK,GAAG,CAACG,IAAI,OAAO;YACpBD,OAAO,IAAI,CAAC;gBAAE,OAAOC,IAAI,IAAI;gBAAE,OAAOA,IAAI,OAAO;YAAC;QACtD;QAGJ,MAAMC,SAAS,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACC,CAAAA,IAAKA,EAAE,OAAO;QAC/C,MAAMC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG;QAErC,OAAO;YACH,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO;YAC1B,SAASJ;YACT,QAAQI,QAAQF,SAAS,EAAE;YAC3BE;QACJ;IACJ;IAEA,MAAM,KAAKC,MAAuC,EAAiB;QAC/D,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAGA,OAAO,QAAQ;QACpC,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY;QAE5B,IAAIA,OAAO,MAAM,IAAIA,OAAO,MAAM,CAAC,MAAM,GAAG,GACxC,MAAM,IAAI,CAAC,OAAO,CAACA,OAAO,MAAM;IAExC;IAEA,MAAM,OAAOC,KAAa,EAAiB;QACvC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAACA;IAC3B;IAEA,OAAOC,QAAkB,EAAuB;QAC5C,IAAI,CAAC,IAAI,CAAC,WAAW;QAErB,MAAMC,aAAa,IAAI,CAAC,kBAAkB;QAC1C,MAAMC,WAAWF,SACZ,GAAG,CAACG,CAAAA,UAAWF,WAAW,IAAI,CAACG,CAAAA,IAAKA,EAAE,OAAO,KAAKD,UAClD,MAAM,CAAC,CAACC,IAA4BA,AAAK,QAALA;QAEzC,IAAI,CAAC,QAAQ,GAAGF;QAChB,OAAOA,SAAS,GAAG,CAACR,CAAAA,MAAQ;gBAAE,IAAIA,IAAI,EAAE;gBAAE,SAASA,IAAI,OAAO;YAAC;IACnE;IAEA,QAAc;QACV,IAAI,CAAC,IAAI,CAAC,WAAW;QACrB,IAAI,CAAC,QAAQ,GAAG,EAAE;IACtB;IAEA,UAAgB,CAAC;IAEjB,MAAc,QAAQC,MAA2B,EAAiB;QAC9D,MAAMU,UAAU,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAACV;QAC/CW,YAAY;YACR,IAAI,CAAC,QAAQ,GAAGD;QACpB;IACJ;IAEQ,qBAAwC;QAC5C,MAAMd,OAAO,IAAIC;QACjB,MAAMe,SAA4B,EAAE;QAEpC,KAAK,MAAMC,UAAU;YAAC,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,IAAI,CAAC,IAAI,CAAC,cAAc;SAAC,CACnF,KAAK,MAAMd,OAAOc,OACd,IAAI,CAACjB,KAAK,GAAG,CAACG,IAAI,OAAO,GAAG;YACxBH,KAAK,GAAG,CAACG,IAAI,OAAO;YACpBa,OAAO,IAAI,CAACb;QAChB;QAIR,OAAOa;IACX;AACJ;AAEO,MAAME,8DAAgCC,8BAAAA,oBAAgC,CAAC;IAC1E,gBAAgB1B;IAChB,cAAc;QAAC2B;QAA6BC;KAAyB;AACzE"}
1
+ {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js","sources":["../../../../../../src/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, runInAction } from \"mobx\";\nimport { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { CmsReferenceValue } from \"~/features/contentEntry/refTypes.js\";\nimport type { IRefEntryOption, IDropdownOption } from \"../abstractions.js\";\nimport { BaseAutocompletePresenter } from \"../BaseAutocompletePresenter.js\";\nimport {\n RefMultiAutocompletePresenter as Abstraction,\n type IRefMultiAutocompleteInitConfig,\n type IRefMultiAutocompleteViewModel\n} from \"./abstractions.js\";\n\nclass RefMultiAutocompletePresenterImpl implements Abstraction.Interface {\n private base: BaseAutocompletePresenter;\n private resolved: IRefEntryOption[] = [];\n\n constructor(\n searchUseCase: SearchContentEntriesUseCase.Interface,\n getEntriesUseCase: GetContentEntriesUseCase.Interface\n ) {\n this.base = new BaseAutocompletePresenter(searchUseCase, getEntriesUseCase);\n makeAutoObservable(this, { vm: computed });\n }\n\n get vm(): IRefMultiAutocompleteViewModel {\n const baseOptions = this.base.activeOptions;\n\n const seen = new Set<string>();\n const merged: IDropdownOption[] = [];\n\n for (const opt of this.resolved) {\n if (!seen.has(opt.entryId)) {\n seen.add(opt.entryId);\n merged.push({ label: opt.name, value: opt.entryId });\n }\n }\n\n for (const opt of baseOptions) {\n if (!seen.has(opt.entryId)) {\n seen.add(opt.entryId);\n merged.push({ label: opt.name, value: opt.entryId });\n }\n }\n\n const values = this.resolved.map(v => v.entryId);\n const ready = this.resolved.length > 0;\n\n return {\n loading: this.base.loading,\n options: merged,\n values: ready ? values : [],\n ready\n };\n }\n\n async init(config: IRefMultiAutocompleteInitConfig): Promise<void> {\n this.base.modelIds = config.modelIds;\n await this.base.loadDefaults();\n\n if (config.values && config.values.length > 0) {\n await this.resolve(config.values);\n }\n }\n\n async search(query: string): Promise<void> {\n await this.base.search(query);\n }\n\n select(entryIds: string[]): CmsReferenceValue[] {\n // Look the entries up before clearing the search, or one picked from the search results is lost.\n const allOptions = this.getAllKnownOptions();\n const selected = entryIds\n .map(entryId => allOptions.find(o => o.entryId === entryId))\n .filter((o): o is IRefEntryOption => o != null);\n\n this.base.clearSearch();\n this.resolved = selected;\n return selected.map(opt => ({ id: opt.id, modelId: opt.modelId }));\n }\n\n clear(): void {\n this.base.clearSearch();\n this.resolved = [];\n }\n\n dispose(): void {}\n\n private async resolve(values: CmsReferenceValue[]): Promise<void> {\n const options = await this.base.resolveEntries(values);\n runInAction(() => {\n this.resolved = options;\n });\n }\n\n private getAllKnownOptions(): IRefEntryOption[] {\n const seen = new Set<string>();\n const result: IRefEntryOption[] = [];\n\n for (const source of [this.resolved, this.base.searchOptions, this.base.defaultOptions]) {\n for (const opt of source) {\n if (!seen.has(opt.entryId)) {\n seen.add(opt.entryId);\n result.push(opt);\n }\n }\n }\n\n return result;\n }\n}\n\nexport const RefMultiAutocompletePresenter = Abstraction.createImplementation({\n implementation: RefMultiAutocompletePresenterImpl,\n dependencies: [SearchContentEntriesUseCase, GetContentEntriesUseCase]\n});\n"],"names":["RefMultiAutocompletePresenterImpl","searchUseCase","getEntriesUseCase","BaseAutocompletePresenter","makeAutoObservable","computed","baseOptions","seen","Set","merged","opt","values","v","ready","config","query","entryIds","allOptions","selected","entryId","o","options","runInAction","result","source","RefMultiAutocompletePresenter","Abstraction","SearchContentEntriesUseCase","GetContentEntriesUseCase"],"mappings":"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"}
@@ -45,8 +45,8 @@ class RefSingleAutocompletePresenterImpl {
45
45
  await this.base.search(query);
46
46
  }
47
47
  select(entryId) {
48
- this.base.clearSearch();
49
48
  const opt = this.findOption(entryId);
49
+ this.base.clearSearch();
50
50
  if (!opt) return null;
51
51
  this.resolved = opt;
52
52
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js","sources":["../../../../../../src/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, runInAction } from \"mobx\";\nimport { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { CmsReferenceValue } from \"~/features/contentEntry/refTypes.js\";\nimport type { IRefEntryOption, IDropdownOption } from \"../abstractions.js\";\nimport { BaseAutocompletePresenter } from \"../BaseAutocompletePresenter.js\";\nimport {\n RefSingleAutocompletePresenter as Abstraction,\n type IRefSingleAutocompleteInitConfig,\n type IRefSingleAutocompleteViewModel\n} from \"./abstractions.js\";\n\nclass RefSingleAutocompletePresenterImpl implements Abstraction.Interface {\n private base: BaseAutocompletePresenter;\n private resolved: IRefEntryOption | null = null;\n\n constructor(\n searchUseCase: SearchContentEntriesUseCase.Interface,\n getEntriesUseCase: GetContentEntriesUseCase.Interface\n ) {\n this.base = new BaseAutocompletePresenter(searchUseCase, getEntriesUseCase);\n makeAutoObservable(this, { vm: computed });\n }\n\n get vm(): IRefSingleAutocompleteViewModel {\n const baseOptions = this.base.activeOptions;\n\n const seen = new Set<string>();\n const merged: IDropdownOption[] = [];\n\n if (this.resolved) {\n seen.add(this.resolved.entryId);\n merged.push({ label: this.resolved.name, value: this.resolved.entryId });\n }\n\n for (const opt of baseOptions) {\n if (!seen.has(opt.entryId)) {\n seen.add(opt.entryId);\n merged.push({ label: opt.name, value: opt.entryId });\n }\n }\n\n return {\n loading: this.base.loading,\n options: merged,\n value: this.resolved ? this.resolved.entryId : undefined,\n canReset: this.resolved !== null\n };\n }\n\n async init(config: IRefSingleAutocompleteInitConfig): Promise<void> {\n this.base.modelIds = config.modelIds;\n await this.base.loadDefaults();\n\n if (config.value) {\n await this.resolve(config.value);\n }\n }\n\n async search(query: string): Promise<void> {\n await this.base.search(query);\n }\n\n select(entryId: string): CmsReferenceValue | null {\n this.base.clearSearch();\n\n const opt = this.findOption(entryId);\n if (!opt) {\n return null;\n }\n this.resolved = opt;\n return { id: opt.id, modelId: opt.modelId };\n }\n\n clear(): void {\n this.base.clearSearch();\n this.resolved = null;\n }\n\n dispose(): void {}\n\n private async resolve(value: CmsReferenceValue): Promise<void> {\n const options = await this.base.resolveEntries([value]);\n runInAction(() => {\n this.resolved = options[0] ?? null;\n });\n }\n\n private findOption(entryId: string): IRefEntryOption | undefined {\n const sources = [\n this.resolved ? [this.resolved] : [],\n this.base.searchOptions,\n this.base.defaultOptions\n ];\n for (const source of sources) {\n const found = source.find(o => o.entryId === entryId);\n if (found) {\n return found;\n }\n }\n return undefined;\n }\n}\n\nexport const RefSingleAutocompletePresenter = Abstraction.createImplementation({\n implementation: RefSingleAutocompletePresenterImpl,\n dependencies: [SearchContentEntriesUseCase, GetContentEntriesUseCase]\n});\n"],"names":["RefSingleAutocompletePresenterImpl","searchUseCase","getEntriesUseCase","BaseAutocompletePresenter","makeAutoObservable","computed","baseOptions","seen","Set","merged","opt","undefined","config","query","entryId","value","options","runInAction","sources","source","found","o","RefSingleAutocompletePresenter","Abstraction","SearchContentEntriesUseCase","GetContentEntriesUseCase"],"mappings":";;;;;AAYA,MAAMA;IAIF,YACIC,aAAoD,EACpDC,iBAAqD,CACvD;aALM,QAAQ,GAA2B;QAMvC,IAAI,CAAC,IAAI,GAAG,IAAIC,0BAA0BF,eAAeC;QACzDE,mBAAmB,IAAI,EAAE;YAAE,IAAIC;QAAS;IAC5C;IAEA,IAAI,KAAsC;QACtC,MAAMC,cAAc,IAAI,CAAC,IAAI,CAAC,aAAa;QAE3C,MAAMC,OAAO,IAAIC;QACjB,MAAMC,SAA4B,EAAE;QAEpC,IAAI,IAAI,CAAC,QAAQ,EAAE;YACfF,KAAK,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO;YAC9BE,OAAO,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO;YAAC;QAC1E;QAEA,KAAK,MAAMC,OAAOJ,YACd,IAAI,CAACC,KAAK,GAAG,CAACG,IAAI,OAAO,GAAG;YACxBH,KAAK,GAAG,CAACG,IAAI,OAAO;YACpBD,OAAO,IAAI,CAAC;gBAAE,OAAOC,IAAI,IAAI;gBAAE,OAAOA,IAAI,OAAO;YAAC;QACtD;QAGJ,OAAO;YACH,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO;YAC1B,SAASD;YACT,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,GAAGE;YAC/C,UAAU,AAAkB,SAAlB,IAAI,CAAC,QAAQ;QAC3B;IACJ;IAEA,MAAM,KAAKC,MAAwC,EAAiB;QAChE,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAGA,OAAO,QAAQ;QACpC,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY;QAE5B,IAAIA,OAAO,KAAK,EACZ,MAAM,IAAI,CAAC,OAAO,CAACA,OAAO,KAAK;IAEvC;IAEA,MAAM,OAAOC,KAAa,EAAiB;QACvC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAACA;IAC3B;IAEA,OAAOC,OAAe,EAA4B;QAC9C,IAAI,CAAC,IAAI,CAAC,WAAW;QAErB,MAAMJ,MAAM,IAAI,CAAC,UAAU,CAACI;QAC5B,IAAI,CAACJ,KACD,OAAO;QAEX,IAAI,CAAC,QAAQ,GAAGA;QAChB,OAAO;YAAE,IAAIA,IAAI,EAAE;YAAE,SAASA,IAAI,OAAO;QAAC;IAC9C;IAEA,QAAc;QACV,IAAI,CAAC,IAAI,CAAC,WAAW;QACrB,IAAI,CAAC,QAAQ,GAAG;IACpB;IAEA,UAAgB,CAAC;IAEjB,MAAc,QAAQK,KAAwB,EAAiB;QAC3D,MAAMC,UAAU,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;YAACD;SAAM;QACtDE,YAAY;YACR,IAAI,CAAC,QAAQ,GAAGD,OAAO,CAAC,EAAE,IAAI;QAClC;IACJ;IAEQ,WAAWF,OAAe,EAA+B;QAC7D,MAAMI,UAAU;YACZ,IAAI,CAAC,QAAQ,GAAG;gBAAC,IAAI,CAAC,QAAQ;aAAC,GAAG,EAAE;YACpC,IAAI,CAAC,IAAI,CAAC,aAAa;YACvB,IAAI,CAAC,IAAI,CAAC,cAAc;SAC3B;QACD,KAAK,MAAMC,UAAUD,QAAS;YAC1B,MAAME,QAAQD,OAAO,IAAI,CAACE,CAAAA,IAAKA,EAAE,OAAO,KAAKP;YAC7C,IAAIM,OACA,OAAOA;QAEf;IAEJ;AACJ;AAEO,MAAME,gEAAiCC,+BAAAA,oBAAgC,CAAC;IAC3E,gBAAgBvB;IAChB,cAAc;QAACwB;QAA6BC;KAAyB;AACzE"}
1
+ {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js","sources":["../../../../../../src/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, runInAction } from \"mobx\";\nimport { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { CmsReferenceValue } from \"~/features/contentEntry/refTypes.js\";\nimport type { IRefEntryOption, IDropdownOption } from \"../abstractions.js\";\nimport { BaseAutocompletePresenter } from \"../BaseAutocompletePresenter.js\";\nimport {\n RefSingleAutocompletePresenter as Abstraction,\n type IRefSingleAutocompleteInitConfig,\n type IRefSingleAutocompleteViewModel\n} from \"./abstractions.js\";\n\nclass RefSingleAutocompletePresenterImpl implements Abstraction.Interface {\n private base: BaseAutocompletePresenter;\n private resolved: IRefEntryOption | null = null;\n\n constructor(\n searchUseCase: SearchContentEntriesUseCase.Interface,\n getEntriesUseCase: GetContentEntriesUseCase.Interface\n ) {\n this.base = new BaseAutocompletePresenter(searchUseCase, getEntriesUseCase);\n makeAutoObservable(this, { vm: computed });\n }\n\n get vm(): IRefSingleAutocompleteViewModel {\n const baseOptions = this.base.activeOptions;\n\n const seen = new Set<string>();\n const merged: IDropdownOption[] = [];\n\n if (this.resolved) {\n seen.add(this.resolved.entryId);\n merged.push({ label: this.resolved.name, value: this.resolved.entryId });\n }\n\n for (const opt of baseOptions) {\n if (!seen.has(opt.entryId)) {\n seen.add(opt.entryId);\n merged.push({ label: opt.name, value: opt.entryId });\n }\n }\n\n return {\n loading: this.base.loading,\n options: merged,\n value: this.resolved ? this.resolved.entryId : undefined,\n canReset: this.resolved !== null\n };\n }\n\n async init(config: IRefSingleAutocompleteInitConfig): Promise<void> {\n this.base.modelIds = config.modelIds;\n await this.base.loadDefaults();\n\n if (config.value) {\n await this.resolve(config.value);\n }\n }\n\n async search(query: string): Promise<void> {\n await this.base.search(query);\n }\n\n select(entryId: string): CmsReferenceValue | null {\n // Look the entry up before clearing the search, or one picked from the search results is lost.\n const opt = this.findOption(entryId);\n this.base.clearSearch();\n if (!opt) {\n return null;\n }\n this.resolved = opt;\n return { id: opt.id, modelId: opt.modelId };\n }\n\n clear(): void {\n this.base.clearSearch();\n this.resolved = null;\n }\n\n dispose(): void {}\n\n private async resolve(value: CmsReferenceValue): Promise<void> {\n const options = await this.base.resolveEntries([value]);\n runInAction(() => {\n this.resolved = options[0] ?? null;\n });\n }\n\n private findOption(entryId: string): IRefEntryOption | undefined {\n const sources = [\n this.resolved ? [this.resolved] : [],\n this.base.searchOptions,\n this.base.defaultOptions\n ];\n for (const source of sources) {\n const found = source.find(o => o.entryId === entryId);\n if (found) {\n return found;\n }\n }\n return undefined;\n }\n}\n\nexport const RefSingleAutocompletePresenter = Abstraction.createImplementation({\n implementation: RefSingleAutocompletePresenterImpl,\n dependencies: [SearchContentEntriesUseCase, GetContentEntriesUseCase]\n});\n"],"names":["RefSingleAutocompletePresenterImpl","searchUseCase","getEntriesUseCase","BaseAutocompletePresenter","makeAutoObservable","computed","baseOptions","seen","Set","merged","opt","undefined","config","query","entryId","value","options","runInAction","sources","source","found","o","RefSingleAutocompletePresenter","Abstraction","SearchContentEntriesUseCase","GetContentEntriesUseCase"],"mappings":";;;;;AAYA,MAAMA;IAIF,YACIC,aAAoD,EACpDC,iBAAqD,CACvD;aALM,QAAQ,GAA2B;QAMvC,IAAI,CAAC,IAAI,GAAG,IAAIC,0BAA0BF,eAAeC;QACzDE,mBAAmB,IAAI,EAAE;YAAE,IAAIC;QAAS;IAC5C;IAEA,IAAI,KAAsC;QACtC,MAAMC,cAAc,IAAI,CAAC,IAAI,CAAC,aAAa;QAE3C,MAAMC,OAAO,IAAIC;QACjB,MAAMC,SAA4B,EAAE;QAEpC,IAAI,IAAI,CAAC,QAAQ,EAAE;YACfF,KAAK,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO;YAC9BE,OAAO,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO;YAAC;QAC1E;QAEA,KAAK,MAAMC,OAAOJ,YACd,IAAI,CAACC,KAAK,GAAG,CAACG,IAAI,OAAO,GAAG;YACxBH,KAAK,GAAG,CAACG,IAAI,OAAO;YACpBD,OAAO,IAAI,CAAC;gBAAE,OAAOC,IAAI,IAAI;gBAAE,OAAOA,IAAI,OAAO;YAAC;QACtD;QAGJ,OAAO;YACH,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO;YAC1B,SAASD;YACT,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,GAAGE;YAC/C,UAAU,AAAkB,SAAlB,IAAI,CAAC,QAAQ;QAC3B;IACJ;IAEA,MAAM,KAAKC,MAAwC,EAAiB;QAChE,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAGA,OAAO,QAAQ;QACpC,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY;QAE5B,IAAIA,OAAO,KAAK,EACZ,MAAM,IAAI,CAAC,OAAO,CAACA,OAAO,KAAK;IAEvC;IAEA,MAAM,OAAOC,KAAa,EAAiB;QACvC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAACA;IAC3B;IAEA,OAAOC,OAAe,EAA4B;QAE9C,MAAMJ,MAAM,IAAI,CAAC,UAAU,CAACI;QAC5B,IAAI,CAAC,IAAI,CAAC,WAAW;QACrB,IAAI,CAACJ,KACD,OAAO;QAEX,IAAI,CAAC,QAAQ,GAAGA;QAChB,OAAO;YAAE,IAAIA,IAAI,EAAE;YAAE,SAASA,IAAI,OAAO;QAAC;IAC9C;IAEA,QAAc;QACV,IAAI,CAAC,IAAI,CAAC,WAAW;QACrB,IAAI,CAAC,QAAQ,GAAG;IACpB;IAEA,UAAgB,CAAC;IAEjB,MAAc,QAAQK,KAAwB,EAAiB;QAC3D,MAAMC,UAAU,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;YAACD;SAAM;QACtDE,YAAY;YACR,IAAI,CAAC,QAAQ,GAAGD,OAAO,CAAC,EAAE,IAAI;QAClC;IACJ;IAEQ,WAAWF,OAAe,EAA+B;QAC7D,MAAMI,UAAU;YACZ,IAAI,CAAC,QAAQ,GAAG;gBAAC,IAAI,CAAC,QAAQ;aAAC,GAAG,EAAE;YACpC,IAAI,CAAC,IAAI,CAAC,aAAa;YACvB,IAAI,CAAC,IAAI,CAAC,cAAc;SAC3B;QACD,KAAK,MAAMC,UAAUD,QAAS;YAC1B,MAAME,QAAQD,OAAO,IAAI,CAACE,CAAAA,IAAKA,EAAE,OAAO,KAAKP;YAC7C,IAAIM,OACA,OAAOA;QAEf;IAEJ;AACJ;AAEO,MAAME,gEAAiCC,+BAAAA,oBAAgC,CAAC;IAC3E,gBAAgBvB;IAChB,cAAc;QAACwB;QAA6BC;KAAyB;AACzE"}