@edifice.io/react 2.6.0 → 2.6.1-develop.20260729194603

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 (32) hide show
  1. package/dist/components/Badge/Badge.d.ts +19 -13
  2. package/dist/components/Badge/Badge.js +6 -6
  3. package/dist/components/ButtonBeta/ButtonBeta.d.ts +5 -0
  4. package/dist/components/ButtonBeta/ButtonBeta.js +5 -2
  5. package/dist/components/Layout/Layout.js +10 -3
  6. package/dist/components/Layout/components/HeaderNotificationsOverlay.d.ts +5 -0
  7. package/dist/components/Layout/components/HeaderNotificationsOverlay.js +18 -0
  8. package/dist/components/PageLayout/store/overlayStore.js +1 -1
  9. package/dist/components/Popover/Popover.d.ts +7 -0
  10. package/dist/components/Popover/Popover.js +2 -1
  11. package/dist/homepage.js +12 -10
  12. package/dist/hooks/index.d.ts +1 -0
  13. package/dist/hooks/useBrowserInfo/useBrowserInfo.js +1 -2
  14. package/dist/hooks/useUiOverride/index.d.ts +1 -0
  15. package/dist/hooks/useUiOverride/useUiOverride.d.ts +5 -0
  16. package/dist/hooks/useUiOverride/useUiOverride.js +13 -0
  17. package/dist/index.js +24 -20
  18. package/dist/modules/editor/components/Renderer/AttachmentRenderer.js +12 -3
  19. package/dist/modules/homepage/components/Header/Header.d.ts +3 -1
  20. package/dist/modules/homepage/components/Header/Header.js +6 -5
  21. package/dist/modules/homepage/components/HomeCard/HomeCardHeader.js +1 -1
  22. package/dist/modules/homepage/components/MessageFlashList/MessageFlash.js +2 -2
  23. package/dist/modules/homepage/components/MessageFlashList/index.d.ts +1 -0
  24. package/dist/modules/homepage/components/Notifications/services/api/notificationService.js +1 -2
  25. package/dist/modules/homepage/components/UserSpace/UserSpace.js +2 -2
  26. package/dist/providers/MockedProvider/MockedProvider.js +4 -3
  27. package/dist/utilities/create-selectors/create-selectors.d.ts +37 -0
  28. package/dist/utilities/create-selectors/index.d.ts +1 -0
  29. package/dist/utilities/index.d.ts +1 -0
  30. package/package.json +8 -7
  31. package/dist/utilities/create-selectors/createSelectors.d.ts +0 -10
  32. /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 : beta.
31
- * Beta Badge is used to indicate that a feature is in beta phase.
32
- * The color prop allows to customize the badge color to match the app color.
33
- * Defaults to black if not provided.
34
- * Beta Badge has a fixed text 'BÊTA' unless children is provided.
35
- * If app is provided, the color of the Beta Badge is derived from the application colors.
36
- * Example:
37
- * <Badge variant={{ type: 'beta', color: '#823AA1', app: myApp }} />
38
- * where myApp is of type IWebApp.
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 BetaBadgeVariant = {
41
- type: 'beta';
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 | BetaBadgeVariant;
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 badgeColorClassName = "";
20
- if (variant.type === "beta" && variant.app) {
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
- badgeColorClassName = `${colorAppClassName} ${backgroundLightAppClassName} ${borderAppClassName}`;
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 === "beta" && badgeColorClassName, className);
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 === "beta" && (children ?? "BÊTA"),
28
- variant.type !== "chip" && variant.type !== "beta" && children
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 hasIcon = !!(leftIcon || rightIcon), classes = clsx("btn-beta", `btn-beta-${color}`, {
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": hasIcon && !!children,
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,5 @@
1
+ declare const HeaderNotificationsOverlay: {
2
+ (): import("react/jsx-runtime").JSX.Element;
3
+ displayName: string;
4
+ };
5
+ export default HeaderNotificationsOverlay;
@@ -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/createSelectors.js";
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/MessageFlashList.js";
5
- import { default as default6 } from "./modules/homepage/components/Notifications/NotificationItem.js";
6
- import { default as default7 } from "./modules/homepage/components/Notifications/NotificationList.js";
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 default8 } from "./modules/homepage/components/SchoolSpace/SchoolSpace.js";
9
- import { default as default9 } from "./modules/homepage/components/UserSpace/UserSpace.js";
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 MessageFlashList,
26
+ default5 as MessageFlash,
27
+ default6 as MessageFlashList,
26
28
  MessageFlashListContainer,
27
- default6 as NotificationItem,
28
- default7 as NotificationList,
29
+ default7 as NotificationItem,
30
+ default8 as NotificationList,
29
31
  NotificationListContainer,
30
- default8 as SchoolSpace,
32
+ default9 as SchoolSpace,
31
33
  SchoolSpaceContainer,
32
- default9 as UserSpace,
34
+ default10 as UserSpace,
33
35
  UserSpaceContainer
34
36
  };
@@ -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
- var _a, _b, _c;
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,5 @@
1
+ /** Read the platform-driven UI override registered under `key` in the theme-conf, normalized to an object form. */
2
+ export default function useUiOverride(key: string): {
3
+ variant: string;
4
+ theme?: string;
5
+ } | undefined;
@@ -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 "./modules/modals/ResourceModal/hooks/useUpdateMutation.js";
110
- import { default as default109 } from "./hooks/useUpload/useUpload.js";
111
- import { default as default110 } from "./hooks/useUploadFiles/useUploadFiles.js";
112
- import { default as default111 } from "./hooks/useUser/useUser.js";
113
- import { default as default112 } from "./hooks/useWorkspaceFile/useWorkspaceFile.js";
114
- import { default as default113 } from "./hooks/useWorkspaceFolders/useWorkspaceFolders.js";
115
- import { WORKSPACE_SHARED_FOLDER_ID, WORKSPACE_USER_FOLDER_ID, default as default114 } from "./hooks/useWorkspaceFolders/useWorkspaceFoldersTree.js";
116
- import { default as default115 } from "./hooks/useWorkspaceSearch/useWorkspaceSearch.js";
117
- import { default as default116 } from "./hooks/useXitiTrackPageLoad/useXitiTrackPageLoad.js";
118
- import { default as default117 } from "./hooks/useZendeskGuide/useZendeskGuide.js";
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 useUpdateMutation,
346
- default109 as useUpload,
347
- default110 as useUploadFiles,
348
- default111 as useUser,
349
- default112 as useWorkspaceFile,
350
- default113 as useWorkspaceFolders,
351
- default114 as useWorkspaceFoldersTree,
352
- default115 as useWorkspaceSearch,
353
- default116 as useXitiTrackPageLoad,
354
- default117 as useZendeskGuide,
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
- editor.commands.unsetAttachment(documentId), setAttachmentArrayAttrs((oldAttachments) => oldAttachments.filter((_, i) => i !== index));
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
@@ -37,7 +38,7 @@ const Header = ({
37
38
  } = useUser(), hasCarbonioPreauthWorkflow = useHasWorkflow("org.entcore.auth.controllers.CarbonioPreauthController|preauth") || !1, classes = clsx("header-beta d-print-none no-2d no-1d"), {
38
39
  userAvatar,
39
40
  userName,
40
- communityWorkflow,
41
+ communitiesWorkflow,
41
42
  conversationWorflow
42
43
  } = useHeader({
43
44
  user,
@@ -47,11 +48,11 @@ 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, {}) }) }),
54
- communityWorkflow && /* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsx(NavLink, { link: "/communities", translate: t("navbar.community"), "data-testid": "header-community-button", children: /* @__PURE__ */ jsx(SvgIconCommunitiesBeta, { className: "icon community" }) }) }),
55
+ communitiesWorkflow && /* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsx(NavLink, { link: "/communities", translate: t("navbar.community"), "data-testid": "header-community-button", children: /* @__PURE__ */ jsx(SvgIconCommunitiesBeta, { className: "icon community" }) }) }),
55
56
  conversationWorflow && /* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsxs(NavLink, { className: "position-relative", link: "/conversation/conversation", translate: t("conversation"), "data-testid": "header-messagerie-button", children: [
56
57
  /* @__PURE__ */ jsx(SvgIconMessagesBeta, {}),
57
58
  hasMessages && /* @__PURE__ */ jsx(Badge, { variant: {
@@ -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: "top-100 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 d-flex align-items-center gap-8", "data-testid": "header-logout-button", children: [
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: "default", variant: "ghost", onClick: onActionClick, leftIcon: actionLeftIcon, rightIcon: actionRightIcon, "data-testid": "home-card-header-action", children: actionLabel })
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
- md
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: md ? "row" : "column", justify: "between", className: "message-flash-footer", children: [
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
  ] })
@@ -1,4 +1,5 @@
1
1
  export * from './MessageFlash';
2
2
  export * from './MessageFlashList';
3
3
  export * from './MessageFlashListContainer';
4
+ export { default as MessageFlash } from './MessageFlash';
4
5
  export { default as MessageFlashList } from './MessageFlashList';
@@ -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__ */ jsx(HomeCard.Header, { title: /* @__PURE__ */ jsxs(Flex, { className: "user-space", direction: "row", gap: "8", children: [
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 { QueryClientProvider, QueryClient } from "@tanstack/react-query";
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 queryClient = new QueryClient(), MockedProvider = ({
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';
@@ -1,4 +1,5 @@
1
1
  export * from './check-user-rights';
2
+ export * from './create-selectors';
2
3
  export * from './emptyscreen-mapping';
3
4
  export * from './react-query';
4
5
  export * from './refs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edifice.io/react",
3
- "version": "2.6.0",
3
+ "version": "2.6.1-develop.20260729194603",
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.0",
144
- "@edifice.io/tiptap-extensions": "2.6.0",
145
- "@edifice.io/utilities": "2.6.0"
143
+ "@edifice.io/bootstrap": "2.6.1-develop.20260729194603",
144
+ "@edifice.io/tiptap-extensions": "2.6.1-develop.20260729194603",
145
+ "@edifice.io/utilities": "2.6.1-develop.20260729194603"
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/config": "2.6.0",
177
- "@edifice.io/client": "2.6.0"
176
+ "@edifice.io/client": "2.6.1-develop.20260729194603",
177
+ "@edifice.io/config": "2.6.1-develop.20260729194603"
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 {};