@edifice.io/react 2.6.3-develop-integration.20260826175449 → 2.6.3-develop-pedago.20260909111933

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.
@@ -6,6 +6,11 @@ export type NotificationBadgeVariant = {
6
6
  type: 'notification';
7
7
  level: 'success' | 'warning' | 'danger' | 'info';
8
8
  };
9
+ /** Badge variant : status */
10
+ export type StatusBadgeVariant = {
11
+ type: 'status';
12
+ level: 'validate' | 'success' | 'warning' | 'danger' | 'info';
13
+ };
9
14
  /** Badge variant : content */
10
15
  export type ContentBadgeVariant = {
11
16
  type: 'content';
@@ -49,7 +54,7 @@ export type AppVersionBadgeVariant = {
49
54
  color?: string;
50
55
  app?: IWebApp;
51
56
  };
52
- export type BadgeVariants = NotificationBadgeVariant | ContentBadgeVariant | ProfileBadgeVariant | ChipBadgeVariant | LinkBadgeVariant | AppVersionBadgeVariant;
57
+ export type BadgeVariants = NotificationBadgeVariant | StatusBadgeVariant | ContentBadgeVariant | ProfileBadgeVariant | ChipBadgeVariant | LinkBadgeVariant | AppVersionBadgeVariant;
53
58
  export interface BadgeProps extends React.ComponentPropsWithRef<'span'> {
54
59
  /**
55
60
  * Badge variant : notification, link or profile (Teacher|Student|Relative|Personnel)
@@ -21,7 +21,7 @@ const Badge = /* @__PURE__ */ forwardRef(({
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 === "appVersion" && appVersionBadgeColorClassName, 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 === "status" && `badge-status badge-status-${variant.level} caption fw-normal`, 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
27
  variant.type === "appVersion" && (children ?? "BÊTA"),
@@ -5,7 +5,6 @@ import { useEdificeClient } from "../../../providers/EdificeClientProvider/Edifi
5
5
  import { useEdificeTheme } from "../../../providers/EdificeThemeProvider/EdificeThemeProvider.hook.js";
6
6
  import SvgIconRafterDown from "../../../modules/icons/components/IconRafterDown.js";
7
7
  import SvgIconCommunities from "../../../modules/icons/components/nav/IconCommunities.js";
8
- import SvgIconCommunity from "../../../modules/icons/components/nav/IconCommunity.js";
9
8
  import SvgIconDisconnect from "../../../modules/icons/components/nav/IconDisconnect.js";
10
9
  import SvgIconHome from "../../../modules/icons/components/nav/IconHome.js";
11
10
  import SvgIconMyApps from "../../../modules/icons/components/nav/IconMyApps.js";
@@ -67,7 +66,6 @@ const Header = ({
67
66
  userAvatar,
68
67
  userName,
69
68
  welcomeUser,
70
- communityWorkflow,
71
69
  communitiesWorkflow,
72
70
  conversationWorflow,
73
71
  searchWorkflow,
@@ -176,10 +174,6 @@ const Header = ({
176
174
  /* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsxs("div", { className: "dropdown", children: [
177
175
  /* @__PURE__ */ jsx("button", { className: "nav-link btn btn-naked d-md-none", type: "button", "aria-controls": "dropdown-navbar", "aria-expanded": !isCollapsed, "aria-label": t("navbar.open.menu"), onClick: toggleCollapsedNav, children: /* @__PURE__ */ jsx(SvgIconRafterDown, { className: "icon rafter-down", width: "20", height: "20", color: "#fff" }) }),
178
176
  /* @__PURE__ */ jsxs("ul", { className: `dropdown-menu dropdown-menu-end ${isCollapsed ? "" : "show"}`, id: "dropdown-navbar", children: [
179
- communityWorkflow && /* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsxs("a", { href: "/community", className: "nav-link dropdown-item", children: [
180
- /* @__PURE__ */ jsx(SvgIconCommunity, { className: "icon community" }),
181
- /* @__PURE__ */ jsx("span", { className: "nav-text", children: t("navbar.community") })
182
- ] }) }),
183
177
  communitiesWorkflow && /* @__PURE__ */ jsx(NavItem, { children: /* @__PURE__ */ jsxs("a", { href: "/communities", className: "nav-link dropdown-item", children: [
184
178
  /* @__PURE__ */ jsx(SvgIconCommunities, { className: "icon communities" }),
185
179
  /* @__PURE__ */ jsx("span", { className: "nav-text", children: t("navbar.community") })
@@ -14,7 +14,7 @@ function useHeader({
14
14
  t
15
15
  } = useTranslation(), title = t(appCode), [isCollapsed, setIsCollapsed] = useState(!0), [appsRef, isAppsHovered] = useHover(), popoverAppsId = useId(), popoverSearchId = useId(), userAvatar = avatar, userName = user == null ? void 0 : user.username, welcomeUser = t("welcome", {
16
16
  username: user == null ? void 0 : user.firstName
17
- }), bookmarkedApps = useBookmark(), communityWorkflow = useHasWorkflow("net.atos.entng.community.controllers.CommunityController|view"), communitiesWorkflow = useHasWorkflow("community.access"), conversationWorflow = useHasWorkflow("org.entcore.conversation.controllers.ConversationController|view"), searchWorkflow = useHasWorkflow("fr.openent.searchengine.controllers.SearchEngineController|view"), toggleCollapsedNav = useCallback(() => {
17
+ }), bookmarkedApps = useBookmark(), communitiesWorkflow = useHasWorkflow("community.access"), conversationWorflow = useHasWorkflow("org.entcore.conversation.controllers.ConversationController|view"), searchWorkflow = useHasWorkflow("fr.openent.searchengine.controllers.SearchEngineController|view"), toggleCollapsedNav = useCallback(() => {
18
18
  setIsCollapsed(!isCollapsed);
19
19
  }, [isCollapsed]);
20
20
  return useMemo(() => ({
@@ -27,13 +27,12 @@ function useHeader({
27
27
  userAvatar,
28
28
  userName,
29
29
  welcomeUser,
30
- communityWorkflow,
31
30
  communitiesWorkflow,
32
31
  conversationWorflow,
33
32
  searchWorkflow,
34
33
  isCollapsed,
35
34
  toggleCollapsedNav
36
- }), [appsRef, bookmarkedApps, communitiesWorkflow, communityWorkflow, conversationWorflow, isAppsHovered, isCollapsed, popoverAppsId, popoverSearchId, searchWorkflow, title, toggleCollapsedNav, userAvatar, userName, welcomeUser]);
35
+ }), [appsRef, bookmarkedApps, communitiesWorkflow, conversationWorflow, isAppsHovered, isCollapsed, popoverAppsId, popoverSearchId, searchWorkflow, title, toggleCollapsedNav, userAvatar, userName, welcomeUser]);
37
36
  }
38
37
  export {
39
38
  useHeader as default
@@ -1 +1,2 @@
1
1
  export { default as useUploadFiles } from './useUploadFiles';
2
+ export type { UploadSourceFileInfo } from './useUploadFiles';
@@ -1,6 +1,16 @@
1
1
  import { WorkspaceElement, WorkspaceVisibility } from '@edifice.io/client';
2
+ /**
3
+ * Snapshot of a source file taken at upload time, so the upload result can be
4
+ * re-ordered later on against the original file metadata.
5
+ */
6
+ export interface UploadSourceFileInfo {
7
+ /** Original file name. */
8
+ name: string;
9
+ /** `File.lastModified` of the source file (ms since epoch). */
10
+ lastModified: number;
11
+ }
2
12
  declare const useUploadFiles: ({ handleOnChange, visibility, application, }: {
3
- handleOnChange: (uploadedFiles: WorkspaceElement[]) => void;
13
+ handleOnChange: (uploadedFiles: WorkspaceElement[], sourceFilesInfo: Record<string, UploadSourceFileInfo>) => void;
4
14
  visibility?: WorkspaceVisibility;
5
15
  application?: string;
6
16
  }) => {
@@ -1,4 +1,4 @@
1
- import { useState, useCallback, useEffect } from "react";
1
+ import { useState, useRef, useCallback, useEffect } from "react";
2
2
  import { ImageResizer, addTimestampToImageUrl } from "@edifice.io/utilities";
3
3
  import { useDropzoneContext } from "../../components/Dropzone/DropzoneContext.js";
4
4
  import useWorkspaceFile from "../useWorkspaceFile/useWorkspaceFile.js";
@@ -8,7 +8,7 @@ const useUploadFiles = ({
8
8
  visibility,
9
9
  application
10
10
  }) => {
11
- const [uploadedFiles, setUploadedFiles] = useState([]), [editingImage, setEditingImage] = useState(void 0), {
11
+ const [uploadedFiles, setUploadedFiles] = useState([]), sourceFilesInfoRef = useRef({}), [editingImage, setEditingImage] = useState(void 0), {
12
12
  files,
13
13
  deleteFile,
14
14
  replaceFileAt,
@@ -34,7 +34,10 @@ const useUploadFiles = ({
34
34
  } catch (err) {
35
35
  console.error(err);
36
36
  }
37
- !resource && !replacement && (resource = await uploadFile(file)), resource ? setUploadedFiles((prevFiles) => [...prevFiles, resource]) : resetInputValue();
37
+ !resource && !replacement && (resource = await uploadFile(file)), resource ? (resource._id && (sourceFilesInfoRef.current[resource._id] = {
38
+ name: file.name,
39
+ lastModified: file.lastModified
40
+ }), setUploadedFiles((prevFiles) => [...prevFiles, resource])) : resetInputValue();
38
41
  });
39
42
  }, [uploadAlternateFile, uploadFile, replaceFileAt]);
40
43
  useEffect(() => {
@@ -47,15 +50,17 @@ const useUploadFiles = ({
47
50
  newFiles.forEach((file) => file && setUploadStatus(file, "idle")), tryUploading(newFiles);
48
51
  }, [files, uploadedFiles, getUploadStatus, setUploadStatus, tryUploading]), useEffect(() => {
49
52
  const sortedUploadedFiles = sortUploadedFiles(files, uploadedFiles);
50
- handleOnChange(sortedUploadedFiles);
53
+ handleOnChange(sortedUploadedFiles, {
54
+ ...sourceFilesInfoRef.current
55
+ });
51
56
  }, [uploadedFiles]);
52
57
  const sortUploadedFiles = (filesArray, uploadedFilesArray) => {
53
- const orderMap = filesArray.reduce((acc, item, index) => (acc[item.name] = index, acc), {});
54
- return uploadedFilesArray.sort((a, b) => orderMap[a.name] - orderMap[b.name]);
58
+ const orderMap = filesArray.reduce((acc, item, index) => (item.name in acc || (acc[item.name] = index), acc), {});
59
+ return [...uploadedFilesArray].sort((a, b) => (orderMap[a.name] ?? Number.MAX_SAFE_INTEGER) - (orderMap[b.name] ?? Number.MAX_SAFE_INTEGER));
55
60
  };
56
61
  async function removeFile(file) {
57
62
  const resource = uploadedFiles.find((uploadedFile) => uploadedFile.name === file.name);
58
- resource && (await remove(resource), clearUploadStatus(file), setUploadedFiles((prevFiles) => prevFiles.filter((prevFile) => prevFile.name !== (resource == null ? void 0 : resource.name)))), deleteFile(file), resetInputValue();
63
+ resource && (await remove(resource), clearUploadStatus(file), resource._id && delete sourceFilesInfoRef.current[resource._id], setUploadedFiles((prevFiles) => prevFiles.filter((prevFile) => prevFile.name !== (resource == null ? void 0 : resource.name)))), deleteFile(file), resetInputValue();
59
64
  }
60
65
  async function updateImage({
61
66
  blob,
@@ -1,7 +1,9 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
- import { useMediaLibraryContext } from "../MediaLibraryContext.js";
2
+ import { useState, useRef, useEffect } from "react";
3
3
  import { useTranslation } from "react-i18next";
4
- import Alert from "../../../../components/Alert/Alert.js";
4
+ import { useMediaLibraryContext } from "../MediaLibraryContext.js";
5
+ import Flex from "../../../../components/Flex/Flex.js";
6
+ import Radio from "../../../../components/Radio/Radio.js";
5
7
  import Dropzone from "../../../../components/Dropzone/Dropzone.js";
6
8
  import UploadFiles from "../../UploadFiles/UploadFiles.js";
7
9
  const acceptedTypes = (type) => {
@@ -18,6 +20,17 @@ const acceptedTypes = (type) => {
18
20
  break;
19
21
  }
20
22
  return acceptedTypes2;
23
+ }, orderUploadedFiles = (uploadedFiles, sourceFilesInfo, byAlpha) => {
24
+ const infoOf = (element) => element._id ? sourceFilesInfo[element._id] : void 0, nameOf = (element) => {
25
+ var _a;
26
+ return ((_a = infoOf(element)) == null ? void 0 : _a.name) ?? element.name;
27
+ }, lastModifiedOf = (element) => {
28
+ var _a;
29
+ return ((_a = infoOf(element)) == null ? void 0 : _a.lastModified) ?? 0;
30
+ };
31
+ return byAlpha ? [...uploadedFiles].sort((a, b) => nameOf(a).localeCompare(nameOf(b), void 0, {
32
+ numeric: !0
33
+ })) : [...uploadedFiles].sort((a, b) => lastModifiedOf(b) - lastModifiedOf(a)).reverse();
21
34
  }, Upload = () => {
22
35
  const {
23
36
  t
@@ -27,12 +40,26 @@ const acceptedTypes = (type) => {
27
40
  multiple,
28
41
  setResult,
29
42
  setResultCounter,
30
- setCancellable
31
- } = useMediaLibraryContext(), handleOnFilesChange = (uploadedFiles) => {
32
- uploadedFiles.length ? (setCancellable(uploadedFiles), setResultCounter(uploadedFiles.length), setResult(uploadedFiles)) : (setCancellable([]), setResultCounter(void 0), setResult(void 0));
43
+ setCancellable,
44
+ setPreSuccess
45
+ } = useMediaLibraryContext(), [sortByAlpha, setSortByAlpha] = useState(!0), [uploadedFiles, setUploadedFiles] = useState([]), sourceFilesInfoRef = useRef({}), handleOnFilesChange = (uploadedFiles2, sourceFilesInfo) => {
46
+ sourceFilesInfoRef.current = sourceFilesInfo, setUploadedFiles(uploadedFiles2), uploadedFiles2.length ? (setCancellable(uploadedFiles2), setResultCounter(uploadedFiles2.length), setResult(uploadedFiles2)) : (setCancellable([]), setResultCounter(void 0), setResult(void 0));
33
47
  };
34
- return /* @__PURE__ */ jsxs("div", { className: "flex-grow-1", children: [
35
- multiple && /* @__PURE__ */ jsx(Alert, { type: "info", className: "flex-shrink-0 mb-16", children: t("bbm.upload.alert") }),
48
+ return useEffect(() => {
49
+ if (!multiple || !uploadedFiles.length) {
50
+ setPreSuccess(void 0);
51
+ return;
52
+ }
53
+ setPreSuccess(() => () => Promise.resolve(orderUploadedFiles(uploadedFiles, sourceFilesInfoRef.current, sortByAlpha)));
54
+ }, [multiple, uploadedFiles, sortByAlpha, setPreSuccess]), /* @__PURE__ */ jsxs("div", { className: "flex-grow-1", children: [
55
+ multiple && /* @__PURE__ */ jsxs(Flex, { className: "mb-16 ms-8", align: "start", direction: "column", gap: "8", children: [
56
+ /* @__PURE__ */ jsxs("div", { children: [
57
+ t("bbm.upload.sort.title"),
58
+ " :"
59
+ ] }),
60
+ /* @__PURE__ */ jsx(Radio, { label: t("bbm.upload.sort.alpha"), name: "media-library-upload-sort", value: "alpha", model: sortByAlpha ? "alpha" : "date", checked: sortByAlpha, onChange: () => setSortByAlpha(!0), "data-testid": "media-library-upload-sort-alpha" }),
61
+ /* @__PURE__ */ jsx(Radio, { label: t("bbm.upload.sort.date"), name: "media-library-upload-sort", value: "date", model: sortByAlpha ? "alpha" : "date", checked: !sortByAlpha, onChange: () => setSortByAlpha(!1), "data-testid": "media-library-upload-sort-date" })
62
+ ] }),
36
63
  /* @__PURE__ */ jsx(Dropzone, { multiple, accept: acceptedTypes(type ?? "embedder"), children: /* @__PURE__ */ jsx(UploadFiles, { onFilesChange: handleOnFilesChange, visibility }) })
37
64
  ] });
38
65
  };
@@ -1,7 +1,8 @@
1
1
  import { WorkspaceElement, WorkspaceVisibility } from '@edifice.io/client';
2
+ import { UploadSourceFileInfo } from '../../../hooks';
2
3
  declare const UploadFiles: {
3
4
  ({ onFilesChange, visibility, }: {
4
- onFilesChange: (uploadedFiles: WorkspaceElement[]) => void;
5
+ onFilesChange: (uploadedFiles: WorkspaceElement[], sourceFilesInfo: Record<string, UploadSourceFileInfo>) => void;
5
6
  visibility?: WorkspaceVisibility;
6
7
  }): import("react/jsx-runtime").JSX.Element;
7
8
  displayName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edifice.io/react",
3
- "version": "2.6.3-develop-integration.20260826175449",
3
+ "version": "2.6.3-develop-pedago.20260909111933",
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.3-develop-integration.20260826175449",
144
- "@edifice.io/tiptap-extensions": "2.6.3-develop-integration.20260826175449",
145
- "@edifice.io/utilities": "2.6.3-develop-integration.20260826175449"
143
+ "@edifice.io/tiptap-extensions": "2.6.3-develop-pedago.20260909111933",
144
+ "@edifice.io/utilities": "2.6.3-develop-pedago.20260909111933",
145
+ "@edifice.io/bootstrap": "2.6.3-develop-pedago.20260909111933"
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/client": "2.6.3-develop-integration.20260826175449",
177
- "@edifice.io/config": "2.6.3-develop-integration.20260826175449"
176
+ "@edifice.io/client": "2.6.3-develop-pedago.20260909111933",
177
+ "@edifice.io/config": "2.6.3-develop-pedago.20260909111933"
178
178
  },
179
179
  "peerDependencies": {
180
180
  "@react-spring/web": "9.7.5",