@edifice.io/react 2.6.0 → 2.6.1
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/dist/components/Badge/Badge.d.ts +19 -13
- package/dist/components/Badge/Badge.js +6 -6
- package/dist/components/ButtonBeta/ButtonBeta.d.ts +5 -0
- package/dist/components/ButtonBeta/ButtonBeta.js +5 -2
- package/dist/components/Layout/Layout.js +10 -3
- package/dist/components/Layout/components/HeaderNotificationsOverlay.d.ts +5 -0
- package/dist/components/Layout/components/HeaderNotificationsOverlay.js +18 -0
- package/dist/components/PageLayout/store/overlayStore.js +1 -1
- package/dist/components/Popover/Popover.d.ts +7 -0
- package/dist/components/Popover/Popover.js +2 -1
- package/dist/homepage.js +12 -10
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useBrowserInfo/useBrowserInfo.js +1 -2
- package/dist/hooks/useUiOverride/index.d.ts +1 -0
- package/dist/hooks/useUiOverride/useUiOverride.d.ts +5 -0
- package/dist/hooks/useUiOverride/useUiOverride.js +13 -0
- package/dist/index.js +24 -20
- package/dist/modules/editor/components/Renderer/AttachmentRenderer.js +12 -3
- package/dist/modules/homepage/components/Header/Header.d.ts +3 -1
- package/dist/modules/homepage/components/Header/Header.js +4 -3
- package/dist/modules/homepage/components/HomeCard/HomeCardHeader.js +1 -1
- package/dist/modules/homepage/components/MessageFlashList/MessageFlash.js +2 -2
- package/dist/modules/homepage/components/MessageFlashList/index.d.ts +1 -0
- package/dist/modules/homepage/components/Notifications/services/api/notificationService.js +1 -2
- package/dist/modules/homepage/components/UserSpace/UserSpace.js +2 -2
- package/dist/providers/MockedProvider/MockedProvider.js +4 -3
- package/dist/utilities/create-selectors/create-selectors.d.ts +37 -0
- package/dist/utilities/create-selectors/index.d.ts +1 -0
- package/dist/utilities/index.d.ts +1 -0
- package/package.json +8 -7
- package/dist/utilities/create-selectors/createSelectors.d.ts +0 -10
- /package/dist/utilities/create-selectors/{createSelectors.js → create-selectors.js} +0 -0
|
@@ -27,22 +27,29 @@ export type LinkBadgeVariant = {
|
|
|
27
27
|
type: 'link';
|
|
28
28
|
};
|
|
29
29
|
/**
|
|
30
|
-
* Badge variant :
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
30
|
+
* Badge variant : App Version.
|
|
31
|
+
*
|
|
32
|
+
* Displays a badge showing the app version, for example: BÊTA, ALPHA, NEW or a custom version label.
|
|
33
|
+
*
|
|
34
|
+
* Default label is BÊTA unless a label is provided as a children of the component.
|
|
35
|
+
* Example of usage:
|
|
36
|
+
* <Badge variant={{ type: 'appVersion', app: myApp }}>NEW</Badge>
|
|
37
|
+
*
|
|
38
|
+
* The color prop allows to customize the badge color to match the app color (Defaults to black if not provided).
|
|
39
|
+
* Example of usage:
|
|
40
|
+
* <Badge variant={{ type: 'appVersion', color: '#823AA1' }} />
|
|
41
|
+
*
|
|
42
|
+
* If app prop is provided, the color of the App Version Badge is derived from the application colors,
|
|
43
|
+
* for example if the app provided is Blog then the Badge will have the same color as Blog app.
|
|
44
|
+
* Example of usage:
|
|
45
|
+
* <Badge variant={{ type: 'appVersion', app: blog }} />
|
|
39
46
|
*/
|
|
40
|
-
export type
|
|
41
|
-
type: '
|
|
47
|
+
export type AppVersionBadgeVariant = {
|
|
48
|
+
type: 'appVersion';
|
|
42
49
|
color?: string;
|
|
43
50
|
app?: IWebApp;
|
|
44
51
|
};
|
|
45
|
-
export type BadgeVariants = NotificationBadgeVariant | ContentBadgeVariant | ProfileBadgeVariant | ChipBadgeVariant | LinkBadgeVariant |
|
|
52
|
+
export type BadgeVariants = NotificationBadgeVariant | ContentBadgeVariant | ProfileBadgeVariant | ChipBadgeVariant | LinkBadgeVariant | AppVersionBadgeVariant;
|
|
46
53
|
export interface BadgeProps extends React.ComponentPropsWithRef<'span'> {
|
|
47
54
|
/**
|
|
48
55
|
* Badge variant : notification, link or profile (Teacher|Student|Relative|Personnel)
|
|
@@ -51,7 +58,6 @@ export interface BadgeProps extends React.ComponentPropsWithRef<'span'> {
|
|
|
51
58
|
variant?: BadgeVariants;
|
|
52
59
|
/**
|
|
53
60
|
* Text or icon (or whatever) to render as children elements.
|
|
54
|
-
* Defaults to 'BÊTA' for beta variant.
|
|
55
61
|
*/
|
|
56
62
|
children?: ReactNode;
|
|
57
63
|
/**
|
|
@@ -16,16 +16,16 @@ const Badge = /* @__PURE__ */ forwardRef(({
|
|
|
16
16
|
getBackgroundLightIconClass,
|
|
17
17
|
getBorderIconClass
|
|
18
18
|
} = useEdificeIcons();
|
|
19
|
-
let
|
|
20
|
-
if (variant.type === "
|
|
19
|
+
let appVersionBadgeColorClassName = "";
|
|
20
|
+
if (variant.type === "appVersion" && variant.app) {
|
|
21
21
|
const colorAppClassName = getIconClass(variant.app), backgroundLightAppClassName = getBackgroundLightIconClass(variant.app), borderAppClassName = getBorderIconClass(variant.app);
|
|
22
|
-
|
|
22
|
+
appVersionBadgeColorClassName = `${colorAppClassName} ${backgroundLightAppClassName} ${borderAppClassName}`;
|
|
23
23
|
}
|
|
24
|
-
const classes = clsx("badge rounded-pill", (variant.type === "content" || variant.type === "user") && "background" in variant ? "bg-gray-200" : (variant.type === "content" || variant.type === "user") && !("background" in variant) ? "border border-0" : "", variant.type === "content" && `text-${variant.level}`, variant.type === "notification" && `badge-notification bg-${variant.level} text-light border border-0`, variant.type === "user" && `badge-profile-${variant.profile.toLowerCase()}`, variant.type === "link" && "badge-link border border-0", variant.type === "chip" && "bg-gray-200", variant.type === "
|
|
24
|
+
const classes = clsx("badge rounded-pill", (variant.type === "content" || variant.type === "user") && "background" in variant ? "bg-gray-200" : (variant.type === "content" || variant.type === "user") && !("background" in variant) ? "border border-0" : "", variant.type === "content" && `text-${variant.level}`, variant.type === "notification" && `badge-notification bg-${variant.level} text-light border border-0`, variant.type === "user" && `badge-profile-${variant.profile.toLowerCase()}`, variant.type === "link" && "badge-link border border-0", variant.type === "chip" && "bg-gray-200", variant.type === "appVersion" && appVersionBadgeColorClassName, className);
|
|
25
25
|
return /* @__PURE__ */ jsxs("span", { ref, className: classes, ...restProps, children: [
|
|
26
26
|
variant.type === "chip" && /* @__PURE__ */ jsx("div", { className: "d-flex fw-800 align-items-center", children }),
|
|
27
|
-
variant.type === "
|
|
28
|
-
variant.type !== "chip" && variant.type !== "
|
|
27
|
+
variant.type === "appVersion" && (children ?? "BÊTA"),
|
|
28
|
+
variant.type !== "chip" && variant.type !== "appVersion" && children
|
|
29
29
|
] });
|
|
30
30
|
});
|
|
31
31
|
export {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
export type ButtonBetaRef = HTMLButtonElement;
|
|
3
3
|
export type ButtonBetaColor = 'default' | 'destructive' | 'secondary' | 'tertiary';
|
|
4
|
+
export type ButtonBetaSizes = 'sm' | 'md';
|
|
4
5
|
export type ButtonBetaVariant = 'filled' | 'outline' | 'ghost';
|
|
5
6
|
export interface ButtonBetaProps extends React.ComponentPropsWithRef<'button'> {
|
|
6
7
|
/**
|
|
@@ -15,6 +16,10 @@ export interface ButtonBetaProps extends React.ComponentPropsWithRef<'button'> {
|
|
|
15
16
|
* `filled`, `outline` or `ghost`
|
|
16
17
|
*/
|
|
17
18
|
variant?: ButtonBetaVariant;
|
|
19
|
+
/**
|
|
20
|
+
* `sm` or `md`
|
|
21
|
+
*/
|
|
22
|
+
size?: ButtonBetaSizes;
|
|
18
23
|
/**
|
|
19
24
|
* Does it has a text ?
|
|
20
25
|
*/
|
|
@@ -5,6 +5,7 @@ import Loading from "../Loading/Loading.js";
|
|
|
5
5
|
const ButtonBeta = /* @__PURE__ */ forwardRef(({
|
|
6
6
|
color = "default",
|
|
7
7
|
type = "button",
|
|
8
|
+
size = "md",
|
|
8
9
|
variant = "filled",
|
|
9
10
|
children,
|
|
10
11
|
isLoading,
|
|
@@ -13,11 +14,13 @@ const ButtonBeta = /* @__PURE__ */ forwardRef(({
|
|
|
13
14
|
className,
|
|
14
15
|
...restProps
|
|
15
16
|
}, ref) => {
|
|
16
|
-
const
|
|
17
|
+
const classes = clsx("btn-beta", `btn-beta-${color}`, {
|
|
17
18
|
"btn-beta--outline": variant === "outline",
|
|
18
19
|
"btn-beta--ghost": variant === "ghost",
|
|
20
|
+
"btn-beta--small": size === "sm",
|
|
19
21
|
"btn-beta--icon-only": !children,
|
|
20
|
-
"btn-beta--with-icon":
|
|
22
|
+
"btn-beta--with-left-icon": !!(leftIcon && children),
|
|
23
|
+
"btn-beta--with-right-icon": !!(rightIcon && children),
|
|
21
24
|
"btn-beta--loading": isLoading
|
|
22
25
|
}, className);
|
|
23
26
|
return /* @__PURE__ */ jsxs("button", { ref, "data-testid": "button-beta", className: classes, type, ...restProps, children: [
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense, lazy } from "react";
|
|
2
3
|
import clsx from "clsx";
|
|
3
4
|
import { Toaster } from "react-hot-toast";
|
|
4
5
|
import { useTranslation } from "react-i18next";
|
|
5
6
|
import { useEdificeTheme } from "../../providers/EdificeThemeProvider/EdificeThemeProvider.hook.js";
|
|
6
7
|
import Header from "./components/Header.js";
|
|
8
|
+
import HeaderNotificationsOverlay from "./components/HeaderNotificationsOverlay.js";
|
|
9
|
+
import { useOverlay } from "../PageLayout/hook/useOverlay.js";
|
|
10
|
+
import useUiOverride from "../../hooks/useUiOverride/useUiOverride.js";
|
|
7
11
|
import useCookiesConsent from "../../hooks/useCookiesConsent/useCookiesConsent.js";
|
|
8
12
|
import useZendeskGuide from "../../hooks/useZendeskGuide/useZendeskGuide.js";
|
|
9
13
|
import useCantoo from "../../hooks/useCantoo/useCantoo.js";
|
|
10
14
|
import Alert from "../Alert/Alert.js";
|
|
11
15
|
import Button from "../Button/Button.js";
|
|
12
|
-
const Layout = ({
|
|
16
|
+
const HeaderV2 = /* @__PURE__ */ lazy(() => import("../../modules/homepage/components/Header/Header.js")), Layout = ({
|
|
13
17
|
children,
|
|
14
18
|
headless = !1,
|
|
15
19
|
whiteBg = !0,
|
|
@@ -18,7 +22,9 @@ const Layout = ({
|
|
|
18
22
|
}) => {
|
|
19
23
|
const {
|
|
20
24
|
theme
|
|
21
|
-
} = useEdificeTheme(), {
|
|
25
|
+
} = useEdificeTheme(), override = useUiOverride("layout.header"), isHeaderV2 = (override == null ? void 0 : override.variant) === "v2", {
|
|
26
|
+
toggleOverlay
|
|
27
|
+
} = useOverlay(), {
|
|
22
28
|
t
|
|
23
29
|
} = useTranslation(), {
|
|
24
30
|
showCookiesConsent,
|
|
@@ -31,11 +37,12 @@ const Layout = ({
|
|
|
31
37
|
"container-fluid": !headless,
|
|
32
38
|
"rounded-4 border": (theme == null ? void 0 : theme.is1d) && !headless,
|
|
33
39
|
"mt-24": (theme == null ? void 0 : theme.is1d) && !headless
|
|
34
|
-
}, className), renderHeader = headless ? null : /* @__PURE__ */ jsx(Header, { is1d: theme == null ? void 0 : theme.is1d, src: theme == null ? void 0 : theme.basePath }), renderCookies = showCookiesConsent && /* @__PURE__ */ jsx(Alert, { type: "info", className: "m-12 rgpd", isConfirm: !0, position: "bottom-right", button: /* @__PURE__ */ jsx(Button, { color: "tertiary", variant: "ghost", onClick: handleConsultCookies, children: t("rgpd.cookies.banner.button.consult") }), onClose: handleCloseCookiesConsent, children: t("rgpd.cookies.banner.text1") }), renderToaster = /* @__PURE__ */ jsx(Toaster, { containerClassName: "toaster-container", toastOptions: {
|
|
40
|
+
}, className), renderHeader = headless ? null : isHeaderV2 ? /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(HeaderV2, { src: theme == null ? void 0 : theme.basePath, dataProduct: override == null ? void 0 : override.theme, onNotificationsClick: toggleOverlay }) }) : /* @__PURE__ */ jsx(Header, { is1d: theme == null ? void 0 : theme.is1d, src: theme == null ? void 0 : theme.basePath }), renderNotificationsOverlay = !headless && isHeaderV2 && /* @__PURE__ */ jsx(HeaderNotificationsOverlay, {}), renderCookies = showCookiesConsent && /* @__PURE__ */ jsx(Alert, { type: "info", className: "m-12 rgpd", isConfirm: !0, position: "bottom-right", button: /* @__PURE__ */ jsx(Button, { color: "tertiary", variant: "ghost", onClick: handleConsultCookies, children: t("rgpd.cookies.banner.button.consult") }), onClose: handleCloseCookiesConsent, children: t("rgpd.cookies.banner.text1") }), renderToaster = /* @__PURE__ */ jsx(Toaster, { containerClassName: "toaster-container", toastOptions: {
|
|
35
41
|
position: "top-right"
|
|
36
42
|
} });
|
|
37
43
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38
44
|
renderHeader,
|
|
45
|
+
renderNotificationsOverlay,
|
|
39
46
|
/* @__PURE__ */ jsx("main", { className: classes, ...restProps, children }),
|
|
40
47
|
renderToaster,
|
|
41
48
|
renderCookies
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { NotificationListContainer } from "../../../modules/homepage/components/Notifications/NotificationListContainer.js";
|
|
3
|
+
import PageLayoutOverlay from "../../PageLayout/components/PageLayoutOverlay.js";
|
|
4
|
+
import { useOverlay } from "../../PageLayout/hook/useOverlay.js";
|
|
5
|
+
const HeaderNotificationsOverlay = () => {
|
|
6
|
+
const {
|
|
7
|
+
isOverlayOpen,
|
|
8
|
+
updateOverlayOpen
|
|
9
|
+
} = useOverlay(), handleClose = () => updateOverlayOpen(!1);
|
|
10
|
+
return (
|
|
11
|
+
// NotificationList renders its own header with a close button, so
|
|
12
|
+
// PageLayoutOverlay's default close button would be redundant (double X).
|
|
13
|
+
/* @__PURE__ */ jsx(PageLayoutOverlay, { onClose: handleClose, closeButton: !1, children: isOverlayOpen && /* @__PURE__ */ jsx(NotificationListContainer, { onCloseNotifications: handleClose }) })
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
HeaderNotificationsOverlay as default
|
|
18
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { create } from "zustand";
|
|
2
|
-
import { createSelectors } from "../../../utilities/create-selectors/
|
|
2
|
+
import { createSelectors } from "../../../utilities/create-selectors/create-selectors.js";
|
|
3
3
|
const useOverlayStore = createSelectors(create((set) => ({
|
|
4
4
|
overlayOpen: !1,
|
|
5
5
|
updateOverlayOpen: (value) => set((previousState) => ({
|
|
@@ -8,6 +8,13 @@ export interface PopoverProps {
|
|
|
8
8
|
* When Popover is visible
|
|
9
9
|
*/
|
|
10
10
|
isVisible?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Horizontal alignment relative to the trigger.
|
|
13
|
+
* `center` (default) centers the popover under the trigger.
|
|
14
|
+
* `end` aligns the popover's right edge with the trigger's right edge,
|
|
15
|
+
* which prevents overflow when the trigger sits near the viewport's right edge.
|
|
16
|
+
*/
|
|
17
|
+
align?: 'center' | 'end';
|
|
11
18
|
/**
|
|
12
19
|
* React Node
|
|
13
20
|
*/
|
|
@@ -21,13 +21,14 @@ const PopoverHeader = ({
|
|
|
21
21
|
const classes = clsx("popover-footer p-8", className);
|
|
22
22
|
return /* @__PURE__ */ jsx("div", { className: classes, children });
|
|
23
23
|
}, Popover = /* @__PURE__ */ forwardRef(({
|
|
24
|
+
align = "center",
|
|
24
25
|
children,
|
|
25
26
|
className,
|
|
26
27
|
id,
|
|
27
28
|
isVisible,
|
|
28
29
|
...restProps
|
|
29
30
|
}, ref) => {
|
|
30
|
-
const classes = clsx("popover d-block position-absolute top-100 start-50 translate-middle-x", className);
|
|
31
|
+
const classes = clsx("popover d-block position-absolute top-100", align === "end" ? "end-0 popover-align-end" : "start-50 translate-middle-x", className);
|
|
31
32
|
return useTransition(isVisible, {
|
|
32
33
|
from: {
|
|
33
34
|
opacity: 0
|
package/dist/homepage.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { default as default2 } from "./modules/homepage/components/Header/Header.js";
|
|
2
2
|
import { default as default3 } from "./modules/homepage/components/HomeCard/HomeCard.js";
|
|
3
3
|
import { default as default4 } from "./modules/homepage/components/LastInfos/LastInfos.js";
|
|
4
|
-
import { default as default5 } from "./modules/homepage/components/MessageFlashList/
|
|
5
|
-
import { default as default6 } from "./modules/homepage/components/
|
|
6
|
-
import { default as default7 } from "./modules/homepage/components/Notifications/
|
|
4
|
+
import { default as default5 } from "./modules/homepage/components/MessageFlashList/MessageFlash.js";
|
|
5
|
+
import { default as default6 } from "./modules/homepage/components/MessageFlashList/MessageFlashList.js";
|
|
6
|
+
import { default as default7 } from "./modules/homepage/components/Notifications/NotificationItem.js";
|
|
7
|
+
import { default as default8 } from "./modules/homepage/components/Notifications/NotificationList.js";
|
|
7
8
|
import { NotificationListContainer } from "./modules/homepage/components/Notifications/NotificationListContainer.js";
|
|
8
|
-
import { default as
|
|
9
|
-
import { default as
|
|
9
|
+
import { default as default9 } from "./modules/homepage/components/SchoolSpace/SchoolSpace.js";
|
|
10
|
+
import { default as default10 } from "./modules/homepage/components/UserSpace/UserSpace.js";
|
|
10
11
|
import { Favorites } from "./modules/homepage/components/Favorites/Favorites.js";
|
|
11
12
|
import { FavoritesContainer } from "./modules/homepage/components/Favorites/FavoritesContainer.js";
|
|
12
13
|
import { LastInfosContainer } from "./modules/homepage/components/LastInfos/LastInfosContainer.js";
|
|
@@ -22,13 +23,14 @@ export {
|
|
|
22
23
|
default4 as LastInfos,
|
|
23
24
|
LastInfosContainer,
|
|
24
25
|
LastInfosList,
|
|
25
|
-
default5 as
|
|
26
|
+
default5 as MessageFlash,
|
|
27
|
+
default6 as MessageFlashList,
|
|
26
28
|
MessageFlashListContainer,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
default7 as NotificationItem,
|
|
30
|
+
default8 as NotificationList,
|
|
29
31
|
NotificationListContainer,
|
|
30
|
-
|
|
32
|
+
default9 as SchoolSpace,
|
|
31
33
|
SchoolSpaceContainer,
|
|
32
|
-
|
|
34
|
+
default10 as UserSpace,
|
|
33
35
|
UserSpaceContainer
|
|
34
36
|
};
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export * from './useToast';
|
|
|
30
30
|
export * from './useToggle';
|
|
31
31
|
export * from './useTrapFocus';
|
|
32
32
|
export * from './useTrashedResource';
|
|
33
|
+
export * from './useUiOverride';
|
|
33
34
|
export * from './useUpload';
|
|
34
35
|
export * from './useUploadFiles';
|
|
35
36
|
export * from './useUser';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { UAParser } from "ua-parser-js";
|
|
2
2
|
function useBrowserInfo(userAgent) {
|
|
3
|
-
|
|
4
|
-
const uaParser = new UAParser(userAgent), os = uaParser.getOS(), device = uaParser.getDevice(), browser = uaParser.getBrowser(), isIphone = ((_a = device.model) == null ? void 0 : _a.indexOf("iPhone")) != -1, isIpod = ((_b = device.model) == null ? void 0 : _b.indexOf("iPod")) != -1, isIpad = ((_c = device.model) == null ? void 0 : _c.indexOf("iPad")) != -1;
|
|
3
|
+
const uaParser = new UAParser(userAgent), os = uaParser.getOS(), device = uaParser.getDevice(), browser = uaParser.getBrowser(), deviceModel = device.model ?? "", isIphone = deviceModel.includes("iPhone"), isIpod = deviceModel.includes("iPod"), isIpad = deviceModel.includes("iPad");
|
|
5
4
|
return {
|
|
6
5
|
os,
|
|
7
6
|
device,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as useUiOverride } from './useUiOverride';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEdificeTheme } from "../../providers/EdificeThemeProvider/EdificeThemeProvider.hook.js";
|
|
2
|
+
function useUiOverride(key) {
|
|
3
|
+
var _a;
|
|
4
|
+
const {
|
|
5
|
+
theme
|
|
6
|
+
} = useEdificeTheme(), raw = (_a = theme == null ? void 0 : theme.uiOverrides) == null ? void 0 : _a[key];
|
|
7
|
+
return typeof raw == "string" ? {
|
|
8
|
+
variant: raw
|
|
9
|
+
} : raw;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
useUiOverride as default
|
|
13
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -106,16 +106,17 @@ import { default as default104 } from "./hooks/useToast/useToast.js";
|
|
|
106
106
|
import { default as default105 } from "./hooks/useToggle/useToggle.js";
|
|
107
107
|
import { default as default106 } from "./hooks/useTrapFocus/useTrapFocus.js";
|
|
108
108
|
import { default as default107 } from "./hooks/useTrashedResource/useTrashedResource.js";
|
|
109
|
-
import { default as default108 } from "./
|
|
110
|
-
import { default as default109 } from "./hooks/
|
|
111
|
-
import { default as default110 } from "./hooks/
|
|
112
|
-
import { default as default111 } from "./hooks/
|
|
113
|
-
import { default as default112 } from "./hooks/
|
|
114
|
-
import { default as default113 } from "./hooks/
|
|
115
|
-
import {
|
|
116
|
-
import { default as default115 } from "./hooks/
|
|
117
|
-
import { default as default116 } from "./hooks/
|
|
118
|
-
import { default as default117 } from "./hooks/
|
|
109
|
+
import { default as default108 } from "./hooks/useUiOverride/useUiOverride.js";
|
|
110
|
+
import { default as default109 } from "./modules/modals/ResourceModal/hooks/useUpdateMutation.js";
|
|
111
|
+
import { default as default110 } from "./hooks/useUpload/useUpload.js";
|
|
112
|
+
import { default as default111 } from "./hooks/useUploadFiles/useUploadFiles.js";
|
|
113
|
+
import { default as default112 } from "./hooks/useUser/useUser.js";
|
|
114
|
+
import { default as default113 } from "./hooks/useWorkspaceFile/useWorkspaceFile.js";
|
|
115
|
+
import { default as default114 } from "./hooks/useWorkspaceFolders/useWorkspaceFolders.js";
|
|
116
|
+
import { WORKSPACE_SHARED_FOLDER_ID, WORKSPACE_USER_FOLDER_ID, default as default115 } from "./hooks/useWorkspaceFolders/useWorkspaceFoldersTree.js";
|
|
117
|
+
import { default as default116 } from "./hooks/useWorkspaceSearch/useWorkspaceSearch.js";
|
|
118
|
+
import { default as default117 } from "./hooks/useXitiTrackPageLoad/useXitiTrackPageLoad.js";
|
|
119
|
+
import { default as default118 } from "./hooks/useZendeskGuide/useZendeskGuide.js";
|
|
119
120
|
import { AccessiblePalette, DefaultPalette } from "./components/ColorPicker/ColorPalette.js";
|
|
120
121
|
import { AntProvider } from "./providers/AntThemeProvider/AntProvider.js";
|
|
121
122
|
import { BetaSwitch } from "./components/BetaSwitch/BetaSwitch.js";
|
|
@@ -146,6 +147,7 @@ import { UserSearch } from "./components/UserSearch/UserSearch.js";
|
|
|
146
147
|
import { VisibleType } from "./components/UserSearch/types/visible.js";
|
|
147
148
|
import { buildTree, determineNewParentId, findItemIndexInTree, flattenNodes, flattenTree, generateUpdateData, getActiveAndOverNodes, getDragDepth, getIndicesToUpdate, getProjection, updateParentIds } from "./components/Tree/utilities/tree-sortable.js";
|
|
148
149
|
import { checkUserRight } from "./utilities/check-user-rights/check-user-rights.js";
|
|
150
|
+
import { createSelectors } from "./utilities/create-selectors/create-selectors.js";
|
|
149
151
|
import { emptyScreenMapping } from "./utilities/emptyscreen-mapping/emptyscreen-mapping.js";
|
|
150
152
|
import { getRotateTransitionStyle } from "./utilities/rotate-transition-style/get-rotate-transition-style.js";
|
|
151
153
|
import { invalidateQueriesWithFirstPage } from "./utilities/react-query/react-query-utils.js";
|
|
@@ -276,6 +278,7 @@ export {
|
|
|
276
278
|
arrayUnique,
|
|
277
279
|
buildTree,
|
|
278
280
|
checkUserRight,
|
|
281
|
+
createSelectors,
|
|
279
282
|
deleteNode,
|
|
280
283
|
determineNewParentId,
|
|
281
284
|
emptyScreenMapping,
|
|
@@ -342,15 +345,16 @@ export {
|
|
|
342
345
|
default107 as useTrashedResource,
|
|
343
346
|
useTreeSortable,
|
|
344
347
|
useTreeView,
|
|
345
|
-
default108 as
|
|
346
|
-
default109 as
|
|
347
|
-
default110 as
|
|
348
|
-
default111 as
|
|
349
|
-
default112 as
|
|
350
|
-
default113 as
|
|
351
|
-
default114 as
|
|
352
|
-
default115 as
|
|
353
|
-
default116 as
|
|
354
|
-
default117 as
|
|
348
|
+
default108 as useUiOverride,
|
|
349
|
+
default109 as useUpdateMutation,
|
|
350
|
+
default110 as useUpload,
|
|
351
|
+
default111 as useUploadFiles,
|
|
352
|
+
default112 as useUser,
|
|
353
|
+
default113 as useWorkspaceFile,
|
|
354
|
+
default114 as useWorkspaceFolders,
|
|
355
|
+
default115 as useWorkspaceFoldersTree,
|
|
356
|
+
default116 as useWorkspaceSearch,
|
|
357
|
+
default117 as useXitiTrackPageLoad,
|
|
358
|
+
default118 as useZendeskGuide,
|
|
355
359
|
wrapTreeNode
|
|
356
360
|
};
|
|
@@ -12,7 +12,9 @@ import { Grid } from "../../../../components/Grid/Grid.js";
|
|
|
12
12
|
const AttachmentRenderer = (props) => {
|
|
13
13
|
const {
|
|
14
14
|
node,
|
|
15
|
-
editor
|
|
15
|
+
editor,
|
|
16
|
+
updateAttributes,
|
|
17
|
+
deleteNode
|
|
16
18
|
} = props, [attachmentArrayAttrs, setAttachmentArrayAttrs] = useState(node.attrs.links), {
|
|
17
19
|
t
|
|
18
20
|
} = useTranslation(), {
|
|
@@ -22,7 +24,14 @@ const AttachmentRenderer = (props) => {
|
|
|
22
24
|
attachmentArrayAttrs !== node.attrs.links && setAttachmentArrayAttrs(node.attrs.links);
|
|
23
25
|
}, [node.attrs.links, attachmentArrayAttrs]);
|
|
24
26
|
const handleDelete = (index, documentId) => {
|
|
25
|
-
|
|
27
|
+
const nextAttachments = (node.attrs.links ?? []).filter((link, i) => {
|
|
28
|
+
const linkDocumentId = link.dataDocumentId ?? link.documentId ?? link["data-document-id"] ?? link.href;
|
|
29
|
+
return !(i === index || String(linkDocumentId ?? "") === String(documentId ?? ""));
|
|
30
|
+
});
|
|
31
|
+
nextAttachments.length > 0 ? updateAttributes == null || updateAttributes({
|
|
32
|
+
...node.attrs,
|
|
33
|
+
links: nextAttachments
|
|
34
|
+
}) : deleteNode == null || deleteNode(), !updateAttributes && !deleteNode && editor.commands.unsetAttachment(documentId), setAttachmentArrayAttrs(nextAttachments);
|
|
26
35
|
};
|
|
27
36
|
return (attachmentArrayAttrs == null ? void 0 : attachmentArrayAttrs.length) !== 0 && /* @__PURE__ */ jsx(NodeViewWrapper, { children: /* @__PURE__ */ jsxs("div", { style: {
|
|
28
37
|
backgroundColor: "#F2F2F2",
|
|
@@ -32,7 +41,7 @@ const AttachmentRenderer = (props) => {
|
|
|
32
41
|
/* @__PURE__ */ jsx("p", { className: "m-12 mt-0", children: t("tiptap.attachments.bloc") }),
|
|
33
42
|
/* @__PURE__ */ jsx(Grid, { children: attachmentArrayAttrs == null ? void 0 : attachmentArrayAttrs.map((attachment, index) => /* @__PURE__ */ jsx(Grid.Col, { sm: "6", children: /* @__PURE__ */ jsx(Attachment, { name: attachment.name, options: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
34
43
|
/* @__PURE__ */ jsx("a", { href: attachment.href, "data-document-id": attachment.dataDocumentId, "data-content-type": attachment.dataContentType, download: !0, children: /* @__PURE__ */ jsx(IconButton, { "aria-label": t("tiptap.attachments.download"), color: "tertiary", type: "button", icon: /* @__PURE__ */ jsx(SvgIconDownload, {}), variant: "ghost" }) }),
|
|
35
|
-
editable && /* @__PURE__ */ jsx(IconButton, { "aria-label": t("tiptap.attachments.delete"), color: "danger", type: "button", icon: /* @__PURE__ */ jsx(SvgIconDelete, {}), variant: "ghost", onClick: () => handleDelete(index, attachment.dataDocumentId) })
|
|
44
|
+
editable && /* @__PURE__ */ jsx(IconButton, { "aria-label": t("tiptap.attachments.delete"), color: "danger", type: "button", icon: /* @__PURE__ */ jsx(SvgIconDelete, {}), variant: "ghost", onClick: () => handleDelete(index, attachment.dataDocumentId ?? attachment["data-document-id"] ?? attachment.href) })
|
|
36
45
|
] }) }) }, index)) })
|
|
37
46
|
] }) });
|
|
38
47
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export interface HeaderProps {
|
|
2
2
|
src: string | undefined;
|
|
3
3
|
onNotificationsClick?: () => void;
|
|
4
|
+
/** Scope this header's CSS theme (`data-product`) to its own subtree, independently of the page's ambient theme. */
|
|
5
|
+
dataProduct?: string;
|
|
4
6
|
}
|
|
5
7
|
declare const Header: {
|
|
6
|
-
({ src, onNotificationsClick, }: HeaderProps): JSX.Element;
|
|
8
|
+
({ src, onNotificationsClick, dataProduct, }: HeaderProps): JSX.Element;
|
|
7
9
|
displayName: string;
|
|
8
10
|
};
|
|
9
11
|
export default Header;
|
|
@@ -25,7 +25,8 @@ import Avatar from "../../../../components/Avatar/Avatar.js";
|
|
|
25
25
|
import { Popover, PopoverBody } from "../../../../components/Popover/Popover.js";
|
|
26
26
|
const Header = ({
|
|
27
27
|
src = "",
|
|
28
|
-
onNotificationsClick
|
|
28
|
+
onNotificationsClick,
|
|
29
|
+
dataProduct
|
|
29
30
|
}) => {
|
|
30
31
|
const {
|
|
31
32
|
t
|
|
@@ -47,7 +48,7 @@ const Header = ({
|
|
|
47
48
|
} = useEdificeTheme(), hasMessages = messages > 0, [userRef, isUserHovered] = useHover(), popoverUserId = useId(), handleNotificationsClick = () => {
|
|
48
49
|
onNotificationsClick == null || onNotificationsClick();
|
|
49
50
|
};
|
|
50
|
-
return /* @__PURE__ */ jsx("header", { className: classes, children: /* @__PURE__ */ jsxs(Navbar, { className: "px-24", children: [
|
|
51
|
+
return /* @__PURE__ */ jsx("header", { className: classes, "data-product": dataProduct, children: /* @__PURE__ */ jsxs(Navbar, { className: "px-24", children: [
|
|
51
52
|
/* @__PURE__ */ jsx(LogoBeta, { src: `${src}/img/illustrations/logo.png` }),
|
|
52
53
|
/* @__PURE__ */ jsxs("ul", { className: "navbar-nav", children: [
|
|
53
54
|
/* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsx(NavLink, { link: "/timeline/timeline", translate: t("navbar.home"), "data-testid": "header-home-button", children: /* @__PURE__ */ jsx(SvgIconHomeBeta, {}) }) }),
|
|
@@ -67,7 +68,7 @@ const Header = ({
|
|
|
67
68
|
/* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsx(ButtonBeta, { leftIcon: /* @__PURE__ */ jsx(SvgIconNotificationBeta, {}), variant: "ghost", onClick: handleNotificationsClick }) }),
|
|
68
69
|
/* @__PURE__ */ jsxs(NavItem, { className: "position-relative", ref: userRef, id: popoverUserId, "aria-haspopup": "true", "aria-expanded": isUserHovered, "data-testid": "header-user-menu-button", children: [
|
|
69
70
|
/* @__PURE__ */ jsx(NavLink, { link: "/userbook/mon-compte", translate: t("navbar.myaccount"), "data-testid": "header-user-profile-button", children: /* @__PURE__ */ jsx(Avatar, { alt: userName, size: "sm", src: userAvatar, variant: "circle", className: "bg-white", width: "32", height: "32" }) }),
|
|
70
|
-
/* @__PURE__ */ jsx(Popover, { className: "
|
|
71
|
+
/* @__PURE__ */ jsx(Popover, { align: "end", className: "widget", id: popoverUserId, isVisible: isUserHovered, children: /* @__PURE__ */ jsx(PopoverBody, { children: /* @__PURE__ */ jsxs("a", { href: "/auth/logout?callback=" + ((theme == null ? void 0 : theme.logoutCallback) ?? ""), className: "nav-link logout d-flex align-items-center gap-8", "data-testid": "header-logout-button", children: [
|
|
71
72
|
/* @__PURE__ */ jsx(SvgIconDisconnect, { className: "icon logout" }),
|
|
72
73
|
/* @__PURE__ */ jsx("span", { id: "logout-label", className: "nav-text", children: t("navbar.disconnect") })
|
|
73
74
|
] }) }) })
|
|
@@ -14,7 +14,7 @@ const HomeCardHeader = ({
|
|
|
14
14
|
const hasAction = !!actionLabel && !!onActionClick;
|
|
15
15
|
return /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "between", gap: "8", className: clsx("home-card-header", className), ...rest, children: [
|
|
16
16
|
/* @__PURE__ */ jsx("h3", { className: "home-card-header-title", children: title }),
|
|
17
|
-
hasAction && /* @__PURE__ */ jsx(ButtonBeta, { color: "
|
|
17
|
+
hasAction && /* @__PURE__ */ jsx(ButtonBeta, { color: "tertiary", size: "sm", variant: "ghost", onClick: onActionClick, leftIcon: actionLeftIcon, rightIcon: actionRightIcon, "data-testid": "home-card-header-action", children: actionLabel })
|
|
18
18
|
] });
|
|
19
19
|
};
|
|
20
20
|
export {
|
|
@@ -20,7 +20,7 @@ const MessageFlash = ({
|
|
|
20
20
|
} = useEdificeClient(), [isCollapsed, setIsCollapsed] = useState(!0), [hasOverflow, setHasOverflow] = useState(!1), contentRef = useRef(null), checkContentTimeoutRef = useRef(void 0), {
|
|
21
21
|
t
|
|
22
22
|
} = useTranslation(), {
|
|
23
|
-
|
|
23
|
+
lg
|
|
24
24
|
} = useBreakpoint();
|
|
25
25
|
let content = "";
|
|
26
26
|
message.contents && (currentLanguage && message.contents[currentLanguage] ? content = message.contents[currentLanguage] : message.contents.fr ? content = message.contents.fr : content = Object.keys(message.contents).map((key) => message.contents[key]).filter((cont) => cont !== null)[0]), useEffect(() => {
|
|
@@ -53,7 +53,7 @@ const MessageFlash = ({
|
|
|
53
53
|
/* @__PURE__ */ jsx("div", { ref: contentRef, className: classesContent, dangerouslySetInnerHTML: {
|
|
54
54
|
__html: content
|
|
55
55
|
} }),
|
|
56
|
-
/* @__PURE__ */ jsxs(Flex, { direction:
|
|
56
|
+
/* @__PURE__ */ jsxs(Flex, { direction: lg ? "row" : "column", justify: "between", className: "message-flash-footer", children: [
|
|
57
57
|
/* @__PURE__ */ jsx("div", { className: "fst-italic", children: message.signature || "" }),
|
|
58
58
|
hasOverflow && /* @__PURE__ */ jsx(ButtonBeta, { "data-testid": isCollapsed ? "message-flash-view-more-button" : "message-flash-view-less-button", color: "default", variant: "ghost", onClick: handleCollapse, "aria-controls": `message-flash-${message.id}-content`, "aria-expanded": !isCollapsed, className: "message-flash-collapse-button", children: t(isCollapsed ? "read.more" : "read.less") })
|
|
59
59
|
] })
|
|
@@ -6,8 +6,7 @@ const createNotificationService = (baseURL) => ({
|
|
|
6
6
|
*/
|
|
7
7
|
getNotifications(types, page) {
|
|
8
8
|
const searchParams = new URLSearchParams({
|
|
9
|
-
page: page.toString()
|
|
10
|
-
mine: "1"
|
|
9
|
+
page: page.toString()
|
|
11
10
|
});
|
|
12
11
|
return types.forEach((type) => {
|
|
13
12
|
searchParams.append("type", type);
|
|
@@ -13,13 +13,13 @@ function UserSpace({
|
|
|
13
13
|
t
|
|
14
14
|
} = useTranslation();
|
|
15
15
|
return /* @__PURE__ */ jsxs(HomeCard, { variant: "user", children: [
|
|
16
|
-
/* @__PURE__ */
|
|
16
|
+
/* @__PURE__ */ jsxs(Flex, { className: "user-space", direction: "row", gap: "8", children: [
|
|
17
17
|
/* @__PURE__ */ jsx(Avatar, { className: "user-space--avatar", size: "auto", alt: name, src: avatar, variant: "circle" }),
|
|
18
18
|
/* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
|
|
19
19
|
/* @__PURE__ */ jsx("div", { "data-testid": "user-space-name", className: "user-space--name", children: name }),
|
|
20
20
|
/* @__PURE__ */ jsx("div", { "data-testid": "user-space-profile", className: "user-space--profile", children: t(profile) })
|
|
21
21
|
] })
|
|
22
|
-
] })
|
|
22
|
+
] }),
|
|
23
23
|
children && /* @__PURE__ */ jsx(HomeCard.Content, { children })
|
|
24
24
|
] });
|
|
25
25
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
3
|
+
import { useState } from "react";
|
|
3
4
|
import { EdificeClientContext } from "../EdificeClientProvider/EdificeClientProvider.context.js";
|
|
4
5
|
import { EdificeThemeContext } from "../EdificeThemeProvider/EdificeThemeProvider.context.js";
|
|
5
6
|
import { mockQueryResult, mockSession, mockConf } from "./MockedProvider.mocks.js";
|
|
6
|
-
const
|
|
7
|
+
const MockedProvider = ({
|
|
7
8
|
children
|
|
8
9
|
}) => {
|
|
9
|
-
const themeContextValue = {
|
|
10
|
+
const [queryClient] = useState(() => new QueryClient()), themeContextValue = {
|
|
10
11
|
theme: "default",
|
|
11
12
|
setTheme: vi.fn()
|
|
12
13
|
}, clientContextValue = {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { StoreApi, UseBoundStore } from 'zustand';
|
|
2
|
+
type WithSelectors<S> = S extends {
|
|
3
|
+
getState: () => infer T;
|
|
4
|
+
} ? S & {
|
|
5
|
+
use: {
|
|
6
|
+
[K in keyof T]: () => T[K];
|
|
7
|
+
};
|
|
8
|
+
} : never;
|
|
9
|
+
/**
|
|
10
|
+
* Augments a Zustand store with auto-generated, per-field selector hooks,
|
|
11
|
+
* so consumers can call `store.use.myValue()` instead of
|
|
12
|
+
* `store((s) => s.myValue)` and never forget a selector.
|
|
13
|
+
*
|
|
14
|
+
* Pattern from the Zustand docs:
|
|
15
|
+
* https://zustand.docs.pmnd.rs/guides/auto-generating-selectors
|
|
16
|
+
*
|
|
17
|
+
* @param _store - The bound store to augment.
|
|
18
|
+
* @returns The same store instance, mutated with a `use` object exposing one
|
|
19
|
+
* selector hook per state key.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* - The store is mutated in place; the returned reference is the input store.
|
|
23
|
+
* - Selectors are generated once, from the keys present at call time. Fields
|
|
24
|
+
* added to the state later won't get a `use.<key>` selector.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const useBearStore = createSelectors(
|
|
29
|
+
* create<BearState>(() => ({ bears: 0 })),
|
|
30
|
+
* );
|
|
31
|
+
*
|
|
32
|
+
* // In a component:
|
|
33
|
+
* const bears = useBearStore.use.bears();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare const createSelectors: <S extends UseBoundStore<StoreApi<object>>>(_store: S) => WithSelectors<S>;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './create-selectors';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edifice.io/react",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "Edifice React Library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -140,9 +140,9 @@
|
|
|
140
140
|
"swiper": "10.1.0",
|
|
141
141
|
"ua-parser-js": "1.0.36",
|
|
142
142
|
"zustand": "4.5.7",
|
|
143
|
-
"@edifice.io/bootstrap": "2.6.
|
|
144
|
-
"@edifice.io/tiptap-extensions": "2.6.
|
|
145
|
-
"@edifice.io/utilities": "2.6.
|
|
143
|
+
"@edifice.io/bootstrap": "2.6.1",
|
|
144
|
+
"@edifice.io/tiptap-extensions": "2.6.1",
|
|
145
|
+
"@edifice.io/utilities": "2.6.1"
|
|
146
146
|
},
|
|
147
147
|
"devDependencies": {
|
|
148
148
|
"@babel/plugin-transform-react-pure-annotations": "7.27.1",
|
|
@@ -173,8 +173,8 @@
|
|
|
173
173
|
"vite": "5.4.14",
|
|
174
174
|
"vite-plugin-dts": "4.5.4",
|
|
175
175
|
"vite-tsconfig-paths": "5.1.4",
|
|
176
|
-
"@edifice.io/
|
|
177
|
-
"@edifice.io/
|
|
176
|
+
"@edifice.io/client": "2.6.1",
|
|
177
|
+
"@edifice.io/config": "2.6.1"
|
|
178
178
|
},
|
|
179
179
|
"peerDependencies": {
|
|
180
180
|
"@react-spring/web": "9.7.5",
|
|
@@ -198,6 +198,7 @@
|
|
|
198
198
|
"format:write": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
199
199
|
"lint": "eslint",
|
|
200
200
|
"test": "vitest",
|
|
201
|
-
"test:watch": "vitest --watch"
|
|
201
|
+
"test:watch": "vitest --watch",
|
|
202
|
+
"test:coverage": "vitest run --coverage"
|
|
202
203
|
}
|
|
203
204
|
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { StoreApi, UseBoundStore } from 'zustand';
|
|
2
|
-
type WithSelectors<S> = S extends {
|
|
3
|
-
getState: () => infer T;
|
|
4
|
-
} ? S & {
|
|
5
|
-
use: {
|
|
6
|
-
[K in keyof T]: () => T[K];
|
|
7
|
-
};
|
|
8
|
-
} : never;
|
|
9
|
-
export declare const createSelectors: <S extends UseBoundStore<StoreApi<object>>>(_store: S) => WithSelectors<S>;
|
|
10
|
-
export {};
|
|
File without changes
|