@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.
- package/ContentEntriesModule.js +5 -0
- package/ContentEntriesModule.js.map +1 -1
- package/admin/components/ContentEntries/Filters/RefFieldRenderer.d.ts +2 -0
- package/admin/components/ContentEntries/Filters/RefFieldRenderer.js +84 -0
- package/admin/components/ContentEntries/Filters/RefFieldRenderer.js.map +1 -0
- package/package.json +23 -23
- package/presentation/contentEntries/preview/LivePreviewPresenter.d.ts +12 -1
- package/presentation/contentEntries/preview/LivePreviewPresenter.js +48 -5
- package/presentation/contentEntries/preview/LivePreviewPresenter.js.map +1 -1
- package/presentation/contentEntries/preview/LivePreviewPresenter.test.d.ts +1 -0
- package/presentation/contentEntries/preview/LivePreviewPresenter.test.js +81 -0
- package/presentation/contentEntries/preview/LivePreviewPresenter.test.js.map +1 -0
- package/presentation/contentEntries/preview/PreviewDecorator.js +16 -3
- package/presentation/contentEntries/preview/PreviewDecorator.js.map +1 -1
- package/presentation/contentEntries/preview/PreviewPane.d.ts +5 -1
- package/presentation/contentEntries/preview/PreviewPane.js +2 -2
- package/presentation/contentEntries/preview/PreviewPane.js.map +1 -1
- package/presentation/contentEntries/preview/abstractions.d.ts +8 -0
- package/presentation/contentEntries/preview/abstractions.js.map +1 -1
- package/presentation/contentEntries/preview/resolvePreviewUrl.d.ts +21 -0
- package/presentation/contentEntries/preview/resolvePreviewUrl.js +50 -1
- package/presentation/contentEntries/preview/resolvePreviewUrl.js.map +1 -1
- package/presentation/contentEntries/preview/resolvePreviewUrl.test.d.ts +1 -0
- package/presentation/contentEntries/preview/resolvePreviewUrl.test.js +127 -0
- package/presentation/contentEntries/preview/resolvePreviewUrl.test.js.map +1 -0
- package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.d.ts +1 -0
- package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js +133 -0
- package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js.map +1 -0
- package/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js +1 -1
- package/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js.map +1 -1
- package/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js +1 -1
- package/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js.map +1 -1
package/ContentEntriesModule.js
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,MAAM,EAAEA,SAAAA,OAAO,EAAE,GAAGC;AACpB,MAAM,EAAEC,SAAAA,OAAO,EAAE,GAAGC;AAEb,MAAMC,uBAAuB,IACzB,WAAP,GACI,wDACI,oBAACC,aAAWA,MAAAA,WAAAA,GACR,oBAACA,YAAY,MAAM;QACf,MAAMC;QACN,uBAAS,oBAACC,yBAAuBA;sBAErC,oBAACF,YAAY,MAAM;QACf,MAAMG;QACN,uBAAS,oBAACC,2BAAyBA;sBAEvC,oBAACJ,YAAY,MAAM;QACf,MAAMK;QACN,uBAAS,oBAACC,6BAA2BA;sBAEzC,oBAACN,YAAY,MAAM;QACf,MAAMO;QACN,uBAAS,oBAACC,6BAA2BA;sBAEzC,oBAACR,YAAY,MAAM;QACf,MAAMS;QACN,uBAAS,oBAACC,6BAA2BA;sBAEzC,oBAACV,YAAY,MAAM;QACf,MAAMW;QACN,uBAAS,oBAACC,wBAAsBA;uBAGxC,oBAAChB,gCAA8BA,MAAAA,WAAAA,GAC3B,oBAACiB,uBAAqBA,OAAAA,WAAAA,GACtB,oBAACC,oBAAkBA,MAAAA,WAAAA,GACf,oBAACnB,QAAQ,MAAM;QAAC,MAAM;QAAU,uBAAS,oBAACoB,gBAAcA;sBACxD,oBAACpB,QAAQ,UAAU;QAAC,MAAM;QAAW,uBAAS,oBAACqB,eAAaA;sBAC5D,oBAACrB,QAAQ,UAAU;QAAC,MAAM;QAAa,uBAAS,oBAACsB,iBAAeA;uBAEpE,oBAACtB,QAAQ,OAAO,CAAC,MAAM;QAAC,MAAM;QAAY,uBAAS,oBAACuB,aAAWA;sBAC/D,oBAACvB,QAAQ,UAAU;QAAC,MAAM;QAAQ,uBAAS,oBAACwB,YAAUA;sBACtD,oBAACxB,QAAQ,UAAU;QAAC,MAAM;QAAU,uBAAS,oBAACyB,cAAYA;sBAC1D,oBAACzB,QAAQ,MAAM,CAAC,MAAM;QAAC,MAAM;QAAQ,uBAAS,oBAAC0B,YAAUA;sBACzD,oBAAC1B,QAAQ,MAAM,CAAC,MAAM;QAAC,MAAM;QAAe,uBAAS,oBAAC2B,sBAAoBA;sBAC1E,oBAAC3B,QAAQ,MAAM,CAAC,MAAM;QAAC,MAAM;QAAU,uBAAS,oBAAC4B,cAAYA;sBAC7D,oBAAC5B,QAAQ,KAAK,CAAC,MAAM;QAAC,MAAM;QAAQ,uBAAS,oBAAC6B,WAASA;sBACvD,oBAACV,oBAAkBA,MAAAA,WAAAA,GACf,oBAACnB,QAAQ,KAAK,CAAC,MAAM;QAAC,MAAM;QAAU,uBAAS,oBAAC8B,mBAAiBA;uBAErE,oBAAC9B,QAAQ,KAAK,CAAC,MAAM;QAAC,MAAM;QAAQ,uBAAS,oBAAC+B,WAASA;sBACvD,oBAAC/B,QAAQ,KAAK,CAAC,MAAM;QAAC,MAAM;QAAa,uBAAS,oBAACgC,gBAAcA;sBACjE,oBAAChC,QAAQ,KAAK,CAAC,MAAM;QAAC,MAAM;QAAU,uBAAS,oBAACiC,aAAWA;QAAK,OAAO;sBACvE,oBAACjC,QAAQ,KAAK,CAAC,MAAM;QACjB,MAAM;QACN,QAAQ;QACR,oBAAM,oBAACkC,UAAQA;QACf,UAAU;QACV,UAAU;QACV,MAAM;sBAEV,oBAAClC,QAAQ,KAAK,CAAC,MAAM;QAAC,MAAM;QAAa,QAAQ;QAAU,oBAAM,oBAACmC,YAAUA;sBAC5E,oBAACnC,QAAQ,KAAK,CAAC,MAAM;QACjB,MAAM;QACN,QAAQ;QACR,oBAAM,oBAACoC,aAAWA;QAClB,UAAU;sBAEd,oBAACpC,QAAQ,KAAK,CAAC,MAAM;QACjB,MAAM;QACN,QAAQ;QACR,oBAAM,oBAACqC,cAAYA;QACnB,UAAU;sBAEd,oBAAClB,oBAAkBA,MAAAA,WAAAA,GACf,oBAACnB,QAAQ,KAAK,CAAC,MAAM;QACjB,MAAM;QACN,QAAQ;QACR,oBAAM,oBAACsC,YAAUA;QACjB,UAAU;sBAEd,oBAACtC,QAAQ,KAAK,CAAC,MAAM;QACjB,MAAM;QACN,QAAQ;QACR,UAAU;QACV,UAAU;QACV,oBAAM,oBAACuC,UAAQA;uBAGvB,oBAACvC,QAAQ,KAAK,CAAC,MAAM;QACjB,MAAM;QACN,QAAQ;QACR,oBAAM,oBAACwC,aAAWA;QAClB,MAAM;QACN,WAAW;QACX,UAAU;QACV,UAAU;QACV,WAAW;uBAGnB,oBAACrC,kCAAgCA,MAAAA,WAAAA,GAC7B,oBAACgB,oBAAkBA,MAAAA,WAAAA,GACf,oBAACjB,QAAQ,YAAY;QACjB,MAAM;QACN,uBAAS,oBAACuC,kBAAgBA;uBAGlC,oBAACvC,QAAQ,YAAY;QAAC,MAAM;QAAe,uBAAS,oBAACwC,wBAAsBA;sBAC3E,oBAACxC,QAAQ,YAAY;QAAC,MAAM;QAAQ,uBAAS,oBAACyC,mBAAiBA;sBAC/D,oBAACxB,oBAAkBA,MAAAA,WAAAA,GACf,oBAACjB,QAAQ,YAAY;QACjB,MAAM;QACN,uBAAS,oBAAC0C,yBAAuBA;sBAErC,oBAAC1C,QAAQ,YAAY;QAAC,MAAM;QAAW,uBAAS,oBAAC2C,sBAAoBA;sBACrE,oBAAC3C,QAAQ,YAAY;QAAC,MAAM;QAAe,uBAAS,oBAAC4C,mBAAiBA;uBAE1E,oBAAC5C,QAAQ,cAAc;QACnB,MAAM;QACN,uBAAS,oBAAC6C,qBAAmBA;QAC7B,OAAO;sBAEX,oBAAC5B,oBAAkBA,MAAAA,WAAAA,GACf,oBAACjB,QAAQ,cAAc;QACnB,MAAM;QACN,uBAAS,oBAAC8C,0BAAwBA"}
|
|
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,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.
|
|
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.
|
|
29
|
+
"@lexical/utils": "0.52.0",
|
|
30
30
|
"@types/react": "18.3.31",
|
|
31
|
-
"@webiny/admin-ui": "6.5.0-beta.
|
|
32
|
-
"@webiny/app": "6.5.0-beta.
|
|
33
|
-
"@webiny/app-aco": "6.5.0-beta.
|
|
34
|
-
"@webiny/app-admin": "6.5.0-beta.
|
|
35
|
-
"@webiny/app-graphql-playground": "6.5.0-beta.
|
|
36
|
-
"@webiny/app-headless-cms-common": "6.5.0-beta.
|
|
37
|
-
"@webiny/cms-sdk": "6.5.0-beta.
|
|
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.
|
|
40
|
-
"@webiny/form": "6.5.0-beta.
|
|
41
|
-
"@webiny/frontend-settings": "6.5.0-beta.
|
|
42
|
-
"@webiny/icons": "6.5.0-beta.
|
|
43
|
-
"@webiny/lexical-editor": "6.5.0-beta.
|
|
44
|
-
"@webiny/lexical-nodes": "6.5.0-beta.
|
|
45
|
-
"@webiny/plugins": "6.5.0-beta.
|
|
46
|
-
"@webiny/react-composition": "6.5.0-beta.
|
|
47
|
-
"@webiny/react-properties": "6.5.0-beta.
|
|
48
|
-
"@webiny/utils": "6.5.0-beta.
|
|
49
|
-
"@webiny/validation": "6.5.0-beta.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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, {
|
|
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
|
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,
|
|
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":";;;;;;;;;;;AAyBO,MAAMA,cAAc,CAAC,EACxBC,MAAM,EACNC,WAAW,EACXC,OAAO,EACPC,SAAS,EACTC,YAAY,EACG;IACf,MAAMC,YAAYC,OAAiC;IACnD,MAAMC,eAAeD,OAAyB;IAC9C,MAAME,eAAeF,OAAOH;IAC5B,MAAMM,kBAAkBH,OAAuC;IAC/D,MAAM,CAACI,OAAOC,SAAS,GAAGC,SAAS;IACnC,MAAM,CAACC,SAASC,WAAW,GAAGF,SAAS;IACvC,MAAM,CAACG,WAAWC,aAAa,GAAGJ,SAAS;IAC3C,MAAM,CAACK,UAAUC,YAAY,GAAGN,SAAuB;IACvD,MAAMO,YAAYC;IAElBZ,aAAa,OAAO,GAAGL;IAEvB,MAAMkB,aAAaC,gBAAgBtB,QAAQC,aAAaG,gBAAgB,CAAC;IAEzE,MAAM,CAACmB,SAASC,WAAW,GAAGZ,SAASS;IAEvCI,UAAU;QACND,WAAWH;IACf,GAAG;QAACA;KAAW;IAEf,MAAMK,YAAa,AAAC;QAChB,MAAMC,aAAaC,eAAe5B,QAAQC;QAC1C,MAAM4B,MAAM,IAAIC,IAAIH;QACpBE,IAAI,YAAY,CAAC,GAAG,CAAC,cAAc;QACnCA,IAAI,YAAY,CAAC,GAAG,CAAC,WAAW;QAChCA,IAAI,YAAY,CAAC,GAAG,CAAC,SAAS3B;QAC9B2B,IAAI,YAAY,CAAC,GAAG,CAAC,WAAWA,IAAI,QAAQ;QAC5CA,IAAI,YAAY,CAAC,GAAG,CAAC,eAAeE,OAAO,QAAQ,CAAC,MAAM;QAC1D,OAAOF,IAAI,QAAQ;IACvB;IAEA,MAAMG,gBAAgBC,YAAY;QAC9B,MAAMC,OAAO1B,aAAa,OAAO;QACjC,IAAI,CAAC0B,QAAQ,CAAC3B,aAAa,OAAO,EAC9B;QAGJ,MAAM4B,aAAaC,KAAK,KAAK,CAACA,KAAK,SAAS,CAACF;QAC7C3B,aAAa,OAAO,CAAC,IAAI,CAAC,gBAAgB4B;QAC1C1B,gBAAgB,OAAO,GAAG0B;IAC9B,GAAG,EAAE;IAELV,UAAU;QACN,MAAMY,SAAShC,UAAU,OAAO;QAChC,IAAI,CAACgC,UAAU,CAACA,OAAO,aAAa,EAChC;QAGJ,MAAMC,eAAe,IAAIR,IAAIO,OAAO,GAAG,EAAE,MAAM;QAC/C,MAAME,eAAe,IAAIC,cAAc,IAAMT,QAAQA,OAAO,QAAQ,CAAC,MAAM;QAC3E,MAAMU,gBAAgB,IAAID,cAAc,IAAMH,OAAO,aAAa,EAAGC;QAErE,MAAMI,YAAY,IAAIC,UAAUJ,cAAcE,eAAe;QAC7DlC,aAAa,OAAO,GAAGmC;QAEvBA,UAAU,EAAE,CAAC,iBAAiB;YAC1B/B,SAAS;YACTG,WAAW;YACXkB;QACJ;QAEAU,UAAU,EAAE,CACR,8BACA,CAACE;YACGzB,UAAU,YAAY,CAACyB;QAC3B;QAGJ,OAAO;YACHF,UAAU,OAAO;YACjBnC,aAAa,OAAO,GAAG;YACvBE,gBAAgB,OAAO,GAAG;YAC1BE,SAAS;QACb;IACJ,GAAG;QAACe;QAAWP;QAAWJ;QAAWiB;KAAc;IAEnDP,UAAU;QACN,IAAI,CAACf,SAAS,CAACP,aAAa,CAACI,aAAa,OAAO,EAC7C;QAGJ,MAAMsC,UAAUT,KAAK,KAAK,CAACA,KAAK,SAAS,CAACjC;QAC1C,MAAM2C,WAAWrC,gBAAgB,OAAO;QAExC,IAAIqC,UAAU;YACV,MAAMC,QAAQC,UAAU,OAAO,CAACF,UAAUD;YAC1C,IAAIE,MAAM,MAAM,GAAG,GACfxC,aAAa,OAAO,CAAC,IAAI,CAAC,kBAAkBwC;QAEpD,OACIxC,aAAa,OAAO,CAAC,IAAI,CAAC,gBAAgBsC;QAG9CpC,gBAAgB,OAAO,GAAGoC;IAC9B,GAAG;QAACnC;QAAOP;KAAU;IAErB,MAAM8C,SAAShB,YAAY;QACvBnB,WAAW;QACXH,SAAS;QACTF,gBAAgB,OAAO,GAAG;QAC1BO,aAAakC,CAAAA,OAAQA,OAAO;IAChC,GAAG,EAAE;IAEL,MAAMC,cAAclB,YAAY;QAC5B,MAAMJ,MAAM,IAAIC,IAAIT;QACpBQ,IAAI,YAAY,CAAC,GAAG,CAAC,WAAW;QAChCA,IAAI,YAAY,CAAC,GAAG,CAAC,WAAWA,IAAI,QAAQ;QAC5CA,IAAI,YAAY,CAAC,GAAG,CAAC,cAAc;QACnCA,IAAI,YAAY,CAAC,GAAG,CAAC,SAAS3B;QAC9B,OAAO2B,IAAI,QAAQ;IACvB,GAAG;QAACR;QAAYnB;KAAQ;IAExB,MAAMkD,UAAUnB,YAAY;QACxBoB,UAAU,SAAS,CAAC,SAAS,CAACF;IAClC,GAAG;QAACA;KAAY;IAEhB,MAAMG,eAAerB,YAAY;QAC7BF,OAAO,IAAI,CAACoB,eAAe;IAC/B,GAAG;QAACA;KAAY;IAEhB,OAAO,WAAP,GACI,oBAAC;QAAI,WAAU;qBACX,oBAAC;QAAI,WAAU;qBACX,oBAAC;QAAI,WAAU;qBACX,oBAACI,OAAKA;QACF,OAAOhC;QACP,UAAUC;QACV;QACA,MAAK;QACL,SAAS;QACT,WAAU;uBAIlB,oBAAC;QAAI,WAAU;qBACX,oBAACgC,YAAUA;QACP,SAASF;QACT,oBAAM,oBAACG,gCAAaA;QACpB,SAAQ;QACR,MAAK;sBAET,oBAACD,YAAUA;QACP,SAASJ;QACT,oBAAM,oBAACM,iCAAeA;QACtB,SAAQ;QACR,MAAK;sBAET,oBAACF,YAAUA;QAAC,SAASP;QAAQ,oBAAM,oBAACU,gBAAWA;QAAK,SAAQ;QAAQ,MAAK;sBACzE,oBAACC,kBAAgBA;QACb,OAAO3C;QACP,UAAU,CAAC4C,QAAkB3C,YAAY2C;QACzC,OAAO;YACH;gBAAE,OAAO;gBAAW,OAAO;gBAAI,MAAM,WAAN,GAAM,oBAACC,2BAAUA;YAAI;YACpD;gBAAE,OAAO;gBAAU,OAAO;gBAAI,MAAM,WAAN,GAAM,oBAACC,+BAAcA;YAAI;SAC1D;wBAKb,oBAAC;QAAI,WAAU;OACVlD,UAAU,WAAVA,GAAU,oBAACmD,eAAaA;QAAC,MAAK;SAAqC,oBACpE,oBAAC;QACG,WAAW,CAAC,2CAA2C,EAAE/C,AAAa,aAAbA,WAAwB,SAAS,IAAI;QAC9F,OAAO;YAAE,OAAOA,AAAa,aAAbA,WAAwB,UAAU;QAAO;qBAEzD,oBAAC;QACG,KAAKF;QACL,KAAKV;QACL,KAAKqB;QACL,OAAM;QACN,QAAO;QACP,WAAU;QACV,SAAQ;;AAMhC"}
|
|
@@ -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":";
|
|
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":["
|
|
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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,
|
package/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js.map
CHANGED
|
@@ -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
|
|
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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"}
|
package/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js
CHANGED
|
@@ -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 {
|
package/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js.map
CHANGED
|
@@ -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
|
|
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"}
|