@seafile/sdoc-editor 1.0.210 → 1.0.212

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 (34) hide show
  1. package/dist/basic-sdk/extension/commons/file-insert-dialog/index.js +1 -1
  2. package/dist/basic-sdk/extension/commons/insert-element-dialog/index.js +21 -0
  3. package/dist/basic-sdk/extension/commons/select-file-dialog/helpers.js +8 -3
  4. package/dist/basic-sdk/extension/commons/select-file-dialog/index.js +48 -8
  5. package/dist/basic-sdk/extension/commons/select-file-dialog/local-files/index.css +1 -0
  6. package/dist/basic-sdk/extension/commons/select-file-dialog/local-files/index.js +6 -2
  7. package/dist/basic-sdk/extension/constants/element-type.js +2 -1
  8. package/dist/basic-sdk/extension/constants/index.js +2 -1
  9. package/dist/basic-sdk/extension/core/queries/index.js +22 -1
  10. package/dist/basic-sdk/extension/plugins/check-list/plugin.js +13 -10
  11. package/dist/basic-sdk/extension/plugins/code-block/plugin.js +48 -37
  12. package/dist/basic-sdk/extension/plugins/header/plugin.js +8 -0
  13. package/dist/basic-sdk/extension/plugins/link/helpers.js +18 -19
  14. package/dist/basic-sdk/extension/plugins/link/plugin.js +1 -1
  15. package/dist/basic-sdk/extension/plugins/paragraph/plugin.js +17 -1
  16. package/dist/basic-sdk/extension/plugins/sdoc-link/helpers.js +3 -3
  17. package/dist/basic-sdk/extension/plugins/sdoc-link/render/render-elem.js +1 -1
  18. package/dist/basic-sdk/extension/plugins/video/dialog/add-video-link-dialog/index.js +99 -0
  19. package/dist/basic-sdk/extension/plugins/video/helpers.js +61 -8
  20. package/dist/basic-sdk/extension/plugins/video/index.css +4 -3
  21. package/dist/basic-sdk/extension/plugins/video/menu/index.js +24 -1
  22. package/dist/basic-sdk/extension/plugins/video/render-elem.js +20 -7
  23. package/dist/constants/index.js +1 -0
  24. package/package.json +1 -1
  25. package/public/locales/cs/sdoc-editor.json +6 -2
  26. package/public/locales/de/sdoc-editor.json +311 -307
  27. package/public/locales/en/sdoc-editor.json +6 -2
  28. package/public/locales/es/sdoc-editor.json +6 -2
  29. package/public/locales/es_AR/sdoc-editor.json +6 -2
  30. package/public/locales/es_MX/sdoc-editor.json +6 -2
  31. package/public/locales/fr/sdoc-editor.json +6 -2
  32. package/public/locales/it/sdoc-editor.json +6 -2
  33. package/public/locales/ru/sdoc-editor.json +7 -3
  34. package/public/locales/zh_CN/sdoc-editor.json +5 -1
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _reactI18next = require("react-i18next");
10
+ var _reactstrap = require("reactstrap");
11
+ var _helpers = require("../../helpers");
12
+ const AddVideoLinkDialog = _ref => {
13
+ let {
14
+ editor,
15
+ className,
16
+ closeDialog,
17
+ handleSubmit
18
+ } = _ref;
19
+ const {
20
+ t
21
+ } = (0, _reactI18next.useTranslation)('sdoc-editor');
22
+ const [linkErrorMessage, setLinkErrorMessage] = (0, _react.useState)('');
23
+ const [url, setURL] = (0, _react.useState)('');
24
+ const isValidVideoLink = link => {
25
+ const videoRegex = /v\.qq\.com|youtube\.com|v\.youku\.com|bilibili\.com/i;
26
+ return videoRegex.test(link);
27
+ };
28
+ const submit = (0, _react.useCallback)(() => {
29
+ setLinkErrorMessage('');
30
+ if (!url) {
31
+ setLinkErrorMessage(t('The_link_address_is_required'));
32
+ return;
33
+ }
34
+ if (!isValidVideoLink(url)) {
35
+ setLinkErrorMessage(t('The_link_address_is_invalid'));
36
+ return;
37
+ }
38
+ (0, _helpers.insertVideo)(editor, [{
39
+ name: url,
40
+ isEmbeddableLink: true
41
+ }], [url]);
42
+ handleSubmit && handleSubmit();
43
+ closeDialog();
44
+
45
+ // eslint-disable-next-line react-hooks/exhaustive-deps
46
+ }, [editor, url]);
47
+ const onKeyDown = (0, _react.useCallback)(event => {
48
+ if (event.keyCode === 13) {
49
+ event.preventDefault();
50
+ submit();
51
+ return;
52
+ }
53
+ // eslint-disable-next-line react-hooks/exhaustive-deps
54
+ }, [editor, url]);
55
+ const handleUrlChange = (0, _react.useCallback)(event => {
56
+ const videoUrl = event.target.value;
57
+ if (videoUrl === url) return;
58
+ setURL(videoUrl);
59
+ }, [url]);
60
+ return /*#__PURE__*/_react.default.createElement(_reactstrap.Modal, {
61
+ isOpen: true,
62
+ autoFocus: false,
63
+ toggle: closeDialog,
64
+ className: className,
65
+ zIndex: 1071,
66
+ returnFocusAfterClose: false
67
+ }, /*#__PURE__*/_react.default.createElement(_reactstrap.ModalHeader, {
68
+ toggle: closeDialog
69
+ }, t('Insert_link')), /*#__PURE__*/_react.default.createElement(_reactstrap.ModalBody, null, /*#__PURE__*/_react.default.createElement(_react.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
70
+ className: "form-group"
71
+ }, /*#__PURE__*/_react.default.createElement(_reactstrap.Label, {
72
+ for: "addLink"
73
+ }, t('Link_address')), /*#__PURE__*/_react.default.createElement("input", {
74
+ onKeyDown: onKeyDown,
75
+ autoFocus: true,
76
+ type: "url",
77
+ className: "form-control",
78
+ id: "addVideoLink",
79
+ value: url || '',
80
+ onChange: handleUrlChange
81
+ }), linkErrorMessage && /*#__PURE__*/_react.default.createElement(_reactstrap.Alert, {
82
+ color: "danger",
83
+ className: "mt-2"
84
+ }, t(linkErrorMessage))), /*#__PURE__*/_react.default.createElement("div", {
85
+ style: {
86
+ textAlign: 'center',
87
+ fontSize: '12px',
88
+ color: '#787774'
89
+ }
90
+ }, t('Support_Youtube_Tencent_Bilibili_and_more')))), /*#__PURE__*/_react.default.createElement(_reactstrap.ModalFooter, null, /*#__PURE__*/_react.default.createElement(_reactstrap.Button, {
91
+ color: "secondary",
92
+ onClick: closeDialog
93
+ }, t('Cancel')), /*#__PURE__*/_react.default.createElement(_reactstrap.Button, {
94
+ color: "primary",
95
+ disabled: false,
96
+ onClick: submit
97
+ }, t('Add_link'))));
98
+ };
99
+ var _default = exports.default = AddVideoLinkDialog;
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.videoFileIcon = exports.isInsertVideoMenuDisabled = exports.insertVideo = exports.getVideoURL = exports.generateVideoNode = exports.formatFileSize = void 0;
7
+ exports.videoFileIcon = exports.parseVideoLink = exports.isInsertVideoMenuDisabled = exports.insertVideo = exports.getVideoURL = exports.generateVideoNode = exports.formatFileSize = void 0;
8
8
  var _urlJoin = _interopRequireDefault(require("url-join"));
9
9
  var _slate = require("@seafile/slate");
10
10
  var _core = require("../../core");
@@ -43,26 +43,79 @@ const isInsertVideoMenuDisabled = (editor, readonly) => {
43
43
  return false;
44
44
  };
45
45
  exports.isInsertVideoMenuDisabled = isInsertVideoMenuDisabled;
46
- const generateVideoNode = (src, videoFiles) => {
46
+ const generateVideoNode = (src, videoInfo) => {
47
47
  const element = (0, _core.generateEmptyElement)(_constants.VIDEO);
48
- return {
48
+ const {
49
+ name,
50
+ size,
51
+ is_embeddable_link
52
+ } = videoInfo;
53
+ return [{
49
54
  ...element,
50
55
  data: {
51
56
  src,
52
- videoFiles
57
+ name,
58
+ size,
59
+ is_embeddable_link
53
60
  }
54
- };
61
+ }];
55
62
  };
56
63
  exports.generateVideoNode = generateVideoNode;
64
+ const parseVideoLink = url => {
65
+ if (!url) return false;
66
+
67
+ // Youtube url conversion
68
+ if (url.includes('youtube.com')) {
69
+ const videoId = new URL(url).searchParams.get('v');
70
+ const videoUrl = videoId ? `https://www.youtube.com/embed/${videoId}/?rel=0` : false;
71
+ return videoUrl;
72
+ }
73
+
74
+ // Tencent url conversion
75
+ if (url.includes('v.qq.com')) {
76
+ const vidMatch = url.match(/\/([^\/]+)\.html/);
77
+ const videoUrl = vidMatch !== null && vidMatch !== void 0 && vidMatch[1] ? `https://v.qq.com/txp/iframe/player.html?vid=${vidMatch[1]}` : false;
78
+ return videoUrl;
79
+ }
80
+
81
+ // Bilibili url conversion
82
+ if (url.includes('bilibili.com')) {
83
+ const vidMatch = url.match(/\/video\/(BV[0-9A-Za-z]+)/);
84
+ const videoUrl = vidMatch !== null && vidMatch !== void 0 && vidMatch[1] ? `https://player.bilibili.com/player.html?bvid=${vidMatch[1]}` : false;
85
+ return videoUrl;
86
+ }
87
+
88
+ // Youku url conversion
89
+ if (url.includes('v.youku.com')) {
90
+ const vidMatch = url.match(/id_([A-Za-z0-9=]+)\.html/);
91
+ const videoUrl = vidMatch !== null && vidMatch !== void 0 && vidMatch[1] ? `https://player.youku.com/embed/${vidMatch[1]}` : false;
92
+ return videoUrl;
93
+ }
94
+
95
+ // Unsupported url
96
+ return false;
97
+ };
98
+ exports.parseVideoLink = parseVideoLink;
57
99
  const insertVideo = function (editor, videoFiles, srcList, selection) {
58
100
  let position = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : _constants.INSERT_POSITION.CURRENT;
59
101
  if (!srcList) return;
60
102
  if (position !== _constants.INSERT_POSITION.AFTER) {
61
103
  if (isInsertVideoMenuDisabled(editor)) return;
62
104
  }
63
- const videoNodes = srcList.map(src => {
64
- return generateVideoNode(src, videoFiles);
65
- });
105
+ const videoInfo = {
106
+ name: videoFiles[0].name || null,
107
+ size: videoFiles[0].size || null,
108
+ is_embeddable_link: videoFiles[0].isEmbeddableLink || false
109
+ };
110
+ let videoNodes;
111
+ // Return when embedding an invalid youtube, tencent or bilibili video url
112
+ if (videoInfo.is_embeddable_link) {
113
+ const parsedSrc = parseVideoLink(srcList[0]);
114
+ if (!parsedSrc) return;
115
+ videoNodes = generateVideoNode(parsedSrc, videoInfo);
116
+ } else {
117
+ videoNodes = generateVideoNode(srcList[0], videoInfo);
118
+ }
66
119
  const validSelection = selection || editor.selection;
67
120
  let path = _slate.Editor.path(editor, validSelection);
68
121
  if (position === _constants.INSERT_POSITION.AFTER) {
@@ -1,7 +1,7 @@
1
1
  .video-loading-placeholder {
2
2
  background-color: #F2F2F2;
3
3
  width: 100%;
4
- height: 45px;
4
+ height: auto;
5
5
  display: flex;
6
6
  align-items: center;
7
7
  }
@@ -49,14 +49,15 @@
49
49
 
50
50
  .sdoc-video-wrapper .sdoc-video-inner {
51
51
  position: relative;
52
- width: 670px;
52
+ width: 100%;
53
+ aspect-ratio: 16 / 9;
53
54
  display: flex;
54
55
  margin-top: 5px;
55
56
  margin-bottom: 10px;
56
57
  }
57
58
 
58
59
  .sdoc-video-inner .sdoc-video-element{
59
- width: 670px;
60
+ width: 100%;
60
61
  }
61
62
 
62
63
  .sdoc-video-inner .sdoc-video-element:focus-visible {
@@ -17,6 +17,7 @@ const VideoMenu = _ref => {
17
17
  let {
18
18
  editor,
19
19
  readonly,
20
+ toggle,
20
21
  eventBus
21
22
  } = _ref;
22
23
  const disabled = (0, _helpers.isInsertVideoMenuDisabled)(editor, readonly);
@@ -30,6 +31,22 @@ const VideoMenu = _ref => {
30
31
  editor
31
32
  });
32
33
  }, [editor, eventBus]);
34
+ const addVideoLink = (0, _react.useCallback)(() => {
35
+ eventBus.dispatch(_constants2.INTERNAL_EVENT.INSERT_ELEMENT, {
36
+ type: _constants.ELEMENT_TYPE.VIDEO_LINK,
37
+ editor
38
+ });
39
+ toggle && toggle();
40
+ // eslint-disable-next-line react-hooks/exhaustive-deps
41
+ }, [editor, eventBus]);
42
+ const openSelectVideoFileDialog = (0, _react.useCallback)(() => {
43
+ eventBus.dispatch(_constants2.INTERNAL_EVENT.INSERT_ELEMENT, {
44
+ type: _constants.ELEMENT_TYPE.VIDEO,
45
+ insertVideo: _helpers.insertVideo
46
+ });
47
+ toggle && toggle();
48
+ // eslint-disable-next-line react-hooks/exhaustive-deps
49
+ }, [toggle, eventBus]);
33
50
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_dropdownMenuItem.default, {
34
51
  disabled: disabled,
35
52
  menuConfig: menuConfig,
@@ -48,6 +65,12 @@ const VideoMenu = _ref => {
48
65
  }, /*#__PURE__*/_react.default.createElement("div", {
49
66
  className: "sdoc-dropdown-menu-item",
50
67
  onClick: openLocalVideoDialog
51
- }, t('Upload_local_video')))));
68
+ }, t('Upload_local_video')), /*#__PURE__*/_react.default.createElement("div", {
69
+ className: "sdoc-dropdown-menu-item",
70
+ onClick: addVideoLink
71
+ }, t('Add_video_link')), /*#__PURE__*/_react.default.createElement("div", {
72
+ className: "sdoc-dropdown-menu-item",
73
+ onClick: openSelectVideoFileDialog
74
+ }, t('Link_Seafile_video_file')))));
52
75
  };
53
76
  var _default = exports.default = VideoMenu;
@@ -12,7 +12,7 @@ var _reactI18next = require("react-i18next");
12
12
  var _helpers = require("./helpers");
13
13
  require("./index.css");
14
14
  const Video = _ref => {
15
- var _data$videoFiles$, _data$videoFiles$2, _videoStates$element$;
15
+ var _videoStates$element$;
16
16
  let {
17
17
  element,
18
18
  editor
@@ -24,8 +24,9 @@ const Video = _ref => {
24
24
  const [isLoaded, setIsLoaded] = (0, _react.useState)(false);
25
25
  const [isSelected, setIsSelected] = (0, _react.useState)(false);
26
26
  const [videoStates, setVideoStates] = (0, _react.useState)({});
27
- const videoFileName = data.videoFiles && ((_data$videoFiles$ = data.videoFiles[0]) === null || _data$videoFiles$ === void 0 ? void 0 : _data$videoFiles$.name) || null;
28
- const videoFileSize = data.videoFiles && ((_data$videoFiles$2 = data.videoFiles[0]) === null || _data$videoFiles$2 === void 0 ? void 0 : _data$videoFiles$2.size) || null;
27
+ const videoName = (data === null || data === void 0 ? void 0 : data.name) || (data === null || data === void 0 ? void 0 : data.src);
28
+ const videoSize = data === null || data === void 0 ? void 0 : data.size;
29
+ const isEmbeddableLink = data === null || data === void 0 ? void 0 : data.is_embeddable_link;
29
30
  const handlePlay = () => {
30
31
  setVideoStates(prev => ({
31
32
  ...prev,
@@ -75,9 +76,9 @@ const Video = _ref => {
75
76
  alt: ""
76
77
  }), /*#__PURE__*/_react.default.createElement("div", {
77
78
  className: "video-file-info"
78
- }, /*#__PURE__*/_react.default.createElement("div", null, videoFileName), /*#__PURE__*/_react.default.createElement("div", {
79
+ }, /*#__PURE__*/_react.default.createElement("div", null, videoName), /*#__PURE__*/_react.default.createElement("div", {
79
80
  className: "file-size"
80
- }, /*#__PURE__*/_react.default.createElement("span", null, (0, _helpers.formatFileSize)(videoFileSize), " \u2014 "), /*#__PURE__*/_react.default.createElement("div", {
81
+ }, /*#__PURE__*/_react.default.createElement("span", null, (0, _helpers.formatFileSize)(videoSize), " \u2014 "), /*#__PURE__*/_react.default.createElement("div", {
81
82
  className: "loading-spinner"
82
83
  }, /*#__PURE__*/_react.default.createElement("div", {
83
84
  className: "spinner"
@@ -92,10 +93,10 @@ const Video = _ref => {
92
93
  style: {
93
94
  visibility: isLoaded ? 'visible' : 'hidden'
94
95
  }
95
- }, /*#__PURE__*/_react.default.createElement("video", {
96
+ }, !isEmbeddableLink && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("video", {
96
97
  className: "sdoc-video-element",
97
98
  ref: videoRef,
98
- src: (0, _helpers.getVideoURL)(data, editor),
99
+ src: (0, _helpers.getVideoURL)(data),
99
100
  controls: true,
100
101
  onClick: onClickVideo,
101
102
  draggable: false,
@@ -111,6 +112,18 @@ const Video = _ref => {
111
112
  visibility: isPaused ? 'visible' : 'hidden'
112
113
  },
113
114
  contentEditable: "false"
115
+ })), isEmbeddableLink && /*#__PURE__*/_react.default.createElement("iframe", {
116
+ className: "sdoc-video-element",
117
+ title: data.src,
118
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
119
+ allowFullScreen: true,
120
+ src: (0, _helpers.getVideoURL)(data),
121
+ onLoad: handleVideoLoad,
122
+ style: {
123
+ width: '100%',
124
+ height: '100%',
125
+ border: 'none'
126
+ }
114
127
  }))));
115
128
  };
116
129
  const SdocVideo = (0, _reactI18next.withTranslation)('sdoc-editor')(Video);
@@ -36,6 +36,7 @@ const EXTERNAL_EVENT = exports.EXTERNAL_EVENT = {
36
36
  PARTICIPANT_REMOVED: 'participant-removed',
37
37
  CREATE_SDOC_FILE: 'create_sdoc_file',
38
38
  CREATE_WIKI_PAGE: 'create_wiki_page',
39
+ ADD_VIDEO_LINK: 'add_video_link',
39
40
  // wiki
40
41
  INSERT_LINK: 'insert_link',
41
42
  // document
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "1.0.210",
3
+ "version": "1.0.212",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -573,7 +573,7 @@
573
573
  "Five_column": "5 columns",
574
574
  "Full_width_mode": "Full width mode",
575
575
  "Video": "Video",
576
- "Upload_local_video": "Upload_local_video",
576
+ "Upload_local_video": "Upload local video",
577
577
  "The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
578
578
  "Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
579
579
  "Link_to_page": "Link to page",
@@ -606,5 +606,9 @@
606
606
  "Ask_AI": "Ask AI",
607
607
  "Enter_reply": "Enter reply",
608
608
  "Processing_content_cannot_be_empty": "Processing content cannot be empty",
609
- "AI_error_message": "Request error, please try again"
609
+ "AI_error_message": "Request error, please try again",
610
+ "Add_video_link": "Add video link",
611
+ "Link_Seafile_video_file": "Link Seafile video file",
612
+ "Select_video_file": "Select video file",
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
610
614
  }