@startlibs/form 1.1.3 → 1.2.0-rc.2

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 (112) hide show
  1. package/lib/AutoComplete.mjs +7 -7
  2. package/lib/AutoComplete.mjs.map +1 -1
  3. package/lib/Checkbox.mjs +4 -2
  4. package/lib/Checkbox.mjs.map +1 -1
  5. package/lib/Combobox.mjs +1 -1
  6. package/lib/Combobox.mjs.map +1 -1
  7. package/lib/DatePicker.mjs +25 -9
  8. package/lib/DatePicker.mjs.map +1 -1
  9. package/lib/Errors.mjs +20 -10
  10. package/lib/Errors.mjs.map +1 -1
  11. package/lib/ExpandableBox.mjs +1 -1
  12. package/lib/ExpandableBox.mjs.map +1 -1
  13. package/lib/Field.mjs +10 -5
  14. package/lib/Field.mjs.map +1 -1
  15. package/lib/FieldBox.mjs +3 -3
  16. package/lib/FieldBox.mjs.map +1 -1
  17. package/lib/InputboxGroup.mjs +1 -1
  18. package/lib/InputboxGroup.mjs.map +1 -1
  19. package/lib/RichText.mjs +11 -6
  20. package/lib/RichText.mjs.map +1 -1
  21. package/lib/TextInput.mjs +4 -4
  22. package/lib/TextInput.mjs.map +1 -1
  23. package/lib/alt/checkbox/SimpleCheckbox.mjs +1 -1
  24. package/lib/alt/checkbox/SimpleCheckbox.mjs.map +1 -1
  25. package/lib/alt/checkbox/ToggleCheckbox.mjs +4 -3
  26. package/lib/alt/checkbox/ToggleCheckbox.mjs.map +1 -1
  27. package/lib/alt/radiobox/CleanTabRadiobox.mjs +1 -1
  28. package/lib/alt/radiobox/CleanTabRadiobox.mjs.map +1 -1
  29. package/lib/alt/radiobox/SimpleRadiobox.mjs +2 -2
  30. package/lib/alt/radiobox/SimpleRadiobox.mjs.map +1 -1
  31. package/lib/alt/radiobox/TabRadiobox.mjs +1 -1
  32. package/lib/alt/radiobox/TabRadiobox.mjs.map +1 -1
  33. package/lib/index.cjs +94 -51
  34. package/lib/index.cjs.map +1 -1
  35. package/lib/index.mjs +2 -2
  36. package/lib/withFormImageInput.mjs +1 -1
  37. package/lib/withFormImageInput.mjs.map +1 -1
  38. package/lib-yak/AutoComplete.mjs +705 -0
  39. package/lib-yak/AutoComplete.mjs.map +1 -0
  40. package/lib-yak/Checkbox.mjs +80 -0
  41. package/lib-yak/Checkbox.mjs.map +1 -0
  42. package/lib-yak/Combobox.mjs +652 -0
  43. package/lib-yak/Combobox.mjs.map +1 -0
  44. package/lib-yak/ComboboxWithCustom.mjs +77 -0
  45. package/lib-yak/ComboboxWithCustom.mjs.map +1 -0
  46. package/lib-yak/DatePicker.mjs +774 -0
  47. package/lib-yak/DatePicker.mjs.map +1 -0
  48. package/lib-yak/EditableBox.mjs +495 -0
  49. package/lib-yak/EditableBox.mjs.map +1 -0
  50. package/lib-yak/Errors.mjs +226 -0
  51. package/lib-yak/Errors.mjs.map +1 -0
  52. package/lib-yak/ExpandableBox.mjs +247 -0
  53. package/lib-yak/ExpandableBox.mjs.map +1 -0
  54. package/lib-yak/Field.mjs +93 -0
  55. package/lib-yak/Field.mjs.map +1 -0
  56. package/lib-yak/FieldBox.mjs +173 -0
  57. package/lib-yak/FieldBox.mjs.map +1 -0
  58. package/lib-yak/Form.mjs +322 -0
  59. package/lib-yak/Form.mjs.map +1 -0
  60. package/lib-yak/FormValue.mjs +111 -0
  61. package/lib-yak/FormValue.mjs.map +1 -0
  62. package/lib-yak/InputboxGroup.mjs +146 -0
  63. package/lib-yak/InputboxGroup.mjs.map +1 -0
  64. package/lib-yak/Radiobox.mjs +69 -0
  65. package/lib-yak/Radiobox.mjs.map +1 -0
  66. package/lib-yak/RichText.mjs +776 -0
  67. package/lib-yak/RichText.mjs.map +1 -0
  68. package/lib-yak/TextInput.mjs +589 -0
  69. package/lib-yak/TextInput.mjs.map +1 -0
  70. package/lib-yak/alt/checkbox/SimpleCheckbox.mjs +225 -0
  71. package/lib-yak/alt/checkbox/SimpleCheckbox.mjs.map +1 -0
  72. package/lib-yak/alt/checkbox/ToggleCheckbox.mjs +173 -0
  73. package/lib-yak/alt/checkbox/ToggleCheckbox.mjs.map +1 -0
  74. package/lib-yak/alt/radiobox/CleanTabRadiobox.mjs +87 -0
  75. package/lib-yak/alt/radiobox/CleanTabRadiobox.mjs.map +1 -0
  76. package/lib-yak/alt/radiobox/IconRadioBox.mjs +138 -0
  77. package/lib-yak/alt/radiobox/IconRadioBox.mjs.map +1 -0
  78. package/lib-yak/alt/radiobox/SimpleRadiobox.mjs +203 -0
  79. package/lib-yak/alt/radiobox/SimpleRadiobox.mjs.map +1 -0
  80. package/lib-yak/alt/radiobox/TabRadiobox.mjs +117 -0
  81. package/lib-yak/alt/radiobox/TabRadiobox.mjs.map +1 -0
  82. package/lib-yak/enhanceInput.mjs +131 -0
  83. package/lib-yak/enhanceInput.mjs.map +1 -0
  84. package/lib-yak/index.mjs +28 -0
  85. package/lib-yak/index.mjs.map +1 -0
  86. package/lib-yak/useConfirmDialog.mjs +117 -0
  87. package/lib-yak/useConfirmDialog.mjs.map +1 -0
  88. package/lib-yak/useDraftConfirmation.mjs +56 -0
  89. package/lib-yak/useDraftConfirmation.mjs.map +1 -0
  90. package/lib-yak/validation.mjs +91 -0
  91. package/lib-yak/validation.mjs.map +1 -0
  92. package/lib-yak/withFormImageInput.mjs +298 -0
  93. package/lib-yak/withFormImageInput.mjs.map +1 -0
  94. package/package.json +13 -3
  95. package/src/AutoComplete.jsx +12 -12
  96. package/src/Checkbox.jsx +2 -2
  97. package/src/Combobox.jsx +1 -1
  98. package/src/DatePicker.jsx +25 -22
  99. package/src/Errors.jsx +18 -8
  100. package/src/ExpandableBox.jsx +3 -3
  101. package/src/Field.jsx +9 -4
  102. package/src/FieldBox.jsx +6 -6
  103. package/src/IconRadioBoxOldHubt.jsx +1 -1
  104. package/src/InputboxGroup.jsx +1 -1
  105. package/src/RichText.jsx +17 -13
  106. package/src/TextInput.jsx +37 -26
  107. package/src/alt/checkbox/SimpleCheckbox.jsx +5 -5
  108. package/src/alt/checkbox/ToggleCheckbox.jsx +9 -8
  109. package/src/alt/radiobox/CleanTabRadiobox.jsx +2 -2
  110. package/src/alt/radiobox/SimpleRadiobox.jsx +7 -7
  111. package/src/alt/radiobox/TabRadiobox.jsx +4 -4
  112. package/src/withFormImageInput.jsx +2 -2
@@ -0,0 +1,776 @@
1
+ import _defineProperty from '@babel/runtime/helpers/defineProperty';
2
+ import _zipObject from 'lodash/fp/zipObject';
3
+ import _last from 'lodash/fp/last';
4
+ import _flatten from 'lodash/fp/flatten';
5
+ import _fromPairs from 'lodash/fp/fromPairs';
6
+ import _pick from 'lodash/fp/pick';
7
+ import _toPairs from 'lodash/fp/toPairs';
8
+ import _identity from 'lodash/fp/identity';
9
+ import { yakDomTag } from '@startlibs/utils/yakDomTag';
10
+ import React, { useRef, useEffect, useState, useContext } from 'react';
11
+ import { styled, css } from '@yak/react';
12
+ import ContentEditableModule from 'react-contenteditable';
13
+ import { darken } from 'polished';
14
+ import { getColor, stopPropagation, linkRegex } from '@startlibs/utils';
15
+ import { usePopupToggle, useEnsureRef, useRefState, useToggle, useLazyConstant, StartlibsContext } from '@startlibs/core';
16
+ import { Errors } from './Errors.mjs';
17
+ import { Field } from './Field.mjs';
18
+ import { WithForm } from './Form.mjs';
19
+ import { TextInput } from './TextInput.mjs';
20
+ import { useInput } from './enhanceInput.mjs';
21
+ import { Icon, Button } from '@startlibs/components';
22
+
23
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
24
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
25
+
26
+ // Node/esbuild interop em .mjs devolve module.exports; Rollup devolve o default. Cobre os dois.
27
+ const ContentEditable = ContentEditableModule.default || ContentEditableModule;
28
+ const InputIcon = styled(Icon)`
29
+ position: absolute;
30
+ outline: none;
31
+ bottom: 0;
32
+ font-size: 21px;
33
+ line-height: 3rem;
34
+ right: 0;
35
+ width: 2rem;
36
+ pointer-events: none;
37
+ color: ${__yakProps => {
38
+ const v = getColor('gray150');
39
+ return typeof v === 'function' ? v(__yakProps) : v;
40
+ }};
41
+ `;
42
+ const FORMAT_TOOLS = {
43
+ 'bold': ['b', 'strong'],
44
+ 'italic': 'i',
45
+ 'underline': 'u',
46
+ 'strikethrough': 'strike',
47
+ 'link': 'a',
48
+ 'insertUnorderedList': ['ul', 'li'],
49
+ 'insertOrderedList': ['ol', 'li'],
50
+ 'outdent': '',
51
+ 'indent': 'blockquote',
52
+ 'fontSizeUp': 'font',
53
+ 'fontSizeDown': 'font'
54
+ };
55
+ const TAG_TO_STYLE_PROPERTY = {
56
+ 'b': 'fontWeight',
57
+ 'strong': 'fontWeight',
58
+ 'u': 'textDecoration',
59
+ 'strike': 'textDecoration',
60
+ 'font': 'fontSize',
61
+ 'i': 'fontStyle'
62
+ };
63
+ let GET_LIMITS_FONT_SIZE_PX = () => {
64
+ const s = document.createElement('span');
65
+ s.style.fontSize = 'medium';
66
+ document.body.appendChild(s);
67
+ const fontSize = parseInt(getComputedStyle(s).fontSize);
68
+ document.body.removeChild(s);
69
+ const limits = [fontSize * 3 / 5, fontSize * 3];
70
+ GET_LIMITS_FONT_SIZE_PX = () => limits;
71
+ return limits;
72
+ };
73
+ const wrapWith = (el, wrapper) => {
74
+ el.parentNode.insertBefore(wrapper, el);
75
+ wrapper.appendChild(el);
76
+ };
77
+ const FIX_STYLE = {
78
+ 'fontWeight': (v, el) => {
79
+ const intFontWeight = parseInt(window.getComputedStyle(el).fontWeight);
80
+ return intFontWeight <= 400 ? 400 : 600;
81
+ },
82
+ 'fontSize': (v, el) => {
83
+ const [min, max] = GET_LIMITS_FONT_SIZE_PX();
84
+ const intFontSize = parseInt(window.getComputedStyle(el).fontSize);
85
+ return Math.min(max, Math.max(min, intFontSize)) + 'px';
86
+ },
87
+ 'textDecoration': (v, el) => {
88
+ const values = v.split(' ').filter(v => v === 'underline' || v === 'line-through');
89
+ if (values.indexOf('underline') >= 0) {
90
+ wrapWith(el, document.createElement('u'));
91
+ }
92
+ if (values.indexOf('line-through') >= 0) {
93
+ wrapWith(el, document.createElement('strike'));
94
+ }
95
+ return '';
96
+ }
97
+ };
98
+
99
+ // Precisa vir antes de RichTextContainer: o yak resolve `${ToolsContainer}` (seletor dentro de
100
+ // props.showToolbar) estaticamente lendo o módulo de cima pra baixo — em tempo de execução
101
+ // (sc) a ordem nunca importou, já que o template só lê ToolsContainer dentro de um callback
102
+ // (props => ...), chamado bem depois do módulo inteiro já ter sido avaliado.
103
+ const ToolsContainer = styled(yakDomTag("div"))`
104
+ background: ${__yakProps => {
105
+ const v = getColor('lightBluishGray');
106
+ return typeof v === 'function' ? v(__yakProps) : v;
107
+ }};
108
+ padding: 0 0.125rem;
109
+ border-top-left-radius: 7px;
110
+ border-top-right-radius: 7px;
111
+ transition: 0.225s;
112
+ max-height: 0;
113
+ pointer-events: none;
114
+ overflow: hidden;
115
+ display: flex;
116
+ align-items: center;
117
+ `;
118
+ const RichTextContainer = styled(yakDomTag("div")).attrs({
119
+ className: "styled-rich-text-container"
120
+ })`
121
+ position:relative;
122
+ width: 100%;
123
+ border: 1px solid ${__yakProps => {
124
+ const v = getColor('gray210');
125
+ return typeof v === 'function' ? v(__yakProps) : v;
126
+ }};
127
+ border-radius: 8px;
128
+ background: white;
129
+ ${props => props.hasErrors && css`border-color: ${__yakProps => {
130
+ const v = getColor(props)('alert');
131
+ return typeof v === 'function' ? v(__yakProps) : v;
132
+ }}`};
133
+ ${props => props.showToolbar && css`
134
+ ${ToolsContainer} {
135
+ transition: 0.5s;
136
+ max-height: 7.5rem;
137
+ transition: 0.6s;
138
+ pointer-events: auto;
139
+ }
140
+ `};
141
+ ${props => props.focus && css`
142
+ border-color: ${__yakProps => {
143
+ const v = getColor('gray180');
144
+ return typeof v === 'function' ? v(__yakProps) : v;
145
+ }};
146
+ box-shadow: 0px 0px 0px 2px rgba(0,0,0,0.1);
147
+ `}
148
+ ${props => props.locked && css`
149
+ padding-right: 2.25rem;
150
+ background: rgba(0,0,0,0.075);
151
+ `}
152
+ ${props => props.linkColor && css`
153
+ a, a:visited, a:focus, a:active {
154
+ color: ${__yakProps => {
155
+ const v = props.linkColor;
156
+ return typeof v === 'function' ? v(__yakProps) : v;
157
+ }};
158
+ }
159
+ a:hover {
160
+ color: ${__yakProps => {
161
+ const v = darken(0.05, props.linkColor);
162
+ return typeof v === 'function' ? v(__yakProps) : v;
163
+ }};
164
+ }
165
+ `}
166
+ ul {
167
+ list-style-type: disc;
168
+ }
169
+ ul, ol {
170
+ padding-inline-start: 25px;
171
+ margin-block-start: 0.5em;
172
+ margin-block-end: 0.5em;
173
+ }
174
+ `;
175
+ const RichTextInput = styled(yakDomTag("div")).attrs({
176
+ className: "styled-rich-text-input"
177
+ })`
178
+ font-size: 14px;
179
+ color: ${__yakProps => {
180
+ const v = getColor('gray60');
181
+ return typeof v === 'function' ? v(__yakProps) : v;
182
+ }};
183
+ background: white;
184
+ border-radius: 7px;
185
+ > div {
186
+ overflow: auto;
187
+ width: 100%;
188
+ outline: none;
189
+ min-height: ${props => props.minHeight || 60}px;
190
+ padding: 0.7rem;
191
+ }
192
+ b, strong {
193
+ font-weight: 600;
194
+ }
195
+ a {
196
+ text-decoration: underline;
197
+ }
198
+ > div[contenteditable=true]:empty:before {
199
+ content: attr(placeholder);
200
+ color: rgba(0,0,0,0.3);
201
+ }
202
+ ${props => props.disabled && css`
203
+ &[disabled] {
204
+ background: rgba(0, 0, 0, 0.075) !important;
205
+ opacity: 0.75 !important;
206
+ }
207
+ `}
208
+ `;
209
+ const ToolButton = styled(Button)`
210
+ min-height: 2rem;
211
+ min-width: 2rem;
212
+ background: transparent;
213
+ box-shadow: none;
214
+ margin: 0.25rem 0.125rem;
215
+ & ~ & {
216
+ margin-left: 0.125rem;
217
+ }
218
+ .icon {
219
+ font-size: 23px;
220
+ color: ${__yakProps => {
221
+ const v = getColor('gray90');
222
+ return typeof v === 'function' ? v(__yakProps) : v;
223
+ }};
224
+ position: absolute;
225
+ top: 50%;
226
+ left: 50%;
227
+ -webkit-transform: translate(-50%,-50%);
228
+ transform: translate(-50%,-50%);
229
+ }
230
+ &:hover, &.activated:hover {
231
+ background: rgba(0,0,0,0.11);
232
+ }
233
+ &.activated {
234
+ background: rgba(0,0,0,0.09);
235
+ }
236
+ &:focus:after {
237
+ content: none;
238
+ }
239
+ `;
240
+ const ToolSeparator = styled(yakDomTag("span"))`
241
+ border-left: 1px solid rgba(0,0,0,0.1);
242
+ margin-left: 0.75rem;
243
+ margin-right: 0.5rem;
244
+ height: 1.5rem;
245
+ `;
246
+ const JS_CLASS = "EDITOR--JS";
247
+ const getNextRegularCharIndex = (text, offset, direction) => {
248
+ const regex = /[A-Za-z0-9ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏàáâãäåæçèéêëìíîïÐÑÒÓÔÕÖØÙÚÛÜÝÞßðñòóôõöøùúûüýþÿ]/g;
249
+ let cursor = offset;
250
+ while ((text[cursor + direction] || '').match(regex)) {
251
+ cursor += direction;
252
+ }
253
+ return cursor;
254
+ };
255
+ const addHttp = s => {
256
+ s = s.replace(/^\s/, '');
257
+ if (s.indexOf('http://') === 0 || s.indexOf('https://') === 0) {
258
+ return s;
259
+ } else {
260
+ return 'http://' + s;
261
+ }
262
+ };
263
+ const setSelectionRange = function (input, selectionStart) {
264
+ let selectionEnd = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
265
+ if (!input) return;
266
+ let range = document.createRange();
267
+ let sel = window.getSelection();
268
+ range.setStart(input, selectionStart);
269
+ if (selectionEnd) {
270
+ range.setEnd(input, selectionEnd);
271
+ } else {
272
+ range.collapse(true);
273
+ }
274
+ sel.removeAllRanges();
275
+ sel.addRange(range);
276
+ };
277
+ const cleanEditor = (editor, acceptedTags) => {
278
+ Array.prototype.slice.call(editor.querySelectorAll('.Apple-interchange-newline')).forEach(el => el.remove());
279
+ Array.prototype.slice.call(editor.querySelectorAll('iframe')).forEach(el => el.remove());
280
+ const acceptedStyles = acceptedTags.map(t => TAG_TO_STYLE_PROPERTY[t]).filter(_identity);
281
+ const newSpace = document.createTextNode('\u00A0');
282
+ if (window.getSelection().rangeCount) {
283
+ const container = window.getSelection().getRangeAt(0).startContainer;
284
+ if (container.nodeType === 3) {
285
+ container.parentNode.insertBefore(newSpace, container.nextSibling);
286
+ } else {
287
+ container.appendChild(newSpace);
288
+ }
289
+ if (newSpace.previousSibling && newSpace.previousSibling.textContent === '\u00A0') {
290
+ newSpace.previousSibling.remove();
291
+ }
292
+ setSelectionRange(newSpace, 0);
293
+ }
294
+ const clean = children => {
295
+ Array.prototype.slice.call(children).forEach(el => {
296
+ try {
297
+ if (el.nodeType === 3) return;
298
+ if (!el.tagName) {
299
+ el.remove();
300
+ return;
301
+ }
302
+ let tagName = el.tagName.toLowerCase();
303
+ const style = getComputedStyle(el);
304
+ const rect = el.getBoundingClientRect();
305
+ const isHidden = style.display === 'none' || rect.width === 0 || rect.height === 0;
306
+ if (isHidden && el.children && !el.children.length && tagName !== 'br') {
307
+ el.remove();
308
+ } else if (acceptedTags.indexOf(tagName) >= 0) {
309
+ clean(el.childNodes);
310
+ el.removeAttribute('class');
311
+ if (tagName === 'a') {
312
+ el.setAttribute('target', '_blank');
313
+ }
314
+ if (el.getAttribute('style')) {
315
+ const usedStyles = _toPairs(_pick(acceptedStyles, el.style)).filter(_ref => {
316
+ let [k, v] = _ref;
317
+ return v !== "";
318
+ });
319
+ const fixedStyle = _fromPairs(usedStyles.map(_ref2 => {
320
+ let [k, v] = _ref2;
321
+ return [k, (FIX_STYLE[k] || _identity)(v, el)];
322
+ }));
323
+ el.removeAttribute('style');
324
+ Object.assign(el.style, fixedStyle);
325
+ }
326
+ } else if (tagName === 'a') {
327
+ clean(el.childNodes);
328
+ if (el.textContent !== el.href || el.textContent.length < 5) el.outerHTML = el.innerHTML;
329
+ } else {
330
+ clean(el.childNodes);
331
+ if (el.hasChildNodes()) {
332
+ const newTagName = style.display === 'inline' ? 'span' : 'div';
333
+ const newEl = document.createElement(newTagName);
334
+ Array.prototype.slice.call(el.childNodes).forEach(child => newEl.appendChild(child));
335
+ el.parentNode.replaceChild(newEl, el);
336
+ } else {
337
+ el.remove();
338
+ }
339
+ }
340
+ } catch (e) {
341
+ if (el && el.remove) el.remove();
342
+ }
343
+ });
344
+ };
345
+ clean(editor.childNodes);
346
+ };
347
+ const RichText = _ref3 => {
348
+ let {
349
+ path,
350
+ inputRef,
351
+ label,
352
+ autoHideToolbar,
353
+ autoFocus,
354
+ disabled,
355
+ minHeight,
356
+ placeholder,
357
+ className,
358
+ formatTools = ['bold', 'italic', 'underline', 'strikethrough', 'separator', 'link'],
359
+ reformatText = _identity,
360
+ mandatory,
361
+ descText,
362
+ helpText,
363
+ linkColor,
364
+ tabIndex = 1,
365
+ value,
366
+ setValue,
367
+ raw,
368
+ locked
369
+ } = _ref3;
370
+ const linkPopup = usePopupToggle();
371
+ const input = useEnsureRef(inputRef);
372
+ const activeLinkNode = useRef();
373
+ const container = useRef();
374
+ useRefState();
375
+
376
+ // auto-hide needs
377
+ useRefState();
378
+ useRefState();
379
+ useRefState([]);
380
+ const containerFocused = useToggle();
381
+ const needsNormalize = useRefState();
382
+ const alreadyCancelled = useRefState();
383
+
384
+ // const [formValue, setFormValue] = useInput({path})
385
+ const [formValue, _setFormValue, {
386
+ hasErrors
387
+ }] = useInput({
388
+ path,
389
+ value,
390
+ setValue
391
+ }, raw);
392
+
393
+ // const tempValue = useConstant(formValue)
394
+ const tempValue = useRef(formValue);
395
+ const setFormValue = v => {
396
+ tempValue.current = v;
397
+ _setFormValue(v);
398
+ };
399
+ useEffect(() => {
400
+ if (value === formValue && formValue !== tempValue) {
401
+ tempValue.current = formValue;
402
+ }
403
+ }, [path]);
404
+ const [activatedTools, setActivatedTools] = useState({});
405
+ const validElements = useLazyConstant(() => ['br', 'div', 'p', 'span'].concat(_flatten(formatTools.map(tool => FORMAT_TOOLS[tool] || []))));
406
+ useEffect(() => {
407
+ cleanEditor(input.current, validElements);
408
+ }, []);
409
+ const focus = () => input.current && input.current.focus();
410
+
411
+ // Foco inicial opcional (ex.: abrir a dialog já no campo de descrição). Em
412
+ // rAF pra rodar depois que a dialog termina de montar e não perder o foco.
413
+ useEffect(() => {
414
+ if (autoFocus) {
415
+ requestAnimationFrame(focus);
416
+ }
417
+ }, []);
418
+ useEffect(() => {
419
+ if (needsNormalize.get()) {
420
+ input.current.normalize();
421
+ needsNormalize.set(false);
422
+ }
423
+ });
424
+ const triggerScrollForPanel = d => {
425
+ requestAnimationFrame(time => {
426
+ if (time > d + 250) return;
427
+ window.dispatchEvent(new Event('scroll'));
428
+ triggerScrollForPanel(d);
429
+ });
430
+ };
431
+ const containerFocus = () => {
432
+ containerFocused.openWith(2);
433
+ triggerScrollForPanel(Date.now());
434
+ };
435
+ const containerBlur = e => {
436
+ if (containerFocused.isOpen !== 1) {
437
+ containerFocused.close();
438
+ triggerScrollForPanel(Date.now());
439
+ }
440
+ };
441
+ const onBlur = e => {
442
+ const newValue = input.current.textContent.length === 0 ? '' : input.current.innerHTML;
443
+ // Só reescreve se o conteúdo mudou de fato: focar/desfocar sem editar não pode
444
+ // marcar o form como alterado (senão dispara a confirmação de descarte à toa).
445
+ // textContent vazio equivale a valor ausente/'' do form.
446
+ if (newValue !== (formValue || '')) {
447
+ setFormValue(newValue);
448
+ }
449
+ };
450
+ const keydownHandler = e => {
451
+ if (containerFocused.isOpen === 2) {
452
+ if (formatTools.indexOf('link') < 0) {
453
+ return;
454
+ }
455
+ if (e.key === "k" && e.ctrlKey || e.key === "k" && e.metaKey) {
456
+ e.preventDefault();
457
+ execCommand(null, 'link');
458
+ }
459
+ }
460
+ };
461
+ useEffect(() => {
462
+ document.addEventListener('keydown', keydownHandler);
463
+ return () => document.removeEventListener('keydown', keydownHandler);
464
+ }, [containerFocused.isOpen]);
465
+ const execCommand = (e, command) => {
466
+ if (command === 'link') {
467
+ let nodeEl;
468
+ if (!isSelectionInsideRichTextEditor()) {
469
+ let anchor = document.createElement('a');
470
+ anchor.innerHTML = '&#8203;';
471
+ input.current.appendChild(anchor);
472
+ nodeEl = anchor;
473
+ } else {
474
+ const range = window.getSelection().getRangeAt(0);
475
+ const selectedNode = range.startContainer;
476
+ if (range.collapsed) {
477
+ const start = getNextRegularCharIndex(selectedNode.textContent, range.startOffset, -1);
478
+ const end = getNextRegularCharIndex(selectedNode.textContent, range.startOffset - 1, 1);
479
+ if (start < range.startOffset && end > range.startOffset - 1) {
480
+ range.setStart(selectedNode, start);
481
+ range.setEnd(selectedNode, end + 1);
482
+ }
483
+ }
484
+ const selectedEl = selectedNode.nodeType === Node.TEXT_NODE ? selectedNode.parentNode : selectedNode;
485
+ const parent = selectedEl.closest('a,.' + JS_CLASS);
486
+ if (parent && parent.matches('a')) {
487
+ nodeEl = parent;
488
+ } else {
489
+ let anchor = document.createElement('a');
490
+ anchor.innerHTML = '&#8203;';
491
+ if (range.collapsed) {
492
+ range.insertNode(anchor);
493
+ } else {
494
+ if (range.commonAncestorContainer === input.current) {
495
+ range.setStart(range.endContainer, 0);
496
+ }
497
+ document.execCommand('createLink', false, '_');
498
+ anchor = window.getSelection().anchorNode;
499
+ }
500
+ const parent = range.commonAncestorContainer.nodeType === 3 ? range.commonAncestorContainer.parentNode : range.commonAncestorContainer;
501
+ const links = parent.querySelectorAll("a[href='_']");
502
+ Array.from(links).slice(0, -1).forEach(removeLinkFromNode);
503
+ nodeEl = _last(links);
504
+ nodeEl.setAttribute('href', ' ');
505
+ }
506
+ }
507
+ tempValue.current = input.current.innerHTML;
508
+ openDialog(nodeEl);
509
+ } else {
510
+ if (!isSelectionInsideRichTextEditor()) {
511
+ input.current.focus();
512
+ const last = document.createTreeWalker(input.current, NodeFilter.SHOW_TEXT).lastChild();
513
+ const range = document.createRange();
514
+ range.selectNodeContents(last || input.current);
515
+ range.collapse(false);
516
+ const sel = window.getSelection();
517
+ sel.removeAllRanges();
518
+ sel.addRange(range);
519
+ }
520
+ document.execCommand(command);
521
+ setFormValue(input.current.innerHTML);
522
+ toggleButtonsState();
523
+ focus();
524
+ }
525
+ };
526
+ const isSelectionInsideRichTextEditor = () => {
527
+ const range = window.getSelection().getRangeAt(0);
528
+ if (!range) return false;
529
+ const el = range.startContainer;
530
+ if (!el) return false;
531
+ const node = el.closest ? el : el.parentNode || document.body;
532
+ return node.closest('.' + JS_CLASS) === input.current;
533
+ };
534
+ const saveRichTextLink = _ref4 => {
535
+ let {
536
+ linkUrl,
537
+ linkText
538
+ } = _ref4;
539
+ const currentActiveLinkNode = activeLinkNode.current;
540
+ currentActiveLinkNode.setAttribute('href', addHttp(linkUrl));
541
+ currentActiveLinkNode.setAttribute('target', '_blank');
542
+ if (currentActiveLinkNode.textContent !== linkText) {
543
+ currentActiveLinkNode.innerText = linkText;
544
+ }
545
+ linkPopup.close();
546
+ setSelectionRange(currentActiveLinkNode.lastChild, currentActiveLinkNode.textContent.length, 0);
547
+ focus();
548
+ setFormValue(input.current.innerHTML);
549
+ };
550
+ const cancelRichTextLink = e => {
551
+ if (alreadyCancelled.get()) return;
552
+ const currentActiveLinkNode = activeLinkNode.current;
553
+ const span = document.createElement('span');
554
+ if (currentActiveLinkNode.nextSibling) currentActiveLinkNode.parentNode.insertBefore(span, currentActiveLinkNode.nextSibling);else currentActiveLinkNode.parentNode.append(span);
555
+ setSelectionRange(span, 0, 0);
556
+ focus();
557
+ if (currentActiveLinkNode.getAttribute('href') === ' ') {
558
+ removeLink(e);
559
+ }
560
+ alreadyCancelled.set(true);
561
+ span.remove();
562
+ if (input.current) {
563
+ input.current.normalize();
564
+ } else {
565
+ needsNormalize.set(true);
566
+ }
567
+ linkPopup.close();
568
+ };
569
+ const handleKeyUp = e => {
570
+ toggleButtonsState();
571
+ setFormValue(reformatText(input.current.innerHTML));
572
+ };
573
+ const handleKeyDown = e => {
574
+ /*if (linkJustCreated.current && e.which === 8) {
575
+ linkJustCreated.current.outerHTML = linkJustCreated.current.innerHTML
576
+ input.current.normalize()
577
+ return
578
+ }
579
+ commands.set([])
580
+ linkJustCreated.set(null)
581
+ const selectedRange = window.getSelection().getRangeAt(0)
582
+ if (!selectedRange) return
583
+ const possibleTextNode = selectedRange.startContainer
584
+ const node = possibleTextNode.nodeType === Node.TEXT_NODE ? possibleTextNode.parentNode : possibleTextNode
585
+ const parent = node.closest('a,.'+JS_CLASS)
586
+ if ((e.key === 'Enter' || e.which === 32) && parent && parent.matches('.'+JS_CLASS)) {
587
+ const range = document.createRange()
588
+ range.setEnd(selectedRange.startContainer, selectedRange.startOffset)
589
+ range.setStart(selectedRange.startContainer, 0)
590
+ traverseAndAddLink(range)
591
+ }*/
592
+ };
593
+ const handlePasteDrop = e => setTimeout(() => pasted(), 1);
594
+ const handleClick = e => {
595
+ toggleButtonsState();
596
+ const el = e.target.closest('a,.' + JS_CLASS);
597
+ if (el && el.matches('a') && el.getAttribute('href')) {
598
+ openDialog(el);
599
+ }
600
+ };
601
+ const openDialog = el => {
602
+ activeLinkNode.current = el;
603
+ alreadyCancelled.set(false);
604
+ linkPopup.openWith({
605
+ linkText: el.textContent,
606
+ linkUrl: (el.getAttribute('href') || '').trim()
607
+ });
608
+ };
609
+ const removeLinkFromNode = node => {
610
+ node.outerHTML = node.innerHTML === '\u200b' ? '' : node.innerHTML;
611
+ };
612
+ const removeLink = e => {
613
+ if (e) e.preventDefault();
614
+ alreadyCancelled.set(true);
615
+ removeLinkFromNode(activeLinkNode.current);
616
+ linkPopup.close();
617
+ if (input.current) {
618
+ setFormValue(input.current.innerHTML);
619
+ input.current.normalize();
620
+ }
621
+ };
622
+ const pasted = e => {
623
+ cleanEditor(input.current, validElements);
624
+ };
625
+ const toggleButtonsState = () => {
626
+ return setActivatedTools(_zipObject(formatTools, formatTools.map(t => window.document.queryCommandState(t))));
627
+ };
628
+ const toolButton = tool => /*#__PURE__*/React.createElement(ToolButton, {
629
+ key: tool,
630
+ tabIndex: -1,
631
+ onClick: e => execCommand(e, tool),
632
+ className: 'button exec-command rich-text-button only-icon ' + (activatedTools[tool] ? 'activated' : ''),
633
+ type: "button"
634
+ }, /*#__PURE__*/React.createElement("span", {
635
+ className: 'icon icon-' + tool
636
+ }));
637
+ const richTextElement = /*#__PURE__*/React.createElement(RichTextContainer, {
638
+ linkColor: linkColor,
639
+ ref: container,
640
+ onFocus: containerFocus,
641
+ onBlur: containerBlur,
642
+ focus: containerFocused.isOpen,
643
+ showToolbar: !disabled & !autoHideToolbar || containerFocused.isOpen,
644
+ className: className
645
+ }, /*#__PURE__*/React.createElement(ToolsContainer, {
646
+ className: "rich-text-tools"
647
+ }, formatTools.map((t, i) => t === 'separator' ? /*#__PURE__*/React.createElement(ToolSeparator, {
648
+ className: "separator",
649
+ key: "separator-" + i
650
+ }) : toolButton(t))), /*#__PURE__*/React.createElement(RichTextInput, null, /*#__PURE__*/React.createElement(ContentEditable, {
651
+ className: JS_CLASS,
652
+ innerRef: input,
653
+ html: tempValue.current || "",
654
+ onBlur: onBlur,
655
+ onClick: handleClick,
656
+ onKeyUp: handleKeyUp,
657
+ onKeyDown: handleKeyDown,
658
+ onPaste: handlePasteDrop,
659
+ onDrop: handlePasteDrop,
660
+ tabIndex: tabIndex,
661
+ disabled: disabled,
662
+ minHeight: minHeight,
663
+ placeholder: placeholder
664
+ })), locked && /*#__PURE__*/React.createElement(InputIcon, {
665
+ icon: "lock"
666
+ }), linkPopup.isOpen && /*#__PURE__*/React.createElement(EditLinkPopup, {
667
+ input: input,
668
+ activeLinkNode: activeLinkNode,
669
+ cancel: cancelRichTextLink,
670
+ values: linkPopup.isOpen,
671
+ removeLink: removeLink,
672
+ saveLink: saveRichTextLink
673
+ }));
674
+ return label ? /*#__PURE__*/React.createElement(Field, {
675
+ labelClick: focus,
676
+ descText: descText,
677
+ helpText: helpText,
678
+ label: label,
679
+ mandatory: mandatory
680
+ }, richTextElement) : richTextElement;
681
+ };
682
+ const EditLinkPopup = _ref5 => {
683
+ let {
684
+ cancel,
685
+ removeLink,
686
+ activeLinkNode,
687
+ input,
688
+ saveLink,
689
+ values
690
+ } = _ref5;
691
+ const linkPopup = useRef();
692
+ const formRef = useRef();
693
+ const linkTextRef = useRef();
694
+ const linkUrlRef = useRef();
695
+ const LABELS = useContext(StartlibsContext).labels.RichText;
696
+ const preValidation = formValues => {
697
+ let errors;
698
+ if (!formValues.linkText) {
699
+ errors = {
700
+ 'linkText': [LABELS.linkTextError]
701
+ };
702
+ }
703
+ if (!formValues.linkUrl || formValues.linkUrl === ' ' || !linkRegex.test(formValues.linkUrl.trim())) {
704
+ errors = _objectSpread(_objectSpread({}, errors), {}, {
705
+ 'linkUrl': [LABELS.linkUrlError]
706
+ });
707
+ }
708
+ return errors;
709
+ };
710
+ const checkEscOrEnter = e => {
711
+ if (e.keyCode === 27) {
712
+ e.stopPropagation();
713
+ cancel(e);
714
+ } else if (e.keyCode === 13) {
715
+ e.preventDefault();
716
+ e.stopPropagation();
717
+ formRef.current.submitForm();
718
+ }
719
+ };
720
+ useEffect(() => {
721
+ const currentActiveLinkNode = activeLinkNode.current;
722
+ const currentLinkPopup = linkPopup.current;
723
+ const offsetLeft = currentActiveLinkNode.getClientRects().length > 1 ? input.offsetLeft : currentActiveLinkNode.offsetLeft;
724
+ const left = currentActiveLinkNode.offsetWidth / 2 - currentLinkPopup.offsetWidth / 2 + offsetLeft;
725
+ const top = currentActiveLinkNode.offsetHeight + currentActiveLinkNode.offsetTop;
726
+ Object.assign(currentLinkPopup.style, {
727
+ 'position': 'absolute',
728
+ 'left': left + 'px',
729
+ 'top': top + 'px'
730
+ });
731
+ if (values.linkText) {
732
+ linkUrlRef.current.focus();
733
+ } else {
734
+ linkTextRef.current.focus();
735
+ }
736
+ return cancel;
737
+ }, []);
738
+ return /*#__PURE__*/React.createElement(WithForm, {
739
+ skipDom: true,
740
+ ref: formRef,
741
+ action: saveLink,
742
+ values: values,
743
+ preValidation: preValidation
744
+ }, form => /*#__PURE__*/React.createElement("div", {
745
+ onClick: stopPropagation(),
746
+ ref: linkPopup,
747
+ onKeyDown: checkEscOrEnter,
748
+ className: "popup rich-text-link-editor ignore-link",
749
+ contentEditable: false
750
+ }, /*#__PURE__*/React.createElement(TextInput, {
751
+ ref: linkTextRef,
752
+ mandatory: true,
753
+ path: "linkText",
754
+ label: LABELS.popupLinkText
755
+ }), /*#__PURE__*/React.createElement(TextInput, {
756
+ ref: linkUrlRef,
757
+ mandatory: true,
758
+ path: "linkUrl",
759
+ label: LABELS.popupLinkUrl
760
+ }), /*#__PURE__*/React.createElement("div", {
761
+ className: "popup-footer"
762
+ }, /*#__PURE__*/React.createElement(Button, {
763
+ className: "remove-link",
764
+ icon: "remove",
765
+ onClick: removeLink
766
+ }, LABELS.popupRemove), /*#__PURE__*/React.createElement(Button, {
767
+ onClick: cancel,
768
+ tabIndex: 0
769
+ }, LABELS.popupCancel), /*#__PURE__*/React.createElement(Button, {
770
+ highlight: true,
771
+ onClick: form.willSubmitForm
772
+ }, LABELS.popupConfirm)), /*#__PURE__*/React.createElement(Errors, null)));
773
+ };
774
+
775
+ export { RichText, RichTextContainer, RichTextInput };
776
+ //# sourceMappingURL=RichText.mjs.map