5htp-core 0.4.8 → 0.4.9-1

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 (188) 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 +2 -6
  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/client/components/inputv3/index.tsx +1 -1
  164. package/src/common/data/rte/nodes.ts +60 -9
  165. package/src/common/validation/index.ts +21 -2
  166. package/src/common/validation/schema.ts +27 -10
  167. package/src/common/validation/validator.ts +12 -4
  168. package/src/common/validation/validators.ts +112 -63
  169. package/src/server/services/router/http/multipart.ts +0 -1
  170. package/src/server/services/schema/index.ts +26 -4
  171. package/src/server/services/schema/request.ts +3 -2
  172. package/src/server/services/schema/rte.ts +110 -0
  173. package/src/{common/data/rte/index.ts → server/utils/rte.ts} +27 -16
  174. package/src/client/components/inputv3/Rte/ExampleTheme.tsx +0 -42
  175. package/src/client/components/inputv3/Rte/ToolbarPlugin.tsx +0 -167
  176. package/src/client/components/inputv3/Rte/icons/LICENSE.md +0 -5
  177. package/src/client/components/inputv3/Rte/icons/arrow-clockwise.svg +0 -4
  178. package/src/client/components/inputv3/Rte/icons/arrow-counterclockwise.svg +0 -4
  179. package/src/client/components/inputv3/Rte/icons/journal-text.svg +0 -5
  180. package/src/client/components/inputv3/Rte/icons/justify.svg +0 -3
  181. package/src/client/components/inputv3/Rte/icons/text-center.svg +0 -3
  182. package/src/client/components/inputv3/Rte/icons/text-left.svg +0 -3
  183. package/src/client/components/inputv3/Rte/icons/text-paragraph.svg +0 -3
  184. package/src/client/components/inputv3/Rte/icons/text-right.svg +0 -3
  185. package/src/client/components/inputv3/Rte/icons/type-bold.svg +0 -3
  186. package/src/client/components/inputv3/Rte/icons/type-italic.svg +0 -3
  187. package/src/client/components/inputv3/Rte/icons/type-strikethrough.svg +0 -3
  188. package/src/client/components/inputv3/Rte/icons/type-underline.svg +0 -3
@@ -10,13 +10,21 @@ import { InputError } from '@common/errors';
10
10
 
11
11
  // Specific
12
12
  import type { TValidateOptions } from './schema';
13
+ import type { SchemaValidators } from './validators';
13
14
  import type { InputBaseProps } from '@client/components/inputv3/base';
14
15
 
15
16
  /*----------------------------------
16
17
  - TYPES
17
18
  ----------------------------------*/
18
19
 
19
- export type TValidator<TValue> = {
20
+ export type TValidatorDefinition<K extends keyof SchemaValidators = keyof SchemaValidators> = [
21
+ type: K,
22
+ args: Parameters<SchemaValidators[K]>,
23
+ returnType: ReturnType<SchemaValidators[K]>
24
+ ]
25
+
26
+ // TODO: remove
27
+ export type TValidatorOptions<TValue> = {
20
28
 
21
29
  rendu?: TFieldRenderer,
22
30
 
@@ -48,7 +56,7 @@ type TValidationFunction<TValue, TAllValues extends {} = {}> = (
48
56
  ) => TValue | typeof EXCLUDE_VALUE;
49
57
 
50
58
  type TValidateReturnType<
51
- TOptions extends TValidator<TValue>,
59
+ TOptions extends TValidatorOptions<TValue>,
52
60
  TValue extends any
53
61
  > = TOptions extends { opt: true }
54
62
  ? (undefined | TValue)
@@ -65,8 +73,8 @@ export const EXCLUDE_VALUE = "action:exclure" as const;
65
73
  ----------------------------------*/
66
74
  export default class Validator<
67
75
  TValue,
68
- TOptions extends TValidator<TValue> = TValidator<TValue>,
69
- TComponent = React.FunctionComponent< InputBaseProps< TValue > >
76
+ TOptions extends TValidatorOptions<TValue> = TValidatorOptions<TValue>,
77
+ //TComponent = React.FunctionComponent< InputBaseProps< TValue > >
70
78
  > {
71
79
 
72
80
  public constructor(
@@ -17,29 +17,93 @@ 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, TValidatorDefinition } 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
- type TSchemaSubtype = Schema<{}> | TSchemaFields;
32
+ type TSchemaSubtype = Schema<{}> | TSchemaFields | TValidatorDefinition;
32
33
 
33
- type TSubtype = TSchemaSubtype | Validator<any>;
34
+ type TSubtype = TSchemaSubtype | Validator<any> | TValidatorDefinition;
34
35
 
35
36
  /*----------------------------------
36
37
  - CONST
37
38
  ----------------------------------*/
38
39
 
40
+ export type TRichTextValidatorOptions = {
41
+ attachements?: TFileValidator
42
+ }
43
+
44
+ export const getFieldValidator = (field: TValidatorDefinition) => {
45
+
46
+ if (Array.isArray(field)) {
47
+
48
+ const [validatorName, validatorArgs] = field;
49
+ const getValidator = validators[validatorName];
50
+ if (getValidator === undefined)
51
+ throw new Error('Unknown validator: ' + validatorName);
52
+
53
+ return getValidator(...validatorArgs);
54
+
55
+ // TSchemaFields
56
+ } else if (field.constructor === Object) {
57
+
58
+ return new Schema(field as TSchemaFields);
59
+
60
+ // Schema
61
+ } else
62
+ return field as Validator<any>
63
+ }
64
+
65
+ // Recursive function to validate each node
66
+ function validateLexicalNode(node: any, opts: TRichTextValidatorOptions ) {
67
+
68
+ // Each node should be an object with a `type` property
69
+ if (typeof node !== 'object' || !node.type || typeof node.type !== 'string')
70
+ throw new InputError("Invalid rich text value (3).");
71
+
72
+ // Validate text nodes
73
+ if (node.type === 'text') {
74
+
75
+ if (typeof node.text !== 'string')
76
+ throw new InputError("Invalid rich text value (4).");
77
+
78
+ // Validate paragraph, heading, or other structural nodes that may contain children
79
+ } else if (['paragraph', 'heading', 'list', 'listitem'].includes(node.type)) {
80
+
81
+ if (!Array.isArray(node.children) || !node.children.every(children => validateLexicalNode(children, opts))) {
82
+ throw new InputError("Invalid rich text value (5).");
83
+ }
84
+
85
+ // Files upload
86
+ } else if (node.type === 'image') {
87
+
88
+ // Check if allowed
89
+ /*if (opts.attachements === undefined)
90
+ throw new InputError("Image attachments not allowed in this rich text field.");*/
91
+
92
+ // TODO: check mime
93
+
94
+
95
+ // Upload file
96
+
97
+
98
+ }
99
+
100
+ return true;
101
+ }
102
+
39
103
  /*----------------------------------
40
104
  - CLASS
41
105
  ----------------------------------*/
42
- export default class SchemaValidators {
106
+ export class SchemaValidators {
43
107
 
44
108
  /*----------------------------------
45
109
  - UTILITIES
@@ -72,7 +136,7 @@ export default class SchemaValidators {
72
136
  /*----------------------------------
73
137
  - CONTENEURS
74
138
  ----------------------------------*/
75
- public object = ( subtype?: TSchemaSubtype, { ...opts }: TValidator<object> & {
139
+ public object = ( subtype?: TSchemaSubtype, { ...opts }: TValidatorOptions<object> & {
76
140
 
77
141
  } = {}) =>
78
142
  new Validator<object>('object', (val, options, path) => {
@@ -86,9 +150,7 @@ export default class SchemaValidators {
86
150
  return val;
87
151
 
88
152
  // If subtype is a schema
89
- const schema = subtype.constructor === Object
90
- ? new Schema(subtype as TSchemaFields)
91
- : subtype as Schema<{}>;
153
+ const schema = getFieldValidator(subtype) as Schema<{}>;
92
154
 
93
155
  // Validate schema
94
156
  const value = schema.validate(val, options, path);
@@ -96,7 +158,7 @@ export default class SchemaValidators {
96
158
  return value;
97
159
  }, opts)
98
160
 
99
- public array = ( subtype: TSubtype, { choice, min, max, ...opts }: TValidator<any[]> & {
161
+ public array = ( subtype: TSubtype, { choice, min, max, ...opts }: TValidatorOptions<any[]> & {
100
162
  choice?: any[],
101
163
  min?: number,
102
164
  max?: number
@@ -116,9 +178,7 @@ export default class SchemaValidators {
116
178
  if (subtype === undefined)
117
179
  return items;
118
180
 
119
- const validator = subtype.constructor === Object
120
- ? new Schema(subtype as TSchemaFields)
121
- : subtype as Schema<{}> | Validator<any>;
181
+ const validator = getFieldValidator(subtype);
122
182
 
123
183
  items = items.map( item =>
124
184
  validator.validate( item, options, path )
@@ -130,7 +190,7 @@ export default class SchemaValidators {
130
190
  //multiple: true, // Sélection multiple
131
191
  })
132
192
 
133
- public choice = (choices?: any[], { multiple, ...opts }: TValidator<any> & {
193
+ public choice = (choices?: any[], { multiple, ...opts }: TValidatorOptions<any> & {
134
194
  multiple?: boolean
135
195
  } = {}) => new Validator<any>('choice', (val, options, path) => {
136
196
 
@@ -169,7 +229,7 @@ export default class SchemaValidators {
169
229
  /*----------------------------------
170
230
  - CHAINES
171
231
  ----------------------------------*/
172
- public string = ({ min, max, in: choices, ...opts }: TValidator<string> & {
232
+ public string = ({ min, max, in: choices, ...opts }: TValidatorOptions<string> & {
173
233
  min?: number,
174
234
  max?: number,
175
235
  in?: string[]
@@ -205,7 +265,7 @@ export default class SchemaValidators {
205
265
 
206
266
  }, opts)
207
267
 
208
- public url = (opts: TValidator<string> & {
268
+ public url = (opts: TValidatorOptions<string> & {
209
269
  normalize?: NormalizeUrlOptions
210
270
  } = {}) =>
211
271
  new Validator<string>('url', (inputVal, options, path) => {
@@ -225,7 +285,7 @@ export default class SchemaValidators {
225
285
  return val;
226
286
  }, opts)
227
287
 
228
- public email = (opts: TValidator<string> & {} = {}) =>
288
+ public email = (opts: TValidatorOptions<string> & {} = {}) =>
229
289
  new Validator<string>('email', (inputVal, options, path) => {
230
290
 
231
291
  let val = this.string(opts).validate(inputVal, options, path);
@@ -263,7 +323,7 @@ export default class SchemaValidators {
263
323
  - NOMBRES
264
324
  ----------------------------------*/
265
325
  // 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> & {
326
+ public number = (withDecimals: boolean) => ({ ...opts }: TValidatorOptions<number> & {
267
327
  min?: number,
268
328
  max?: number,
269
329
  step?: number,
@@ -309,7 +369,7 @@ export default class SchemaValidators {
309
369
 
310
370
  public float = this.number(true)
311
371
 
312
- public bool = (opts: TValidator<boolean> & {} = {}) =>
372
+ public bool = (opts: TValidatorOptions<boolean> & {} = {}) =>
313
373
  new Validator<boolean>('bool', (val, options, path) => {
314
374
 
315
375
  if (typeof val !== 'boolean' && !['true', 'false'].includes(val))
@@ -326,7 +386,7 @@ export default class SchemaValidators {
326
386
  /*----------------------------------
327
387
  - AUTRES
328
388
  ----------------------------------*/
329
- public date = (opts: TValidator<Date> & {
389
+ public date = (opts: TValidatorOptions<Date> & {
330
390
 
331
391
  } = {}) => new Validator<Date>('date', (val, options, path) => {
332
392
 
@@ -350,50 +410,36 @@ export default class SchemaValidators {
350
410
  ...opts,
351
411
  })
352
412
 
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
- }
413
+ public richText(opts: TValidatorOptions<string> & TRichTextValidatorOptions = {}) {
414
+ return new Validator<string>('richText', (val, options, path) => {
376
415
 
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).");
416
+ // We get a stringified json as input since the editor workds with JSON string
417
+ try {
418
+ val = JSON.parse(val);
419
+ } catch (error) {
420
+ throw new InputError("Invalid rich text format.");
381
421
  }
382
422
 
383
- return true;
384
- }
423
+ // Check that the root exists and has a valid type
424
+ if (!val || typeof val !== 'object' || typeof val.root !== 'object' || val.root.type !== 'root')
425
+ throw new InputError("Invalid rich text value (1).");
385
426
 
386
- // Validate each child node in root
387
- for (const child of val.root.children) {
388
- validateNode(child);
389
- }
427
+ // Check if root has children array
428
+ if (!Array.isArray(val.root.children))
429
+ throw new InputError("Invalid rich text value (2).");
390
430
 
391
- return val;
431
+ // Validate each child node in root
432
+ for (const child of val.root.children) {
433
+ validateLexicalNode(child, opts);
434
+ }
392
435
 
393
- }, {
394
- //defaut: new Date,
395
- ...opts,
396
- })
436
+ return val;
437
+
438
+ }, {
439
+ //defaut: new Date,
440
+ ...opts,
441
+ })
442
+ }
397
443
 
398
444
  /*----------------------------------
399
445
  - FICHIER
@@ -402,14 +448,14 @@ export default class SchemaValidators {
402
448
 
403
449
  } = {}) => new Validator<FileToUpload>('file', (val, options, path) => {
404
450
 
405
- if (!(val instanceof FileToUpload))
406
- throw new InputError(`Must be a File (${typeof val} received)`);
407
-
408
451
  // Chaine = url ancien fichier = exclusion de la valeur pour conserver l'ancien fichier
409
452
  // NOTE: Si la valeur est présente mais undefined, alors on supprimera le fichier
410
453
  if (typeof val === 'string')
411
454
  return EXCLUDE_VALUE;
412
455
 
456
+ if (!(val instanceof FileToUpload))
457
+ throw new InputError(`Must be a File (${typeof val} received)`);
458
+
413
459
  // MIME
414
460
  if (type !== undefined) {
415
461
 
@@ -432,5 +478,8 @@ export default class SchemaValidators {
432
478
  //defaut: new Date,
433
479
  ...opts,
434
480
  })
481
+ }
482
+
483
+ const validators = new SchemaValidators();
435
484
 
436
- }
485
+ export default validators;
@@ -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
- import SchemaValidator, { TFileValidator } from '@common/validation/validators';
9
-
10
- import Validator, { EXCLUDE_VALUE,} from '@common/validation/validator';
9
+ import { SchemaValidators, TFileValidator } from '@common/validation/validators';
10
+ import Validator, { TValidatorOptions } from '@common/validation/validator';
11
11
 
12
12
  import type FileToUpload from '@client/components/inputv3/file/FileToUpload';
13
13
 
@@ -19,6 +19,28 @@ import type FileToUpload from '@client/components/inputv3/file/FileToUpload';
19
19
  /*----------------------------------
20
20
  - SERVICE
21
21
  ----------------------------------*/
22
- export default class ServerSchemaValidator extends SchemaValidator {
22
+ export default class ServerSchemaValidator extends SchemaValidators {
23
+
24
+ public constructor( public 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
+ })
23
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
- };