@elementor/editor-site-navigation 0.19.11 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/index.js +128 -50
- package/dist/index.mjs +124 -44
- package/package.json +5 -5
- package/src/api/post.ts +32 -4
- package/src/api/settings.ts +5 -9
- package/src/api/user.ts +20 -0
- package/src/components/panel/actions-menu/actions/__tests__/delete.test.tsx +8 -0
- package/src/components/panel/actions-menu/actions/__tests__/set-home.test.tsx +52 -0
- package/src/components/panel/actions-menu/actions/__tests__/view.test.tsx +4 -0
- package/src/components/panel/actions-menu/actions/delete.tsx +4 -1
- package/src/components/panel/actions-menu/actions/duplicate.tsx +5 -1
- package/src/components/panel/actions-menu/actions/rename.tsx +1 -0
- package/src/components/panel/actions-menu/actions/set-home.tsx +9 -1
- package/src/components/panel/add-new-button.tsx +3 -0
- package/src/components/panel/posts-list/__tests__/post-list-item.test.tsx +20 -0
- package/src/components/panel/posts-list/__tests__/posts-collapsible-list.test.tsx +56 -8
- package/src/components/panel/posts-list/collapsible-list.tsx +1 -1
- package/src/components/panel/posts-list/list-items/list-item-view.tsx +50 -29
- package/src/components/panel/posts-list/posts-collapsible-list.tsx +14 -7
- package/src/components/top-bar/__tests__/add-new-page.test.tsx +36 -0
- package/src/components/top-bar/__tests__/recently-edited.test.tsx +68 -0
- package/src/components/top-bar/create-post-list-item.tsx +3 -1
- package/src/components/top-bar/post-list-item.tsx +1 -0
- package/src/hooks/__tests__/use-homepage.test.ts +1 -1
- package/src/hooks/__tests__/use-posts.test.ts +44 -10
- package/src/hooks/use-posts.ts +25 -4
- package/src/hooks/use-user.ts +11 -0
- package/src/types.ts +8 -1
package/dist/index.mjs
CHANGED
|
@@ -135,6 +135,7 @@ function PostListItem({ post, closePopup, ...props }) {
|
|
|
135
135
|
return /* @__PURE__ */ React3.createElement(
|
|
136
136
|
MenuItem,
|
|
137
137
|
{
|
|
138
|
+
disabled: !post.user_can.edit,
|
|
138
139
|
onClick: async () => {
|
|
139
140
|
closePopup();
|
|
140
141
|
await navigateToDocument(post.id);
|
|
@@ -190,12 +191,41 @@ async function addNewPage() {
|
|
|
190
191
|
import { PlusIcon } from "@elementor/icons";
|
|
191
192
|
import { __ } from "@wordpress/i18n";
|
|
192
193
|
import { __useNavigateToDocument as useNavigateToDocument2 } from "@elementor/editor-documents";
|
|
194
|
+
|
|
195
|
+
// src/hooks/use-user.ts
|
|
196
|
+
import { useQuery as useQuery2 } from "@elementor/query";
|
|
197
|
+
|
|
198
|
+
// src/api/user.ts
|
|
199
|
+
import apiFetch3 from "@wordpress/api-fetch";
|
|
200
|
+
var getUser = () => {
|
|
201
|
+
const baseUri = "/wp/v2/users/me";
|
|
202
|
+
const keys = ["capabilities"];
|
|
203
|
+
const queryParams = new URLSearchParams({
|
|
204
|
+
_fields: keys.join(","),
|
|
205
|
+
context: "edit"
|
|
206
|
+
});
|
|
207
|
+
const uri = baseUri + "?" + queryParams.toString();
|
|
208
|
+
return apiFetch3({ path: uri });
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/hooks/use-user.ts
|
|
212
|
+
var userQueryKey = () => ["site-navigation", "user"];
|
|
213
|
+
function useUser() {
|
|
214
|
+
return useQuery2({
|
|
215
|
+
queryKey: userQueryKey(),
|
|
216
|
+
queryFn: () => getUser()
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/components/top-bar/create-post-list-item.tsx
|
|
193
221
|
function CreatePostListItem({ closePopup, ...props }) {
|
|
194
222
|
const { create, isLoading } = useCreatePage();
|
|
195
223
|
const navigateToDocument = useNavigateToDocument2();
|
|
224
|
+
const { data: user } = useUser();
|
|
196
225
|
return /* @__PURE__ */ React4.createElement(
|
|
197
226
|
MenuItem2,
|
|
198
227
|
{
|
|
228
|
+
disabled: isLoading || !user?.capabilities?.edit_pages,
|
|
199
229
|
onClick: async () => {
|
|
200
230
|
const { id } = await create();
|
|
201
231
|
closePopup();
|
|
@@ -293,13 +323,13 @@ import { __ as __15 } from "@wordpress/i18n";
|
|
|
293
323
|
// src/components/panel/posts-list/posts-collapsible-list.tsx
|
|
294
324
|
import * as React23 from "react";
|
|
295
325
|
import { PageTypeIcon as PageTypeIcon2 } from "@elementor/icons";
|
|
296
|
-
import { Skeleton, Box as Box4, List as List2 } from "@elementor/ui";
|
|
326
|
+
import { Skeleton, Box as Box4, List as List2, Button as Button4, CircularProgress as CircularProgress5 } from "@elementor/ui";
|
|
297
327
|
|
|
298
328
|
// src/hooks/use-posts.ts
|
|
299
|
-
import {
|
|
329
|
+
import { useInfiniteQuery } from "@elementor/query";
|
|
300
330
|
|
|
301
331
|
// src/api/post.ts
|
|
302
|
-
import
|
|
332
|
+
import apiFetch4 from "@wordpress/api-fetch";
|
|
303
333
|
import { __ as __3 } from "@wordpress/i18n";
|
|
304
334
|
var postTypesMap = {
|
|
305
335
|
page: {
|
|
@@ -310,21 +340,32 @@ var postTypesMap = {
|
|
|
310
340
|
rest_base: "pages"
|
|
311
341
|
}
|
|
312
342
|
};
|
|
313
|
-
var
|
|
343
|
+
var POST_PER_PAGE = 10;
|
|
344
|
+
var getRequest2 = async (postTypeSlug, page) => {
|
|
314
345
|
const baseUri = `/wp/v2/${postTypesMap[postTypeSlug].rest_base}`;
|
|
315
|
-
const keys = ["id", "type", "title", "link", "status"];
|
|
346
|
+
const keys = ["id", "type", "title", "link", "status", "user_can"];
|
|
316
347
|
const queryParams = new URLSearchParams({
|
|
317
348
|
status: "any",
|
|
318
|
-
per_page: "-1",
|
|
319
349
|
order: "asc",
|
|
350
|
+
page: page.toString(),
|
|
351
|
+
per_page: POST_PER_PAGE.toString(),
|
|
320
352
|
_fields: keys.join(",")
|
|
321
353
|
});
|
|
322
354
|
const uri = baseUri + "?" + queryParams.toString();
|
|
323
|
-
|
|
355
|
+
const result = await apiFetch4({ path: uri, parse: false });
|
|
356
|
+
const data = await result.json();
|
|
357
|
+
const totalPages = Number(result.headers.get("x-wp-totalpages"));
|
|
358
|
+
const totalPosts = Number(result.headers.get("x-wp-total"));
|
|
359
|
+
return {
|
|
360
|
+
data,
|
|
361
|
+
totalPages,
|
|
362
|
+
totalPosts,
|
|
363
|
+
currentPage: page
|
|
364
|
+
};
|
|
324
365
|
};
|
|
325
366
|
var createRequest = (postTypeSlug, newPost) => {
|
|
326
367
|
const path = `/wp/v2/${postTypesMap[postTypeSlug].rest_base}`;
|
|
327
|
-
return
|
|
368
|
+
return apiFetch4({
|
|
328
369
|
path,
|
|
329
370
|
method: "POST",
|
|
330
371
|
data: newPost
|
|
@@ -333,7 +374,7 @@ var createRequest = (postTypeSlug, newPost) => {
|
|
|
333
374
|
var updateRequest = (postTypeSlug, updatedPost) => {
|
|
334
375
|
const path = `/wp/v2/${postTypesMap[postTypeSlug].rest_base}`;
|
|
335
376
|
const { id, ...data } = updatedPost;
|
|
336
|
-
return
|
|
377
|
+
return apiFetch4({
|
|
337
378
|
path: `${path}/${id}`,
|
|
338
379
|
method: "POST",
|
|
339
380
|
data
|
|
@@ -341,14 +382,14 @@ var updateRequest = (postTypeSlug, updatedPost) => {
|
|
|
341
382
|
};
|
|
342
383
|
var deleteRequest = (postTypeSlug, postId) => {
|
|
343
384
|
const path = `/wp/v2/${postTypesMap[postTypeSlug].rest_base}`;
|
|
344
|
-
return
|
|
385
|
+
return apiFetch4({
|
|
345
386
|
path: `${path}/${postId}`,
|
|
346
387
|
method: "DELETE"
|
|
347
388
|
});
|
|
348
389
|
};
|
|
349
390
|
var duplicateRequest = (originalPost) => {
|
|
350
391
|
const path = `/elementor/v1/site-navigation/duplicate-post`;
|
|
351
|
-
return
|
|
392
|
+
return apiFetch4({
|
|
352
393
|
path,
|
|
353
394
|
method: "POST",
|
|
354
395
|
data: {
|
|
@@ -360,11 +401,26 @@ var duplicateRequest = (originalPost) => {
|
|
|
360
401
|
|
|
361
402
|
// src/hooks/use-posts.ts
|
|
362
403
|
var postsQueryKey = (postTypeSlug) => ["site-navigation", "posts", postTypeSlug];
|
|
404
|
+
var flattenData = (data) => {
|
|
405
|
+
if (!data) {
|
|
406
|
+
return data;
|
|
407
|
+
}
|
|
408
|
+
const flattened = [];
|
|
409
|
+
data.pages.forEach((page) => {
|
|
410
|
+
flattened.push(...page.data);
|
|
411
|
+
});
|
|
412
|
+
return flattened;
|
|
413
|
+
};
|
|
363
414
|
function usePosts(postTypeSlug) {
|
|
364
|
-
|
|
415
|
+
const query = useInfiniteQuery({
|
|
365
416
|
queryKey: postsQueryKey(postTypeSlug),
|
|
366
|
-
queryFn: () => getRequest2(postTypeSlug)
|
|
417
|
+
queryFn: ({ pageParam = 1 }) => getRequest2(postTypeSlug, pageParam),
|
|
418
|
+
initialPageParam: 1,
|
|
419
|
+
getNextPageParam: (lastPage) => {
|
|
420
|
+
return lastPage.currentPage < lastPage.totalPages ? lastPage.currentPage + 1 : void 0;
|
|
421
|
+
}
|
|
367
422
|
});
|
|
423
|
+
return { ...query, data: { posts: flattenData(query.data), total: query.data?.pages[0]?.totalPosts ?? 0 } };
|
|
368
424
|
}
|
|
369
425
|
|
|
370
426
|
// src/contexts/post-list-context.tsx
|
|
@@ -455,7 +511,7 @@ function CollapsibleList({
|
|
|
455
511
|
unmountOnExit: true
|
|
456
512
|
},
|
|
457
513
|
/* @__PURE__ */ React7.createElement(List, { dense: true }, children)
|
|
458
|
-
), /* @__PURE__ */ React7.createElement(Divider2, { sx: {
|
|
514
|
+
), /* @__PURE__ */ React7.createElement(Divider2, { sx: { mt: 1 } }));
|
|
459
515
|
}
|
|
460
516
|
|
|
461
517
|
// src/components/panel/posts-list/post-list-item.tsx
|
|
@@ -705,6 +761,8 @@ import {
|
|
|
705
761
|
ListItemButton,
|
|
706
762
|
ListItemText as ListItemText7,
|
|
707
763
|
Menu as Menu2,
|
|
764
|
+
Tooltip as Tooltip2,
|
|
765
|
+
Typography as Typography3,
|
|
708
766
|
usePopupState as usePopupState2
|
|
709
767
|
} from "@elementor/ui";
|
|
710
768
|
import { DotsVerticalIcon, HomeIcon as HomeIcon2 } from "@elementor/icons";
|
|
@@ -782,6 +840,7 @@ function Rename({ post }) {
|
|
|
782
840
|
title: __7("Rename", "elementor"),
|
|
783
841
|
icon: EraseIcon,
|
|
784
842
|
MenuItemProps: {
|
|
843
|
+
disabled: !post.user_can.edit,
|
|
785
844
|
onClick: () => {
|
|
786
845
|
setEditMode({
|
|
787
846
|
mode: "rename",
|
|
@@ -801,6 +860,7 @@ import { CopyIcon } from "@elementor/icons";
|
|
|
801
860
|
import { __ as __8 } from "@wordpress/i18n";
|
|
802
861
|
function Duplicate({ post, popupState }) {
|
|
803
862
|
const { setEditMode } = usePostListContext();
|
|
863
|
+
const { data: user } = useUser();
|
|
804
864
|
const onClick = () => {
|
|
805
865
|
popupState.close();
|
|
806
866
|
setEditMode({
|
|
@@ -811,12 +871,14 @@ function Duplicate({ post, popupState }) {
|
|
|
811
871
|
}
|
|
812
872
|
});
|
|
813
873
|
};
|
|
874
|
+
const isDisabled = !user?.capabilities?.edit_pages;
|
|
814
875
|
return /* @__PURE__ */ React15.createElement(
|
|
815
876
|
ActionMenuItem,
|
|
816
877
|
{
|
|
817
878
|
title: __8("Duplicate", "elementor"),
|
|
818
879
|
icon: CopyIcon,
|
|
819
880
|
MenuItemProps: {
|
|
881
|
+
disabled: isDisabled,
|
|
820
882
|
onClick
|
|
821
883
|
}
|
|
822
884
|
}
|
|
@@ -843,13 +905,15 @@ function Delete({ post }) {
|
|
|
843
905
|
const [isDialogOpen, setIsDialogOpen] = useState5(false);
|
|
844
906
|
const activeDocument = useActiveDocument3();
|
|
845
907
|
const isPostActive = activeDocument?.id === post.id;
|
|
908
|
+
const userCanDelete = post.user_can.delete;
|
|
909
|
+
const isDisabled = !userCanDelete || post.isHome || isPostActive;
|
|
846
910
|
return /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(
|
|
847
911
|
ActionMenuItem,
|
|
848
912
|
{
|
|
849
913
|
title: __9("Delete", "elementor"),
|
|
850
914
|
icon: TrashIcon,
|
|
851
915
|
MenuItemProps: {
|
|
852
|
-
disabled:
|
|
916
|
+
disabled: isDisabled,
|
|
853
917
|
onClick: () => setIsDialogOpen(true),
|
|
854
918
|
sx: { "&:hover": { color: "error.main" } }
|
|
855
919
|
}
|
|
@@ -917,18 +981,14 @@ import { __ as __11 } from "@wordpress/i18n";
|
|
|
917
981
|
import { useMutation as useMutation2, useQueryClient as useQueryClient2 } from "@elementor/query";
|
|
918
982
|
|
|
919
983
|
// src/api/settings.ts
|
|
920
|
-
import
|
|
984
|
+
import apiFetch5 from "@wordpress/api-fetch";
|
|
921
985
|
var getSettings = () => {
|
|
922
|
-
const baseUri = "/
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
_fields: keys.join(",")
|
|
926
|
-
});
|
|
927
|
-
const uri = baseUri + "?" + queryParams.toString();
|
|
928
|
-
return apiFetch4({ path: uri });
|
|
986
|
+
const baseUri = "/elementor/v1/site-navigation/homepage";
|
|
987
|
+
const uri = baseUri;
|
|
988
|
+
return apiFetch5({ path: uri });
|
|
929
989
|
};
|
|
930
990
|
var updateSettings = (settings) => {
|
|
931
|
-
return
|
|
991
|
+
return apiFetch5({
|
|
932
992
|
path: "/wp/v2/settings",
|
|
933
993
|
method: "POST",
|
|
934
994
|
data: settings
|
|
@@ -968,6 +1028,7 @@ import { CircularProgress as CircularProgress4 } from "@elementor/ui";
|
|
|
968
1028
|
function SetHome({ post, closeMenu }) {
|
|
969
1029
|
const { updateSettingsMutation } = useHomepageActions();
|
|
970
1030
|
const { setError } = usePostListContext();
|
|
1031
|
+
const { data: user } = useUser();
|
|
971
1032
|
const handleClick = async () => {
|
|
972
1033
|
try {
|
|
973
1034
|
await updateSettingsMutation.mutateAsync({ show_on_front: "page", page_on_front: post.id });
|
|
@@ -977,13 +1038,17 @@ function SetHome({ post, closeMenu }) {
|
|
|
977
1038
|
closeMenu();
|
|
978
1039
|
}
|
|
979
1040
|
};
|
|
1041
|
+
const canManageOptions = !!user?.capabilities?.manage_options;
|
|
1042
|
+
const isPostPublished = post.status === "publish";
|
|
1043
|
+
const isPostHomepage = !!post.isHome;
|
|
1044
|
+
const isDisabled = !canManageOptions || isPostHomepage || !isPostPublished || updateSettingsMutation.isPending;
|
|
980
1045
|
return /* @__PURE__ */ React18.createElement(
|
|
981
1046
|
ActionMenuItem,
|
|
982
1047
|
{
|
|
983
1048
|
title: __11("Set as homepage", "elementor"),
|
|
984
1049
|
icon: !updateSettingsMutation.isPending ? HomeIcon : CircularProgress4,
|
|
985
1050
|
MenuItemProps: {
|
|
986
|
-
disabled:
|
|
1051
|
+
disabled: isDisabled,
|
|
987
1052
|
onClick: handleClick
|
|
988
1053
|
}
|
|
989
1054
|
}
|
|
@@ -992,6 +1057,21 @@ function SetHome({ post, closeMenu }) {
|
|
|
992
1057
|
|
|
993
1058
|
// src/components/panel/posts-list/list-items/list-item-view.tsx
|
|
994
1059
|
import { __ as __12 } from "@wordpress/i18n";
|
|
1060
|
+
var DisabledPostTooltip = ({ children, isDisabled }) => {
|
|
1061
|
+
if (isDisabled) {
|
|
1062
|
+
const title = /* @__PURE__ */ React19.createElement(Typography3, { variant: "caption" }, "You cannot edit this page.", /* @__PURE__ */ React19.createElement("br", null), "To edit it directly, contact the site owner");
|
|
1063
|
+
return /* @__PURE__ */ React19.createElement(
|
|
1064
|
+
Tooltip2,
|
|
1065
|
+
{
|
|
1066
|
+
title,
|
|
1067
|
+
placement: "bottom",
|
|
1068
|
+
arrow: false
|
|
1069
|
+
},
|
|
1070
|
+
children
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
return /* @__PURE__ */ React19.createElement(React19.Fragment, null, children);
|
|
1074
|
+
};
|
|
995
1075
|
function ListItemView({ post }) {
|
|
996
1076
|
const activeDocument = useActiveDocument4();
|
|
997
1077
|
const navigateToDocument = useNavigateToDocument5();
|
|
@@ -1003,7 +1083,8 @@ function ListItemView({ post }) {
|
|
|
1003
1083
|
const isActive = activeDocument?.id === post.id;
|
|
1004
1084
|
const status = isActive ? activeDocument?.status.value : post.status;
|
|
1005
1085
|
const title = isActive ? activeDocument?.title : post.title.rendered;
|
|
1006
|
-
|
|
1086
|
+
const isDisabled = !post.user_can.edit;
|
|
1087
|
+
return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement(DisabledPostTooltip, { isDisabled }, /* @__PURE__ */ React19.createElement(
|
|
1007
1088
|
ListItem3,
|
|
1008
1089
|
{
|
|
1009
1090
|
disablePadding: true,
|
|
@@ -1021,6 +1102,7 @@ function ListItemView({ post }) {
|
|
|
1021
1102
|
ListItemButton,
|
|
1022
1103
|
{
|
|
1023
1104
|
selected: isActive,
|
|
1105
|
+
disabled: isDisabled,
|
|
1024
1106
|
onClick: () => {
|
|
1025
1107
|
if (!isActive) {
|
|
1026
1108
|
navigateToDocument(post.id);
|
|
@@ -1028,16 +1110,10 @@ function ListItemView({ post }) {
|
|
|
1028
1110
|
},
|
|
1029
1111
|
dense: true
|
|
1030
1112
|
},
|
|
1031
|
-
/* @__PURE__ */ React19.createElement(
|
|
1032
|
-
ListItemText7,
|
|
1033
|
-
{
|
|
1034
|
-
disableTypography: true
|
|
1035
|
-
},
|
|
1036
|
-
/* @__PURE__ */ React19.createElement(PageTitleAndStatus, { title, status })
|
|
1037
|
-
),
|
|
1113
|
+
/* @__PURE__ */ React19.createElement(ListItemText7, { disableTypography: true }, /* @__PURE__ */ React19.createElement(PageTitleAndStatus, { title, status })),
|
|
1038
1114
|
post.isHome && /* @__PURE__ */ React19.createElement(HomeIcon2, { titleAccess: __12("Homepage", "elementor"), color: "disabled" })
|
|
1039
1115
|
)
|
|
1040
|
-
), /* @__PURE__ */ React19.createElement(
|
|
1116
|
+
)), /* @__PURE__ */ React19.createElement(
|
|
1041
1117
|
Menu2,
|
|
1042
1118
|
{
|
|
1043
1119
|
PaperProps: { sx: { mt: 2, width: 200 } },
|
|
@@ -1078,11 +1154,13 @@ import { PlusIcon as PlusIcon2 } from "@elementor/icons";
|
|
|
1078
1154
|
import { __ as __13 } from "@wordpress/i18n";
|
|
1079
1155
|
function AddNewButton() {
|
|
1080
1156
|
const { setEditMode } = usePostListContext();
|
|
1157
|
+
const { data: user } = useUser();
|
|
1081
1158
|
return /* @__PURE__ */ React21.createElement(
|
|
1082
1159
|
Button3,
|
|
1083
1160
|
{
|
|
1084
1161
|
size: "small",
|
|
1085
1162
|
startIcon: /* @__PURE__ */ React21.createElement(PlusIcon2, null),
|
|
1163
|
+
disabled: !user?.capabilities?.edit_pages,
|
|
1086
1164
|
onClick: () => {
|
|
1087
1165
|
setEditMode({ mode: "create", details: {} });
|
|
1088
1166
|
},
|
|
@@ -1096,7 +1174,7 @@ function AddNewButton() {
|
|
|
1096
1174
|
|
|
1097
1175
|
// src/components/panel/posts-list/error-state.tsx
|
|
1098
1176
|
import { Error404TemplateIcon } from "@elementor/icons";
|
|
1099
|
-
import { Box as Box3, Link, Typography as
|
|
1177
|
+
import { Box as Box3, Link, Typography as Typography4 } from "@elementor/ui";
|
|
1100
1178
|
import { __ as __14 } from "@wordpress/i18n";
|
|
1101
1179
|
import * as React22 from "react";
|
|
1102
1180
|
function ErrorState() {
|
|
@@ -1113,14 +1191,14 @@ function ErrorState() {
|
|
|
1113
1191
|
justifyContent: "center",
|
|
1114
1192
|
alignItems: "center",
|
|
1115
1193
|
gap: "8px"
|
|
1116
|
-
} }, /* @__PURE__ */ React22.createElement(
|
|
1194
|
+
} }, /* @__PURE__ */ React22.createElement(Typography4, { variant: "body1", color: "text.primary" }, __14("We couldn\u2019t display your pages.", "elementor")), /* @__PURE__ */ React22.createElement(Box3, null, /* @__PURE__ */ React22.createElement(Typography4, { variant: "body2", color: "text.primary", sx: { textAlign: "center" } }, __14("It\u2019s probably a temporary issue.", "elementor")), /* @__PURE__ */ React22.createElement(Typography4, { variant: "body2", color: "text.primary", sx: { textAlign: "center" } }, __14("If the problem persists,", "elementor"), " ", /* @__PURE__ */ React22.createElement(Link, { target: "_blank", href: "https://go.elementor.com/wp-editor-support-open-ticket/" }, "Notify support")))));
|
|
1117
1195
|
}
|
|
1118
1196
|
|
|
1119
1197
|
// src/components/panel/posts-list/posts-collapsible-list.tsx
|
|
1120
1198
|
function PostsCollapsibleList({ isOpenByDefault = false }) {
|
|
1121
1199
|
const { type, editMode } = usePostListContext();
|
|
1122
|
-
const { data: posts, isLoading: postsLoading, isError: postsError } = usePosts(type);
|
|
1123
|
-
const { data:
|
|
1200
|
+
const { data: { posts, total }, isLoading: postsLoading, isError: postsError, fetchNextPage, hasNextPage, isFetchingNextPage } = usePosts(type);
|
|
1201
|
+
const { data: homepageId } = useHomepage();
|
|
1124
1202
|
if (postsError) {
|
|
1125
1203
|
return /* @__PURE__ */ React23.createElement(ErrorState, null);
|
|
1126
1204
|
}
|
|
@@ -1135,9 +1213,7 @@ function PostsCollapsibleList({ isOpenByDefault = false }) {
|
|
|
1135
1213
|
/* @__PURE__ */ React23.createElement(Skeleton, { sx: { my: 4 }, animation: "wave", variant: "rounded", width: "110px", height: "28px" })
|
|
1136
1214
|
), /* @__PURE__ */ React23.createElement(Box4, null, /* @__PURE__ */ React23.createElement(Skeleton, { sx: { my: 3 }, animation: "wave", variant: "rounded", width: "100%", height: "24px" }), /* @__PURE__ */ React23.createElement(Skeleton, { sx: { my: 3 }, animation: "wave", variant: "rounded", width: "70%", height: "24px" }), /* @__PURE__ */ React23.createElement(Skeleton, { sx: { my: 3 }, animation: "wave", variant: "rounded", width: "70%", height: "24px" }), /* @__PURE__ */ React23.createElement(Skeleton, { sx: { my: 3 }, animation: "wave", variant: "rounded", width: "70%", height: "24px" })));
|
|
1137
1215
|
}
|
|
1138
|
-
const label = `${postTypesMap[type].labels.plural_name} (${
|
|
1139
|
-
const isHomepageSet = homepageSettings?.show_on_front === "page" && !!homepageSettings?.page_on_front;
|
|
1140
|
-
const homepageId = isHomepageSet ? homepageSettings.page_on_front : null;
|
|
1216
|
+
const label = `${postTypesMap[type].labels.plural_name} (${total.toString()})`;
|
|
1141
1217
|
const mappedPosts = posts.map((post) => {
|
|
1142
1218
|
if (post.id === homepageId) {
|
|
1143
1219
|
return { ...post, isHome: true };
|
|
@@ -1175,12 +1251,16 @@ function PostsCollapsibleList({ isOpenByDefault = false }) {
|
|
|
1175
1251
|
sortedPosts.map((post) => {
|
|
1176
1252
|
return /* @__PURE__ */ React23.createElement(PostListItem2, { key: post.id, post });
|
|
1177
1253
|
}),
|
|
1178
|
-
["duplicate", "create"].includes(editMode.mode) && /* @__PURE__ */ React23.createElement(PostListItem2, null)
|
|
1254
|
+
["duplicate", "create"].includes(editMode.mode) && /* @__PURE__ */ React23.createElement(PostListItem2, null),
|
|
1255
|
+
hasNextPage && /* @__PURE__ */ React23.createElement(Box4, { sx: {
|
|
1256
|
+
display: "flex",
|
|
1257
|
+
justifyContent: "center"
|
|
1258
|
+
} }, /* @__PURE__ */ React23.createElement(Button4, { onClick: fetchNextPage, color: "secondary" }, isFetchingNextPage ? /* @__PURE__ */ React23.createElement(CircularProgress5, null) : "Load More"))
|
|
1179
1259
|
)));
|
|
1180
1260
|
}
|
|
1181
1261
|
|
|
1182
1262
|
// src/components/panel/error-snackbar.tsx
|
|
1183
|
-
import { Snackbar, Alert, Typography as
|
|
1263
|
+
import { Snackbar, Alert, Typography as Typography5 } from "@elementor/ui";
|
|
1184
1264
|
import * as React24 from "react";
|
|
1185
1265
|
var ErrorSnackbar = ({ open, onClose }) => {
|
|
1186
1266
|
return /* @__PURE__ */ React24.createElement(
|
|
@@ -1194,7 +1274,7 @@ var ErrorSnackbar = ({ open, onClose }) => {
|
|
|
1194
1274
|
}
|
|
1195
1275
|
},
|
|
1196
1276
|
/* @__PURE__ */ React24.createElement(Alert, { onClose, severity: "error", sx: { width: "100%" } }, /* @__PURE__ */ React24.createElement(
|
|
1197
|
-
|
|
1277
|
+
Typography5,
|
|
1198
1278
|
{
|
|
1199
1279
|
component: "span",
|
|
1200
1280
|
sx: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-site-navigation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@elementor/editor-app-bar": "^0.9.
|
|
35
|
+
"@elementor/editor-app-bar": "^0.9.8",
|
|
36
36
|
"@elementor/editor-documents": "^0.10.1",
|
|
37
|
-
"@elementor/editor-panels": "^0.4.
|
|
37
|
+
"@elementor/editor-panels": "^0.4.7",
|
|
38
38
|
"@elementor/editor-v1-adapters": "^0.6.0",
|
|
39
39
|
"@elementor/env": "^0.3.2",
|
|
40
40
|
"@elementor/icons": "^0.7.2",
|
|
41
|
-
"@elementor/query": "^0.
|
|
41
|
+
"@elementor/query": "^0.2.0",
|
|
42
42
|
"@elementor/ui": "^1.4.61",
|
|
43
43
|
"@wordpress/api-fetch": "^6.42.0",
|
|
44
44
|
"@wordpress/i18n": "^4.45.0",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"elementor": {
|
|
51
51
|
"type": "extension"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "780188b3bf19f2d02edabce30374f89b1fecbc07"
|
|
54
54
|
}
|
package/src/api/post.ts
CHANGED
|
@@ -24,21 +24,49 @@ export const postTypesMap = {
|
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
export const
|
|
27
|
+
export const POST_PER_PAGE = 10;
|
|
28
|
+
|
|
29
|
+
type WpPostsResponse = {
|
|
30
|
+
json: () => Promise<Post[]>,
|
|
31
|
+
headers: {
|
|
32
|
+
get: ( key: string ) => string | null,
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PostsResponse = {
|
|
37
|
+
data: Post[],
|
|
38
|
+
totalPages: number,
|
|
39
|
+
totalPosts: number,
|
|
40
|
+
currentPage: number,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const getRequest = async ( postTypeSlug: Slug, page: number ): Promise<PostsResponse> => {
|
|
28
44
|
const baseUri = `/wp/v2/${ postTypesMap[ postTypeSlug ].rest_base }`;
|
|
29
45
|
|
|
30
|
-
const keys: Array<keyof Post> = [ 'id', 'type', 'title', 'link', 'status' ];
|
|
46
|
+
const keys: Array<keyof Post> = [ 'id', 'type', 'title', 'link', 'status', 'user_can' ];
|
|
31
47
|
|
|
32
48
|
const queryParams = new URLSearchParams( {
|
|
33
49
|
status: 'any',
|
|
34
|
-
per_page: '-1',
|
|
35
50
|
order: 'asc',
|
|
51
|
+
page: page.toString(),
|
|
52
|
+
per_page: POST_PER_PAGE.toString(),
|
|
36
53
|
_fields: keys.join( ',' ),
|
|
37
54
|
} );
|
|
38
55
|
|
|
39
56
|
const uri = baseUri + '?' + queryParams.toString();
|
|
40
57
|
|
|
41
|
-
|
|
58
|
+
const result = await apiFetch<WpPostsResponse>( { path: uri, parse: false } );
|
|
59
|
+
const data = await result.json();
|
|
60
|
+
|
|
61
|
+
const totalPages = Number( result.headers.get( 'x-wp-totalpages' ) );
|
|
62
|
+
const totalPosts = Number( result.headers.get( 'x-wp-total' ) );
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
data,
|
|
66
|
+
totalPages,
|
|
67
|
+
totalPosts,
|
|
68
|
+
currentPage: page,
|
|
69
|
+
};
|
|
42
70
|
};
|
|
43
71
|
|
|
44
72
|
export const createRequest = ( postTypeSlug: Slug, newPost: NewPost ) => {
|
package/src/api/settings.ts
CHANGED
|
@@ -5,18 +5,14 @@ export type Settings = {
|
|
|
5
5
|
page_on_front: number,
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
const baseUri = '/wp/v2/settings';
|
|
10
|
-
|
|
11
|
-
const keys: Array<keyof Settings> = [ 'show_on_front', 'page_on_front' ];
|
|
8
|
+
type Homepage = number;
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} );
|
|
10
|
+
export const getSettings = () => {
|
|
11
|
+
const baseUri = '/elementor/v1/site-navigation/homepage';
|
|
16
12
|
|
|
17
|
-
const uri = baseUri
|
|
13
|
+
const uri = baseUri;
|
|
18
14
|
|
|
19
|
-
return apiFetch<
|
|
15
|
+
return apiFetch<Homepage>( { path: uri } );
|
|
20
16
|
};
|
|
21
17
|
|
|
22
18
|
export const updateSettings = ( settings: Settings ) => {
|
package/src/api/user.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
2
|
+
|
|
3
|
+
export type User = {
|
|
4
|
+
capabilities: Record<string, boolean>,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const getUser = () => {
|
|
8
|
+
const baseUri = '/wp/v2/users/me';
|
|
9
|
+
|
|
10
|
+
const keys: Array<keyof User> = [ 'capabilities' ];
|
|
11
|
+
|
|
12
|
+
const queryParams = new URLSearchParams( {
|
|
13
|
+
_fields: keys.join( ',' ),
|
|
14
|
+
context: 'edit',
|
|
15
|
+
} );
|
|
16
|
+
|
|
17
|
+
const uri = baseUri + '?' + queryParams.toString();
|
|
18
|
+
|
|
19
|
+
return apiFetch<User>( { path: uri } );
|
|
20
|
+
};
|
|
@@ -33,6 +33,10 @@ describe( '@elementor/editor-site-navigation/pages-panel-actions - Delete', () =
|
|
|
33
33
|
type: 'page',
|
|
34
34
|
link: 'https://example.local/test-page',
|
|
35
35
|
isHome: false,
|
|
36
|
+
user_can: {
|
|
37
|
+
edit: true,
|
|
38
|
+
delete: true,
|
|
39
|
+
},
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
// Act.
|
|
@@ -70,6 +74,10 @@ describe( '@elementor/editor-site-navigation/pages-panel-actions - Delete', () =
|
|
|
70
74
|
type: 'page',
|
|
71
75
|
link: 'https://example.local/test-page',
|
|
72
76
|
isHome: true,
|
|
77
|
+
user_can: {
|
|
78
|
+
edit: true,
|
|
79
|
+
delete: true,
|
|
80
|
+
},
|
|
73
81
|
};
|
|
74
82
|
|
|
75
83
|
// Act.
|
|
@@ -2,6 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
|
|
2
2
|
import SetHome from '../set-home';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { Post } from '../../../../../types';
|
|
5
|
+
import useUser from '../../../../../hooks/use-user';
|
|
5
6
|
|
|
6
7
|
const mockMutateAsync = jest.fn();
|
|
7
8
|
jest.mock( '../../../../../hooks/use-homepage-actions', () => ( {
|
|
@@ -14,6 +15,15 @@ jest.mock( '../../../../../hooks/use-homepage-actions', () => ( {
|
|
|
14
15
|
} ) ),
|
|
15
16
|
} ) );
|
|
16
17
|
|
|
18
|
+
jest.mock( '../../../../../hooks/use-user', () => (
|
|
19
|
+
{
|
|
20
|
+
default: jest.fn( () => ( { isLoading: false, data: { capabilities: {
|
|
21
|
+
manage_options: true,
|
|
22
|
+
} } } ) ),
|
|
23
|
+
__esModule: true,
|
|
24
|
+
}
|
|
25
|
+
) );
|
|
26
|
+
|
|
17
27
|
describe( '@elementor/editor-site-navigation - SetHome', () => {
|
|
18
28
|
afterAll( () => {
|
|
19
29
|
jest.clearAllMocks();
|
|
@@ -30,6 +40,10 @@ describe( '@elementor/editor-site-navigation - SetHome', () => {
|
|
|
30
40
|
type: 'page',
|
|
31
41
|
link: 'https://example.local/test-page',
|
|
32
42
|
isHome: false,
|
|
43
|
+
user_can: {
|
|
44
|
+
edit: true,
|
|
45
|
+
delete: true,
|
|
46
|
+
},
|
|
33
47
|
};
|
|
34
48
|
|
|
35
49
|
// Act.
|
|
@@ -55,6 +69,44 @@ describe( '@elementor/editor-site-navigation - SetHome', () => {
|
|
|
55
69
|
type: 'page',
|
|
56
70
|
link: 'https://example.local/test-page',
|
|
57
71
|
isHome: false,
|
|
72
|
+
user_can: {
|
|
73
|
+
edit: true,
|
|
74
|
+
delete: true,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Act.
|
|
79
|
+
render( <SetHome post={ post } closeMenu={ () => {} } /> );
|
|
80
|
+
|
|
81
|
+
// Assert.
|
|
82
|
+
const button = screen.getByRole( 'menuitem' );
|
|
83
|
+
expect( button ).toHaveAttribute( 'aria-disabled', 'true' );
|
|
84
|
+
} );
|
|
85
|
+
|
|
86
|
+
it( 'should render Set as homepage disabled when the user cant manage options', () => {
|
|
87
|
+
// Arrange.
|
|
88
|
+
jest.mocked( useUser ).mockReturnValue( {
|
|
89
|
+
isLoading: false,
|
|
90
|
+
data: {
|
|
91
|
+
capabilities: {
|
|
92
|
+
manage_options: false,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
} as unknown as ReturnType<typeof useUser> );
|
|
96
|
+
|
|
97
|
+
const post: Post = {
|
|
98
|
+
id: 1,
|
|
99
|
+
title: {
|
|
100
|
+
rendered: 'Test Page',
|
|
101
|
+
},
|
|
102
|
+
status: 'publish',
|
|
103
|
+
type: 'page',
|
|
104
|
+
link: 'https://example.local/test-page',
|
|
105
|
+
isHome: false,
|
|
106
|
+
user_can: {
|
|
107
|
+
edit: true,
|
|
108
|
+
delete: true,
|
|
109
|
+
},
|
|
58
110
|
};
|
|
59
111
|
|
|
60
112
|
// Act.
|
|
@@ -23,6 +23,9 @@ export default function Delete( { post }: { post: Post } ) {
|
|
|
23
23
|
const activeDocument = useActiveDocument();
|
|
24
24
|
|
|
25
25
|
const isPostActive = activeDocument?.id === post.id;
|
|
26
|
+
const userCanDelete = post.user_can.delete;
|
|
27
|
+
|
|
28
|
+
const isDisabled = ( ! userCanDelete ) || post.isHome || isPostActive;
|
|
26
29
|
|
|
27
30
|
return (
|
|
28
31
|
<>
|
|
@@ -31,7 +34,7 @@ export default function Delete( { post }: { post: Post } ) {
|
|
|
31
34
|
icon={ TrashIcon }
|
|
32
35
|
MenuItemProps={
|
|
33
36
|
{
|
|
34
|
-
disabled:
|
|
37
|
+
disabled: isDisabled,
|
|
35
38
|
onClick: () => setIsDialogOpen( true ),
|
|
36
39
|
sx: { '&:hover': { color: 'error.main' } },
|
|
37
40
|
}
|