5htp-core 0.4.8 → 0.4.9

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 (187) hide show
  1. package/package.json +5 -1
  2. package/src/client/assets/css/components/table.less +2 -0
  3. package/src/client/components/Form.ts +1 -1
  4. package/src/client/components/button.tsx +2 -1
  5. package/src/client/components/containers/Popover/index.tsx +2 -2
  6. package/src/client/components/dropdown/index.tsx +16 -6
  7. package/src/client/components/input/Slider/index.tsx +0 -2
  8. package/src/client/components/inputv3/Rte/Editor.tsx +271 -0
  9. package/src/client/components/inputv3/Rte/ToolbarPlugin/BlockFormat.tsx +220 -0
  10. package/src/client/components/inputv3/Rte/ToolbarPlugin/ElementFormat.tsx +107 -0
  11. package/src/client/components/inputv3/Rte/ToolbarPlugin/index.tsx +768 -0
  12. package/src/client/components/inputv3/Rte/appSettings.ts +36 -0
  13. package/src/client/components/inputv3/Rte/context/FlashMessageContext.tsx +68 -0
  14. package/src/client/components/inputv3/Rte/context/SettingsContext.tsx +71 -0
  15. package/src/client/components/inputv3/Rte/context/SharedAutocompleteContext.tsx +71 -0
  16. package/src/client/components/inputv3/Rte/context/SharedHistoryContext.tsx +35 -0
  17. package/src/client/components/inputv3/Rte/currentEditor.ts +42 -0
  18. package/src/client/components/inputv3/Rte/hooks/useFlashMessage.tsx +16 -0
  19. package/src/client/components/inputv3/Rte/hooks/useReport.ts +67 -0
  20. package/src/client/components/inputv3/Rte/images/emoji/1F600.png +0 -0
  21. package/src/client/components/inputv3/Rte/images/emoji/1F641.png +0 -0
  22. package/src/client/components/inputv3/Rte/images/emoji/1F642.png +0 -0
  23. package/src/client/components/inputv3/Rte/images/emoji/2764.png +0 -0
  24. package/src/client/components/inputv3/Rte/images/emoji/LICENSE.md +5 -0
  25. package/src/client/components/inputv3/Rte/images/icons/draggable-block-menu.svg +1 -0
  26. package/src/client/components/inputv3/Rte/images/icons/prettier-error.svg +1 -0
  27. package/src/client/components/inputv3/Rte/images/icons/prettier.svg +1 -0
  28. package/src/client/components/inputv3/Rte/images/image/LICENSE.md +5 -0
  29. package/src/client/components/inputv3/Rte/images/image-broken.svg +4 -0
  30. package/src/client/components/inputv3/Rte/images/logo.svg +1 -0
  31. package/src/client/components/inputv3/Rte/index.tsx +63 -79
  32. package/src/client/components/inputv3/Rte/nodes/AutocompleteNode.tsx +119 -0
  33. package/src/client/components/inputv3/Rte/nodes/EmojiNode.tsx +102 -0
  34. package/src/client/components/inputv3/Rte/nodes/EquationComponent.tsx +141 -0
  35. package/src/client/components/inputv3/Rte/nodes/EquationNode.tsx +174 -0
  36. package/src/client/components/inputv3/Rte/nodes/FigmaNode.tsx +135 -0
  37. package/src/client/components/inputv3/Rte/nodes/ImageComponent.tsx +468 -0
  38. package/src/client/components/inputv3/Rte/nodes/ImageNode.css +43 -0
  39. package/src/client/components/inputv3/Rte/nodes/ImageNode.tsx +266 -0
  40. package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageComponent.tsx +402 -0
  41. package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageNode.css +94 -0
  42. package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageNode.tsx +294 -0
  43. package/src/client/components/inputv3/Rte/nodes/KeywordNode.ts +67 -0
  44. package/src/client/components/inputv3/Rte/nodes/LayoutContainerNode.ts +137 -0
  45. package/src/client/components/inputv3/Rte/nodes/LayoutItemNode.ts +71 -0
  46. package/src/client/components/inputv3/Rte/nodes/MentionNode.ts +130 -0
  47. package/src/client/components/inputv3/Rte/nodes/PageBreakNode/index.css +62 -0
  48. package/src/client/components/inputv3/Rte/nodes/PageBreakNode/index.tsx +170 -0
  49. package/src/client/components/inputv3/Rte/nodes/PlaygroundNodes.ts +76 -0
  50. package/src/client/components/inputv3/Rte/nodes/PollComponent.tsx +249 -0
  51. package/src/client/components/inputv3/Rte/nodes/PollNode.css +187 -0
  52. package/src/client/components/inputv3/Rte/nodes/PollNode.tsx +209 -0
  53. package/src/client/components/inputv3/Rte/nodes/StickyComponent.tsx +261 -0
  54. package/src/client/components/inputv3/Rte/nodes/StickyNode.css +37 -0
  55. package/src/client/components/inputv3/Rte/nodes/StickyNode.tsx +150 -0
  56. package/src/client/components/inputv3/Rte/nodes/TweetNode.tsx +223 -0
  57. package/src/client/components/inputv3/Rte/nodes/YouTubeNode.tsx +184 -0
  58. package/src/client/components/inputv3/Rte/plugins/ActionsPlugin/index.tsx +334 -0
  59. package/src/client/components/inputv3/Rte/plugins/AutoEmbedPlugin/index.tsx +352 -0
  60. package/src/client/components/inputv3/Rte/plugins/AutoLinkPlugin/index.tsx +32 -0
  61. package/src/client/components/inputv3/Rte/plugins/AutocompletePlugin/index.tsx +2529 -0
  62. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/CopyButton/index.tsx +70 -0
  63. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/PrettierButton/index.css +14 -0
  64. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/PrettierButton/index.tsx +156 -0
  65. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/index.css +54 -0
  66. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/index.tsx +190 -0
  67. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/utils.ts +33 -0
  68. package/src/client/components/inputv3/Rte/plugins/CodeHighlightPlugin/index.ts +21 -0
  69. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/Collapsible.css +57 -0
  70. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleContainerNode.ts +168 -0
  71. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleContentNode.ts +127 -0
  72. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleTitleNode.ts +152 -0
  73. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleUtils.ts +17 -0
  74. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/index.ts +284 -0
  75. package/src/client/components/inputv3/Rte/plugins/ComponentPickerPlugin/index.tsx +370 -0
  76. package/src/client/components/inputv3/Rte/plugins/ContextMenuPlugin/index.tsx +270 -0
  77. package/src/client/components/inputv3/Rte/plugins/DocsPlugin/index.tsx +20 -0
  78. package/src/client/components/inputv3/Rte/plugins/DragDropPastePlugin/index.ts +51 -0
  79. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.css +36 -0
  80. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.tsx +43 -0
  81. package/src/client/components/inputv3/Rte/plugins/EmojiPickerPlugin/index.tsx +198 -0
  82. package/src/client/components/inputv3/Rte/plugins/EmojisPlugin/index.ts +75 -0
  83. package/src/client/components/inputv3/Rte/plugins/EquationsPlugin/index.tsx +82 -0
  84. package/src/client/components/inputv3/Rte/plugins/FigmaPlugin/index.tsx +40 -0
  85. package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.css +41 -0
  86. package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.tsx +393 -0
  87. package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.css +141 -0
  88. package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.tsx +388 -0
  89. package/src/client/components/inputv3/Rte/plugins/ImagesPlugin/index.tsx +350 -0
  90. package/src/client/components/inputv3/Rte/plugins/InlineImagePlugin/index.tsx +336 -0
  91. package/src/client/components/inputv3/Rte/plugins/KeywordsPlugin/index.ts +56 -0
  92. package/src/client/components/inputv3/Rte/plugins/LayoutPlugin/InsertLayoutDialog.tsx +58 -0
  93. package/src/client/components/inputv3/Rte/plugins/LayoutPlugin/LayoutPlugin.tsx +219 -0
  94. package/src/client/components/inputv3/Rte/plugins/LinkPlugin/index.tsx +34 -0
  95. package/src/client/components/inputv3/Rte/plugins/ListMaxIndentLevelPlugin/index.ts +85 -0
  96. package/src/client/components/inputv3/Rte/plugins/MarkdownShortcutPlugin/index.tsx +16 -0
  97. package/src/client/components/inputv3/Rte/plugins/MarkdownTransformers/index.ts +324 -0
  98. package/src/client/components/inputv3/Rte/plugins/MaxLengthPlugin/index.tsx +53 -0
  99. package/src/client/components/inputv3/Rte/plugins/MentionsPlugin/index.tsx +696 -0
  100. package/src/client/components/inputv3/Rte/plugins/PageBreakPlugin/index.tsx +57 -0
  101. package/src/client/components/inputv3/Rte/plugins/PasteLogPlugin/index.tsx +54 -0
  102. package/src/client/components/inputv3/Rte/plugins/PollPlugin/index.tsx +86 -0
  103. package/src/client/components/inputv3/Rte/plugins/SpeechToTextPlugin/index.ts +125 -0
  104. package/src/client/components/inputv3/Rte/plugins/StickyPlugin/index.ts +22 -0
  105. package/src/client/components/inputv3/Rte/plugins/TabFocusPlugin/index.tsx +65 -0
  106. package/src/client/components/inputv3/Rte/plugins/TableActionMenuPlugin/index.tsx +773 -0
  107. package/src/client/components/inputv3/Rte/plugins/TableCellResizer/index.css +12 -0
  108. package/src/client/components/inputv3/Rte/plugins/TableCellResizer/index.tsx +436 -0
  109. package/src/client/components/inputv3/Rte/plugins/TableHoverActionsPlugin/index.tsx +287 -0
  110. package/src/client/components/inputv3/Rte/plugins/TableOfContentsPlugin/index.css +95 -0
  111. package/src/client/components/inputv3/Rte/plugins/TableOfContentsPlugin/index.tsx +197 -0
  112. package/src/client/components/inputv3/Rte/plugins/TablePlugin.tsx +178 -0
  113. package/src/client/components/inputv3/Rte/plugins/TestRecorderPlugin/index.tsx +468 -0
  114. package/src/client/components/inputv3/Rte/plugins/TreeViewPlugin/index.tsx +26 -0
  115. package/src/client/components/inputv3/Rte/plugins/TwitterPlugin/index.ts +41 -0
  116. package/src/client/components/inputv3/Rte/plugins/TypingPerfPlugin/index.ts +117 -0
  117. package/src/client/components/inputv3/Rte/plugins/YouTubePlugin/index.ts +41 -0
  118. package/src/client/components/inputv3/Rte/shared/canUseDOM.ts +4 -0
  119. package/src/client/components/inputv3/Rte/shared/caretFromPoint.ts +40 -0
  120. package/src/client/components/inputv3/Rte/shared/environment.ts +56 -0
  121. package/src/client/components/inputv3/Rte/shared/invariant.ts +26 -0
  122. package/src/client/components/inputv3/Rte/shared/normalizeClassNames.ts +21 -0
  123. package/src/client/components/inputv3/Rte/shared/react-test-utils.ts +18 -0
  124. package/src/client/components/inputv3/Rte/shared/reactPatches.ts +22 -0
  125. package/src/client/components/inputv3/Rte/shared/simpleDiffWithCursor.ts +49 -0
  126. package/src/client/components/inputv3/Rte/shared/useLayoutEffect.ts +19 -0
  127. package/src/client/components/inputv3/Rte/shared/warnOnlyOnce.ts +20 -0
  128. package/src/client/components/inputv3/Rte/style.less +30 -60
  129. package/src/client/components/inputv3/Rte/themes/CommentEditorTheme.css +13 -0
  130. package/src/client/components/inputv3/Rte/themes/CommentEditorTheme.ts +20 -0
  131. package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.css +447 -0
  132. package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.ts +120 -0
  133. package/src/client/components/inputv3/Rte/themes/StickyEditorTheme.css +13 -0
  134. package/src/client/components/inputv3/Rte/themes/StickyEditorTheme.ts +20 -0
  135. package/src/client/components/inputv3/Rte/ui/ColorPicker.css +88 -0
  136. package/src/client/components/inputv3/Rte/ui/ColorPicker.tsx +365 -0
  137. package/src/client/components/inputv3/Rte/ui/ContentEditable.css +44 -0
  138. package/src/client/components/inputv3/Rte/ui/ContentEditable.tsx +36 -0
  139. package/src/client/components/inputv3/Rte/ui/DropDown.tsx +259 -0
  140. package/src/client/components/inputv3/Rte/ui/DropdownColorPicker.tsx +41 -0
  141. package/src/client/components/inputv3/Rte/ui/EquationEditor.css +38 -0
  142. package/src/client/components/inputv3/Rte/ui/EquationEditor.tsx +56 -0
  143. package/src/client/components/inputv3/Rte/ui/FileInput.tsx +38 -0
  144. package/src/client/components/inputv3/Rte/ui/FlashMessage.css +28 -0
  145. package/src/client/components/inputv3/Rte/ui/FlashMessage.tsx +29 -0
  146. package/src/client/components/inputv3/Rte/ui/ImageResizer.tsx +316 -0
  147. package/src/client/components/inputv3/Rte/ui/Input.css +32 -0
  148. package/src/client/components/inputv3/Rte/ui/KatexRenderer.tsx +54 -0
  149. package/src/client/components/inputv3/Rte/ui/Switch.tsx +36 -0
  150. package/src/client/components/inputv3/Rte/utils/docSerialization.ts +77 -0
  151. package/src/client/components/inputv3/Rte/utils/emoji-list.ts +16615 -0
  152. package/src/client/components/inputv3/Rte/utils/getDOMRangeRect.ts +27 -0
  153. package/src/client/components/inputv3/Rte/utils/getSelectedNode.ts +27 -0
  154. package/src/client/components/inputv3/Rte/utils/guard.ts +10 -0
  155. package/src/client/components/inputv3/Rte/utils/isMobileWidth.ts +7 -0
  156. package/src/client/components/inputv3/Rte/utils/joinClasses.ts +13 -0
  157. package/src/client/components/inputv3/Rte/utils/setFloatingElemPosition.ts +51 -0
  158. package/src/client/components/inputv3/Rte/utils/setFloatingElemPositionForLinkEditor.ts +46 -0
  159. package/src/client/components/inputv3/Rte/utils/swipe.ts +127 -0
  160. package/src/client/components/inputv3/Rte/utils/url.ts +38 -0
  161. package/src/client/components/inputv3/base.tsx +8 -5
  162. package/src/client/components/inputv3/file/index.tsx +11 -5
  163. package/src/common/data/rte/nodes.ts +60 -9
  164. package/src/common/validation/index.ts +21 -2
  165. package/src/common/validation/schema.ts +42 -10
  166. package/src/common/validation/validator.ts +12 -4
  167. package/src/common/validation/validators.ts +82 -53
  168. package/src/server/services/router/http/multipart.ts +0 -1
  169. package/src/server/services/schema/index.ts +24 -2
  170. package/src/server/services/schema/request.ts +3 -2
  171. package/src/server/services/schema/rte.ts +110 -0
  172. package/src/{common/data/rte/index.ts → server/utils/rte.ts} +27 -16
  173. package/src/client/components/inputv3/Rte/ExampleTheme.tsx +0 -42
  174. package/src/client/components/inputv3/Rte/ToolbarPlugin.tsx +0 -167
  175. package/src/client/components/inputv3/Rte/icons/LICENSE.md +0 -5
  176. package/src/client/components/inputv3/Rte/icons/arrow-clockwise.svg +0 -4
  177. package/src/client/components/inputv3/Rte/icons/arrow-counterclockwise.svg +0 -4
  178. package/src/client/components/inputv3/Rte/icons/journal-text.svg +0 -5
  179. package/src/client/components/inputv3/Rte/icons/justify.svg +0 -3
  180. package/src/client/components/inputv3/Rte/icons/text-center.svg +0 -3
  181. package/src/client/components/inputv3/Rte/icons/text-left.svg +0 -3
  182. package/src/client/components/inputv3/Rte/icons/text-paragraph.svg +0 -3
  183. package/src/client/components/inputv3/Rte/icons/text-right.svg +0 -3
  184. package/src/client/components/inputv3/Rte/icons/type-bold.svg +0 -3
  185. package/src/client/components/inputv3/Rte/icons/type-italic.svg +0 -3
  186. package/src/client/components/inputv3/Rte/icons/type-strikethrough.svg +0 -3
  187. package/src/client/components/inputv3/Rte/icons/type-underline.svg +0 -3
@@ -17,15 +17,16 @@ import FileToUpload from '@client/components/inputv3/file/FileToUpload';
17
17
 
18
18
  // Speciific
19
19
  import Schema, { TSchemaFields } from './schema'
20
- import Validator, { TValidator, EXCLUDE_VALUE } from './validator'
20
+ import Validator, { TValidatorOptions, EXCLUDE_VALUE } from './validator'
21
21
 
22
22
  /*----------------------------------
23
23
  - TYPES
24
24
  ----------------------------------*/
25
25
 
26
- export type TFileValidator = TValidator<FileToUpload> & {
26
+ export type TFileValidator = TValidatorOptions<FileToUpload> & {
27
27
  type?: string[], // Raccourci, ou liste de mimetype
28
- taille?: number
28
+ taille?: number,
29
+ disk?: string, // Disk to upload files to
29
30
  }
30
31
 
31
32
  type TSchemaSubtype = Schema<{}> | TSchemaFields;
@@ -36,6 +37,48 @@ type TSubtype = TSchemaSubtype | Validator<any>;
36
37
  - CONST
37
38
  ----------------------------------*/
38
39
 
40
+ export type TRichTextValidatorOptions = {
41
+ attachements?: TFileValidator
42
+ }
43
+
44
+ // Recursive function to validate each node
45
+ function validateLexicalNode(node: any, opts: TRichTextValidatorOptions ) {
46
+
47
+ // Each node should be an object with a `type` property
48
+ if (typeof node !== 'object' || !node.type || typeof node.type !== 'string')
49
+ throw new InputError("Invalid rich text value (3).");
50
+
51
+ // Validate text nodes
52
+ if (node.type === 'text') {
53
+
54
+ if (typeof node.text !== 'string')
55
+ throw new InputError("Invalid rich text value (4).");
56
+
57
+ // Validate paragraph, heading, or other structural nodes that may contain children
58
+ } else if (['paragraph', 'heading', 'list', 'listitem'].includes(node.type)) {
59
+
60
+ if (!Array.isArray(node.children) || !node.children.every(children => validateLexicalNode(children, opts))) {
61
+ throw new InputError("Invalid rich text value (5).");
62
+ }
63
+
64
+ // Files upload
65
+ } else if (node.type === 'image') {
66
+
67
+ // Check if allowed
68
+ /*if (opts.attachements === undefined)
69
+ throw new InputError("Image attachments not allowed in this rich text field.");*/
70
+
71
+ // TODO: check mime
72
+
73
+
74
+ // Upload file
75
+
76
+
77
+ }
78
+
79
+ return true;
80
+ }
81
+
39
82
  /*----------------------------------
40
83
  - CLASS
41
84
  ----------------------------------*/
@@ -72,7 +115,7 @@ export default class SchemaValidators {
72
115
  /*----------------------------------
73
116
  - CONTENEURS
74
117
  ----------------------------------*/
75
- public object = ( subtype?: TSchemaSubtype, { ...opts }: TValidator<object> & {
118
+ public object = ( subtype?: TSchemaSubtype, { ...opts }: TValidatorOptions<object> & {
76
119
 
77
120
  } = {}) =>
78
121
  new Validator<object>('object', (val, options, path) => {
@@ -96,7 +139,7 @@ export default class SchemaValidators {
96
139
  return value;
97
140
  }, opts)
98
141
 
99
- public array = ( subtype: TSubtype, { choice, min, max, ...opts }: TValidator<any[]> & {
142
+ public array = ( subtype: TSubtype, { choice, min, max, ...opts }: TValidatorOptions<any[]> & {
100
143
  choice?: any[],
101
144
  min?: number,
102
145
  max?: number
@@ -130,7 +173,7 @@ export default class SchemaValidators {
130
173
  //multiple: true, // Sélection multiple
131
174
  })
132
175
 
133
- public choice = (choices?: any[], { multiple, ...opts }: TValidator<any> & {
176
+ public choice = (choices?: any[], { multiple, ...opts }: TValidatorOptions<any> & {
134
177
  multiple?: boolean
135
178
  } = {}) => new Validator<any>('choice', (val, options, path) => {
136
179
 
@@ -169,7 +212,7 @@ export default class SchemaValidators {
169
212
  /*----------------------------------
170
213
  - CHAINES
171
214
  ----------------------------------*/
172
- public string = ({ min, max, in: choices, ...opts }: TValidator<string> & {
215
+ public string = ({ min, max, in: choices, ...opts }: TValidatorOptions<string> & {
173
216
  min?: number,
174
217
  max?: number,
175
218
  in?: string[]
@@ -205,7 +248,7 @@ export default class SchemaValidators {
205
248
 
206
249
  }, opts)
207
250
 
208
- public url = (opts: TValidator<string> & {
251
+ public url = (opts: TValidatorOptions<string> & {
209
252
  normalize?: NormalizeUrlOptions
210
253
  } = {}) =>
211
254
  new Validator<string>('url', (inputVal, options, path) => {
@@ -225,7 +268,7 @@ export default class SchemaValidators {
225
268
  return val;
226
269
  }, opts)
227
270
 
228
- public email = (opts: TValidator<string> & {} = {}) =>
271
+ public email = (opts: TValidatorOptions<string> & {} = {}) =>
229
272
  new Validator<string>('email', (inputVal, options, path) => {
230
273
 
231
274
  let val = this.string(opts).validate(inputVal, options, path);
@@ -263,7 +306,7 @@ export default class SchemaValidators {
263
306
  - NOMBRES
264
307
  ----------------------------------*/
265
308
  // On ne spread pas min et max afin quils soient passés dans les props du composant
266
- public number = (withDecimals: boolean) => ({ ...opts }: TValidator<number> & {
309
+ public number = (withDecimals: boolean) => ({ ...opts }: TValidatorOptions<number> & {
267
310
  min?: number,
268
311
  max?: number,
269
312
  step?: number,
@@ -309,7 +352,7 @@ export default class SchemaValidators {
309
352
 
310
353
  public float = this.number(true)
311
354
 
312
- public bool = (opts: TValidator<boolean> & {} = {}) =>
355
+ public bool = (opts: TValidatorOptions<boolean> & {} = {}) =>
313
356
  new Validator<boolean>('bool', (val, options, path) => {
314
357
 
315
358
  if (typeof val !== 'boolean' && !['true', 'false'].includes(val))
@@ -326,7 +369,7 @@ export default class SchemaValidators {
326
369
  /*----------------------------------
327
370
  - AUTRES
328
371
  ----------------------------------*/
329
- public date = (opts: TValidator<Date> & {
372
+ public date = (opts: TValidatorOptions<Date> & {
330
373
 
331
374
  } = {}) => new Validator<Date>('date', (val, options, path) => {
332
375
 
@@ -350,50 +393,36 @@ export default class SchemaValidators {
350
393
  ...opts,
351
394
  })
352
395
 
353
- public richText = (opts: TValidator<string> & {
354
-
355
- } = {}) => new Validator<string>('richText', (val, options, path) => {
356
-
357
- // Check that the root exists and has a valid type
358
- if (!val || typeof val !== 'object' || typeof val.root !== 'object' || val.root.type !== 'root')
359
- throw new InputError("Invalid rich text value (1).");
360
-
361
- // Check if root has children array
362
- if (!Array.isArray(val.root.children))
363
- throw new InputError("Invalid rich text value (2).");
364
-
365
- // Recursive function to validate each node
366
- function validateNode(node) {
367
- // Each node should be an object with a `type` property
368
- if (typeof node !== 'object' || !node.type || typeof node.type !== 'string')
369
- throw new InputError("Invalid rich text value (3).");
370
-
371
- // Validate text nodes
372
- if (node.type === 'text') {
373
- if (typeof node.text !== 'string')
374
- throw new InputError("Invalid rich text value (4).");
375
- }
396
+ public richText(opts: TValidatorOptions<string> & TRichTextValidatorOptions = {}) {
397
+ return new Validator<string>('richText', (val, options, path) => {
376
398
 
377
- // Validate paragraph, heading, or other structural nodes that may contain children
378
- if (['paragraph', 'heading', 'list', 'listitem'].includes(node.type))
379
- if (!Array.isArray(node.children) || !node.children.every(validateNode)) {
380
- throw new InputError("Invalid rich text value (5).");
399
+ // We get a stringified json as input since the editor workds with JSON string
400
+ try {
401
+ val = JSON.parse(val);
402
+ } catch (error) {
403
+ throw new InputError("Invalid rich text format.");
381
404
  }
382
405
 
383
- return true;
384
- }
406
+ // Check that the root exists and has a valid type
407
+ if (!val || typeof val !== 'object' || typeof val.root !== 'object' || val.root.type !== 'root')
408
+ throw new InputError("Invalid rich text value (1).");
385
409
 
386
- // Validate each child node in root
387
- for (const child of val.root.children) {
388
- validateNode(child);
389
- }
410
+ // Check if root has children array
411
+ if (!Array.isArray(val.root.children))
412
+ throw new InputError("Invalid rich text value (2).");
390
413
 
391
- return val;
414
+ // Validate each child node in root
415
+ for (const child of val.root.children) {
416
+ validateLexicalNode(child, opts);
417
+ }
392
418
 
393
- }, {
394
- //defaut: new Date,
395
- ...opts,
396
- })
419
+ return val;
420
+
421
+ }, {
422
+ //defaut: new Date,
423
+ ...opts,
424
+ })
425
+ }
397
426
 
398
427
  /*----------------------------------
399
428
  - FICHIER
@@ -402,14 +431,14 @@ export default class SchemaValidators {
402
431
 
403
432
  } = {}) => new Validator<FileToUpload>('file', (val, options, path) => {
404
433
 
405
- if (!(val instanceof FileToUpload))
406
- throw new InputError(`Must be a File (${typeof val} received)`);
407
-
408
434
  // Chaine = url ancien fichier = exclusion de la valeur pour conserver l'ancien fichier
409
435
  // NOTE: Si la valeur est présente mais undefined, alors on supprimera le fichier
410
436
  if (typeof val === 'string')
411
437
  return EXCLUDE_VALUE;
412
438
 
439
+ if (!(val instanceof FileToUpload))
440
+ throw new InputError(`Must be a File (${typeof val} received)`);
441
+
413
442
  // MIME
414
443
  if (type !== undefined) {
415
444
 
@@ -4,7 +4,6 @@
4
4
 
5
5
  // Npm
6
6
  import mime from 'mime-types';
7
- import multer from 'multer';
8
7
 
9
8
  // Core
10
9
  import FileToUpload from '@client/components/inputv3/file/FileToUpload';
@@ -3,11 +3,11 @@
3
3
  ----------------------------------*/
4
4
 
5
5
  // Core
6
+ import type { Application } from '@server/app';
6
7
 
7
8
  // Specific
8
9
  import SchemaValidator, { TFileValidator } from '@common/validation/validators';
9
-
10
- import Validator, { EXCLUDE_VALUE,} from '@common/validation/validator';
10
+ import Validator, { TValidatorOptions } from '@common/validation/validator';
11
11
 
12
12
  import type FileToUpload from '@client/components/inputv3/file/FileToUpload';
13
13
 
@@ -21,4 +21,26 @@ import type FileToUpload from '@client/components/inputv3/file/FileToUpload';
21
21
  ----------------------------------*/
22
22
  export default class ServerSchemaValidator extends SchemaValidator {
23
23
 
24
+ public constructor( private app: Application ) {
25
+ super();
26
+ }
27
+
28
+ public richText = (opts: TValidatorOptions<string> & {
29
+ attachements?: TFileValidator
30
+ } = {}) => new Validator<string>('richText', (val, options, path) => {
31
+
32
+ // Default validation
33
+ val = super.richText(opts).validate(val, options, path);
34
+
35
+ // Uploads are done in the business code since the process is specific to every case:
36
+ // - ID in the destination directory
37
+ // - Cleanup before upload
38
+
39
+ return val;
40
+
41
+ }, {
42
+ //defaut: new Date,
43
+ ...opts,
44
+ })
45
+
24
46
  }
@@ -34,7 +34,7 @@ export default class RequestValidator extends ServerSchemaValidator implements R
34
34
  public app = router.app
35
35
  ) {
36
36
 
37
- super();
37
+ super(app);
38
38
 
39
39
  }
40
40
 
@@ -49,7 +49,8 @@ export default class RequestValidator extends ServerSchemaValidator implements R
49
49
  // Les InputError seront propagées vers le middleware dédié à la gestion des erreurs
50
50
  const values = schema.validate( this.request.data, {
51
51
  debug: this.config.debug,
52
- validateDeps: false
52
+ validateDeps: false,
53
+ validators: this
53
54
  }, []);
54
55
 
55
56
  return values;
@@ -0,0 +1,110 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Node
6
+ import path from 'path';
7
+ import md5 from 'md5';
8
+ import { fromBuffer } from 'file-type';
9
+
10
+ // Npm
11
+ import type { SerializedEditorState, SerializedLexicalNode } from 'lexical';
12
+
13
+ // Core
14
+ import type Driver from '@server/services/disks/driver';
15
+
16
+ type SerializedLexicalNodeWithSrc = SerializedLexicalNode & { src: string };
17
+
18
+ /*----------------------------------
19
+ - METHODS
20
+ ----------------------------------*/
21
+ const uploadAttachments = async (
22
+ value: SerializedEditorState,
23
+ disk: Driver,
24
+ destination: string,
25
+ oldState?: SerializedEditorState
26
+ ) => {
27
+
28
+ const usedFilesUrl: string[] = [];
29
+
30
+ // Deep check for images / files
31
+ const findFiles = async (
32
+ node: SerializedLexicalNode,
33
+ callback: (node: SerializedLexicalNodeWithSrc) => Promise<void>
34
+ ) => {
35
+
36
+ // Attachment
37
+ if (node.type === 'image' || node.type === 'file') {
38
+ if (typeof node.src === 'string') {
39
+ await callback(node as SerializedLexicalNodeWithSrc);
40
+ }
41
+ // Recursion
42
+ } else if (node.children) {
43
+ for (const child of node.children) {
44
+ await findFiles(child, callback);
45
+ }
46
+ }
47
+ }
48
+
49
+ const uploadFile = async (node: SerializedLexicalNodeWithSrc) => {
50
+
51
+ // Already uploaded
52
+ if (!node.src.startsWith('data:')) {
53
+ usedFilesUrl.push(node.src);
54
+ return node.src;
55
+ }
56
+
57
+ // Transform into buffer
58
+ node.src = node.src.replace(/^data:\w+\/\w+;base64,/, '');
59
+ const fileData = Buffer.from(node.src, 'base64');
60
+
61
+ // Parse file type from buffer
62
+ const fileType = await fromBuffer(fileData);
63
+ if (!fileType)
64
+ throw new Error('Failed to detect file type');
65
+
66
+ // Upload file to disk
67
+ const fileName = md5(node.src) + '.' + fileType.ext;
68
+ const filePath = path.join(destination, fileName);
69
+ const upoadedFile = await disk.outputFile('data', filePath, fileData, {
70
+ encoding: 'binary'
71
+ });
72
+
73
+ // Replace node.src with url
74
+ node.src = upoadedFile.path;
75
+ usedFilesUrl.push(node.src);
76
+ }
77
+
78
+ const deleteUnusedFile = async (node: SerializedLexicalNodeWithSrc) => {
79
+
80
+ // Should be a URL
81
+ if (node.src.startsWith('data:') || !node.src.startsWith('http'))
82
+ return;
83
+
84
+ // This file is used
85
+ if (usedFilesUrl.includes(node.src))
86
+ return;
87
+
88
+ // Extract file name
89
+ const fileName = path.basename(node.src);
90
+ const filePath = path.join(destination, fileName);
91
+
92
+ // Delete file from disk
93
+ await disk.delete('data', filePath);
94
+ console.log('Deleted file:', filePath);
95
+ }
96
+
97
+ // Find files in the editor state
98
+ for (const child of value.root.children) {
99
+ await findFiles(child, uploadFile);
100
+ }
101
+
102
+ // Old state given: remove unused attachments
103
+ if (oldState !== undefined) {
104
+ for (const child of oldState.root.children) {
105
+ await findFiles(child, deleteUnusedFile);
106
+ }
107
+ }
108
+ }
109
+
110
+ export default { uploadAttachments }
@@ -1,26 +1,35 @@
1
- import { createHeadlessEditor } from '@lexical/headless';
2
- import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
3
- import { $getRoot } from 'lexical';
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
4
 
5
+ // Npm
6
+ import { createHeadlessEditor } from '@lexical/headless';
7
+ import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
8
+ import { $getRoot, SerializedEditorState } from 'lexical';
5
9
  import { JSDOM } from 'jsdom';
6
10
 
7
- import editorNodes from './nodes';
11
+ // Core
12
+ import editorNodes from '@common/data/rte/nodes';
13
+ import ExampleTheme from '@client/components/inputv3/Rte/themes/PlaygroundEditorTheme';
8
14
 
9
- export const htmlToJson = async (htmlString: string) => {
15
+ /*----------------------------------
16
+ - FUNCTIONS
17
+ ----------------------------------*/
18
+ export const htmlToJson = async (htmlString: string): Promise<SerializedEditorState> => {
10
19
 
11
- const editor = createHeadlessEditor({
12
- nodes: editorNodes
20
+ const editor = createHeadlessEditor({
21
+ nodes: editorNodes,
22
+ theme: ExampleTheme,
13
23
  });
14
-
24
+
15
25
  await editor.update(() => {
16
26
 
17
27
  const root = $getRoot();
18
28
 
19
- // In a headless environment you can use a package such as JSDom to parse the HTML string.
20
29
  const dom = new JSDOM(htmlString);
21
30
 
22
31
  // Once you have the DOM instance it's easy to generate LexicalNodes.
23
- const lexicalNodes = $generateNodesFromDOM(editor, dom.window.document);
32
+ const lexicalNodes = $generateNodesFromDOM(editor, dom ? dom.window.document : window.document);
24
33
 
25
34
  lexicalNodes.forEach((node) => root.append(node));
26
35
  });
@@ -30,18 +39,20 @@ export const htmlToJson = async (htmlString: string) => {
30
39
  };
31
40
 
32
41
  export const jsonToHtml = async (jsonString: string) => {
33
-
34
42
 
43
+ // Server side: simulate DOM environment
35
44
  const dom = new JSDOM(`<!DOCTYPE html><body></body>`);
36
45
  global.window = dom.window;
37
46
  global.document = dom.window.document;
38
47
  global.DOMParser = dom.window.DOMParser;
39
48
  global.MutationObserver = dom.window.MutationObserver;
40
-
49
+
41
50
  // Create a headless Lexical editor instance
42
51
  const editor = createHeadlessEditor({
43
52
  namespace: 'headless',
44
53
  editable: false,
54
+ nodes: editorNodes,
55
+ theme: ExampleTheme,
45
56
  });
46
57
 
47
58
  // Set the editor state from JSON
@@ -49,14 +60,14 @@ export const jsonToHtml = async (jsonString: string) => {
49
60
  if (state.isEmpty())
50
61
  return null;
51
62
 
52
- editor.setEditorState( state );
63
+ editor.setEditorState(state);
53
64
 
54
65
  // Generate HTML from the Lexical nodes
55
66
  const html = await editor.getEditorState().read(() => {
56
67
  return $generateHtmlFromNodes(editor);
57
- });
58
-
59
- // Clean up global variables set for JSDOM to avoid memory leaks
68
+ });
69
+
70
+ // Clean up global variables set for JSDOM to avoid memory leaks
60
71
  delete global.window;
61
72
  delete global.document;
62
73
  delete global.DOMParser;
@@ -1,42 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- */
8
- export default {
9
- code: 'editor-code',
10
- heading: {
11
- h1: 'editor-heading-h1',
12
- h2: 'editor-heading-h2',
13
- h3: 'editor-heading-h3',
14
- h4: 'editor-heading-h4',
15
- h5: 'editor-heading-h5',
16
- },
17
- image: 'editor-image',
18
- link: 'editor-link',
19
- list: {
20
- listitem: 'editor-listitem',
21
- nested: {
22
- listitem: 'editor-nested-listitem',
23
- },
24
- ol: 'editor-list-ol',
25
- ul: 'editor-list-ul',
26
- },
27
- ltr: 'ltr',
28
- paragraph: 'editor-paragraph',
29
- placeholder: 'editor-placeholder',
30
- quote: 'editor-quote',
31
- rtl: 'rtl',
32
- text: {
33
- bold: 'editor-text-bold',
34
- code: 'editor-text-code',
35
- hashtag: 'editor-text-hashtag',
36
- italic: 'editor-text-italic',
37
- overflowed: 'editor-text-overflowed',
38
- strikethrough: 'editor-text-strikethrough',
39
- underline: 'editor-text-underline',
40
- underlineStrikethrough: 'editor-text-underlineStrikethrough',
41
- },
42
- };