@griddo/ax 11.10.41 → 11.10.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/config/griddo-config/index.js +1 -0
  2. package/package.json +2 -2
  3. package/src/components/BulkSelectionOptions/index.tsx +78 -28
  4. package/src/components/BulkSelectionOptions/style.tsx +7 -1
  5. package/src/components/BulkSelectionOptions/utils.tsx +25 -0
  6. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +2 -2
  7. package/src/components/ConfigPanel/GlobalPageForm/index.tsx +14 -4
  8. package/src/components/ConfigPanel/index.tsx +6 -0
  9. package/src/components/Fields/ComponentArray/SameComponentArray/index.tsx +9 -9
  10. package/src/components/Fields/ReferenceField/ItemList/Item/index.tsx +5 -6
  11. package/src/components/Fields/ReferenceField/ItemList/Item/style.tsx +16 -10
  12. package/src/components/Fields/ReferenceField/ManualPanel/Item/index.tsx +3 -4
  13. package/src/components/Fields/ReferenceField/ManualPanel/Item/style.tsx +7 -1
  14. package/src/components/Fields/VisualUniqueSelection/ScrollableSelection/index.tsx +3 -3
  15. package/src/components/Fields/VisualUniqueSelection/index.tsx +2 -4
  16. package/src/components/FloatingMenu/index.tsx +5 -5
  17. package/src/components/Loader/components/SmallCircle.js +29 -0
  18. package/src/components/Loader/index.tsx +3 -2
  19. package/src/components/MainWrapper/AppBar/atoms.tsx +4 -6
  20. package/src/components/MainWrapper/AppBar/index.tsx +14 -12
  21. package/src/components/MainWrapper/AppBar/style.tsx +23 -18
  22. package/src/components/MainWrapper/index.tsx +3 -3
  23. package/src/components/OcassionalToast/index.tsx +1 -1
  24. package/src/components/OcassionalToast/style.tsx +6 -0
  25. package/src/components/PageFinder/SelectionListItem/index.tsx +5 -4
  26. package/src/components/PageFinder/SelectionListItem/style.tsx +7 -1
  27. package/src/components/PageFinder/index.tsx +1 -0
  28. package/src/constants/index.ts +27 -0
  29. package/src/forms/elements.tsx +8 -24
  30. package/src/forms/index.tsx +0 -2
  31. package/src/helpers/containerEvaluations.tsx +5 -0
  32. package/src/helpers/content.ts +35 -0
  33. package/src/helpers/index.tsx +91 -89
  34. package/src/helpers/strings.tsx +19 -0
  35. package/src/helpers/types.ts +1 -0
  36. package/src/hooks/forms.tsx +2 -1
  37. package/src/locales/en-US.ts +29 -0
  38. package/src/locales/es-ES.ts +29 -0
  39. package/src/locales/index.ts +11 -0
  40. package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +5 -0
  41. package/src/modules/Categories/CategoriesList/index.tsx +10 -4
  42. package/src/modules/Content/BulkHeader/index.tsx +11 -0
  43. package/src/modules/Content/PageItem/index.tsx +1 -0
  44. package/src/modules/Content/atoms.tsx +1 -1
  45. package/src/modules/Content/index.tsx +27 -2
  46. package/src/modules/FileDrive/BulkGridHeader/index.tsx +6 -1
  47. package/src/modules/FileDrive/BulkListHeader/index.tsx +5 -0
  48. package/src/modules/FileDrive/index.tsx +10 -0
  49. package/src/modules/Forms/FormCategoriesList/BulkHeader/index.tsx +15 -2
  50. package/src/modules/Forms/FormCategoriesList/index.tsx +8 -0
  51. package/src/modules/Forms/FormEditor/index.tsx +10 -1
  52. package/src/modules/Forms/FormList/BulkHeader/index.tsx +5 -0
  53. package/src/modules/Forms/FormList/index.tsx +12 -3
  54. package/src/modules/Forms/FormUseModal/FormUseItem/index.tsx +1 -2
  55. package/src/modules/Forms/FormUseModal/index.tsx +3 -3
  56. package/src/modules/GlobalEditor/index.tsx +1 -0
  57. package/src/modules/MediaGallery/BulkGridHeader/index.tsx +6 -1
  58. package/src/modules/MediaGallery/BulkListHeader/index.tsx +15 -2
  59. package/src/modules/MediaGallery/index.tsx +47 -36
  60. package/src/modules/Navigation/Defaults/BulkHeader/index.tsx +5 -0
  61. package/src/modules/Navigation/Defaults/index.tsx +26 -23
  62. package/src/modules/PageEditor/Editor/index.tsx +6 -0
  63. package/src/modules/PageEditor/index.tsx +52 -0
  64. package/src/modules/Redirects/BulkHeader/index.tsx +6 -1
  65. package/src/modules/Redirects/index.tsx +7 -2
  66. package/src/modules/Settings/Integrations/BulkHeader/index.tsx +5 -0
  67. package/src/modules/Settings/Integrations/index.tsx +7 -1
  68. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +5 -0
  69. package/src/modules/Sites/SitesList/index.tsx +7 -2
  70. package/src/modules/StructuredData/Form/index.tsx +1 -0
  71. package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +5 -0
  72. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/atoms.tsx +3 -2
  73. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +17 -11
  74. package/src/modules/StructuredData/StructuredDataList/index.tsx +6 -0
  75. package/src/modules/Users/Roles/BulkHeader/index.tsx +5 -0
  76. package/src/modules/Users/Roles/index.tsx +6 -1
  77. package/src/modules/Users/UserList/BulkHeader/index.tsx +5 -0
  78. package/src/modules/Users/UserList/index.tsx +12 -7
  79. package/tsconfig.paths.json +4 -1
@@ -89,6 +89,7 @@ const griddoAxAliases = {
89
89
  "@ax/schemas": path.resolve(__dirname, "../../src/schemas"),
90
90
  "@ax/services": path.resolve(__dirname, "../../src/services"),
91
91
  "@ax/locales": path.resolve(__dirname, "../../src/locales"),
92
+ "@ax/constants": path.resolve(__dirname, "../../src/constants"),
92
93
  };
93
94
 
94
95
  const griddoComponentsLibAliases = getComponentsLibAliases();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "11.10.41",
4
+ "version": "11.10.42",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Diego M. Béjar <diego.bejar@secuoyas.com>",
@@ -217,5 +217,5 @@
217
217
  "publishConfig": {
218
218
  "access": "public"
219
219
  },
220
- "gitHead": "37d4927a4cb852f84d5f3db98dfb8dd629ba032e"
220
+ "gitHead": "9ae94989b7a0c987e39ebcaaa7f5398aae266c88"
221
221
  }
@@ -1,42 +1,90 @@
1
- import { Button, CheckField, ExportButton } from "@ax/components";
1
+ import { useEffect, useState } from "react";
2
+
3
+ import { Button, CheckField, ExportButton, Loader } from "@ax/components";
4
+ import type { ItemLabel } from "@ax/constants";
2
5
  import type { IBulkSelectedItems } from "@ax/hooks";
6
+ import { LOCALE, pluralize } from "@ax/locales";
3
7
  import type { IBulkAction } from "@ax/types";
4
8
 
9
+ import { getBulkLoadingText } from "./utils";
10
+
5
11
  import * as S from "./style";
6
12
 
7
13
  const BulkSelectionOptions = (props: IBulkSelectionProps) => {
8
- const { isScrolling, checkState, actions, selectItems, className, exportAction, selectedItems } = props;
14
+ const {
15
+ isScrolling,
16
+ checkState,
17
+ actions,
18
+ selectItems,
19
+ className,
20
+ exportAction,
21
+ selectedItems,
22
+ isLoading,
23
+ itemLabel = { one: "item", other: "items" },
24
+ } = props;
9
25
 
10
26
  const filteredActions = actions.filter((action) => !!action);
27
+ const [currentAction, setCurrentAction] = useState<string>("");
28
+
29
+ const currentActionText = getBulkLoadingText(currentAction);
30
+ const allSelectedItemsText = selectedItems?.all.length.toString();
31
+ const itemLabelText = pluralize(itemLabel, selectedItems?.all.length);
32
+ const pleaseWaitText = `${LOCALE.common.pleaseWait}...`;
33
+ const bulkProcessingText = `${currentActionText} ${allSelectedItemsText} ${itemLabelText}. ${pleaseWaitText}`;
34
+
35
+ useEffect(() => {
36
+ if (!isLoading) {
37
+ setCurrentAction("");
38
+ }
39
+ }, [isLoading]);
40
+
41
+ const handleActionClick = (action: () => void, actionText: string) => {
42
+ setCurrentAction(actionText);
43
+ action();
44
+ };
11
45
 
12
46
  return (
13
47
  <S.BulkHeader isScrolling={isScrolling} data-testid="bulk-header-wrapper" className={className}>
14
- <S.BulkActionsWrapper>
15
- <S.SelectAllCheckField>
16
- <CheckField
17
- key="selectAll"
18
- name="selectAll"
19
- value="selectAll"
20
- onChange={selectItems}
21
- checked={checkState.isAllSelected}
22
- indeterminate={checkState.indeterminate}
23
- disabled={false}
24
- error={false}
25
- />
26
- </S.SelectAllCheckField>
27
- <S.SelectedCount>{selectedItems?.all?.length} selected</S.SelectedCount>
28
- <S.BulkActions>
29
- {filteredActions.map(
30
- (item) =>
31
- item && (
32
- <Button key={item.text} type="button" buttonStyle="text" icon={item.icon} onClick={item.action}>
33
- {item.text}
34
- </Button>
35
- ),
36
- )}
37
- {exportAction && <ExportButton onClick={exportAction} />}
38
- </S.BulkActions>
39
- </S.BulkActionsWrapper>
48
+ {isLoading ? (
49
+ <>
50
+ <Loader name="smallCircle" size="16" />
51
+ <S.LoadingText>{bulkProcessingText}</S.LoadingText>
52
+ </>
53
+ ) : (
54
+ <S.BulkActionsWrapper>
55
+ <S.SelectAllCheckField>
56
+ <CheckField
57
+ key="selectAll"
58
+ name="selectAll"
59
+ value="selectAll"
60
+ onChange={selectItems}
61
+ checked={checkState.isAllSelected}
62
+ indeterminate={checkState.indeterminate}
63
+ disabled={isLoading || false}
64
+ error={false}
65
+ />
66
+ </S.SelectAllCheckField>
67
+ <S.SelectedCount>{selectedItems?.all?.length} selected</S.SelectedCount>
68
+ <S.BulkActions>
69
+ {filteredActions.map(
70
+ (item) =>
71
+ item && (
72
+ <Button
73
+ key={item.text}
74
+ type="button"
75
+ buttonStyle="text"
76
+ icon={item.icon}
77
+ onClick={() => handleActionClick(item.action, item.text)}
78
+ disabled={isLoading}
79
+ >
80
+ {item.text}
81
+ </Button>
82
+ ),
83
+ )}
84
+ {exportAction && <ExportButton onClick={exportAction} />}
85
+ </S.BulkActions>
86
+ </S.BulkActionsWrapper>
87
+ )}
40
88
  </S.BulkHeader>
41
89
  );
42
90
  };
@@ -50,6 +98,8 @@ interface IBulkSelectionProps {
50
98
  className?: string;
51
99
  exportAction?: (formats: (string | number)[]) => void;
52
100
  selectedItems: IBulkSelectedItems;
101
+ isLoading: boolean;
102
+ itemLabel: ItemLabel;
53
103
  }
54
104
 
55
105
  export default BulkSelectionOptions;
@@ -43,4 +43,10 @@ const SelectedCount = styled.span`
43
43
  font-weight: ${(p) => p.theme.fontWeight.regular};
44
44
  `;
45
45
 
46
- export { BulkHeader, BulkActionsWrapper, BulkActions, SelectAllCheckField, Counter, SelectedCount };
46
+ const LoadingText = styled.span`
47
+ margin-left: ${({ theme }) => theme.spacing.xs};
48
+ color: ${({ theme }) => theme.color.interactive01};
49
+ ${({ theme }) => theme.textStyle.uiButton};
50
+ `;
51
+
52
+ export { BulkActions, BulkActionsWrapper, BulkHeader, Counter, LoadingText, SelectAllCheckField, SelectedCount };
@@ -0,0 +1,25 @@
1
+ import { LOCALE } from "@ax/locales";
2
+
3
+ // TODO: Tipar las acciones para que no sean cualquier string...
4
+ // export type BulkActions = "publish" | "unpublish" | "delete" | "processing" | "Move to" | "Deativate";
5
+
6
+ /**
7
+ * Returns the appropriate loading text for bulk actions based on the current action and loading state.
8
+ *
9
+ * @param currentAction - The current action being performed ("publish", "unpublish", "delete", etc.).
10
+ * @returns The localized loading message corresponding to the current bulk action.
11
+ */
12
+ export const getBulkLoadingText = (currentAction: string) => {
13
+ const options = {
14
+ publish: LOCALE.common.publishing,
15
+ unpublish: LOCALE.common.unpublishing,
16
+ delete: LOCALE.common.deleting,
17
+ processing: LOCALE.common.processing,
18
+ download: LOCALE.common.downloading,
19
+ "Move to": LOCALE.common.moving,
20
+ Deactivate: LOCALE.common.deactivating,
21
+ Activate: LOCALE.common.activating,
22
+ };
23
+
24
+ return options[currentAction as keyof typeof options] || options.processing;
25
+ };
@@ -60,7 +60,6 @@ const PageConnectedField = (props: any): JSX.Element => {
60
60
  const overrideableFields = ["header", "footer"];
61
61
  const isOverride = selectedContent.type && overrideableFields.includes(selectedContent.type);
62
62
  const isPageHome = componentType === "page" && selectedContent.isHome;
63
- const allowModifySlug = !selectedContent.id && !selectedContent.isHome;
64
63
  const isNewPage = componentType === "page" && !selectedContent.id;
65
64
  const error = errors.find((err: any) => err.editorID === selectedEditorID && err.key === objKey);
66
65
  const isFormPage = selectedContent.component === "FormPage";
@@ -141,7 +140,8 @@ const PageConnectedField = (props: any): JSX.Element => {
141
140
  if (isPageTitle) {
142
141
  setCurrentPageName(value);
143
142
 
144
- if (allowModifySlug) {
143
+ const canModifySlug = !selectedContent.id && !selectedContent.isHome;
144
+ if (canModifySlug) {
145
145
  updateEditorContent(selectedEditorID, "slug", slugify(value));
146
146
  }
147
147
 
@@ -11,7 +11,7 @@ const noteTitle = "Global content";
11
11
  const errorText = "You don't have the permissions to edit the original content.";
12
12
 
13
13
  const GlobalPageForm = (props: IGlobalPageFormProps): JSX.Element => {
14
- const { selectedTab, setSelectedTab, schema, pageTitle, setHistoryPush, actions, header, footer } = props;
14
+ const { selectedTab, setSelectedTab, schema, pageTitle, setHistoryPush, actions, header, footer, isDirty, onNavigateWithDirty } = props;
15
15
  const tabs = ["content", "config"];
16
16
 
17
17
  const { themes } = useThemes();
@@ -24,9 +24,17 @@ const GlobalPageForm = (props: IGlobalPageFormProps): JSX.Element => {
24
24
 
25
25
  const handleClick = async () => {
26
26
  if (isAllowedToEditGlobalData) {
27
- await handleGetGlobalPage();
28
- const path = "/data/pages/editor";
29
- setHistoryPush?.(path, true);
27
+ const navigate = async () => {
28
+ await handleGetGlobalPage();
29
+ const path = "/data/pages/editor";
30
+ setHistoryPush?.(path, true);
31
+ };
32
+
33
+ if (isDirty && onNavigateWithDirty) {
34
+ onNavigateWithDirty(navigate);
35
+ } else {
36
+ await navigate();
37
+ }
30
38
  } else {
31
39
  actions.setNotificationAction({
32
40
  type: "error",
@@ -176,6 +184,8 @@ export interface IGlobalPageFormProps {
176
184
  setHistoryPush?: (path: string, isEditor: boolean) => void;
177
185
  header?: number | null;
178
186
  footer?: number | null;
187
+ isDirty?: boolean;
188
+ onNavigateWithDirty?: (navigateCallback: () => void) => void;
179
189
  actions: {
180
190
  getGlobalFromLocalPageAction(): Promise<void>;
181
191
  saveCurrentSiteInfoAction(): Promise<void>;
@@ -37,6 +37,8 @@ const ConfigPanel = (props: IStateProps): JSX.Element => {
37
37
  footer,
38
38
  isEditLive,
39
39
  isDisabled = false,
40
+ isDirty,
41
+ onNavigateWithDirty,
40
42
  } = props;
41
43
 
42
44
  const wrapperRef = useRef<HTMLDivElement>(null);
@@ -74,6 +76,8 @@ const ConfigPanel = (props: IStateProps): JSX.Element => {
74
76
  actions={actions}
75
77
  header={header}
76
78
  footer={footer}
79
+ isDirty={isDirty}
80
+ onNavigateWithDirty={onNavigateWithDirty}
77
81
  />
78
82
  );
79
83
  } else {
@@ -139,6 +143,8 @@ export interface IStateProps {
139
143
  footer?: number | null;
140
144
  isEditLive?: boolean;
141
145
  isDisabled?: boolean;
146
+ isDirty?: boolean;
147
+ onNavigateWithDirty?: (navigateCallback: () => void) => void;
142
148
  }
143
149
 
144
150
  export default ConfigPanel;
@@ -6,6 +6,7 @@ import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"
6
6
  import type { IComponent, IModule, INotification, ISchemaField, ITemplate, ModuleCategoryInfo } from "@ax/types";
7
7
  import { ComponentContainer, Toast } from "@ax/components";
8
8
  import { useBulkSelection, useToast } from "@ax/hooks";
9
+ import { replaceElements } from "@ax/forms";
9
10
 
10
11
  import AddItemButton from "./AddItemButton";
11
12
  import { getComponentProps, getModulesToPaste, getTypefromKey } from "../helpers";
@@ -40,6 +41,10 @@ const SameComponentArray = (props: ISameComponentArrayProps): JSX.Element => {
40
41
  const type = getTypefromKey(objKey);
41
42
  const { contentType = type } = field;
42
43
  const componentIDs: number[] = value?.length ? value.map((element) => element.editorID) : [];
44
+ const componentType = field.reference ? selectedContent[field.reference] : selectedContent.kind;
45
+ const isModuleArr = contentType === "modules";
46
+ const isComponentModule = contentType === "components";
47
+ const showAddItemButton = !maxItems || (value && value.length < maxItems);
43
48
 
44
49
  const {
45
50
  addModuleAction,
@@ -57,7 +62,6 @@ const SameComponentArray = (props: ISameComponentArrayProps): JSX.Element => {
57
62
  useBulkSelection(componentIDs);
58
63
  const { isVisible, toggleToast, setIsVisible, state: toastState } = useToast();
59
64
 
60
-
61
65
  const sensors = useSensors(
62
66
  useSensor(PointerSensor, {
63
67
  activationConstraint: {
@@ -80,17 +84,13 @@ const SameComponentArray = (props: ISameComponentArrayProps): JSX.Element => {
80
84
  field,
81
85
  );
82
86
 
87
+ const replacedElementsToPaste = replaceElements(modulesToPaste, componentType);
88
+ const showPasteModuleButton = showAddItemButton && replacedElementsToPaste.length > 0;
89
+
83
90
  const getText = (name: string, index: number) => {
84
91
  return value && value.length > 1 ? `#${index + 1} ${name}` : name;
85
92
  };
86
93
 
87
- const componentType = field.reference ? selectedContent[field.reference] : selectedContent.kind;
88
-
89
- const isModuleArr = contentType === "modules";
90
- const isComponentModule = contentType === "components";
91
- const showAddItemButton = !maxItems || (value && value.length < maxItems);
92
- const showPasteModuleButton = showAddItemButton && modulesToPaste.length > 0;
93
-
94
94
  const handleAddModule = (moduleType: string) => addModuleAction(moduleType, objKey, editorID, isComponentModule);
95
95
 
96
96
  const handleAddComponent = () => addComponentAction?.(componentType, objKey);
@@ -242,7 +242,7 @@ const SameComponentArray = (props: ISameComponentArrayProps): JSX.Element => {
242
242
  setNotification={setNotificationAction}
243
243
  setHistoryPush={setHistoryPush}
244
244
  arrayKey={objKey}
245
- modulesToPaste={modulesToPaste}
245
+ modulesToPaste={replacedElementsToPaste}
246
246
  slots={maxItems ? maxItems - value.length : null}
247
247
  />
248
248
  )}
@@ -1,10 +1,8 @@
1
- import React from "react";
2
- import { useSortable } from "@dnd-kit/sortable";
3
-
4
- import { IDataSource, IStructuredDataContent } from "@ax/types";
5
- import { getFormattedDateWithTimezone } from "@ax/helpers";
6
-
7
1
  import { Icon } from "@ax/components";
2
+ import { getFormattedDateWithTimezone, getSlugFromUrl } from "@ax/helpers";
3
+ import type { IDataSource, IStructuredDataContent } from "@ax/types";
4
+
5
+ import { useSortable } from "@dnd-kit/sortable";
8
6
 
9
7
  import * as S from "./style";
10
8
 
@@ -50,6 +48,7 @@ const Item = (props: IProps): JSX.Element => {
50
48
  <S.StyledActionMenu icon="more" options={menuOptions} size="s" tooltip="Actions" />
51
49
  </S.Header>
52
50
  <S.Title>{item.content.title}</S.Title>
51
+ <S.Slug>{getSlugFromUrl(item.relatedPage?.url)}</S.Slug>
53
52
  </S.TextWrapper>
54
53
  </S.Item>
55
54
  );
@@ -1,9 +1,8 @@
1
- import React from "react";
2
- import styled from "styled-components";
3
- import { CSS, Transform } from "@dnd-kit/utilities";
4
-
5
1
  import { ActionMenu } from "@ax/components";
6
2
 
3
+ import { CSS, type Transform } from "@dnd-kit/utilities";
4
+ import styled from "styled-components";
5
+
7
6
  const StyledActionMenu = styled((props) => <ActionMenu {...props} />)`
8
7
  width: ${(p) => p.theme.spacing.m};
9
8
  height: ${(p) => p.theme.spacing.m};
@@ -54,6 +53,7 @@ const Header = styled.div`
54
53
  margin-bottom: ${(p) => p.theme.spacing.xxs};
55
54
  `;
56
55
 
56
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: conflicts with js Date
57
57
  const Date = styled.div`
58
58
  ${(p) => p.theme.textStyle.uiXS};
59
59
  color: ${(p) => p.theme.color.textMediumEmphasis};
@@ -96,15 +96,21 @@ const IconHandleWrapper = styled.div`
96
96
  }
97
97
  `;
98
98
 
99
+ const Slug = styled.span`
100
+ ${({ theme }) => theme.textStyle.uiXS};
101
+ color: ${({ theme }) => theme.colors.textMediumEmphasis};
102
+ `;
103
+
99
104
  export {
100
- Item,
101
- TextWrapper,
102
- Header,
103
- Date,
104
- Type,
105
- Title,
106
105
  ButtonsWrapper,
106
+ Date,
107
107
  HandleWrapper,
108
+ Header,
108
109
  IconHandleWrapper,
110
+ Item,
111
+ Slug,
109
112
  StyledActionMenu,
113
+ TextWrapper,
114
+ Title,
115
+ Type,
110
116
  };
@@ -1,8 +1,6 @@
1
- import React from "react";
2
-
3
- import { IDataSource, IStructuredDataContent } from "@ax/types";
4
- import { getFormattedDateWithTimezone } from "@ax/helpers";
5
1
  import { CheckField, Tooltip } from "@ax/components";
2
+ import { getFormattedDateWithTimezone, getSlugFromUrl } from "@ax/helpers";
3
+ import type { IDataSource, IStructuredDataContent } from "@ax/types";
6
4
 
7
5
  import * as S from "./style";
8
6
 
@@ -24,6 +22,7 @@ const Item = (props: IProps): JSX.Element => {
24
22
  {item.modified && <S.Date>{getFormattedDateWithTimezone(item.modified, "d MMM Y")}</S.Date>}
25
23
  </S.Header>
26
24
  <S.Title>{item.content.title}</S.Title>
25
+ <S.Slug>{getSlugFromUrl(item.relatedPage?.url)}</S.Slug>
27
26
  </S.TextWrapper>
28
27
  </S.Item>
29
28
  </Tooltip>
@@ -1,5 +1,6 @@
1
1
  import styled from "styled-components";
2
2
 
3
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: conflicts with js Date
3
4
  const Date = styled.div`
4
5
  ${(p) => p.theme.textStyle.uiXS};
5
6
  `;
@@ -47,4 +48,9 @@ const Header = styled.div`
47
48
  margin-bottom: ${(p) => p.theme.spacing.xxs};
48
49
  `;
49
50
 
50
- export { Item, TextWrapper, Header, Date, Type, Title };
51
+ const Slug = styled.span`
52
+ ${({ theme }) => theme.textStyle.uiXS};
53
+ color: ${({ theme }) => theme.colors.textMediumEmphasis};
54
+ `;
55
+
56
+ export { Date, Header, Item, Slug, TextWrapper, Title, Type };
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useEffect } from "react";
1
+ import { useState, useRef, useEffect } from "react";
2
2
 
3
3
  import { IconAction, VisualOption } from "@ax/components";
4
4
  import { getOptions } from "../utils";
@@ -33,10 +33,10 @@ const ScrollableSelection = (props: IScrollableSelectionProps): JSX.Element => {
33
33
  }, [carouselIndex, carouselItemWidth, columns]);
34
34
 
35
35
  useEffect(() => {
36
- if (carouselRef && carouselRef.current) {
36
+ if (carouselRef?.current) {
37
37
  setCarouselItemWidth(Math.floor(carouselRef.current.offsetWidth / columns));
38
38
  }
39
- }, [carouselRef, columns]);
39
+ }, [columns]);
40
40
 
41
41
  return (
42
42
  <S.Wrapper data-testid="scrollableSelectionComponent">
@@ -1,7 +1,5 @@
1
- import React from "react";
2
-
3
- import ImageSelection, { IImageSelectionProps } from "./ImageSelection";
4
- import ScrollableSelection, { IScrollableSelectionProps } from "./ScrollableSelection";
1
+ import ImageSelection, { type IImageSelectionProps } from "./ImageSelection";
2
+ import ScrollableSelection, { type IScrollableSelectionProps } from "./ScrollableSelection";
5
3
 
6
4
  const VisualUniqueSelection = (props: IImageSelectionProps & IScrollableSelectionProps): JSX.Element => {
7
5
  const { elementUniqueSelection = false } = props;
@@ -1,4 +1,5 @@
1
- import React, { useEffect, useRef, useState } from "react";
1
+ import type React from "react";
2
+ import { useEffect, useRef, useState } from "react";
2
3
 
3
4
  import { useHandleClickOutside } from "@ax/hooks";
4
5
 
@@ -27,8 +28,7 @@ const FloatingMenu = (props: IFloatingProps): JSX.Element => {
27
28
  useEffect(() => {
28
29
  if (isOpen && menuOptionsRef?.current) {
29
30
  const bounding = menuOptionsRef.current.children[0].getBoundingClientRect();
30
- const boundingChild =
31
- menuOptionsRef.current.children[0] && menuOptionsRef.current.children[0].getBoundingClientRect();
31
+ const boundingChild = menuOptionsRef.current.children[0]?.getBoundingClientRect();
32
32
  if (bounding.bottom > (window.innerHeight || document.documentElement.clientHeight)) {
33
33
  menuOptionsRef.current.scrollIntoView({ block: "end", behavior: "smooth" });
34
34
  }
@@ -52,7 +52,7 @@ const FloatingMenu = (props: IFloatingProps): JSX.Element => {
52
52
 
53
53
  const closeWhenChecked = (e: React.MouseEvent<HTMLDivElement>) => {
54
54
  const target = e.target as HTMLInputElement;
55
- const isChecked = target && target.checked;
55
+ const isChecked = target?.checked;
56
56
  isChecked && setOpen(false);
57
57
  };
58
58
 
@@ -96,7 +96,7 @@ const FloatingMenu = (props: IFloatingProps): JSX.Element => {
96
96
  data-testid="floating-menu"
97
97
  >
98
98
  <S.ButtonWrapper ref={buttonRef} data-testid="floating-menu-button">
99
- <Button />
99
+ <Button disabled={disabled} />
100
100
  </S.ButtonWrapper>
101
101
 
102
102
  {isOpen && (
@@ -0,0 +1,29 @@
1
+ function SvgSmallCircle(props) {
2
+ return (
3
+ <svg viewBox="0 0 100 100" {...props}>
4
+ <title>spinning</title>
5
+ <circle
6
+ cx="50"
7
+ cy="50"
8
+ r="40"
9
+ fill="none"
10
+ stroke="#5057FF"
11
+ stroke-width="10"
12
+ stroke-linecap="round"
13
+ stroke-dasharray="200 300"
14
+ >
15
+ <animateTransform
16
+ attributeName="transform"
17
+ attributeType="XML"
18
+ type="rotate"
19
+ from="0 50 50"
20
+ to="360 50 50"
21
+ dur="0.75s"
22
+ repeatCount="indefinite"
23
+ />
24
+ </circle>
25
+ </svg>
26
+ );
27
+ }
28
+
29
+ export default SvgSmallCircle;
@@ -2,13 +2,14 @@ import React from "react";
2
2
 
3
3
  import Circle from "./components/Circle";
4
4
  import Dots from "./components/Dots";
5
+ import SmallCircle from "./components/SmallCircle";
5
6
 
6
- const components: Record<string, () => JSX.Element> = { Circle, Dots };
7
+ const components: Record<string, () => JSX.Element> = { Circle, Dots, SmallCircle };
7
8
 
8
9
  const getImage = (name: string) => {
9
10
  try {
10
11
  return components[name];
11
- } catch (err) {
12
+ } catch (_err) {
12
13
  return null;
13
14
  }
14
15
  };
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import { Button, Icon } from "@ax/components";
3
2
 
4
3
  import * as S from "./style";
@@ -16,8 +15,7 @@ const ActionMenuItem = (item: any, color?: boolean) => {
16
15
  const ActionMenuBtn = (props: any) => {
17
16
  const { menu } = props;
18
17
  return (
19
- menu &&
20
- menu.button && (
18
+ menu?.button && (
21
19
  <S.ButtonWrapper>
22
20
  <Button type="button" buttonStyle="line" onClick={menu.button.action} disabled={menu.button.disabled}>
23
21
  {menu.button.label}
@@ -34,13 +32,13 @@ const ActionMenu = (props: any) => {
34
32
  <S.ActionMenu>
35
33
  <S.ActionMenuTitle> More actions </S.ActionMenuTitle>
36
34
  <ActionMenuBtn menu={menu} />
37
- {menu && menu.options.map((item: any) => item && ActionMenuItem(item))}
35
+ {menu?.options.map((item: any) => item && ActionMenuItem(item))}
38
36
  </S.ActionMenu>
39
37
  );
40
38
  };
41
39
 
42
40
  const DownArrowButton = (props: any) => (
43
- <S.IconWrapper inversed={props.inversed} data-testid="down-arrow-button-wrapper">
41
+ <S.IconWrapper inversed={props.inversed} disabled={props.disabled} data-testid="down-arrow-button-wrapper">
44
42
  <Icon name="DownArrow" />
45
43
  </S.IconWrapper>
46
44
  );
@@ -57,4 +55,4 @@ const ActionSimpleMenu = (props: any) => {
57
55
  );
58
56
  };
59
57
 
60
- export { ActionMenu, DownArrowButton, ActionSimpleMenu };
58
+ export { ActionMenu, ActionSimpleMenu, DownArrowButton };
@@ -1,23 +1,23 @@
1
- import React, { useState } from "react";
2
- import { withRouter, RouteComponentProps } from "react-router-dom";
1
+ import { useState } from "react";
2
+ import { type RouteComponentProps, withRouter } from "react-router-dom";
3
3
 
4
- import { IErrorItem, ILanguage } from "@ax/types";
5
- import { getScheduleFormatDate, trimText } from "@ax/helpers";
6
- import { useModal } from "@ax/hooks";
7
4
  import {
8
5
  Button,
9
- Icon,
10
- IconAction,
6
+ ErrorCenter,
7
+ ExportButton,
11
8
  Flag,
12
9
  FloatingMenu,
10
+ Icon,
11
+ IconAction,
13
12
  LanguageMenu,
13
+ Modal,
14
+ SearchField,
14
15
  Tabs,
15
16
  Tooltip,
16
- ErrorCenter,
17
- SearchField,
18
- Modal,
19
- ExportButton,
20
17
  } from "@ax/components";
18
+ import { getScheduleFormatDate, trimText } from "@ax/helpers";
19
+ import { useModal } from "@ax/hooks";
20
+ import type { IErrorItem, ILanguage } from "@ax/types";
21
21
 
22
22
  import { ActionMenu, ActionSimpleMenu, DownArrowButton } from "./atoms";
23
23
 
@@ -53,6 +53,7 @@ const AppBar = (props: IProps): JSX.Element => {
53
53
  isDirty,
54
54
  exportAction,
55
55
  scheduledPublication,
56
+ isSaving,
56
57
  } = props;
57
58
 
58
59
  const publishedTooltip: Record<string, string> = {
@@ -275,7 +276,7 @@ const AppBar = (props: IProps): JSX.Element => {
275
276
  )}
276
277
  {downArrowMenu?.displayed && (downArrowMenu.button || downArrowMenu.options.length > 0) && (
277
278
  <Tooltip content="Actions" hideOnClick bottom>
278
- <FloatingMenu Button={DownArrowButton} isInAppBar={true}>
279
+ <FloatingMenu Button={DownArrowButton} isInAppBar={true} disabled={isSaving}>
279
280
  <ActionMenu menu={downArrowMenu} />
280
281
  </FloatingMenu>
281
282
  </Tooltip>
@@ -317,6 +318,7 @@ export interface IAppBarProps {
317
318
  isDirty?: boolean;
318
319
  exportAction?(formats: (number | string)[]): void;
319
320
  scheduledPublication?: string | null;
321
+ isSaving?: boolean;
320
322
  }
321
323
 
322
324
  type IProps = IAppBarProps & RouteComponentProps;