@edifice.io/react 2.3.1-develop-b2school-actualites.20250911174726 → 2.3.1-develop-b2school.20250916133741

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/editor.js CHANGED
@@ -1,25 +1,23 @@
1
1
  import { EditorContent, Editor, useEditor } from "@tiptap/react";
2
2
  import { default as default2 } from "@tiptap/starter-kit";
3
3
  import { default as default3 } from "./modules/editor/components/Editor/Editor.js";
4
- import { default as default4 } from "./modules/editor/components/Editor/EditorPreview.js";
5
- import { default as default5 } from "./modules/editor/components/Editor/EditorPreviewSkeleton.js";
6
- import { default as default6 } from "./modules/editor/components/Editor/EditorSkeleton.js";
7
- import { default as default7 } from "./modules/editor/components/BubbleMenuEditImage/BubbleMenuEditImage.js";
8
- import { default as default8 } from "./modules/editor/components/NodeView/AttachmentNodeView.js";
9
- import { default as default9 } from "./modules/editor/components/NodeView/AudioNodeView.js";
10
- import { default as default10 } from "./modules/editor/components/NodeView/ConversationHistoryNodeView.js";
11
- import { default as default11 } from "./modules/editor/components/NodeView/ImageNodeView.js";
12
- import { default as default12 } from "./modules/editor/components/NodeView/LinkerNodeView.js";
13
- import { default as default13 } from "./modules/editor/components/NodeView/VideoNodeView.js";
14
- import { default as default14 } from "./modules/editor/components/NodeView/InformationPaneNodeView.js";
15
- import { default as default15 } from "./modules/editor/components/Renderer/AttachmentRenderer.js";
16
- import { default as default16 } from "./modules/editor/components/Renderer/AudioRenderer.js";
17
- import { default as default17 } from "./modules/editor/components/Renderer/ConversationHistoryRenderer.js";
18
- import { default as default18 } from "./modules/editor/components/Renderer/LinkerRenderer.js";
19
- import { default as default19 } from "./modules/editor/components/Renderer/MediaRenderer.js";
20
- import { default as default20 } from "./modules/editor/components/Renderer/InformationPaneRenderer.js";
21
- import { default as default21 } from "./modules/editor/components/Toolbar/TableToolbar.js";
22
- import { default as default22 } from "./modules/editor/components/Toolbar/LinkToolbar.js";
4
+ import { default as default4 } from "./modules/editor/components/Editor/EditorSkeleton.js";
5
+ import { default as default5 } from "./modules/editor/components/BubbleMenuEditImage/BubbleMenuEditImage.js";
6
+ import { default as default6 } from "./modules/editor/components/NodeView/AttachmentNodeView.js";
7
+ import { default as default7 } from "./modules/editor/components/NodeView/AudioNodeView.js";
8
+ import { default as default8 } from "./modules/editor/components/NodeView/ConversationHistoryNodeView.js";
9
+ import { default as default9 } from "./modules/editor/components/NodeView/ImageNodeView.js";
10
+ import { default as default10 } from "./modules/editor/components/NodeView/LinkerNodeView.js";
11
+ import { default as default11 } from "./modules/editor/components/NodeView/VideoNodeView.js";
12
+ import { default as default12 } from "./modules/editor/components/NodeView/InformationPaneNodeView.js";
13
+ import { default as default13 } from "./modules/editor/components/Renderer/AttachmentRenderer.js";
14
+ import { default as default14 } from "./modules/editor/components/Renderer/AudioRenderer.js";
15
+ import { default as default15 } from "./modules/editor/components/Renderer/ConversationHistoryRenderer.js";
16
+ import { default as default16 } from "./modules/editor/components/Renderer/LinkerRenderer.js";
17
+ import { default as default17 } from "./modules/editor/components/Renderer/MediaRenderer.js";
18
+ import { default as default18 } from "./modules/editor/components/Renderer/InformationPaneRenderer.js";
19
+ import { default as default19 } from "./modules/editor/components/Toolbar/TableToolbar.js";
20
+ import { default as default20 } from "./modules/editor/components/Toolbar/LinkToolbar.js";
23
21
  import { EditorToolbar } from "./modules/editor/components/EditorToolbar/EditorToolbar.js";
24
22
  import { useActionOptions } from "./modules/editor/hooks/useActionOptions.js";
25
23
  import { useCommentEditor } from "./modules/editor/hooks/useCommentEditor.js";
@@ -35,31 +33,29 @@ import { useSpeechSynthetisis } from "./modules/editor/hooks/useSpeechSynthetisi
35
33
  import { useTipTapEditor } from "./modules/editor/hooks/useTipTapEditor.js";
36
34
  import { useCantooEditor } from "./modules/editor/hooks/useCantooEditor.js";
37
35
  export {
38
- default8 as AttachmentNodeView,
39
- default15 as AttachmentRenderer,
40
- default9 as AudioNodeView,
41
- default16 as AudioRenderer,
42
- default7 as BubbleMenuEditImage,
43
- default10 as ConversationHistoryNodeView,
44
- default17 as ConversationHistoryRenderer,
36
+ default6 as AttachmentNodeView,
37
+ default13 as AttachmentRenderer,
38
+ default7 as AudioNodeView,
39
+ default14 as AudioRenderer,
40
+ default5 as BubbleMenuEditImage,
41
+ default8 as ConversationHistoryNodeView,
42
+ default15 as ConversationHistoryRenderer,
45
43
  default3 as Editor,
46
44
  EditorContent,
47
45
  EditorContext,
48
46
  Editor as EditorInstance,
49
- default4 as EditorPreview,
50
- default5 as EditorPreviewSkeleton,
51
- default6 as EditorSkeleton,
47
+ default4 as EditorSkeleton,
52
48
  EditorToolbar,
53
- default11 as ImageNodeView,
54
- default14 as InformationPaneNodeView,
55
- default20 as InformationPaneRenderer,
56
- default22 as LinkToolbar,
57
- default12 as LinkerNodeView,
58
- default18 as LinkerRenderer,
59
- default19 as MediaRenderer,
49
+ default9 as ImageNodeView,
50
+ default12 as InformationPaneNodeView,
51
+ default18 as InformationPaneRenderer,
52
+ default20 as LinkToolbar,
53
+ default10 as LinkerNodeView,
54
+ default16 as LinkerRenderer,
55
+ default17 as MediaRenderer,
60
56
  default2 as StarterKit,
61
- default21 as TableToolbar,
62
- default13 as VideoNodeView,
57
+ default19 as TableToolbar,
58
+ default11 as VideoNodeView,
63
59
  useActionOptions,
64
60
  useCantooEditor,
65
61
  useCommentEditor,
@@ -1,5 +1,3 @@
1
1
  export * from './Editor';
2
2
  export { default as Editor } from './Editor';
3
- export { default as EditorPreview } from './EditorPreview';
4
- export { default as EditorPreviewSkeleton } from './EditorPreviewSkeleton';
5
3
  export { default as EditorSkeleton } from './EditorSkeleton';
@@ -1,23 +1,18 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { forwardRef, useState, useRef, useImperativeHandle, useEffect, useCallback } from "react";
3
3
  import { odeServices } from "@edifice.io/client";
4
4
  import { getBestSupportedMimeType, convertMsToMS } from "@edifice.io/utilities";
5
5
  import { useTranslation } from "react-i18next";
6
- import SvgIconPause from "../../icons/components/IconPause.js";
7
- import SvgIconPlayFilled from "../../icons/components/IconPlayFilled.js";
8
- import SvgIconRecordStop from "../../icons/components/IconRecordStop.js";
9
6
  import SvgIconRecordVideo from "../../icons/components/IconRecordVideo.js";
10
7
  import SvgIconRecord from "../../icons/components/IconRecord.js";
11
- import SvgIconRefresh from "../../icons/components/IconRefresh.js";
12
- import SvgIconSave from "../../icons/components/IconSave.js";
8
+ import { useCameras } from "./useCameras.js";
9
+ import { VideoRecorderToolbar } from "./VideoRecorderToolbar.js";
13
10
  import useUpload from "../../../hooks/useUpload/useUpload.js";
14
- import useBrowserInfo from "../../../hooks/useBrowserInfo/useBrowserInfo.js";
15
11
  import FormControl from "../../../components/Form/FormControl.js";
16
12
  import Label from "../../../components/Label/Label.js";
17
13
  import Select from "../../../components/Select/Select.js";
18
14
  import LoadingScreen from "../../../components/LoadingScreen/LoadingScreen.js";
19
- import { Toolbar } from "../../../components/Toolbar/Toolbar.js";
20
- const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwardRef(({
15
+ const VideoRecorder = /* @__PURE__ */ forwardRef(({
21
16
  appCode,
22
17
  caption,
23
18
  onSuccess,
@@ -25,17 +20,14 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
25
20
  onRecordUpdated,
26
21
  hideSaveAction = !1
27
22
  }, ref) => {
28
- const [maxDuration, setMaxDuration] = useState(18e4), [inputDevices, setInputDevices] = useState([]), [recording, setRecording] = useState(!1), [recorded, setRecorded] = useState(!1), [playing, setPlaying] = useState(!1), [saving, setSaving] = useState(!1), [saved, setSaved] = useState(!1), [mediaStreamConstraints, setMediaStreamConstraints] = useState({
29
- audio: !0,
30
- video: {
31
- facingMode: "environment",
32
- aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT
33
- }
34
- }), [stream, setStream] = useState(), [mimeType, setMimeType] = useState(""), [recordedChunks, setRecordedChunks] = useState([]), [recordedVideo, setRecordedVideo] = useState(), [recordedTime, setRecordedTime] = useState(0), [playedTime, setPlayedTime] = useState(0), videoRef = useRef(null), recorderRef = useRef(null), {
23
+ const {
24
+ inputDevices,
25
+ setPreferedDevice,
26
+ restartStream,
27
+ stream
28
+ } = useCameras(), [maxDuration, setMaxDuration] = useState(18e4), [recording, setRecording] = useState(!1), [recorded, setRecorded] = useState(!1), [playing, setPlaying] = useState(!1), [saving, setSaving] = useState(!1), [saved, setSaved] = useState(!1), [mimeType, setMimeType] = useState(""), [recordedChunks, setRecordedChunks] = useState([]), [recordedVideo, setRecordedVideo] = useState(), [recordedTime, setRecordedTime] = useState(0), [playedTime, setPlayedTime] = useState(0), videoRef = useRef(null), recorderRef = useRef(null), {
35
29
  uploadBlob
36
- } = useUpload(void 0, appCode), {
37
- device
38
- } = useBrowserInfo(navigator.userAgent);
30
+ } = useUpload(void 0, appCode);
39
31
  useImperativeHandle(ref, () => ({
40
32
  save: handleSave
41
33
  }));
@@ -43,10 +35,23 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
43
35
  t
44
36
  } = useTranslation();
45
37
  useEffect(() => {
46
- initMaxDuration(), initInputDevices();
47
- }, []), useEffect(() => (stream || enableStream(mediaStreamConstraints), () => {
48
- stream && stream.getTracks().forEach((track) => track.stop());
49
- }), [stream]), useEffect(() => {
38
+ initMaxDuration();
39
+ }, []);
40
+ async function initMaxDuration() {
41
+ try {
42
+ const videoConfResponse = await odeServices.video().getVideoConf();
43
+ setMaxDuration(videoConfResponse.maxDuration * 60 * 1e3);
44
+ } catch {
45
+ setMaxDuration(3 * 60 * 1e3);
46
+ }
47
+ }
48
+ useEffect(() => {
49
+ try {
50
+ videoRef.current && (videoRef.current.src && (window.URL.revokeObjectURL(videoRef.current.src), videoRef.current.src = ""), stream && (videoRef.current.srcObject = stream, videoRef.current.autoplay = !0, videoRef.current.volume = 1, videoRef.current.muted = !0));
51
+ } catch (err) {
52
+ console.error(err);
53
+ }
54
+ }, [stream]), useEffect(() => {
50
55
  if (recordedChunks.length && !recording && videoRef.current) {
51
56
  const finalVideo = new Blob(recordedChunks, {
52
57
  type: mimeType
@@ -76,40 +81,7 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
76
81
  };
77
82
  }
78
83
  }, [playing]);
79
- const initMaxDuration = async () => {
80
- const videoConfResponse = await odeServices.video().getVideoConf();
81
- setMaxDuration((videoConfResponse.maxDuration ?? 3) * 60 * 1e3);
82
- }, initInputDevices = async () => {
83
- const videoDevices = (await navigator.mediaDevices.enumerateDevices()).filter((device2) => device2.kind === "videoinput");
84
- switch (device.type) {
85
- case "mobile":
86
- case "tablet": {
87
- const backCamera = {
88
- deviceId: "environment",
89
- label: t("video.back.camera"),
90
- groupId: "",
91
- kind: "videoinput"
92
- }, frontCamera = {
93
- deviceId: "user",
94
- label: t("video.front.camera"),
95
- groupId: "",
96
- kind: "videoinput"
97
- };
98
- (videoDevices == null ? void 0 : videoDevices.length) > 1 ? setInputDevices([backCamera, frontCamera]) : setInputDevices([backCamera]);
99
- break;
100
- }
101
- default:
102
- setInputDevices(videoDevices);
103
- break;
104
- }
105
- }, enableStream = async (mediaStreamConstraints2) => {
106
- try {
107
- const mediaStream = await navigator.mediaDevices.getUserMedia(mediaStreamConstraints2);
108
- setStream(mediaStream), videoRef.current && (videoRef.current.src && (window.URL.revokeObjectURL(videoRef.current.src), videoRef.current.src = ""), videoRef.current.srcObject = mediaStream, videoRef.current.autoplay = !0, videoRef.current.volume = 1, videoRef.current.muted = !0);
109
- } catch (err) {
110
- console.error(err);
111
- }
112
- }, handleRecord = useCallback(() => {
84
+ const handleRecord = useCallback(() => {
113
85
  setRecording(!0), videoRef && videoRef.current && (videoRef.current.muted = !0);
114
86
  const mimeType2 = getBestSupportedMimeType();
115
87
  setMimeType(mimeType2), stream && (recorderRef.current = new MediaRecorder(stream, {
@@ -126,7 +98,7 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
126
98
  var _a, _b;
127
99
  videoRef && videoRef.current && (videoRef.current.muted = !1), playing ? ((_b = videoRef == null ? void 0 : videoRef.current) == null || _b.pause(), setPlaying(!1)) : ((_a = videoRef == null ? void 0 : videoRef.current) == null || _a.play(), setPlaying(!0));
128
100
  }, [playing]), handleReset = () => {
129
- setRecorded(!1), setRecording(!1), setPlaying(!1), setSaved(!1), setRecordedTime(0), setRecordedChunks([]), setRecordedVideo(void 0), enableStream(mediaStreamConstraints), onRecordUpdated && onRecordUpdated();
101
+ setRecorded(!1), setRecording(!1), setPlaying(!1), setSaved(!1), setRecordedTime(0), setRecordedChunks([]), setRecordedVideo(void 0), restartStream(), onRecordUpdated && onRecordUpdated();
130
102
  }, handleSave = async () => {
131
103
  var _a;
132
104
  if ((_a = videoRef == null ? void 0 : videoRef.current) == null || _a.pause(), setSaving(!0), !recordedVideo) {
@@ -141,95 +113,14 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
141
113
  onError("Error while uploading video"), setSaving(!1), setSaved(!0);
142
114
  }, handleEnded = () => {
143
115
  setPlaying(!1), setPlayedTime(0), videoRef.current && (videoRef.current.currentTime = 0);
144
- }, handleInputDeviceChange = (option) => {
116
+ }, handleInputDeviceChange = useCallback((option) => {
145
117
  var _a;
146
118
  const selectedDevice = inputDevices.find((inputDevice) => inputDevice.label === option);
147
- let mediaStreamConstraints2 = {};
148
- selectedDevice != null && selectedDevice.deviceId ? ((selectedDevice == null ? void 0 : selectedDevice.deviceId) === "environment" || (selectedDevice == null ? void 0 : selectedDevice.deviceId) === "user" ? mediaStreamConstraints2 = {
149
- audio: !0,
150
- video: {
151
- aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
152
- facingMode: selectedDevice == null ? void 0 : selectedDevice.deviceId
153
- }
154
- } : mediaStreamConstraints2 = {
155
- audio: !0,
156
- video: {
157
- aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
158
- deviceId: selectedDevice.deviceId
159
- }
160
- }, setMediaStreamConstraints(mediaStreamConstraints2), stream && (((_a = recorderRef.current) == null ? void 0 : _a.state) === "recording" && (recorderRef.current.requestData(), recorderRef.current.stop()), stream.getTracks().forEach((track) => track.stop()), setStream(void 0)), enableStream(mediaStreamConstraints2)) : console.error("Selected input device id is null");
161
- };
162
- useEffect(() => {
119
+ ((_a = recorderRef.current) == null ? void 0 : _a.state) === "recording" && (recorderRef.current.requestData(), recorderRef.current.stop()), setPreferedDevice(selectedDevice);
120
+ }, [inputDevices, stream]);
121
+ return useEffect(() => {
163
122
  recordedTime >= maxDuration && handleStop();
164
- }, [recordedTime, handleStop]);
165
- const toolbarItems = [{
166
- type: "icon",
167
- name: "record",
168
- props: {
169
- icon: /* @__PURE__ */ jsx(SvgIconRecord, { color: recording || recorded ? "" : "red" }),
170
- color: "danger",
171
- disabled: recording || recorded || saving,
172
- onClick: handleRecord,
173
- "aria-label": t("bbm.video.record.start")
174
- },
175
- tooltip: t("bbm.video.record.start")
176
- }, {
177
- type: "icon",
178
- name: "stop",
179
- props: {
180
- icon: /* @__PURE__ */ jsx(SvgIconRecordStop, {}),
181
- disabled: !recording || recorded || saving,
182
- onClick: handleStop,
183
- "aria-label": t("bbm.video.record.stop")
184
- },
185
- tooltip: t("bbm.video.record.stop")
186
- }, {
187
- type: "icon",
188
- name: "play",
189
- visibility: playing ? "hide" : "show",
190
- props: {
191
- icon: /* @__PURE__ */ jsx(SvgIconPlayFilled, {}),
192
- disabled: !recorded || saving,
193
- onClick: handlePlayPause,
194
- "aria-label": t("bbm.video.play.start")
195
- },
196
- tooltip: t("bbm.video.play.start")
197
- }, {
198
- type: "icon",
199
- name: "pause",
200
- visibility: playing ? "show" : "hide",
201
- props: {
202
- icon: /* @__PURE__ */ jsx(SvgIconPause, {}),
203
- disabled: !recorded || saving,
204
- onClick: handlePlayPause,
205
- "aria-label": t("bbm.video.play.pause")
206
- },
207
- tooltip: t("bbm.video.play.pause")
208
- }, {
209
- type: "divider"
210
- }, {
211
- type: "icon",
212
- name: "reset",
213
- props: {
214
- icon: /* @__PURE__ */ jsx(SvgIconRefresh, {}),
215
- disabled: !recorded || saving,
216
- onClick: handleReset,
217
- "aria-label": t("bbm.video.record.reset")
218
- },
219
- tooltip: t("bbm.video.record.reset")
220
- }, {
221
- type: "icon",
222
- name: "save",
223
- visibility: hideSaveAction ? "hide" : "show",
224
- props: {
225
- icon: /* @__PURE__ */ jsx(SvgIconSave, {}),
226
- disabled: !recorded || saving || saved,
227
- onClick: handleSave,
228
- "aria-label": t("bbm.video.record.save")
229
- },
230
- tooltip: t("bbm.video.record.save")
231
- }];
232
- return /* @__PURE__ */ jsxs("div", { className: "video-recorder d-flex flex-fill flex-column align-items-center pb-8", children: [
123
+ }, [recordedTime, handleStop]), /* @__PURE__ */ jsxs("div", { className: "video-recorder d-flex flex-fill flex-column align-items-center pb-8", children: [
233
124
  /* @__PURE__ */ jsx("div", { className: "video-recorder-caption d-none d-md-block", children: caption }),
234
125
  inputDevices.length > 1 && /* @__PURE__ */ jsx("div", { className: "video-recorder-devices mb-12", children: /* @__PURE__ */ jsxs(FormControl, { id: "selectInputDevice", children: [
235
126
  /* @__PURE__ */ jsx(Label, { className: "d-none d-md-block", children: t("bbm.video.record.select.devices.label") }),
@@ -255,7 +146,7 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
255
146
  ] })
256
147
  ] })
257
148
  ] }),
258
- stream && /* @__PURE__ */ jsx(Toolbar, { items: toolbarItems, className: "position-absolute bottom-0 start-50 bg-white" })
149
+ stream && /* @__PURE__ */ jsx(VideoRecorderToolbar, { playing, recording, recorded, saving, saved, hideSaveAction, handleRecord, handleStop, handlePlayPause, handleReset, handleSave })
259
150
  ] }),
260
151
  saving && /* @__PURE__ */ jsx(LoadingScreen, { position: !1, caption: t("bbm.video.save.loader.caption") })
261
152
  ] });
@@ -0,0 +1,18 @@
1
+ import { WorkspaceElement } from '@edifice.io/client';
2
+ export interface VideoRecorderToolbarProps {
3
+ playing: boolean;
4
+ recording: boolean;
5
+ recorded: boolean;
6
+ saving: boolean;
7
+ saved: boolean;
8
+ hideSaveAction: boolean;
9
+ handleRecord: () => void;
10
+ handleStop: () => void;
11
+ handlePlayPause: () => void;
12
+ handleReset: () => void;
13
+ handleSave: () => void;
14
+ }
15
+ export interface VideoRecorderRef {
16
+ save: () => Promise<WorkspaceElement | undefined>;
17
+ }
18
+ export declare const VideoRecorderToolbar: ({ playing, recording, recorded, saving, saved, hideSaveAction, handleRecord, handleStop, handlePlayPause, handleReset, handleSave, }: VideoRecorderToolbarProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,96 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import SvgIconPause from "../../icons/components/IconPause.js";
4
+ import SvgIconPlayFilled from "../../icons/components/IconPlayFilled.js";
5
+ import SvgIconRecordStop from "../../icons/components/IconRecordStop.js";
6
+ import SvgIconRecord from "../../icons/components/IconRecord.js";
7
+ import SvgIconRefresh from "../../icons/components/IconRefresh.js";
8
+ import SvgIconSave from "../../icons/components/IconSave.js";
9
+ import { Toolbar } from "../../../components/Toolbar/Toolbar.js";
10
+ const VideoRecorderToolbar = ({
11
+ playing,
12
+ recording,
13
+ recorded,
14
+ saving,
15
+ saved,
16
+ hideSaveAction,
17
+ handleRecord,
18
+ handleStop,
19
+ handlePlayPause,
20
+ handleReset,
21
+ handleSave
22
+ }) => {
23
+ const {
24
+ t
25
+ } = useTranslation(), toolbarItems = [{
26
+ type: "icon",
27
+ name: "record",
28
+ props: {
29
+ icon: /* @__PURE__ */ jsx(SvgIconRecord, { color: recording || recorded ? "" : "red" }),
30
+ color: "danger",
31
+ disabled: recording || recorded || saving,
32
+ onClick: handleRecord,
33
+ "aria-label": t("bbm.video.record.start")
34
+ },
35
+ tooltip: t("bbm.video.record.start")
36
+ }, {
37
+ type: "icon",
38
+ name: "stop",
39
+ props: {
40
+ icon: /* @__PURE__ */ jsx(SvgIconRecordStop, {}),
41
+ disabled: !recording || recorded || saving,
42
+ onClick: handleStop,
43
+ "aria-label": t("bbm.video.record.stop")
44
+ },
45
+ tooltip: t("bbm.video.record.stop")
46
+ }, {
47
+ type: "icon",
48
+ name: "play",
49
+ visibility: playing ? "hide" : "show",
50
+ props: {
51
+ icon: /* @__PURE__ */ jsx(SvgIconPlayFilled, {}),
52
+ disabled: !recorded || saving,
53
+ onClick: handlePlayPause,
54
+ "aria-label": t("bbm.video.play.start")
55
+ },
56
+ tooltip: t("bbm.video.play.start")
57
+ }, {
58
+ type: "icon",
59
+ name: "pause",
60
+ visibility: playing ? "show" : "hide",
61
+ props: {
62
+ icon: /* @__PURE__ */ jsx(SvgIconPause, {}),
63
+ disabled: !recorded || saving,
64
+ onClick: handlePlayPause,
65
+ "aria-label": t("bbm.video.play.pause")
66
+ },
67
+ tooltip: t("bbm.video.play.pause")
68
+ }, {
69
+ type: "divider"
70
+ }, {
71
+ type: "icon",
72
+ name: "reset",
73
+ props: {
74
+ icon: /* @__PURE__ */ jsx(SvgIconRefresh, {}),
75
+ disabled: !recorded || saving,
76
+ onClick: handleReset,
77
+ "aria-label": t("bbm.video.record.reset")
78
+ },
79
+ tooltip: t("bbm.video.record.reset")
80
+ }, {
81
+ type: "icon",
82
+ name: "save",
83
+ visibility: hideSaveAction ? "hide" : "show",
84
+ props: {
85
+ icon: /* @__PURE__ */ jsx(SvgIconSave, {}),
86
+ disabled: !recorded || saving || saved,
87
+ onClick: handleSave,
88
+ "aria-label": t("bbm.video.record.save")
89
+ },
90
+ tooltip: t("bbm.video.record.save")
91
+ }];
92
+ return /* @__PURE__ */ jsx(Toolbar, { items: toolbarItems, className: "position-absolute bottom-0 start-50 bg-white" });
93
+ };
94
+ export {
95
+ VideoRecorderToolbar
96
+ };
@@ -0,0 +1,10 @@
1
+ export declare function useCameras(): {
2
+ /** Readonly list (array) of available video input devices. */
3
+ inputDevices: MediaDeviceInfo[];
4
+ /** Select which input video device to use. */
5
+ setPreferedDevice: (device?: MediaDeviceInfo) => void;
6
+ /** The current video stream. */
7
+ stream: MediaStream | undefined;
8
+ /** Start a video stream from the default or prefered device. */
9
+ restartStream: () => void;
10
+ };
@@ -0,0 +1,86 @@
1
+ import { useState, useEffect } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import useBrowserInfo from "../../../hooks/useBrowserInfo/useBrowserInfo.js";
4
+ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16;
5
+ function useCameras() {
6
+ const {
7
+ device
8
+ } = useBrowserInfo(navigator.userAgent), {
9
+ t
10
+ } = useTranslation(), [inputDevices, setInputDevices] = useState([]), [mediaStreamConstraints, setMediaStreamConstraints] = useState({
11
+ audio: !0,
12
+ video: {
13
+ facingMode: "environment",
14
+ aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT
15
+ }
16
+ }), [stream, setStream] = useState();
17
+ async function getVideoInputDevices() {
18
+ return (await navigator.mediaDevices.enumerateDevices()).filter((device2) => device2.kind === "videoinput");
19
+ }
20
+ async function enableStream(mediaStreamConstraints2) {
21
+ try {
22
+ const mediaStream = await navigator.mediaDevices.getUserMedia(mediaStreamConstraints2);
23
+ setStream((previousStream) => (previousStream == null || previousStream.getTracks().forEach((track) => track.stop()), mediaStream));
24
+ } catch (err) {
25
+ console.error(err);
26
+ }
27
+ }
28
+ const restartStream = () => {
29
+ enableStream(mediaStreamConstraints);
30
+ }, setPreferedDevice = (device2) => {
31
+ let mediaStreamConstraints2 = {};
32
+ device2 != null && device2.deviceId ? ((device2 == null ? void 0 : device2.deviceId) === "environment" || (device2 == null ? void 0 : device2.deviceId) === "user" ? mediaStreamConstraints2 = {
33
+ audio: !0,
34
+ video: {
35
+ aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
36
+ facingMode: device2 == null ? void 0 : device2.deviceId
37
+ }
38
+ } : mediaStreamConstraints2 = {
39
+ audio: !0,
40
+ video: {
41
+ aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
42
+ deviceId: device2.deviceId
43
+ }
44
+ }, setMediaStreamConstraints(mediaStreamConstraints2), restartStream()) : console.error("Selected input device id is null");
45
+ };
46
+ return useEffect(() => {
47
+ async function initInputDevices() {
48
+ await enableStream(mediaStreamConstraints);
49
+ const videoDevices = await getVideoInputDevices();
50
+ switch (device.type) {
51
+ case "mobile":
52
+ case "tablet": {
53
+ const backCamera = {
54
+ deviceId: "environment",
55
+ label: t("video.back.camera"),
56
+ groupId: "",
57
+ kind: "videoinput"
58
+ }, frontCamera = {
59
+ deviceId: "user",
60
+ label: t("video.front.camera"),
61
+ groupId: "",
62
+ kind: "videoinput"
63
+ };
64
+ (videoDevices == null ? void 0 : videoDevices.length) > 1 ? setInputDevices([backCamera, frontCamera]) : setInputDevices([backCamera]);
65
+ break;
66
+ }
67
+ default:
68
+ setInputDevices(videoDevices);
69
+ break;
70
+ }
71
+ }
72
+ initInputDevices();
73
+ }, []), {
74
+ /** Readonly list (array) of available video input devices. */
75
+ inputDevices,
76
+ /** Select which input video device to use. */
77
+ setPreferedDevice,
78
+ /** The current video stream. */
79
+ stream,
80
+ /** Start a video stream from the default or prefered device. */
81
+ restartStream
82
+ };
83
+ }
84
+ export {
85
+ useCameras
86
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edifice.io/react",
3
- "version": "2.3.1-develop-b2school-actualites.20250911174726",
3
+ "version": "2.3.1-develop-b2school.20250916133741",
4
4
  "description": "Edifice React Library",
5
5
  "keywords": [
6
6
  "react",
@@ -130,9 +130,9 @@
130
130
  "react-slugify": "^3.0.3",
131
131
  "swiper": "^10.1.0",
132
132
  "ua-parser-js": "^1.0.36",
133
- "@edifice.io/bootstrap": "2.3.1-develop-b2school-actualites.20250911174726",
134
- "@edifice.io/tiptap-extensions": "2.3.1-develop-b2school-actualites.20250911174726",
135
- "@edifice.io/utilities": "2.3.1-develop-b2school-actualites.20250911174726"
133
+ "@edifice.io/bootstrap": "2.3.1-develop-b2school.20250916133741",
134
+ "@edifice.io/tiptap-extensions": "2.3.1-develop-b2school.20250916133741",
135
+ "@edifice.io/utilities": "2.3.1-develop-b2school.20250916133741"
136
136
  },
137
137
  "devDependencies": {
138
138
  "@babel/plugin-transform-react-pure-annotations": "^7.23.3",
@@ -163,8 +163,8 @@
163
163
  "vite": "^5.4.11",
164
164
  "vite-plugin-dts": "^4.1.0",
165
165
  "vite-tsconfig-paths": "^5.0.1",
166
- "@edifice.io/client": "2.3.1-develop-b2school-actualites.20250911174726",
167
- "@edifice.io/config": "2.3.1-develop-b2school-actualites.20250911174726"
166
+ "@edifice.io/client": "2.3.1-develop-b2school.20250916133741",
167
+ "@edifice.io/config": "2.3.1-develop-b2school.20250916133741"
168
168
  },
169
169
  "peerDependencies": {
170
170
  "@react-spring/web": "^9.7.5",
@@ -1,14 +0,0 @@
1
- /**
2
- * Editor component properties
3
- */
4
- export interface EditorPreviewProps {
5
- /** Rich content to render. */
6
- content: string;
7
- /** Display with or without a border */
8
- variant?: 'outline' | 'ghost';
9
- onDetailClick?: () => void;
10
- onMediaClick?: () => void;
11
- maxMediaDisplayed?: number;
12
- }
13
- declare const EditorPreview: ({ content, variant, onDetailClick, onMediaClick, maxMediaDisplayed, }: EditorPreviewProps) => import("react/jsx-runtime").JSX.Element;
14
- export default EditorPreview;
@@ -1,56 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
3
- import clsx from "clsx";
4
- import { getThumbnail } from "@edifice.io/utilities";
5
- import { useTranslation } from "react-i18next";
6
- import Image from "../../../../components/Image/Image.js";
7
- const EditorPreview = ({
8
- content,
9
- variant = "outline",
10
- onDetailClick,
11
- onMediaClick,
12
- maxMediaDisplayed = 3
13
- }) => {
14
- const {
15
- t
16
- } = useTranslation(), [summaryContent, setSummaryContent] = useState(""), [medias, setMedias] = useState([]), borderClass = clsx(variant === "outline" && "border rounded-3 py-12 px-16"), hasMediaCallback = onDetailClick || onMediaClick, handleMediaClick = (e) => {
17
- onMediaClick && (e.stopPropagation(), onMediaClick());
18
- };
19
- return useEffect(() => {
20
- const contentHTML = content;
21
- if (contentHTML) {
22
- const doc = new DOMParser().parseFromString(contentHTML, "text/html"), mediaElements = Array.from(doc.querySelectorAll("img, video, iframe, audio, embed"));
23
- setMedias(mediaElements.filter((el) => el.tagName.toLowerCase() === "img").map((el) => {
24
- const image = el;
25
- return image.src ? {
26
- url: getThumbnail(image.src, 0, 300),
27
- alt: image.alt
28
- } : {
29
- url: "",
30
- alt: ""
31
- };
32
- })), mediaElements.forEach((el) => {
33
- var _a;
34
- return (_a = el.parentNode) == null ? void 0 : _a.removeChild(el);
35
- }), setSummaryContent(doc.body.textContent || "");
36
- }
37
- }, [content]), /* @__PURE__ */ jsxs("div", { className: borderClass, "data-testid": "editor-preview", onClick: onDetailClick, tabIndex: onDetailClick ? -1 : void 0, role: onDetailClick ? "button" : void 0, children: [
38
- /* @__PURE__ */ jsx("div", { className: "flex-fill text-truncate text-truncate-2 post-preview-content overflow-hidden", children: summaryContent }),
39
- /* @__PURE__ */ jsx("div", { onClick: handleMediaClick, tabIndex: hasMediaCallback ? -1 : void 0, role: hasMediaCallback ? "button" : void 0, className: "d-flex align-items-center justify-content-center gap-24 px-32 pt-16", children: medias.slice(0, maxMediaDisplayed).map((media, index) => /* @__PURE__ */ jsxs("div", { className: clsx("position-relative col-12 col-md-4 ", {
40
- "d-none d-md-block": index >= 1
41
- }), style: {
42
- maxWidth: "150px"
43
- }, children: [
44
- /* @__PURE__ */ jsx(Image, { alt: media.alt, objectFit: "cover", ratio: "16", className: "rounded", src: media.url, sizes: "" }),
45
- (index === 0 || index === 2) && medias.length - (index + 1) > 0 && /* @__PURE__ */ jsx("div", { className: clsx("position-absolute top-0 bottom-0 start-0 end-0 d-flex justify-content-center align-items-center rounded text-light bg-dark bg-opacity-50", {
46
- "d-flex d-md-none": index === 0,
47
- "d-none d-md-flex": index === 2
48
- }), children: t("editor.preview.moreMedia", {
49
- mediaCount: medias.length - (index + 1)
50
- }) })
51
- ] }, media.url)) })
52
- ] });
53
- };
54
- export {
55
- EditorPreview as default
56
- };
@@ -1,8 +0,0 @@
1
- /**
2
- * Editor component properties
3
- */
4
- export interface EditorPreviewSkeletonProps {
5
- variant?: 'outline' | 'ghost';
6
- }
7
- declare const EditorPreview: ({ variant }: EditorPreviewSkeletonProps) => import("react/jsx-runtime").JSX.Element;
8
- export default EditorPreview;
@@ -1,24 +0,0 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import clsx from "clsx";
3
- import TextSkeleton from "../../../../components/Skeleton/TextSkeleton.js";
4
- import Image from "../../../../components/Image/Image.js";
5
- const EditorPreview = ({
6
- variant = "outline"
7
- }) => {
8
- const borderClass = clsx(variant === "outline" && "border rounded-3"), contentClass = clsx("mt-16", variant === "outline" && "my-12 mx-16");
9
- return /* @__PURE__ */ jsx("div", { className: borderClass, "data-testid": "editor-preview", children: /* @__PURE__ */ jsxs("div", { className: contentClass, children: [
10
- /* @__PURE__ */ jsx(TextSkeleton, { className: "col-12" }),
11
- /* @__PURE__ */ jsx(TextSkeleton, { className: "col-12" }),
12
- /* @__PURE__ */ jsxs("div", { className: "d-flex justify-content-center gap-24 px-32 pt-16", children: [
13
- /* @__PURE__ */ jsx("div", { style: {
14
- maxWidth: "150px"
15
- }, className: "col-12 col-md-4", children: /* @__PURE__ */ jsx(Image, { alt: "", objectFit: "cover", ratio: "16", className: "rounded placeholder", src: "", sizes: "" }) }),
16
- /* @__PURE__ */ jsx("div", { style: {
17
- maxWidth: "150px"
18
- }, className: "col-12 col-md-4", children: /* @__PURE__ */ jsx(Image, { alt: "", objectFit: "cover", ratio: "16", className: "rounded placeholder", src: "", sizes: "" }) })
19
- ] })
20
- ] }) });
21
- };
22
- export {
23
- EditorPreview as default
24
- };