@griddo/ax 11.14.2-rc.0 → 11.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/jest/reactEasyCropMock.js +15 -0
- package/config/jest/reactTimezoneMock.js +13 -0
- package/package.json +221 -219
- package/public/img/welcome.svg +127 -0
- package/src/__tests__/components/Browser/Browser.test.tsx +27 -51
- package/src/__tests__/components/CategoryCell/CategoryCell.test.tsx +10 -5
- package/src/__tests__/components/ElementsTooltip/ElementsTooltip.test.tsx +27 -14
- package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +2 -0
- package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +138 -1
- package/src/__tests__/components/ImageDragAndDrop/CropStep/CropStep.test.tsx +84 -0
- package/src/__tests__/components/ImageDragAndDrop/ImageDragAndDrop.test.tsx +173 -0
- package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +3 -4
- package/src/__tests__/components/ProfileImage/ProfileImage.test.tsx +120 -0
- package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +8 -0
- package/src/__tests__/components/UserRolesAndSites/RoleItem/RoleItem.test.tsx +190 -0
- package/src/__tests__/components/UserRolesAndSites/UserRolesAndSites.test.tsx +471 -0
- package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +15 -2
- package/src/__tests__/modules/Sites/Sites.test.tsx +68 -224
- package/src/__tests__/modules/Sites/SitesList/ListView/BulkHeader/BulkHeader.test.tsx +21 -17
- package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +65 -565
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/DataStep/DataStep.test.tsx +109 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/FinalStep/FinalStep.test.tsx +157 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/CropView.test.tsx +51 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/ImageStep.test.tsx +70 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/UploadView.test.tsx +92 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/TimezoneStep/TimezoneStep.test.tsx +94 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/WelcomeModal.test.tsx +78 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/WelcomeStep/WelcomeStep.test.tsx +39 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/utils.test.ts +55 -0
- package/src/api/sites.tsx +4 -4
- package/src/components/Avatar/index.tsx +26 -5
- package/src/components/Avatar/style.tsx +20 -10
- package/src/components/Browser/index.tsx +7 -1
- package/src/components/ConfigPanel/index.tsx +11 -7
- package/src/components/ElementsTooltip/index.tsx +96 -34
- package/src/components/ElementsTooltip/style.tsx +12 -1
- package/src/components/Fields/FileField/index.tsx +16 -18
- package/src/components/Fields/HeadingField/index.tsx +1 -1
- package/src/components/Fields/ImageField/index.tsx +9 -38
- package/src/components/Fields/ImageField/style.tsx +12 -1
- package/src/components/Fields/ToggleField/index.tsx +1 -1
- package/src/components/Fields/Wysiwyg/index.tsx +25 -20
- package/src/components/FileGallery/GalleryPanel/index.tsx +15 -7
- package/src/components/FileGallery/index.tsx +33 -28
- package/src/components/Gallery/GalleryPanel/index.tsx +5 -16
- package/src/components/Gallery/index.tsx +0 -2
- package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +11 -2
- package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +21 -3
- package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +2 -2
- package/src/components/HeadingsPreviewModal/index.tsx +13 -3
- package/src/components/HeadingsPreviewModal/style.tsx +18 -0
- package/src/components/HeadingsPreviewModal/utils.tsx +31 -3
- package/src/components/Image/index.tsx +2 -2
- package/src/components/ImageDragAndDrop/CropStep/index.tsx +95 -0
- package/src/components/ImageDragAndDrop/CropStep/style.tsx +101 -0
- package/src/{modules/MediaGallery → components}/ImageDragAndDrop/index.tsx +103 -40
- package/src/{modules/MediaGallery → components}/ImageDragAndDrop/style.tsx +14 -2
- package/src/components/KeywordsPreviewModal/atoms.tsx +2 -2
- package/src/components/KeywordsPreviewModal/index.tsx +6 -6
- package/src/components/KeywordsPreviewModal/utils.tsx +2 -2
- package/src/components/ProfileImage/index.tsx +55 -0
- package/src/components/ProfileImage/style.tsx +58 -0
- package/src/components/ResizePanel/ResizeHandle/index.tsx +44 -6
- package/src/components/ResizePanel/ResizeHandle/style.tsx +7 -0
- package/src/components/ResizePanel/index.tsx +25 -4
- package/src/components/Tabs/style.tsx +1 -1
- package/src/components/Tag/index.tsx +0 -1
- package/src/components/UserRolesAndSites/RoleItem/index.tsx +42 -0
- package/src/components/UserRolesAndSites/RoleItem/style.tsx +29 -0
- package/src/components/UserRolesAndSites/index.tsx +102 -0
- package/src/components/UserRolesAndSites/style.tsx +67 -0
- package/src/components/index.tsx +6 -0
- package/src/constants/index.ts +13 -1
- package/src/containers/App/actions.tsx +8 -1
- package/src/containers/Sites/actions.tsx +26 -0
- package/src/containers/Sites/constants.tsx +1 -0
- package/src/containers/Sites/interfaces.tsx +6 -0
- package/src/containers/Sites/reducer.tsx +5 -1
- package/src/containers/Users/reducer.tsx +6 -5
- package/src/guards/routeLeaving/index.tsx +9 -11
- package/src/helpers/images.tsx +50 -3
- package/src/helpers/index.tsx +2 -1
- package/src/hooks/forms.tsx +45 -48
- package/src/hooks/index.tsx +2 -1
- package/src/hooks/modals.tsx +4 -3
- package/src/hooks/window.ts +50 -2
- package/src/modules/ActivityLog/ItemLogUser/UserItem/index.tsx +1 -1
- package/src/modules/App/Routing/Logout/index.tsx +3 -5
- package/src/modules/App/Routing/NavMenu/NavItem/index.tsx +73 -52
- package/src/modules/App/Routing/NavMenu/NavItem/style.tsx +21 -7
- package/src/modules/App/Routing/NavMenu/index.tsx +59 -54
- package/src/modules/App/Routing/NavMenu/style.tsx +13 -11
- package/src/modules/CreatePass/index.tsx +1 -1
- package/src/modules/FileDrive/FileDragAndDrop/index.tsx +11 -8
- package/src/modules/FileDrive/FileModal/index.tsx +8 -9
- package/src/modules/FileDrive/index.tsx +1 -18
- package/src/modules/Forms/FormEditor/index.tsx +1 -1
- package/src/modules/FramePreview/HeadingsOverlay/index.tsx +22 -11
- package/src/modules/FramePreview/HeadingsOverlay/style.tsx +1 -1
- package/src/modules/MediaGallery/ImageModal/index.tsx +1 -5
- package/src/modules/MediaGallery/index.tsx +1 -3
- package/src/modules/Settings/Globals/constants.tsx +942 -106
- package/src/modules/Sites/SitesList/AllSitesHeader/index.tsx +33 -0
- package/src/modules/Sites/SitesList/AllSitesHeader/style.tsx +35 -0
- package/src/modules/Sites/SitesList/GridView/GridHeaderFilter/index.tsx +5 -5
- package/src/modules/Sites/SitesList/GridView/GridSiteItem/index.tsx +23 -119
- package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/index.tsx +4 -4
- package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +4 -3
- package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +23 -120
- package/src/modules/Sites/SitesList/{RecentSiteItem → RecentSites/RecentSiteItem}/index.tsx +4 -5
- package/src/modules/Sites/SitesList/RecentSites/index.tsx +49 -0
- package/src/modules/Sites/SitesList/RecentSites/style.tsx +92 -0
- package/src/modules/Sites/SitesList/SiteModal/index.tsx +8 -7
- package/src/modules/Sites/SitesList/WelcomeModal/DataStep/index.tsx +72 -0
- package/src/modules/Sites/SitesList/WelcomeModal/DataStep/style.tsx +59 -0
- package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/constants.tsx +78 -0
- package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/index.tsx +78 -0
- package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/style.tsx +141 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/index.tsx +93 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/style.tsx +77 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/index.tsx +100 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/style.tsx +94 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/index.tsx +44 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/style.tsx +31 -0
- package/src/modules/Sites/SitesList/WelcomeModal/TimezoneStep/index.tsx +51 -0
- package/src/modules/Sites/SitesList/WelcomeModal/TimezoneStep/style.tsx +52 -0
- package/src/modules/Sites/SitesList/WelcomeModal/WelcomeStep/index.tsx +40 -0
- package/src/modules/Sites/SitesList/WelcomeModal/WelcomeStep/style.tsx +53 -0
- package/src/modules/Sites/SitesList/WelcomeModal/index.tsx +215 -0
- package/src/modules/Sites/SitesList/WelcomeModal/style.tsx +12 -0
- package/src/modules/Sites/SitesList/WelcomeModal/utils.ts +26 -0
- package/src/modules/Sites/SitesList/atoms.tsx +4 -4
- package/src/modules/Sites/SitesList/hooks.tsx +149 -16
- package/src/modules/Sites/SitesList/index.tsx +127 -125
- package/src/modules/Sites/SitesList/style.tsx +1 -117
- package/src/modules/Sites/SitesList/utils.tsx +9 -2
- package/src/modules/Sites/index.tsx +19 -8
- package/src/modules/Users/Profile/index.tsx +169 -31
- package/src/modules/Users/Profile/style.tsx +81 -1
- package/src/modules/Users/Roles/RoleItem/index.tsx +2 -2
- package/src/modules/Users/UserCreate/SiteItem/index.tsx +11 -14
- package/src/modules/Users/UserForm/atoms.tsx +3 -3
- package/src/modules/Users/UserForm/index.tsx +25 -29
- package/src/modules/Users/UserForm/style.tsx +15 -2
- package/src/modules/Users/UserList/UserItem/index.tsx +4 -4
- package/src/routes/index.tsx +1 -0
- package/src/types/index.tsx +2 -0
- /package/src/modules/Sites/SitesList/{RecentSiteItem → RecentSites/RecentSiteItem}/style.tsx +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getCompanyFromEmail, getMostFrequentRole } from "@ax/modules/Sites/SitesList/WelcomeModal/utils";
|
|
2
|
+
|
|
3
|
+
describe("getCompanyFromEmail", () => {
|
|
4
|
+
it("should extract company name from professional email", () => {
|
|
5
|
+
expect(getCompanyFromEmail("user@secuoyas.com")).toBe("Secuoyas");
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("should capitalize the first letter of the company name", () => {
|
|
9
|
+
expect(getCompanyFromEmail("user@acme.io")).toBe("Acme");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("should return empty string for generic email providers", () => {
|
|
13
|
+
expect(getCompanyFromEmail("user@gmail.com")).toBe("");
|
|
14
|
+
expect(getCompanyFromEmail("user@hotmail.com")).toBe("");
|
|
15
|
+
expect(getCompanyFromEmail("user@outlook.com")).toBe("");
|
|
16
|
+
expect(getCompanyFromEmail("user@yahoo.com")).toBe("");
|
|
17
|
+
expect(getCompanyFromEmail("user@icloud.com")).toBe("");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should return empty string when email has no domain", () => {
|
|
21
|
+
expect(getCompanyFromEmail("usernodomain")).toBe("");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return the full subdomain string before the TLD", () => {
|
|
25
|
+
expect(getCompanyFromEmail("user@mail.company.com")).toBe("Mail.company");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("getMostFrequentRole", () => {
|
|
30
|
+
it("should return null for empty userRoles", () => {
|
|
31
|
+
expect(getMostFrequentRole([])).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should return the ID of the most frequent role", () => {
|
|
35
|
+
const userRoles = [
|
|
36
|
+
{ siteId: 1, roles: [1, 2] },
|
|
37
|
+
{ siteId: 2, roles: [1] },
|
|
38
|
+
];
|
|
39
|
+
expect(getMostFrequentRole(userRoles)).toBe(1);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should handle single role occurrence", () => {
|
|
43
|
+
const userRoles = [{ siteId: 1, roles: [5] }];
|
|
44
|
+
expect(getMostFrequentRole(userRoles)).toBe(5);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should return correct role when multiple roles have different frequencies", () => {
|
|
48
|
+
const userRoles = [
|
|
49
|
+
{ siteId: 1, roles: [1, 2, 3] },
|
|
50
|
+
{ siteId: 2, roles: [1, 2] },
|
|
51
|
+
{ siteId: 3, roles: [1] },
|
|
52
|
+
];
|
|
53
|
+
expect(getMostFrequentRole(userRoles)).toBe(1);
|
|
54
|
+
});
|
|
55
|
+
});
|
package/src/api/sites.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AxiosResponse } from "axios";
|
|
2
|
-
import { IGetGlobalPagesParams, IGetSitePagesParams, IGetSitesParams } from "@ax/types";
|
|
1
|
+
import type { AxiosResponse } from "axios";
|
|
2
|
+
import type { IGetGlobalPagesParams, IGetSitePagesParams, IGetSitesParams } from "@ax/types";
|
|
3
3
|
import { template } from "./config";
|
|
4
|
-
import { sendRequest, IServiceConfig, sendInitialRequest } from "./utils";
|
|
4
|
+
import { sendRequest, type IServiceConfig, sendInitialRequest } from "./utils";
|
|
5
5
|
|
|
6
6
|
const SERVICES: { [key: string]: IServiceConfig } = {
|
|
7
7
|
GET_ALL_SITES: {
|
|
@@ -123,7 +123,7 @@ const SERVICES: { [key: string]: IServiceConfig } = {
|
|
|
123
123
|
|
|
124
124
|
const getAllSites = async (params: IGetSitesParams = { recentSitesNumber: 7 }) => {
|
|
125
125
|
const { host, endpoint } = SERVICES.GET_ALL_SITES;
|
|
126
|
-
const { token, language, recentSitesNumber, searchQuery, filterQuery, page, itemsPerPage, pagination } = params;
|
|
126
|
+
const { token, language, recentSitesNumber = 7, searchQuery, filterQuery, page, itemsPerPage, pagination } = params;
|
|
127
127
|
|
|
128
128
|
const queryParams =
|
|
129
129
|
language && recentSitesNumber
|
|
@@ -1,14 +1,32 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { getInitials } from "@ax/helpers";
|
|
3
2
|
|
|
4
3
|
import * as S from "./style";
|
|
5
4
|
|
|
6
|
-
const
|
|
7
|
-
|
|
5
|
+
const AVATAR_COLORS = ["#7C9CDE", "#E07C7C", "#7CC8A0", "#C97CCC", "#D4A76A", "#6AB8C8", "#C8A86A", "#8E7CC8"];
|
|
6
|
+
|
|
7
|
+
const getAvatarColor = (name?: string): string => {
|
|
8
|
+
if (!name) return AVATAR_COLORS[0];
|
|
9
|
+
let hash = 0;
|
|
10
|
+
const normalized = name.toLowerCase();
|
|
11
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
12
|
+
hash = ((hash << 5) - hash) + normalized.charCodeAt(i);
|
|
13
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
14
|
+
}
|
|
15
|
+
return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
|
|
16
|
+
};
|
|
8
17
|
|
|
18
|
+
const Avatar = (props: IAvatarProps): JSX.Element => {
|
|
19
|
+
const { image, name, size, fontSize, bold, background } = props;
|
|
9
20
|
return (
|
|
10
21
|
<S.AvatarWrapper data-testid="avatar-wrapper" size={size}>
|
|
11
|
-
<S.Avatar
|
|
22
|
+
<S.Avatar
|
|
23
|
+
image={image}
|
|
24
|
+
initials={getInitials(name)}
|
|
25
|
+
color={background ? getAvatarColor(name) : undefined}
|
|
26
|
+
fontSize={fontSize}
|
|
27
|
+
bold={bold}
|
|
28
|
+
data-testid="avatar"
|
|
29
|
+
/>
|
|
12
30
|
</S.AvatarWrapper>
|
|
13
31
|
);
|
|
14
32
|
};
|
|
@@ -16,7 +34,10 @@ const Avatar = (props: IAvatarProps): JSX.Element => {
|
|
|
16
34
|
export interface IAvatarProps {
|
|
17
35
|
image?: string | null;
|
|
18
36
|
name?: string;
|
|
19
|
-
size?:
|
|
37
|
+
size?: number;
|
|
38
|
+
fontSize?: number;
|
|
39
|
+
background?: boolean;
|
|
40
|
+
bold?: boolean;
|
|
20
41
|
}
|
|
21
42
|
|
|
22
43
|
export default Avatar;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
const Avatar = styled.span<{
|
|
4
|
+
image?: string | null;
|
|
5
|
+
initials: string;
|
|
6
|
+
color?: string;
|
|
7
|
+
fontSize?: number;
|
|
8
|
+
bold?: boolean;
|
|
9
|
+
}>`
|
|
9
10
|
border-radius: 50%;
|
|
10
|
-
|
|
11
|
-
height: ${(p) => (p.size === "s" ? " 24px" : "32px")};
|
|
12
|
-
background: ${(p) => `url(${p.image})`}, ${(p) => p.theme.colors.uiBackground03};
|
|
11
|
+
background: ${(p) => `url(${p.image})`}, ${(p) => (p.color ? p.color : p.theme.colors.uiBackground03)};
|
|
13
12
|
background-size: cover;
|
|
14
13
|
background-repeat: no-repeat;
|
|
15
14
|
background-position: center center;
|
|
@@ -19,8 +18,19 @@ const Avatar = styled.span<{ image?: string | null; initials: string; size?: str
|
|
|
19
18
|
|
|
20
19
|
&:after {
|
|
21
20
|
content: ${(p) => !p.image && JSON.stringify(p.initials)};
|
|
22
|
-
color: ${(p) => p.theme.colors.textMediumEmphasis};
|
|
21
|
+
color: ${(p) => (p.bold ? p.theme.colors.textHighEmphasis : p.theme.colors.textMediumEmphasis)};
|
|
23
22
|
${(p) => p.theme.textStyle.uiXS};
|
|
23
|
+
font-size: ${(p) => (p.fontSize ? `${p.fontSize}px` : "12px")};
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const AvatarWrapper = styled.div<{ size?: number }>`
|
|
28
|
+
width: ${(p) => (p.size ? `${p.size}px` : "32px")};
|
|
29
|
+
height: ${(p) => (p.size ? `${p.size}px` : "32px")};
|
|
30
|
+
|
|
31
|
+
${Avatar} {
|
|
32
|
+
width: ${(p) => (p.size ? `${p.size}px` : "32px")};
|
|
33
|
+
height: ${(p) => (p.size ? `${p.size}px` : "32px")};
|
|
24
34
|
}
|
|
25
35
|
`;
|
|
26
36
|
|
|
@@ -88,10 +88,16 @@ const Browser = (props: IBrowserProps): JSX.Element => {
|
|
|
88
88
|
const el = frameWrapperRef.current;
|
|
89
89
|
if (!el) return;
|
|
90
90
|
|
|
91
|
+
const getScrollbarWidth = () => {
|
|
92
|
+
return window.innerWidth - document.documentElement.clientWidth || 15;
|
|
93
|
+
};
|
|
94
|
+
|
|
91
95
|
let lastWidth = 0;
|
|
96
|
+
const scrollbarThreshold = getScrollbarWidth();
|
|
97
|
+
|
|
92
98
|
const observer = new ResizeObserver(([entry]) => {
|
|
93
99
|
const containerWidth = entry.contentRect.width;
|
|
94
|
-
if (containerWidth > 0 && Math.abs(containerWidth - lastWidth) >
|
|
100
|
+
if (containerWidth > 0 && Math.abs(containerWidth - lastWidth) > scrollbarThreshold) {
|
|
95
101
|
lastWidth = containerWidth;
|
|
96
102
|
const resolution = calcDefaultResolution(containerWidth, resolutionOptions);
|
|
97
103
|
const newZoom = calcAutoZoom(containerWidth, resolution);
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { isEmptyObj } from "@ax/helpers";
|
|
4
3
|
import { Loading } from "@ax/components";
|
|
5
|
-
import {
|
|
4
|
+
import { isEmptyObj } from "@ax/helpers";
|
|
5
|
+
import { useFirefoxScrollLock } from "@ax/hooks";
|
|
6
|
+
import type { IBreadcrumbItem, IUserEditing } from "@ax/types";
|
|
6
7
|
|
|
7
8
|
import Form from "./Form";
|
|
8
|
-
import NavigationForm from "./NavigationForm";
|
|
9
9
|
import GlobalPageForm from "./GlobalPageForm";
|
|
10
|
-
import PreviewForm from "./PreviewForm";
|
|
11
10
|
import Header from "./Header";
|
|
11
|
+
import NavigationForm from "./NavigationForm";
|
|
12
|
+
import PreviewForm from "./PreviewForm";
|
|
12
13
|
|
|
13
14
|
import * as S from "./style";
|
|
15
|
+
|
|
14
16
|
const navigationModulesTypes = ["header", "footer"];
|
|
15
17
|
|
|
16
|
-
const ConfigPanel = (props: IStateProps)
|
|
18
|
+
const ConfigPanel = (props: IStateProps) => {
|
|
17
19
|
const {
|
|
18
20
|
isLoading,
|
|
19
21
|
schema,
|
|
@@ -48,10 +50,12 @@ const ConfigPanel = (props: IStateProps): JSX.Element => {
|
|
|
48
50
|
useEffect(() => {
|
|
49
51
|
if (lastElementAddedId && wrapperRef.current) {
|
|
50
52
|
const element = document.querySelector(`.editorId-${lastElementAddedId}`);
|
|
51
|
-
element
|
|
53
|
+
element?.scrollIntoView();
|
|
52
54
|
}
|
|
53
55
|
}, [lastElementAddedId]);
|
|
54
56
|
|
|
57
|
+
useFirefoxScrollLock(wrapperRef);
|
|
58
|
+
|
|
55
59
|
if (isLoading || isEmptyObj(schema)) {
|
|
56
60
|
return <Loading />;
|
|
57
61
|
}
|
|
@@ -1,8 +1,51 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import { useRef, useState } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
|
|
2
5
|
import { trimText } from "@ax/helpers";
|
|
3
6
|
|
|
4
7
|
import * as S from "./style";
|
|
5
8
|
|
|
9
|
+
const PortalTooltip = (props: {
|
|
10
|
+
elementsRows: string[][];
|
|
11
|
+
defaultColor?: string;
|
|
12
|
+
colors?: any;
|
|
13
|
+
prefix: string;
|
|
14
|
+
rounded: boolean;
|
|
15
|
+
size: "S" | "M";
|
|
16
|
+
tooltipRef: React.RefObject<HTMLDivElement>;
|
|
17
|
+
isVisible: boolean;
|
|
18
|
+
elementRect: DOMRect | null;
|
|
19
|
+
}) => {
|
|
20
|
+
const { elementsRows, defaultColor, colors, prefix, rounded, size, tooltipRef, isVisible, elementRect } = props;
|
|
21
|
+
|
|
22
|
+
if (!isVisible || !elementRect) return null;
|
|
23
|
+
|
|
24
|
+
const top = elementRect.top - 8;
|
|
25
|
+
const left = elementRect.left + elementRect.width / 2;
|
|
26
|
+
|
|
27
|
+
return createPortal(
|
|
28
|
+
<S.PortalTooltip data-testid="portal-tooltip" style={{ top, left }} ref={tooltipRef}>
|
|
29
|
+
{elementsRows.map((row, idx) => {
|
|
30
|
+
return (
|
|
31
|
+
<S.Row data-testid="row-element" key={idx}>
|
|
32
|
+
{row.map((element, idx) => {
|
|
33
|
+
const color = defaultColor || colors?.[element];
|
|
34
|
+
return (
|
|
35
|
+
<S.Element data-testid="div-element" key={idx} color={color} rounded={rounded} size={size}>
|
|
36
|
+
{prefix}
|
|
37
|
+
{element}
|
|
38
|
+
</S.Element>
|
|
39
|
+
);
|
|
40
|
+
})}
|
|
41
|
+
</S.Row>
|
|
42
|
+
);
|
|
43
|
+
})}
|
|
44
|
+
</S.PortalTooltip>,
|
|
45
|
+
document.body,
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
6
49
|
const ElementsTooltip = (props: IElementsTooltipProps): JSX.Element => {
|
|
7
50
|
const {
|
|
8
51
|
elements,
|
|
@@ -16,6 +59,11 @@ const ElementsTooltip = (props: IElementsTooltipProps): JSX.Element => {
|
|
|
16
59
|
size = "S",
|
|
17
60
|
} = props;
|
|
18
61
|
|
|
62
|
+
const tooltipRef = useRef<HTMLDivElement>(null);
|
|
63
|
+
const elementRef = useRef<HTMLDivElement>(null);
|
|
64
|
+
const [isTooltipVisible, setIsTooltipVisible] = useState(false);
|
|
65
|
+
const [elementRect, setElementRect] = useState<DOMRect | null>(null);
|
|
66
|
+
|
|
19
67
|
if (!elements) return <></>;
|
|
20
68
|
|
|
21
69
|
const visibleElements = elements.slice(0, defaultElements);
|
|
@@ -28,42 +76,56 @@ const ElementsTooltip = (props: IElementsTooltipProps): JSX.Element => {
|
|
|
28
76
|
elementsRows[row] = isNewRow ? [element] : [...elementsRows[row], element];
|
|
29
77
|
});
|
|
30
78
|
|
|
79
|
+
const handleMouseEnter = () => {
|
|
80
|
+
if (elementRef.current) {
|
|
81
|
+
setElementRect(elementRef.current.getBoundingClientRect());
|
|
82
|
+
}
|
|
83
|
+
setIsTooltipVisible(true);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleMouseLeave = () => {
|
|
87
|
+
setIsTooltipVisible(false);
|
|
88
|
+
};
|
|
89
|
+
|
|
31
90
|
return (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
91
|
+
<>
|
|
92
|
+
<S.Wrapper data-testid="elements-wrapper">
|
|
93
|
+
{visibleElements.map((fullElement, idx) => {
|
|
94
|
+
const element = defaultElements === 1 && maxChar ? trimText(fullElement, maxChar) : fullElement;
|
|
95
|
+
const color = defaultColor || colors?.[element];
|
|
36
96
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
97
|
+
return (
|
|
98
|
+
<S.Element data-testid="element" key={idx} color={color} rounded={rounded} size={size}>
|
|
99
|
+
{prefix}
|
|
100
|
+
{element}
|
|
101
|
+
</S.Element>
|
|
102
|
+
);
|
|
103
|
+
})}
|
|
104
|
+
{remainingElements > 0 && (
|
|
105
|
+
<S.Element
|
|
106
|
+
data-testid="remaining-element"
|
|
107
|
+
rounded={rounded}
|
|
108
|
+
size={size}
|
|
109
|
+
ref={elementRef}
|
|
110
|
+
onMouseEnter={handleMouseEnter}
|
|
111
|
+
onMouseLeave={handleMouseLeave}
|
|
112
|
+
>
|
|
113
|
+
+{remainingElements}
|
|
41
114
|
</S.Element>
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{element}
|
|
57
|
-
</S.Element>
|
|
58
|
-
);
|
|
59
|
-
})}
|
|
60
|
-
</S.Row>
|
|
61
|
-
);
|
|
62
|
-
})}
|
|
63
|
-
</S.Tooltip>
|
|
64
|
-
</S.Element>
|
|
65
|
-
)}
|
|
66
|
-
</S.Wrapper>
|
|
115
|
+
)}
|
|
116
|
+
</S.Wrapper>
|
|
117
|
+
<PortalTooltip
|
|
118
|
+
elementsRows={elementsRows}
|
|
119
|
+
defaultColor={defaultColor}
|
|
120
|
+
colors={colors}
|
|
121
|
+
prefix={prefix}
|
|
122
|
+
rounded={rounded}
|
|
123
|
+
size={size}
|
|
124
|
+
tooltipRef={tooltipRef}
|
|
125
|
+
isVisible={isTooltipVisible}
|
|
126
|
+
elementRect={elementRect}
|
|
127
|
+
/>
|
|
128
|
+
</>
|
|
67
129
|
);
|
|
68
130
|
};
|
|
69
131
|
|
|
@@ -26,6 +26,7 @@ const Element = styled.div<{ color?: string; rounded: boolean; size: "S" | "M" }
|
|
|
26
26
|
background-color: ${(p) => (p.color ? p.color : p.theme.colors.uiBackground01)};
|
|
27
27
|
border-radius: ${(p) => (p.rounded ? "34px" : p.theme.radii.xs)};
|
|
28
28
|
white-space: nowrap;
|
|
29
|
+
cursor: pointer;
|
|
29
30
|
|
|
30
31
|
&:hover > ${Tooltip} {
|
|
31
32
|
display: block;
|
|
@@ -36,4 +37,14 @@ const Row = styled.div`
|
|
|
36
37
|
display: flex;
|
|
37
38
|
`;
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
const PortalTooltip = styled.div`
|
|
41
|
+
position: fixed;
|
|
42
|
+
z-index: 9999;
|
|
43
|
+
padding: ${(p) => p.theme.spacing.s};
|
|
44
|
+
box-shadow: ${(p) => p.theme.shadow.shadowL};
|
|
45
|
+
background-color: ${(p) => p.theme.colors.uiBarBackground};
|
|
46
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
47
|
+
transform: translate(-50%, -100%);
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
export { Wrapper, Element, Tooltip, Row, PortalTooltip };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { useModal } from "@ax/hooks";
|
|
4
|
-
import { ISite } from "@ax/types";
|
|
1
|
+
import { FileGallery, Icon, Modal, TextField } from "@ax/components";
|
|
2
|
+
import { VALID_DOCUMENT_FORMATS } from "@ax/constants";
|
|
5
3
|
import { formatBytes, getFormattedDateWithTimezone } from "@ax/helpers";
|
|
6
|
-
import {
|
|
4
|
+
import { useModal } from "@ax/hooks";
|
|
5
|
+
import type { ISite } from "@ax/types";
|
|
7
6
|
|
|
8
7
|
import * as S from "./style";
|
|
9
8
|
|
|
@@ -12,11 +11,8 @@ const FileField = (props: IFileFieldProps): JSX.Element => {
|
|
|
12
11
|
|
|
13
12
|
const { isOpen, toggleModal } = useModal(false);
|
|
14
13
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
? allowedFormats
|
|
18
|
-
: ["pdf", "doc", "docx", "xls", "xlsx", "zip", "csv", "txt", "mov", "mp4", "wmv", "avi", "webm", "mkv"];
|
|
19
|
-
const videoFormats = ["mov", "mp4", "wmv", "avi", "webm", "mkv"];
|
|
14
|
+
const hasCustomFormats = !!allowedFormats?.length;
|
|
15
|
+
|
|
20
16
|
const handleClick = () => {
|
|
21
17
|
if (!disabled) {
|
|
22
18
|
toggleModal();
|
|
@@ -25,11 +21,10 @@ const FileField = (props: IFileFieldProps): JSX.Element => {
|
|
|
25
21
|
|
|
26
22
|
const addFile = (newFile: any) => {
|
|
27
23
|
onChange(newFile);
|
|
28
|
-
toggleModal();
|
|
29
24
|
};
|
|
30
25
|
|
|
31
26
|
const handleOnClickUrl = () => {
|
|
32
|
-
if (value
|
|
27
|
+
if (value?.url) {
|
|
33
28
|
const win = window.open(value.url, "_blank");
|
|
34
29
|
if (win) {
|
|
35
30
|
win.focus();
|
|
@@ -60,9 +55,9 @@ const FileField = (props: IFileFieldProps): JSX.Element => {
|
|
|
60
55
|
},
|
|
61
56
|
];
|
|
62
57
|
|
|
63
|
-
const fileName = value
|
|
64
|
-
const fileDate = value
|
|
65
|
-
const fileSize = value
|
|
58
|
+
const fileName = value?.url ? value.url.split("/").pop() : "File title";
|
|
59
|
+
const fileDate = value?.uploadDate ? getFormattedDateWithTimezone(value.uploadDate, "d MMM Y") : "--";
|
|
60
|
+
const fileSize = value?.sizeBytes ? formatBytes(value.sizeBytes) : "-- KB";
|
|
66
61
|
|
|
67
62
|
return (
|
|
68
63
|
<>
|
|
@@ -73,7 +68,7 @@ const FileField = (props: IFileFieldProps): JSX.Element => {
|
|
|
73
68
|
<div>{fileSize}</div>
|
|
74
69
|
</S.FileData>
|
|
75
70
|
</S.FileDataWrapper>
|
|
76
|
-
{value
|
|
71
|
+
{value?.url && (
|
|
77
72
|
<S.TextFieldWrapper data-testid="text-field-wrapper">
|
|
78
73
|
<TextField
|
|
79
74
|
name="url"
|
|
@@ -93,7 +88,8 @@ const FileField = (props: IFileFieldProps): JSX.Element => {
|
|
|
93
88
|
</S.IconWrapper>
|
|
94
89
|
</S.Field>
|
|
95
90
|
<S.HelpText>
|
|
96
|
-
Valid formats:
|
|
91
|
+
Valid formats:{" "}
|
|
92
|
+
{hasCustomFormats ? allowedFormats.join(", ") : `${VALID_DOCUMENT_FORMATS.join(", ")} and videos`}. Max.
|
|
97
93
|
size: 50MB
|
|
98
94
|
</S.HelpText>
|
|
99
95
|
</>
|
|
@@ -108,7 +104,9 @@ const FileField = (props: IFileFieldProps): JSX.Element => {
|
|
|
108
104
|
</S.Component>
|
|
109
105
|
)}
|
|
110
106
|
<Modal isOpen={isOpen} hide={toggleModal} size="XL" title="Select file">
|
|
111
|
-
{isOpen &&
|
|
107
|
+
{isOpen && (
|
|
108
|
+
<FileGallery customFormats={allowedFormats} addFile={addFile} toggleModal={toggleModal} site={site} />
|
|
109
|
+
)}
|
|
112
110
|
</Modal>
|
|
113
111
|
</>
|
|
114
112
|
);
|
|
@@ -28,7 +28,7 @@ const HeadingField = (props: IHeadingFieldProps): JSX.Element => {
|
|
|
28
28
|
return (
|
|
29
29
|
<>
|
|
30
30
|
{toolbar ? (
|
|
31
|
-
<Wysiwyg
|
|
31
|
+
<Wysiwyg site={site} inline={true} value={contentValue} onChange={handleChange} />
|
|
32
32
|
) : (
|
|
33
33
|
<TextField value={contentValue} onChange={handleChange} />
|
|
34
34
|
)}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Icon, IconAction, Gallery, Modal, Image, FieldsBehavior } from "@ax/components";
|
|
1
|
+
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
3
2
|
|
|
3
|
+
import { FieldsBehavior, Gallery, Icon, IconAction, Image, ImageDragAndDrop, Modal } from "@ax/components";
|
|
4
4
|
import { formatBytes, getFormattedDateWithTimezone } from "@ax/helpers";
|
|
5
|
-
import { IImage, ISite } from "@ax/types";
|
|
6
5
|
import { useModal } from "@ax/hooks";
|
|
7
|
-
import
|
|
6
|
+
import type { IImage, ISite } from "@ax/types";
|
|
8
7
|
|
|
9
8
|
import * as S from "./style";
|
|
10
9
|
|
|
@@ -30,42 +29,17 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
30
29
|
const { isOpen, toggleModal } = useModal(isModalOpen);
|
|
31
30
|
const { isOpen: isOpenDD, toggleModal: toggleModalDD } = useModal(isModalOpen);
|
|
32
31
|
const [previewSrc, setPreviewSrc] = useState<string>();
|
|
33
|
-
const [previewHeight, setPreviewHeight] = useState<{ height: string | number }>({ height: "auto" });
|
|
34
32
|
const previewRef = useRef<HTMLDivElement>(null);
|
|
35
|
-
const [imageLoaded, setImageLoaded] = useState(false);
|
|
36
|
-
|
|
37
|
-
const validFormats = ["jpeg", "jpg", "png", "svg", "gif"];
|
|
38
33
|
|
|
39
34
|
const imageUrl = value ? (typeof value === "string" ? value : value.url) : "";
|
|
40
35
|
const imagePosition = value && typeof value === "object" ? value.position : "center";
|
|
41
36
|
|
|
42
37
|
useEffect(() => {
|
|
43
38
|
setPreviewSrc(imageUrl);
|
|
44
|
-
setImageLoaded(false);
|
|
45
39
|
}, [imageUrl]);
|
|
46
40
|
|
|
47
|
-
const calculateImageHeight = () => {
|
|
48
|
-
if (previewRef.current) {
|
|
49
|
-
const height = previewRef.current.getBoundingClientRect().height;
|
|
50
|
-
|
|
51
|
-
if (!cropPreview && height < 180) {
|
|
52
|
-
setPreviewHeight({ height: "180px" });
|
|
53
|
-
} else {
|
|
54
|
-
setPreviewHeight({ height: "auto" });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const handleImageLoad = () => {
|
|
60
|
-
if (!imageLoaded) {
|
|
61
|
-
calculateImageHeight();
|
|
62
|
-
setImageLoaded(true);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
41
|
const handleDelete = () => {
|
|
67
42
|
if (!disabled) {
|
|
68
|
-
setPreviewHeight({ height: "auto" });
|
|
69
43
|
setPreviewSrc("");
|
|
70
44
|
onChange({});
|
|
71
45
|
}
|
|
@@ -76,7 +50,7 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
76
50
|
const url = typeof img === "string" ? img : img.url;
|
|
77
51
|
setPreviewSrc(url);
|
|
78
52
|
onChange(img);
|
|
79
|
-
setIsGalleryOpened
|
|
53
|
+
setIsGalleryOpened?.();
|
|
80
54
|
error && handleValidation && handleValidation(url, validators);
|
|
81
55
|
isOpenDD && toggleModalDD();
|
|
82
56
|
}
|
|
@@ -84,14 +58,14 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
84
58
|
|
|
85
59
|
const handleClick = () => {
|
|
86
60
|
if (!disabled) {
|
|
87
|
-
setIsGalleryOpened
|
|
61
|
+
setIsGalleryOpened?.();
|
|
88
62
|
noGallery ? toggleModalDD() : toggleModal();
|
|
89
63
|
}
|
|
90
64
|
};
|
|
91
65
|
|
|
92
66
|
const handleChange = () => {
|
|
93
67
|
if (!disabled) {
|
|
94
|
-
setIsGalleryOpened
|
|
68
|
+
setIsGalleryOpened?.();
|
|
95
69
|
noGallery ? toggleModalDD() : toggleModal();
|
|
96
70
|
}
|
|
97
71
|
};
|
|
@@ -113,6 +87,8 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
113
87
|
|
|
114
88
|
const handleUpload = (file: IImage[]) => getImageSelected(file[0]);
|
|
115
89
|
|
|
90
|
+
const imageElement = useMemo(() => <Image url={previewSrc} width="320" />, [previewSrc]);
|
|
91
|
+
|
|
116
92
|
return (
|
|
117
93
|
<>
|
|
118
94
|
<S.FieldWrapper
|
|
@@ -139,11 +115,7 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
139
115
|
</S.ImageDataWrapper>
|
|
140
116
|
)}
|
|
141
117
|
<S.Preview preview={!!previewSrc} data-testid="previewDiv" ref={previewRef}>
|
|
142
|
-
{previewSrc &&
|
|
143
|
-
<S.ImageContainer style={{ height: previewHeight.height }}>
|
|
144
|
-
<Image url={previewSrc} width={320} onLoad={handleImageLoad} />
|
|
145
|
-
</S.ImageContainer>
|
|
146
|
-
)}
|
|
118
|
+
{previewSrc && <S.ImageContainer cropPreview={cropPreview}>{imageElement}</S.ImageContainer>}
|
|
147
119
|
<S.PositionWrapper>
|
|
148
120
|
<S.PositionTitle>
|
|
149
121
|
<Icon name="grid" size="16" />
|
|
@@ -184,7 +156,6 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
184
156
|
<ImageDragAndDrop
|
|
185
157
|
siteID={site ? site.id : "global"}
|
|
186
158
|
isAllowedToUpload={true}
|
|
187
|
-
validFormats={validFormats}
|
|
188
159
|
handleUpload={handleUpload}
|
|
189
160
|
visible={false}
|
|
190
161
|
/>
|
|
@@ -143,10 +143,21 @@ const ImageDataWrapper = styled.div`
|
|
|
143
143
|
max-width: ${(p) => `calc(${p.theme.spacing.xl} * 5)`};
|
|
144
144
|
`;
|
|
145
145
|
|
|
146
|
-
const ImageContainer = styled.div
|
|
146
|
+
const ImageContainer = styled.div<{ cropPreview?: boolean }>`
|
|
147
147
|
display: flex;
|
|
148
148
|
justify-content: center;
|
|
149
149
|
align-items: center;
|
|
150
|
+
${(p) => (p.cropPreview ? "height: 180px;" : "min-height: 180px;")}
|
|
151
|
+
width: 100%;
|
|
152
|
+
overflow: hidden;
|
|
153
|
+
|
|
154
|
+
img {
|
|
155
|
+
object-fit: ${(p) => (p.cropPreview ? "cover" : "contain")};
|
|
156
|
+
object-position: center;
|
|
157
|
+
max-width: 100%;
|
|
158
|
+
height: 100%;
|
|
159
|
+
width: 100%;
|
|
160
|
+
}
|
|
150
161
|
`;
|
|
151
162
|
|
|
152
163
|
const FileName = styled.div`
|