@edifice.io/react 2.3.1-develop-b2school-actualites.20250916153920 → 2.3.1-develop-b2school.20250918153251
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.d.ts +0 -2
- package/dist/editor.js +34 -38
- package/dist/icons.js +286 -288
- package/dist/index.js +0 -4
- package/dist/modules/editor/components/Editor/index.d.ts +0 -2
- package/dist/modules/icons/components/index.d.ts +0 -1
- package/dist/modules/multimedia/VideoRecorder/VideoRecorder.js +34 -143
- package/dist/modules/multimedia/VideoRecorder/VideoRecorderToolbar.d.ts +18 -0
- package/dist/modules/multimedia/VideoRecorder/VideoRecorderToolbar.js +96 -0
- package/dist/modules/multimedia/VideoRecorder/useCameras.d.ts +10 -0
- package/dist/modules/multimedia/VideoRecorder/useCameras.js +86 -0
- package/package.json +6 -6
- package/dist/components/Divider/Divider.d.ts +0 -5
- package/dist/components/Divider/Divider.js +0 -21
- package/dist/components/Divider/index.d.ts +0 -1
- package/dist/components/SeparatedInfo/SeparatedInfo.d.ts +0 -6
- package/dist/components/SeparatedInfo/SeparatedInfo.js +0 -13
- package/dist/components/SeparatedInfo/index.d.ts +0 -1
- package/dist/modules/editor/components/Editor/EditorPreview.d.ts +0 -14
- package/dist/modules/editor/components/Editor/EditorPreview.js +0 -56
- package/dist/modules/editor/components/Editor/EditorPreviewSkeleton.d.ts +0 -8
- package/dist/modules/editor/components/Editor/EditorPreviewSkeleton.js +0 -24
- package/dist/modules/icons/components/IconClockAlert.d.ts +0 -7
- package/dist/modules/icons/components/IconClockAlert.js +0 -17
package/dist/index.js
CHANGED
|
@@ -98,14 +98,12 @@ import { default as default97 } from "./modules/multimedia/VideoRecorder/VideoRe
|
|
|
98
98
|
import { default as default98 } from "./modules/multimedia/Workspace/Workspace.js";
|
|
99
99
|
import { default as default99 } from "./modules/multimedia/WorkspaceFolders/WorkspaceFolders.js";
|
|
100
100
|
import { AccessiblePalette, DefaultPalette } from "./components/ColorPicker/ColorPalette.js";
|
|
101
|
-
import { Divider } from "./components/Divider/Divider.js";
|
|
102
101
|
import { DropzoneContext, useDropzoneContext } from "./components/Dropzone/DropzoneContext.js";
|
|
103
102
|
import { Column, Grid } from "./components/Grid/Grid.js";
|
|
104
103
|
import { Layout } from "./components/Layout/Layout.js";
|
|
105
104
|
import { List } from "./components/List/List.js";
|
|
106
105
|
import { Menu } from "./components/Menu/components/Menu.js";
|
|
107
106
|
import { Popover, PopoverBody, PopoverFooter, PopoverHeader } from "./components/Popover/Popover.js";
|
|
108
|
-
import { SeparatedInfo } from "./components/SeparatedInfo/SeparatedInfo.js";
|
|
109
107
|
import { Tabs } from "./components/Tabs/components/Tabs.js";
|
|
110
108
|
import { Toolbar } from "./components/Toolbar/Toolbar.js";
|
|
111
109
|
import { useTreeSortable } from "./components/Tree/hooks/useTreeSortable.js";
|
|
@@ -150,7 +148,6 @@ export {
|
|
|
150
148
|
default18 as Combobox,
|
|
151
149
|
default83 as ConfirmModal,
|
|
152
150
|
DefaultPalette,
|
|
153
|
-
Divider,
|
|
154
151
|
DndTree,
|
|
155
152
|
default19 as Dropdown,
|
|
156
153
|
default20 as Dropzone,
|
|
@@ -195,7 +192,6 @@ export {
|
|
|
195
192
|
default35 as SearchBar,
|
|
196
193
|
default13 as SearchButton,
|
|
197
194
|
default36 as Select,
|
|
198
|
-
SeparatedInfo,
|
|
199
195
|
default89 as ShareBlog,
|
|
200
196
|
default88 as ShareModal,
|
|
201
197
|
default44 as SortableTree,
|
|
@@ -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';
|
|
@@ -24,7 +24,6 @@ export { default as IconCantoo } from './IconCantoo';
|
|
|
24
24
|
export { default as IconCenter } from './IconCenter';
|
|
25
25
|
export { default as IconCheck } from './IconCheck';
|
|
26
26
|
export { default as IconChecklist } from './IconChecklist';
|
|
27
|
-
export { default as IconClockAlert } from './IconClockAlert';
|
|
28
27
|
export { default as IconClock } from './IconClock';
|
|
29
28
|
export { default as IconCloseFullScreen } from './IconCloseFullScreen';
|
|
30
29
|
export { default as IconClose } from './IconClose';
|
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
import {
|
|
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
|
|
12
|
-
import
|
|
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
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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()
|
|
47
|
-
}, [])
|
|
48
|
-
|
|
49
|
-
|
|
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 = ""), videoRef.current.srcObject instanceof MediaStream && (videoRef.current.srcObject = null), stream && (videoRef.current.srcObject = stream, videoRef.current.autoplay = !0, videoRef.current.volume = 1, videoRef.current.muted = !0, videoRef.current.load()));
|
|
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
|
|
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),
|
|
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) {
|
|
@@ -144,92 +116,11 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
|
|
|
144
116
|
}, handleInputDeviceChange = (option) => {
|
|
145
117
|
var _a;
|
|
146
118
|
const selectedDevice = inputDevices.find((inputDevice) => inputDevice.label === option);
|
|
147
|
-
|
|
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");
|
|
119
|
+
((_a = recorderRef.current) == null ? void 0 : _a.state) === "recording" && (recorderRef.current.requestData(), recorderRef.current.stop()), setPreferedDevice(selectedDevice);
|
|
161
120
|
};
|
|
162
|
-
useEffect(() => {
|
|
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(
|
|
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: !0
|
|
13
|
+
}), [stream, setStream] = useState();
|
|
14
|
+
async function getVideoInputDevices() {
|
|
15
|
+
return (await navigator.mediaDevices.enumerateDevices()).filter((device2) => device2.kind === "videoinput");
|
|
16
|
+
}
|
|
17
|
+
const enableStream = async (constraints) => {
|
|
18
|
+
try {
|
|
19
|
+
const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
20
|
+
setStream(mediaStream);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error(err);
|
|
23
|
+
}
|
|
24
|
+
}, restartStream = () => {
|
|
25
|
+
stream == null || stream.getTracks().forEach((track) => track.stop()), enableStream(mediaStreamConstraints);
|
|
26
|
+
}, setPreferedDevice = (device2) => {
|
|
27
|
+
let constraints = {};
|
|
28
|
+
device2 != null && device2.deviceId ? (device2.deviceId === "environment" || device2.deviceId === "user" ? constraints = {
|
|
29
|
+
audio: !0,
|
|
30
|
+
video: {
|
|
31
|
+
aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
|
|
32
|
+
facingMode: device2 == null ? void 0 : device2.deviceId
|
|
33
|
+
}
|
|
34
|
+
} : constraints = {
|
|
35
|
+
audio: !0,
|
|
36
|
+
video: {
|
|
37
|
+
aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
|
|
38
|
+
deviceId: {
|
|
39
|
+
exact: device2.deviceId
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}, setMediaStreamConstraints(constraints)) : console.error("Selected input device id is null");
|
|
43
|
+
};
|
|
44
|
+
return useEffect(() => {
|
|
45
|
+
restartStream();
|
|
46
|
+
}, [mediaStreamConstraints]), 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
|
|
3
|
+
"version": "2.3.1-develop-b2school.20250918153251",
|
|
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
|
|
134
|
-
"@edifice.io/tiptap-extensions": "2.3.1-develop-b2school
|
|
135
|
-
"@edifice.io/utilities": "2.3.1-develop-b2school
|
|
133
|
+
"@edifice.io/bootstrap": "2.3.1-develop-b2school.20250918153251",
|
|
134
|
+
"@edifice.io/tiptap-extensions": "2.3.1-develop-b2school.20250918153251",
|
|
135
|
+
"@edifice.io/utilities": "2.3.1-develop-b2school.20250918153251"
|
|
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
|
|
167
|
-
"@edifice.io/config": "2.3.1-develop-b2school
|
|
166
|
+
"@edifice.io/client": "2.3.1-develop-b2school.20250918153251",
|
|
167
|
+
"@edifice.io/config": "2.3.1-develop-b2school.20250918153251"
|
|
168
168
|
},
|
|
169
169
|
"peerDependencies": {
|
|
170
170
|
"@react-spring/web": "^9.7.5",
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import Flex from "../Flex/Flex.js";
|
|
3
|
-
function Divider({
|
|
4
|
-
children,
|
|
5
|
-
color = "var(--edifice-gray-400)"
|
|
6
|
-
}) {
|
|
7
|
-
return /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "around", gap: "16", wrap: "nowrap", className: "flex-fill", children: [
|
|
8
|
-
/* @__PURE__ */ jsx("hr", { className: "divider m-12 ms-0 flex-fill", style: {
|
|
9
|
-
borderColor: color
|
|
10
|
-
} }),
|
|
11
|
-
/* @__PURE__ */ jsxs(Flex, { gap: "12", align: "center", justify: "around", children: [
|
|
12
|
-
...children
|
|
13
|
-
] }),
|
|
14
|
-
/* @__PURE__ */ jsx("hr", { className: "divider m-12 me-0 flex-fill", style: {
|
|
15
|
-
borderColor: color
|
|
16
|
-
} })
|
|
17
|
-
] });
|
|
18
|
-
}
|
|
19
|
-
export {
|
|
20
|
-
Divider
|
|
21
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './Divider';
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { jsxs } from "react/jsx-runtime";
|
|
2
|
-
import Flex from "../Flex/Flex.js";
|
|
3
|
-
function SeparatedInfo({
|
|
4
|
-
children,
|
|
5
|
-
className
|
|
6
|
-
}) {
|
|
7
|
-
return /* @__PURE__ */ jsxs(Flex, { direction: "row", className: `separated-info ${className}`, children: [
|
|
8
|
-
...children
|
|
9
|
-
] });
|
|
10
|
-
}
|
|
11
|
-
export {
|
|
12
|
-
SeparatedInfo
|
|
13
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './SeparatedInfo';
|
|
@@ -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
|
-
};
|