@truedat/ie 8.5.3 → 8.5.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/ie",
3
- "version": "8.5.3",
3
+ "version": "8.5.4",
4
4
  "description": "Truedat Web Ingests",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -51,7 +51,7 @@
51
51
  "@testing-library/jest-dom": "^6.6.3",
52
52
  "@testing-library/react": "^16.3.0",
53
53
  "@testing-library/user-event": "^14.6.1",
54
- "@truedat/test": "8.5.3",
54
+ "@truedat/test": "8.5.4",
55
55
  "identity-obj-proxy": "^3.0.0",
56
56
  "jest": "^29.7.0",
57
57
  "redux-saga-test-plan": "^4.0.6"
@@ -61,6 +61,7 @@
61
61
  "axios": "^1.15.0",
62
62
  "file-saver": "^2.0.5",
63
63
  "graphql": "^16.11.0",
64
+ "immutable": "^4.3.7",
64
65
  "is-hotkey": "^0.2.0",
65
66
  "is-url": "^1.2.4",
66
67
  "lodash": "^4.17.21",
@@ -82,7 +83,9 @@
82
83
  "redux-saga-routines": "^3.2.3",
83
84
  "reselect": "^5.1.1",
84
85
  "semantic-ui-react": "^3.0.0-beta.2",
86
+ "slate": "^0.47.9",
87
+ "slate-react": "^0.22.10",
85
88
  "swr": "^2.3.3"
86
89
  },
87
- "gitHead": "9d67766fe0910fe519b6f1f2042b4a5dcc28fad5"
90
+ "gitHead": "75272567eb3ec948a5cdeb8346ef9cacac58267f"
88
91
  }
@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
3
3
  import { connect } from "react-redux";
4
4
  import { Segment, Header } from "semantic-ui-react";
5
5
  import { FormattedMessage } from "react-intl";
6
- import { RichTextEditor } from "@truedat/core/components";
6
+ import RichTextEditor from "./RichTextEditor";
7
7
 
8
8
  const DynamicFormViewer = lazy(
9
9
  () => import("@truedat/df/components/DynamicFormViewer")
@@ -13,7 +13,8 @@ import {
13
13
  import { compose } from "redux";
14
14
  import { connect } from "react-redux";
15
15
  import { injectIntl, FormattedMessage } from "react-intl";
16
- import { HistoryBackButton, RichTextEditor } from "@truedat/core/components";
16
+ import { HistoryBackButton } from "@truedat/core/components";
17
+ import RichTextEditor from "./RichTextEditor";
17
18
  import { selectTemplate, selectDomain } from "@truedat/df/routines";
18
19
  import { applyTemplate } from "@truedat/df/utils";
19
20
  import { ingestAction } from "../routines";
@@ -13,7 +13,8 @@ import {
13
13
  import { compose } from "redux";
14
14
  import { connect } from "react-redux";
15
15
  import { injectIntl, FormattedMessage } from "react-intl";
16
- import { HistoryBackButton, RichTextEditor } from "@truedat/core/components";
16
+ import { HistoryBackButton } from "@truedat/core/components";
17
+ import RichTextEditor from "./RichTextEditor";
17
18
  import {
18
19
  selectTemplate,
19
20
  selectDomain,
@@ -13,11 +13,8 @@ import {
13
13
  import { compose } from "redux";
14
14
  import { connect } from "react-redux";
15
15
  import { injectIntl, FormattedMessage } from "react-intl";
16
- import {
17
- DomainSelector,
18
- HistoryBackButton,
19
- RichTextEditor,
20
- } from "@truedat/core/components";
16
+ import { DomainSelector, HistoryBackButton } from "@truedat/core/components";
17
+ import RichTextEditor from "./RichTextEditor";
21
18
  import { ingestAction } from "../routines";
22
19
 
23
20
  const SelectableDynamicForm = lazy(
@@ -0,0 +1,365 @@
1
+ import _ from "lodash/fp";
2
+ import { useState, useEffect } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { isKeyHotkey } from "is-hotkey";
5
+ import isUrl from "is-url";
6
+ import { Editor, getEventTransfer } from "slate-react";
7
+ import { Value } from "slate";
8
+ import { Menu, Icon } from "semantic-ui-react";
9
+ import { validUrl } from "@truedat/core/services/validation";
10
+ import SafeLink from "@truedat/core/components/SafeLink";
11
+
12
+ const DEFAULT_NODE = "paragraph";
13
+
14
+ function wrapLink(change, value) {
15
+ const href = validUrl(value);
16
+ if (href) {
17
+ change.wrapInline({
18
+ type: "link",
19
+ data: { href },
20
+ });
21
+
22
+ change.moveToEnd();
23
+ }
24
+ }
25
+
26
+ function unwrapLink(change) {
27
+ change.unwrapInline("link");
28
+ }
29
+
30
+ function renderBlock(props, _editor, next) {
31
+ const { attributes, children, node } = props;
32
+
33
+ switch (node.type) {
34
+ // case 'block-quote':
35
+ // return <blockquote {...attributes}>{children}</blockquote>
36
+ case "bulleted-list":
37
+ return <ul {...attributes}>{children}</ul>;
38
+ case "heading-one":
39
+ return (
40
+ <h1 style={{ fontSize: "18px" }} {...attributes}>
41
+ {children}
42
+ </h1>
43
+ );
44
+ case "heading-two":
45
+ return (
46
+ <h2 style={{ fontSize: "15px" }} {...attributes}>
47
+ {children}
48
+ </h2>
49
+ );
50
+ case "list-item":
51
+ return <li {...attributes}>{children}</li>;
52
+ case "numbered-list":
53
+ return <ol {...attributes}>{children}</ol>;
54
+ case "link":
55
+ return (
56
+ <SafeLink href={node.data?.get("href")} {...attributes}>
57
+ {children}
58
+ </SafeLink>
59
+ );
60
+ default:
61
+ return next();
62
+ }
63
+ }
64
+
65
+ function renderMark(props, _editor, next) {
66
+ const { children, mark, attributes } = props;
67
+
68
+ switch (mark.type) {
69
+ case "bold":
70
+ return <strong {...attributes}>{children}</strong>;
71
+ // case 'code':
72
+ // return <code {...attributes}>{children}</code>
73
+ case "italic":
74
+ return <em {...attributes}>{children}</em>;
75
+ case "underlined":
76
+ return <u {...attributes}>{children}</u>;
77
+ default:
78
+ return next();
79
+ }
80
+ }
81
+
82
+ function renderInline(props, _editor, next) {
83
+ const { attributes, children, node } = props;
84
+
85
+ switch (node.type) {
86
+ case "link": {
87
+ const props = _.omit(["ref"])(attributes);
88
+ return (
89
+ <SafeLink href={node.data?.get("href")} {...props}>
90
+ {children}
91
+ </SafeLink>
92
+ );
93
+ }
94
+
95
+ default:
96
+ return next();
97
+ }
98
+ }
99
+
100
+ function onKeyDown(event, editor, next) {
101
+ const isBoldHotkey = isKeyHotkey("mod+b");
102
+ const isItalicHotkey = isKeyHotkey("mod+i");
103
+ const isUnderlinedHotkey = isKeyHotkey("mod+u");
104
+ const mark = isBoldHotkey(event)
105
+ ? "bold"
106
+ : isItalicHotkey(event)
107
+ ? "italic"
108
+ : isUnderlinedHotkey(event)
109
+ ? "underlined"
110
+ : undefined;
111
+ if (!_.isNil(mark)) {
112
+ event.preventDefault();
113
+ editor.toggleMark(mark);
114
+ return true;
115
+ } else {
116
+ next();
117
+ }
118
+ }
119
+
120
+ const EMPTY_VALUE = {
121
+ document: {
122
+ nodes: [
123
+ {
124
+ object: "block",
125
+ type: "paragraph",
126
+ nodes: [],
127
+ },
128
+ ],
129
+ },
130
+ };
131
+
132
+ export const RichTextEditor = ({
133
+ readOnly,
134
+ value: propValue,
135
+ label,
136
+ required,
137
+ onChange,
138
+ name,
139
+ }) => {
140
+ const [jsonValue, setJsonValue] = useState(
141
+ _.isEmpty(propValue) ? EMPTY_VALUE : propValue
142
+ );
143
+ const [value, setValue] = useState(Value.fromJSON(jsonValue));
144
+ const [editor, setEditor] = useState();
145
+
146
+ useEffect(() => {
147
+ if (
148
+ (!_.isEmpty(propValue) && !_.equals(propValue)(jsonValue)) ||
149
+ (readOnly && !_.equals(value)(Value.fromJSON(propValue)))
150
+ ) {
151
+ setValue(Value.fromJSON(propValue));
152
+ }
153
+ }, [propValue]);
154
+
155
+ const renderMarkButton = (type, icon) => {
156
+ const isActive = value?.activeMarks.some((mark) => mark.type == type);
157
+
158
+ return (
159
+ <Menu.Item
160
+ icon
161
+ active={isActive}
162
+ onMouseDown={(e) => {
163
+ e.preventDefault();
164
+ editor?.toggleMark(type);
165
+ }}
166
+ onClick={() => {}}
167
+ >
168
+ <Icon name={icon} />
169
+ </Menu.Item>
170
+ );
171
+ };
172
+
173
+ const hasBlock = (type) => value?.blocks.some((node) => node.type == type);
174
+
175
+ const onClickBlock = (type) => {
176
+ const { value } = editor || {};
177
+ const { document } = value || {};
178
+
179
+ if (type != "bulleted-list" && type != "numbered-list") {
180
+ const isActive = hasBlock(type);
181
+ const isList = hasBlock("list-item");
182
+
183
+ if (isList) {
184
+ editor
185
+ .setBlocks(isActive ? DEFAULT_NODE : type)
186
+ .unwrapBlock("bulleted-list")
187
+ .unwrapBlock("numbered-list");
188
+ } else {
189
+ editor.setBlocks(isActive ? DEFAULT_NODE : type);
190
+ }
191
+ } else {
192
+ const isList = hasBlock("list-item");
193
+ const isType = value?.blocks.some((block) => {
194
+ return !!document?.getClosest(
195
+ block.key,
196
+ (parent) => parent.type == type
197
+ );
198
+ });
199
+
200
+ if (isList && isType) {
201
+ editor
202
+ .setBlocks(DEFAULT_NODE)
203
+ .unwrapBlock("bulleted-list")
204
+ .unwrapBlock("numbered-list");
205
+ } else if (isList) {
206
+ editor
207
+ .unwrapBlock(
208
+ type == "bulleted-list" ? "numbered-list" : "bulleted-list"
209
+ )
210
+ .wrapBlock(type);
211
+ } else {
212
+ editor.setBlocks("list-item").wrapBlock(type);
213
+ }
214
+ }
215
+ };
216
+
217
+ const renderBlockButton = (type, icon) => {
218
+ const { document, blocks } = value || {};
219
+ const parent =
220
+ blocks?.size != 0 ? document?.getParent(blocks?.first().key) : null;
221
+ const isActive = _.includes(type)(["numbered-list", "bulleted-list"])
222
+ ? hasBlock("list-item") && parent && parent?.type === type
223
+ : hasBlock(type);
224
+
225
+ return (
226
+ <Menu.Item
227
+ icon
228
+ active={isActive}
229
+ onMouseDown={(e) => {
230
+ e.preventDefault();
231
+ onClickBlock(type);
232
+ }}
233
+ onClick={() => {}}
234
+ >
235
+ <Icon name={icon} />
236
+ </Menu.Item>
237
+ );
238
+ };
239
+
240
+ const hasLinks = () => value?.inlines.some((inline) => inline.type == "link");
241
+
242
+ const onClickLink = () => {
243
+ const { value } = editor || {};
244
+
245
+ if (hasLinks()) {
246
+ editor.command(unwrapLink);
247
+ } else if (value?.selection.isExpanded) {
248
+ const href = window.prompt("Introduzca la URL del Link:");
249
+ editor.command(wrapLink, href);
250
+ } else {
251
+ const href = window.prompt("Introduzca la URL del Link:");
252
+ const text = window.prompt("Introduzca el Texto del Link:");
253
+ editor
254
+ .insertText(text)
255
+ .moveFocusForward(0 - text.length)
256
+ .command(wrapLink, href);
257
+ }
258
+ };
259
+
260
+ const renderLinkButton = (icon) => {
261
+ const isActive = hasLinks();
262
+
263
+ return (
264
+ <Menu.Item
265
+ icon
266
+ active={isActive}
267
+ onMouseDown={(e) => {
268
+ e.preventDefault();
269
+ onClickLink();
270
+ }}
271
+ onClick={() => {}}
272
+ >
273
+ <Icon name={icon} />
274
+ </Menu.Item>
275
+ );
276
+ };
277
+
278
+ const handleChange = ({ value }) => {
279
+ const { text } = value?.document || {};
280
+ const json = text ? value?.toJSON() : {};
281
+ setJsonValue(json);
282
+ setValue(value);
283
+ if (onChange) {
284
+ onChange(undefined, { name, value: json });
285
+ }
286
+ };
287
+
288
+ const onPaste = (event, editor, next) => {
289
+ if (editor?.value?.selection?.isCollapsed) return next();
290
+
291
+ const transfer = getEventTransfer(event);
292
+ const { type, text } = transfer;
293
+ if (type !== "text" && type !== "html") return next();
294
+ if (!isUrl(text)) return next();
295
+
296
+ if (hasLinks()) {
297
+ editor.command(unwrapLink);
298
+ }
299
+
300
+ editor.command(wrapLink, text);
301
+ };
302
+
303
+ return readOnly ? (
304
+ <Editor
305
+ readOnly
306
+ value={value}
307
+ renderBlock={renderBlock}
308
+ renderMark={renderMark}
309
+ renderInline={renderInline}
310
+ />
311
+ ) : (
312
+ <div>
313
+ {label ? (
314
+ <div className={required ? "required field" : "field"}>
315
+ <label>{label}</label>
316
+ </div>
317
+ ) : null}
318
+ <div
319
+ style={{
320
+ border: "1px solid rgba(34, 36, 38, 0.15)",
321
+ borderRadius: "0.28571429rem",
322
+ }}
323
+ >
324
+ <Menu>
325
+ {renderMarkButton("bold", "bold")}
326
+ {renderMarkButton("italic", "italic")}
327
+ {renderMarkButton("underlined", "underline")}
328
+ {/* {renderMarkButton('code', 'file code')} */}
329
+ {renderBlockButton("heading-one", "header")}
330
+ {renderBlockButton("heading-two", "h")}
331
+ {/* {renderBlockButton('block-quote', 'quote right')} */}
332
+ {renderBlockButton("numbered-list", "list ol")}
333
+ {renderBlockButton("bulleted-list", "list ul")}
334
+ {renderLinkButton("linkify")}
335
+ </Menu>
336
+ <div style={{ padding: "10px" }}>
337
+ <Editor
338
+ style={{ WebkitUserModify: "read-write !important" }}
339
+ spellCheck
340
+ autoFocus
341
+ value={value}
342
+ onChange={handleChange}
343
+ onPaste={onPaste}
344
+ onKeyDown={onKeyDown}
345
+ renderBlock={renderBlock}
346
+ renderMark={renderMark}
347
+ renderInline={renderInline}
348
+ ref={setEditor}
349
+ />
350
+ </div>
351
+ </div>
352
+ </div>
353
+ );
354
+ };
355
+
356
+ RichTextEditor.propTypes = {
357
+ label: PropTypes.string,
358
+ name: PropTypes.string,
359
+ onChange: PropTypes.func,
360
+ readOnly: PropTypes.bool,
361
+ required: PropTypes.bool,
362
+ value: PropTypes.object,
363
+ };
364
+
365
+ export default RichTextEditor;
@@ -29,21 +29,21 @@ const getIngestFieldChanges = (
29
29
  msg_key,
30
30
  fields,
31
31
  templateFields,
32
- fieldsWithoutValue
32
+ fieldsWithoutValue,
33
33
  ) =>
34
34
  _.flow(
35
35
  _.toPairs,
36
36
  _.map(([k, v]) => ({
37
37
  msg_key: fromValueType(msg_key, k, v, fieldsWithoutValue),
38
38
  msg_params: [getFieldLabel(k, templateFields), toStr(v)],
39
- }))
39
+ })),
40
40
  )(fields);
41
41
 
42
42
  const getFieldLabel = (field, fields) =>
43
43
  _.flow(
44
44
  _.find((f) => _.prop("name")(f) == field),
45
45
  _.defaultTo({}),
46
- _.prop("label")
46
+ _.prop("label"),
47
47
  )(fields) || field;
48
48
 
49
49
  const fromValueType = (msg_key, field, value, fieldsWithoutValue) =>
@@ -90,22 +90,22 @@ const getUpdateIngestDraftEvent = (e, templateFields, fieldsWithoutValue) => {
90
90
  "changed_field",
91
91
  changed,
92
92
  templateFields,
93
- fieldsWithoutValue
93
+ fieldsWithoutValue,
94
94
  ),
95
95
  getIngestFieldChanges(
96
96
  "added_field",
97
97
  added,
98
98
  templateFields,
99
- fieldsWithoutValue
99
+ fieldsWithoutValue,
100
100
  ),
101
101
  getIngestFieldChanges(
102
102
  "removed_field",
103
103
  removed,
104
104
  templateFields,
105
- fieldsWithoutValue
105
+ fieldsWithoutValue,
106
106
  ),
107
107
  ]),
108
- _.flatten
108
+ _.flatten,
109
109
  )([]);
110
110
  }
111
111
 
@@ -127,20 +127,14 @@ const getParsedEvents = createSelector(
127
127
  const templateFields = _.flow(
128
128
  _.prop("content"),
129
129
  _.map(_.prop("fields")),
130
- _.flatten
130
+ _.flatten,
131
131
  )(template);
132
132
 
133
133
  const fieldsWithoutValue = _.flow(
134
134
  _.filter((f) =>
135
- _.includes(_.prop("type")(f))([
136
- "enriched_text",
137
- "url",
138
- "table",
139
- "system",
140
- "image",
141
- ])
135
+ _.includes(_.prop("type")(f))(["url", "table", "system", "image"]),
142
136
  ),
143
- _.map(_.prop("name"))
137
+ _.map(_.prop("name")),
144
138
  )(templateFields);
145
139
  return _.map((e) => {
146
140
  const { event } = e;
@@ -161,7 +155,7 @@ const getParsedEvents = createSelector(
161
155
  return getEmptyPayloadEvent(e);
162
156
  }
163
157
  })(events);
164
- }
158
+ },
165
159
  );
166
160
 
167
161
  export { getParsedEvents };