@pie-lib/editable-html 9.1.1 → 9.1.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/src/editor.jsx CHANGED
@@ -352,7 +352,10 @@ export class Editor extends React.Component {
352
352
  log('[onBlur] node: ', node);
353
353
 
354
354
  return new Promise(resolve => {
355
- this.setState({ focusedNode: !node ? null : node }, this.handleBlur.bind(this, resolve));
355
+ this.setState(
356
+ { preBlurValue: this.state.value, focusedNode: !node ? null : node },
357
+ this.handleBlur.bind(this, resolve)
358
+ );
356
359
  this.props.onBlur(event);
357
360
  });
358
361
  };
@@ -361,6 +364,8 @@ export class Editor extends React.Component {
361
364
  const editorDOM = document.querySelector(`[data-key="${this.state.value.document.key}"]`);
362
365
 
363
366
  setTimeout(() => {
367
+ const { value: stateValue } = this.state;
368
+
364
369
  if (!this.wrapperRef) {
365
370
  return;
366
371
  }
@@ -375,7 +380,10 @@ export class Editor extends React.Component {
375
380
 
376
381
  if (!isInCurrentComponent) {
377
382
  editorDOM.removeEventListener('blur', this.handleDomBlur);
378
- this.onBlur(e);
383
+
384
+ if (stateValue.isFocused) {
385
+ this.onBlur(e);
386
+ }
379
387
  }
380
388
  }, 50);
381
389
  };
@@ -489,6 +497,14 @@ export class Editor extends React.Component {
489
497
  });
490
498
  };
491
499
 
500
+ getFocusedValue = () => {
501
+ if (this.state.value.isFocused) {
502
+ return this.state.value;
503
+ }
504
+
505
+ return this.state.preBlurValue;
506
+ };
507
+
492
508
  UNSAFE_componentWillReceiveProps(props) {
493
509
  if (!props.value.document.equals(this.props.value.document)) {
494
510
  this.setState({
@@ -711,6 +727,7 @@ export class Editor extends React.Component {
711
727
  focus={this.focus}
712
728
  onKeyDown={onKeyDown}
713
729
  onChange={this.onChange}
730
+ getFocusedValue={this.getFocusedValue}
714
731
  onBlur={this.onBlur}
715
732
  onDrop={(event, editor) => this.onDropPaste(event, editor, true)}
716
733
  onPaste={(event, editor) => this.onDropPaste(event, editor)}
@@ -720,7 +737,12 @@ export class Editor extends React.Component {
720
737
  normalize={this.normalize}
721
738
  readOnly={disabled}
722
739
  spellCheck={spellCheck}
723
- className={classes.slateEditor}
740
+ className={classNames(
741
+ {
742
+ [classes.noPadding]: toolbarOpts && toolbarOpts.noBorder
743
+ },
744
+ classes.slateEditor
745
+ )}
724
746
  style={{
725
747
  minHeight: sizeStyle.minHeight,
726
748
  height: sizeStyle.height,
@@ -769,6 +791,9 @@ const styles = {
769
791
  },
770
792
  toolbarOnTop: {
771
793
  marginTop: '45px'
794
+ },
795
+ noPadding: {
796
+ padding: '0 !important'
772
797
  }
773
798
  };
774
799
 
@@ -1,10 +1,12 @@
1
1
  import React from 'react';
2
2
  import { withStyles } from '@material-ui/core/styles';
3
- import Popover from '@material-ui/core/Popover';
3
+ import Popper from '@material-ui/core/Popper';
4
4
  import Typography from '@material-ui/core/Typography';
5
5
 
6
6
  const styles = () => ({
7
7
  popover: {
8
+ background: '#fff',
9
+ padding: '10px',
8
10
  pointerEvents: 'none',
9
11
  zIndex: 99999
10
12
  },
@@ -19,8 +21,8 @@ const styles = () => ({
19
21
  }
20
22
  });
21
23
 
22
- const CustomPopOver = withStyles(styles)(({ classes, children, ...props }) => (
23
- <Popover
24
+ const CustomPopper = withStyles(styles)(({ classes, children, ...props }) => (
25
+ <Popper
24
26
  id="mouse-over-popover"
25
27
  open
26
28
  className={classes.popover}
@@ -36,10 +38,11 @@ const CustomPopOver = withStyles(styles)(({ classes, children, ...props }) => (
36
38
  horizontal: 'left'
37
39
  }}
38
40
  disableRestoreFocus
41
+ disableAutoFocus
39
42
  {...props}
40
43
  >
41
44
  <Typography classes={{ root: classes.typography }}>{children}</Typography>
42
- </Popover>
45
+ </Popper>
43
46
  ));
44
47
 
45
- export default CustomPopOver;
48
+ export default CustomPopper;
@@ -5,18 +5,11 @@ import get from 'lodash/get';
5
5
 
6
6
  import { PureToolbar } from '@pie-lib/math-toolbar';
7
7
 
8
- import CustomPopOver from './custom-popover';
8
+ import CustomPopper from './custom-popper';
9
9
  import { insertSnackBar } from '../respArea/utils';
10
10
  import { characterIcons, spanishConfig, specialConfig } from './utils';
11
11
  const log = debug('@pie-lib:editable-html:plugins:characters');
12
12
 
13
- const removeDialogs = () => {
14
- const prevDialogs = document.querySelectorAll('.insert-character-dialog');
15
-
16
- log('[characters:removeDialogs]');
17
- prevDialogs.forEach(s => s.remove());
18
- };
19
-
20
13
  const removePopOvers = () => {
21
14
  const prevPopOvers = document.querySelectorAll('#mouse-over-popover');
22
15
 
@@ -24,16 +17,22 @@ const removePopOvers = () => {
24
17
  prevPopOvers.forEach(s => s.remove());
25
18
  };
26
19
 
27
- const insertDialog = ({ value, callback, opts }) => {
20
+ export const removeDialogs = () => {
21
+ const prevDialogs = document.querySelectorAll('.insert-character-dialog');
22
+
23
+ log('[characters:removeDialogs]');
24
+ prevDialogs.forEach(s => s.remove());
25
+ removePopOvers();
26
+ };
27
+
28
+ const insertDialog = ({ editorDOM, value, callback, opts }) => {
28
29
  const newEl = document.createElement('div');
29
- const initialBodyOverflow = document.body.style.overflow;
30
30
 
31
31
  log('[characters:insertDialog]');
32
32
 
33
33
  removeDialogs();
34
34
 
35
35
  newEl.className = 'insert-character-dialog';
36
- document.body.style.overflow = 'hidden';
37
36
 
38
37
  let configToUse;
39
38
 
@@ -85,33 +84,48 @@ const insertDialog = ({ value, callback, opts }) => {
85
84
 
86
85
  popoverEl = document.createElement('div');
87
86
  ReactDOM.render(
88
- <CustomPopOver onClose={closePopOver} anchorEl={event.currentTarget}>
87
+ <CustomPopper onClose={closePopOver} anchorEl={event.currentTarget}>
89
88
  <div>{el.label}</div>
90
89
 
91
90
  <div style={infoStyle}>{el.description}</div>
92
91
 
93
92
  <div style={infoStyle}>{el.unicode}</div>
94
- </CustomPopOver>,
93
+ </CustomPopper>,
95
94
  popoverEl
96
95
  );
97
96
 
98
97
  document.body.appendChild(newEl);
99
98
  };
100
99
 
100
+ let firstCallMade = false;
101
+
102
+ const listener = e => {
103
+ // this will be triggered right after setting it because
104
+ // this toolbar is added on the mousedown event
105
+ // so right after mouseup, the click will be triggered
106
+ if (firstCallMade) {
107
+ const focusIsInModals =
108
+ newEl.contains(e.target) || (popoverEl && popoverEl.contains(e.target));
109
+ const focusIsInEditor = editorDOM.contains(e.target);
110
+
111
+ if (!(focusIsInModals || focusIsInEditor)) {
112
+ handleClose();
113
+ }
114
+ } else {
115
+ firstCallMade = true;
116
+ }
117
+ };
118
+
101
119
  const handleClose = () => {
120
+ callback(undefined, true);
102
121
  newEl.remove();
103
122
  closePopOver();
104
- document.body.style.overflow = initialBodyOverflow;
105
- callback(undefined, true);
123
+ document.body.removeEventListener('click', listener);
106
124
  };
107
125
 
108
126
  const handleChange = val => {
109
127
  if (typeof val === 'string') {
110
- callback(val);
111
- }
112
-
113
- if (configToUse.autoClose) {
114
- handleClose();
128
+ callback(val, true);
115
129
  }
116
130
  };
117
131
 
@@ -158,31 +172,40 @@ const insertDialog = ({ value, callback, opts }) => {
158
172
  const cursorItem = document.querySelector(`[data-key="${value.anchorKey}"]`);
159
173
 
160
174
  if (cursorItem) {
175
+ const bodyRect = document.body.getBoundingClientRect();
161
176
  const boundRect = cursorItem.getBoundingClientRect();
162
177
 
163
178
  document.body.appendChild(newEl);
164
- newEl.style.position = 'fixed';
165
- newEl.style.top = `${boundRect.top - newEl.offsetHeight - 10}px`;
166
- newEl.style.left = `${boundRect.left + cursorItem.offsetWidth + 10}px`;
179
+ newEl.style.position = 'absolute';
180
+ newEl.style.top = `${boundRect.top + Math.abs(bodyRect.top) - newEl.offsetHeight - 10}px`;
167
181
  newEl.style.zIndex = 99999;
168
182
 
169
- let firstCallMade = false;
170
-
171
- const listener = () => {
172
- // this will be triggered right after setting it because
173
- // this toolbar is added on the mousedown event
174
- // so right after mouseup, the click will be triggered
175
- if (firstCallMade) {
176
- document.body.removeEventListener('click', listener);
177
- handleClose();
178
- } else {
179
- firstCallMade = true;
180
- }
181
- };
182
-
183
- if (configToUse.autoClose) {
184
- document.body.addEventListener('click', listener);
183
+ const leftValue = `${boundRect.left +
184
+ Math.abs(bodyRect.left) +
185
+ cursorItem.offsetWidth +
186
+ 10}px`;
187
+
188
+ const rightValue = `${boundRect.x}px`;
189
+
190
+ newEl.style.left = leftValue;
191
+
192
+ const leftAlignedWidth = newEl.offsetWidth;
193
+
194
+ newEl.style.left = 'unset';
195
+ newEl.style.right = rightValue;
196
+
197
+ const rightAlignedWidth = newEl.offsetWidth;
198
+
199
+ newEl.style.left = 'unset';
200
+ newEl.style.right = 'unset';
201
+
202
+ if (leftAlignedWidth >= rightAlignedWidth) {
203
+ newEl.style.left = leftValue;
204
+ } else {
205
+ newEl.style.right = rightValue;
185
206
  }
207
+
208
+ document.body.addEventListener('click', listener);
186
209
  }
187
210
  });
188
211
  };
@@ -201,12 +224,15 @@ const CharacterIcon = ({ letter }) => (
201
224
  export default function CharactersPlugin(opts) {
202
225
  removeDialogs();
203
226
  return {
204
- name: 'math',
227
+ name: 'characters',
205
228
  toolbar: {
206
229
  icon: <CharacterIcon letter={opts.characterIcon || characterIcons[opts.language] || 'ñ'} />,
207
- onClick: (value, onChange) => {
230
+ onClick: (value, onChange, getFocusedValue) => {
231
+ const editorDOM = document.querySelector(`[data-key="${value.document.key}"]`);
208
232
  let valueToUse = value;
209
233
  const callback = (char, focus) => {
234
+ valueToUse = getFocusedValue();
235
+
210
236
  if (char) {
211
237
  const change = valueToUse
212
238
  .change()
@@ -220,15 +246,13 @@ export default function CharactersPlugin(opts) {
220
246
  log('[characters:click]');
221
247
 
222
248
  if (focus) {
223
- const editorDOM = document.querySelector(`[data-key="${valueToUse.document.key}"]`);
224
-
225
249
  if (editorDOM) {
226
250
  editorDOM.focus();
227
251
  }
228
252
  }
229
253
  };
230
254
 
231
- insertDialog({ value: valueToUse, callback, opts });
255
+ insertDialog({ editorDOM, value: valueToUse, callback, opts });
232
256
  }
233
257
  },
234
258
 
@@ -4,8 +4,7 @@ export const spanishConfig = {
4
4
  ['Á', 'É', 'Í', 'Ó', 'Ú'],
5
5
  ['—', '«', '»', 'ñ', 'ü'],
6
6
  ['-', '¿', '¡', 'Ñ', 'Ü']
7
- ],
8
- autoClose: true
7
+ ]
9
8
  };
10
9
 
11
10
  export const specialConfig = {
@@ -40,7 +40,7 @@ export const ToolbarButton = props => {
40
40
  <Button
41
41
  active={isActive}
42
42
  disabled={disabled}
43
- onClick={() => props.onClick(props.value, props.onChange)}
43
+ onClick={() => props.onClick(props.value, props.onChange, props.getFocusedValue)}
44
44
  extraStyles={props.buttonStyles}
45
45
  >
46
46
  {props.icon}
@@ -60,6 +60,7 @@ export const DefaultToolbar = ({
60
60
  pluginProps,
61
61
  value,
62
62
  onChange,
63
+ getFocusedValue,
63
64
  onDone,
64
65
  classes,
65
66
  showDone,
@@ -71,7 +72,15 @@ export const DefaultToolbar = ({
71
72
  <div className={classes.defaultToolbar}>
72
73
  <div className={classes.buttonsContainer}>
73
74
  {filtered.map((p, index) => {
74
- return <ToolbarButton {...p} key={index} value={value} onChange={onChange} />;
75
+ return (
76
+ <ToolbarButton
77
+ {...p}
78
+ key={index}
79
+ value={value}
80
+ onChange={onChange}
81
+ getFocusedValue={getFocusedValue}
82
+ />
83
+ );
75
84
  })}
76
85
  </div>
77
86
  {showDone && !deletable && <DoneButton onClick={onDone} />}
@@ -85,6 +94,7 @@ DefaultToolbar.propTypes = {
85
94
  pluginProps: PropTypes.object,
86
95
  value: SlatePropTypes.value.isRequired,
87
96
  onChange: PropTypes.func.isRequired,
97
+ getFocusedValue: PropTypes.func.isRequired,
88
98
  onDone: PropTypes.func.isRequired,
89
99
  showDone: PropTypes.bool,
90
100
  addArea: PropTypes.bool,
@@ -17,6 +17,7 @@ export class EditorAndToolbar extends React.Component {
17
17
  value: SlatePropTypes.value.isRequired,
18
18
  plugins: PropTypes.array.isRequired,
19
19
  onChange: PropTypes.func.isRequired,
20
+ getFocusedValue: PropTypes.func.isRequired,
20
21
  onDone: PropTypes.func.isRequired,
21
22
  onDataChange: PropTypes.func,
22
23
  toolbarRef: PropTypes.func,
@@ -49,6 +50,7 @@ export class EditorAndToolbar extends React.Component {
49
50
  value,
50
51
  plugins,
51
52
  onChange,
53
+ getFocusedValue,
52
54
  onDone,
53
55
  focusedNode,
54
56
  autoWidth,
@@ -94,7 +96,7 @@ export class EditorAndToolbar extends React.Component {
94
96
  classes.root
95
97
  )}
96
98
  >
97
- <div className={holderNames} id={'holder'}>
99
+ <div className={holderNames}>
98
100
  <div
99
101
  className={classNames(
100
102
  {
@@ -113,6 +115,7 @@ export class EditorAndToolbar extends React.Component {
113
115
  value={value}
114
116
  isFocused={inFocus}
115
117
  onChange={onChange}
118
+ getFocusedValue={getFocusedValue}
116
119
  onDone={onDone}
117
120
  onDataChange={onDataChange}
118
121
  toolbarRef={toolbarRef}
@@ -13,6 +13,7 @@ import { DoneButton } from './done-button';
13
13
  import { findSingleNode, findParentNode } from '../utils';
14
14
  import { withStyles } from '@material-ui/core/styles';
15
15
  import DefaultToolbar from './default-toolbar';
16
+ import { removeDialogs as removeCharacterDialogs } from '../characters';
16
17
 
17
18
  const log = debug('@pie-lib:editable-html:plugins:toolbar');
18
19
 
@@ -50,6 +51,7 @@ export class Toolbar extends React.Component {
50
51
  isFocused: PropTypes.bool,
51
52
  autoWidth: PropTypes.bool,
52
53
  onChange: PropTypes.func.isRequired,
54
+ getFocusedValue: PropTypes.func.isRequired,
53
55
  pluginProps: PropTypes.object,
54
56
  toolbarOpts: PropTypes.shape({
55
57
  position: PropTypes.oneOf(['bottom', 'top']),
@@ -68,6 +70,10 @@ export class Toolbar extends React.Component {
68
70
  };
69
71
  }
70
72
 
73
+ componentWillUnmount() {
74
+ removeCharacterDialogs();
75
+ }
76
+
71
77
  hasMark = type => {
72
78
  const { value } = this.props;
73
79
  return value.marks.some(mark => mark.type == type);
@@ -136,6 +142,7 @@ export class Toolbar extends React.Component {
136
142
  value,
137
143
  autoWidth,
138
144
  onChange,
145
+ getFocusedValue,
139
146
  isFocused,
140
147
  onDone,
141
148
  toolbarRef
@@ -248,6 +255,7 @@ export class Toolbar extends React.Component {
248
255
  pluginProps={pluginProps}
249
256
  value={value}
250
257
  onChange={onChange}
258
+ getFocusedValue={getFocusedValue}
251
259
  showDone={defaultToolbarShowDone}
252
260
  onDone={handleDone}
253
261
  deletable={deletable}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/plugins/characters/custom-popover.js"],"names":["styles","popover","pointerEvents","zIndex","paper","padding","height","width","typography","fontSize","textAlign","CustomPopOver","classes","children","props","vertical","horizontal","root"],"mappings":";;;;;;;;;;;;;AAAA;;AACA;;AACA;;AACA;;;;AAEA,IAAMA,MAAM,GAAG,SAATA,MAAS;AAAA,SAAO;AACpBC,IAAAA,OAAO,EAAE;AACPC,MAAAA,aAAa,EAAE,MADR;AAEPC,MAAAA,MAAM,EAAE;AAFD,KADW;AAKpBC,IAAAA,KAAK,EAAE;AACLC,MAAAA,OAAO,EAAE,EADJ;AAELC,MAAAA,MAAM,EAAE,MAFH;AAGLC,MAAAA,KAAK,EAAE;AAHF,KALa;AAUpBC,IAAAA,UAAU,EAAE;AACVC,MAAAA,QAAQ,EAAE,EADA;AAEVC,MAAAA,SAAS,EAAE;AAFD;AAVQ,GAAP;AAAA,CAAf;;AAgBA,IAAMC,aAAa,GAAG,wBAAWX,MAAX,EAAmB;AAAA,MAAGY,OAAH,QAAGA,OAAH;AAAA,MAAYC,QAAZ,QAAYA,QAAZ;AAAA,MAAyBC,KAAzB;AAAA,sBACvC,gCAAC,mBAAD;AACE,IAAA,EAAE,EAAC,oBADL;AAEE,IAAA,IAAI,MAFN;AAGE,IAAA,SAAS,EAAEF,OAAO,CAACX,OAHrB;AAIE,IAAA,OAAO,EAAE;AACPG,MAAAA,KAAK,EAAEQ,OAAO,CAACR;AADR,KAJX;AAOE,IAAA,YAAY,EAAE;AACZW,MAAAA,QAAQ,EAAE,QADE;AAEZC,MAAAA,UAAU,EAAE;AAFA,KAPhB;AAWE,IAAA,eAAe,EAAE;AACfD,MAAAA,QAAQ,EAAE,KADK;AAEfC,MAAAA,UAAU,EAAE;AAFG,KAXnB;AAeE,IAAA,mBAAmB;AAfrB,KAgBMF,KAhBN,gBAkBE,gCAAC,sBAAD;AAAY,IAAA,OAAO,EAAE;AAAEG,MAAAA,IAAI,EAAEL,OAAO,CAACJ;AAAhB;AAArB,KAAoDK,QAApD,CAlBF,CADuC;AAAA,CAAnB,CAAtB;eAuBeF,a","sourcesContent":["import React from 'react';\nimport { withStyles } from '@material-ui/core/styles';\nimport Popover from '@material-ui/core/Popover';\nimport Typography from '@material-ui/core/Typography';\n\nconst styles = () => ({\n popover: {\n pointerEvents: 'none',\n zIndex: 99999\n },\n paper: {\n padding: 20,\n height: 'auto',\n width: 'auto'\n },\n typography: {\n fontSize: 50,\n textAlign: 'center'\n }\n});\n\nconst CustomPopOver = withStyles(styles)(({ classes, children, ...props }) => (\n <Popover\n id=\"mouse-over-popover\"\n open\n className={classes.popover}\n classes={{\n paper: classes.paper\n }}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left'\n }}\n transformOrigin={{\n vertical: 'top',\n horizontal: 'left'\n }}\n disableRestoreFocus\n {...props}\n >\n <Typography classes={{ root: classes.typography }}>{children}</Typography>\n </Popover>\n));\n\nexport default CustomPopOver;\n"],"file":"custom-popover.js"}