@pie-lib/editable-html 9.0.2 → 9.0.5

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": "@pie-lib/editable-html",
3
- "version": "9.0.2",
3
+ "version": "9.0.5",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "main": "lib/index.js",
@@ -12,7 +12,7 @@
12
12
  "@material-ui/styles": "^3.0.0-alpha.10",
13
13
  "@pie-lib/drag": "^1.1.52",
14
14
  "@pie-lib/math-rendering": "^2.4.5",
15
- "@pie-lib/math-toolbar": "^1.10.2",
15
+ "@pie-lib/math-toolbar": "^1.10.3",
16
16
  "@pie-lib/render-ui": "^4.13.1",
17
17
  "change-case": "^3.0.2",
18
18
  "classnames": "^2.2.6",
@@ -29,6 +29,7 @@
29
29
  "slate-edit-list": "^0.11.3",
30
30
  "slate-edit-table": "^0.17.0",
31
31
  "slate-html-serializer": "^0.6.12",
32
+ "slate-plain-serializer": "^0.5.26",
32
33
  "slate-prop-types": "^0.4.38",
33
34
  "slate-react": "^0.14.3",
34
35
  "slate-schema-violations": "^0.1.39",
@@ -46,6 +47,6 @@
46
47
  "publishConfig": {
47
48
  "access": "public"
48
49
  },
49
- "gitHead": "115df735f550ec05ba33dd6c654bd956f1a2b806",
50
+ "gitHead": "45e12d524142a6576c9ebb9292de01a34e88fc03",
50
51
  "scripts": {}
51
52
  }
package/src/editor.jsx CHANGED
@@ -11,6 +11,8 @@ import debug from 'debug';
11
11
  import { withStyles } from '@material-ui/core/styles';
12
12
  import classNames from 'classnames';
13
13
  import { color } from '@pie-lib/render-ui';
14
+ import Plain from 'slate-plain-serializer';
15
+
14
16
  import { getBase64 } from './serialization';
15
17
 
16
18
  export { ALL_PLUGINS, DEFAULT_PLUGINS, serialization };
@@ -31,7 +33,7 @@ const defaultResponseAreaProps = {
31
33
  onHandleAreaChange: () => {}
32
34
  };
33
35
 
34
- const defaultLanguageCharactersProps = [{ language: 'spanish' }, { language: 'special' }];
36
+ const defaultLanguageCharactersProps = [];
35
37
 
36
38
  const createToolbarOpts = toolbarOpts => {
37
39
  return {
@@ -100,7 +102,7 @@ export class Editor extends React.Component {
100
102
  }),
101
103
  className: PropTypes.string,
102
104
  maxImageWidth: PropTypes.number,
103
- maxImageHeight: PropTypes.number,
105
+ maxImageHeight: PropTypes.number
104
106
  };
105
107
 
106
108
  static defaultProps = {
@@ -350,7 +352,7 @@ export class Editor extends React.Component {
350
352
  log('[onBlur] node: ', node);
351
353
 
352
354
  return new Promise(resolve => {
353
- this.setState({ focusedNode: node }, this.handleBlur.bind(this, resolve));
355
+ this.setState({ focusedNode: !node ? null : node }, this.handleBlur.bind(this, resolve));
354
356
  this.props.onBlur(event);
355
357
  });
356
358
  };
@@ -572,14 +574,21 @@ export class Editor extends React.Component {
572
574
  };
573
575
 
574
576
  onDropPaste = async (event, change, dropContext) => {
575
- if (!this.props.imageSupport) {
576
- return;
577
- }
578
577
  const editor = change.editor;
579
578
  const transfer = getEventTransfer(event);
580
- const file = transfer.files[0];
579
+ const file = transfer.files && transfer.files[0];
580
+
581
+ const type = transfer.type;
582
+ const fragment = transfer.fragment;
583
+ const text = transfer.text;
581
584
 
582
- if (file && file.type === 'image/jpeg' || file.type === 'image/jpg' || file.type === 'image/png') {
585
+ if (
586
+ file &&
587
+ (file.type === 'image/jpeg' || file.type === 'image/jpg' || file.type === 'image/png')
588
+ ) {
589
+ if (!this.props.imageSupport) {
590
+ return;
591
+ }
583
592
  try {
584
593
  log('[onDropPaste]');
585
594
  const src = await getBase64(file);
@@ -606,6 +615,27 @@ export class Editor extends React.Component {
606
615
  } catch (err) {
607
616
  log('[onDropPaste] error: ', err);
608
617
  }
618
+ } else if (type === 'fragment') {
619
+ change.insertFragment(fragment);
620
+ } else if (type === 'text' || type === 'html') {
621
+ if (!text) {
622
+ return;
623
+ }
624
+ const {
625
+ value: { document, selection, startBlock }
626
+ } = change;
627
+
628
+ if (startBlock.isVoid) {
629
+ return;
630
+ }
631
+
632
+ const defaultBlock = startBlock;
633
+ const defaultMarks = document.getInsertMarksAtRange(selection);
634
+ const frag = Plain.deserialize(text, {
635
+ defaultBlock,
636
+ defaultMarks
637
+ }).document;
638
+ change.insertFragment(frag);
609
639
  }
610
640
  };
611
641
 
package/src/index.jsx CHANGED
@@ -95,9 +95,9 @@ export default class EditableHtml extends React.Component {
95
95
  c.focus();
96
96
 
97
97
  if (position === 'end' && lastText) {
98
- c.moveFocusTo(lastText.key, lastText.text.length).moveAnchorTo(
98
+ c.moveFocusTo(lastText.key, lastText.text?.length).moveAnchorTo(
99
99
  lastText.key,
100
- lastText.text.length
100
+ lastText.text?.length
101
101
  );
102
102
  }
103
103
 
@@ -0,0 +1,69 @@
1
+ import React from 'react';
2
+ import DialogContent from '@material-ui/core/DialogContent';
3
+ import ArrowBackIos from '@material-ui/icons/ArrowBackIos';
4
+ import TextField from '@material-ui/core/TextField';
5
+ import DialogActions from '@material-ui/core/DialogActions';
6
+ import Button from '@material-ui/core/Button';
7
+ import Dialog from '@material-ui/core/Dialog';
8
+ import PropTypes from "prop-types";
9
+
10
+ export class AltDialog extends React.Component {
11
+ static propTypes = {
12
+ onDone: PropTypes.func.isRequired,
13
+ alt: PropTypes.string
14
+ };
15
+
16
+ constructor(props) {
17
+ super(props);
18
+
19
+ const { alt } = props;
20
+
21
+ this.state = {
22
+ value: alt
23
+ };
24
+ }
25
+
26
+ closeDialog = () => {
27
+ const allDialogs = document.querySelectorAll('#text-dialog');
28
+
29
+ allDialogs.forEach(function(s) {
30
+ return s.remove();
31
+ });
32
+ };
33
+
34
+ onDone = () => {
35
+ const { onDone } = this.props;
36
+ const { value } = this.state;
37
+
38
+ onDone(value);
39
+ this.closeDialog();
40
+ };
41
+
42
+ render() {
43
+ const { value } = this.state;
44
+
45
+ return (
46
+ <Dialog open disablePortal onClose={this.closeDialog} id="text-dialog" hideBackdrop>
47
+ <DialogContent>
48
+ <div style={{ display: 'flex' }}>
49
+ <ArrowBackIos style={{ paddingTop: '6px' }} />
50
+ <TextField
51
+ multiline
52
+ placeholder={'Enter an Alt Text description of this image'}
53
+ helperText={
54
+ 'Users with visual limitations rely on Alt Text, since screen readers cannot otherwise describe the contents of an image.'
55
+ }
56
+ value={value}
57
+ onChange={event => this.setState({ value: event.target.value })}
58
+ />
59
+ </div>
60
+ </DialogContent>
61
+ <DialogActions>
62
+ <Button onClick={this.onDone}>Done</Button>
63
+ </DialogActions>
64
+ </Dialog>
65
+ );
66
+ }
67
+ }
68
+
69
+ export default AltDialog;
@@ -208,6 +208,7 @@ export class Component extends React.Component {
208
208
  const deleteStatus = node.data.get('deleteStatus');
209
209
  const alignment = node.data.get('alignment');
210
210
  const percent = node.data.get('percent');
211
+ const alt = node.data.get('alt');
211
212
  let margin;
212
213
  let justifyContent;
213
214
 
@@ -261,6 +262,7 @@ export class Component extends React.Component {
261
262
  src={src}
262
263
  style={size}
263
264
  onLoad={this.loadImage}
265
+ alt={alt}
264
266
  />
265
267
  <div
266
268
  ref={ref => {
@@ -1,9 +1,12 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
3
  import debug from 'debug';
4
+ import ReactDOM from 'react-dom';
4
5
  import { withStyles } from '@material-ui/core/styles';
6
+ import classNames from 'classnames';
5
7
 
6
8
  import { MarkButton } from '../toolbar/toolbar-buttons';
9
+ import AltDialog from './alt-dialog';
7
10
 
8
11
  const log = debug('@pie-lib:editable-html:plugins:image:image-toolbar');
9
12
 
@@ -24,16 +27,36 @@ AlignmentButton.propTypes = {
24
27
  export class ImageToolbar extends React.Component {
25
28
  static propTypes = {
26
29
  onChange: PropTypes.func.isRequired,
27
- classes: PropTypes.object.isRequired
30
+ classes: PropTypes.object.isRequired,
31
+ alignment: PropTypes.string,
32
+ alt: PropTypes.string,
33
+ imageLoaded: PropTypes.bool
34
+ };
35
+
36
+ onAltTextDone = newAlt => {
37
+ log('[onAltTextDone]: alt:', newAlt);
38
+
39
+ this.props.onChange({ alt: newAlt });
28
40
  };
29
41
 
30
42
  onAlignmentClick = alignment => {
31
43
  log('[onAlignmentClick]: alignment:', alignment);
32
- this.props.onChange(alignment);
44
+ this.props.onChange({ alignment });
45
+ };
46
+
47
+ renderDialog = () => {
48
+ const { alt } = this.props;
49
+ const popoverEl = document.createElement('div');
50
+
51
+ const el = <AltDialog alt={alt} onDone={this.onAltTextDone} />;
52
+
53
+ ReactDOM.render(el, popoverEl);
54
+
55
+ document.body.appendChild(popoverEl);
33
56
  };
34
57
 
35
58
  render() {
36
- const { classes, alignment } = this.props;
59
+ const { classes, alignment, imageLoaded } = this.props;
37
60
 
38
61
  return (
39
62
  <div className={classes.holder}>
@@ -52,6 +75,15 @@ export class ImageToolbar extends React.Component {
52
75
  active={alignment === 'right'}
53
76
  onClick={this.onAlignmentClick}
54
77
  />
78
+ <span
79
+ className={classNames({
80
+ [classes.disabled]: !imageLoaded,
81
+ [classes.altButton]: true
82
+ })}
83
+ onMouseDown={event => imageLoaded && this.renderDialog(event)}
84
+ >
85
+ Alt text
86
+ </span>
55
87
  </div>
56
88
  );
57
89
  }
@@ -62,7 +94,15 @@ const styles = theme => ({
62
94
  paddingLeft: theme.spacing.unit,
63
95
  display: 'flex',
64
96
  alignItems: 'center'
65
- }
97
+ },
98
+ disabled: {
99
+ opacity: 0.5
100
+ },
101
+ altButton: {
102
+ borderLeft: '1px solid grey',
103
+ paddingLeft: 8,
104
+ marginLeft: 4,
105
+ },
66
106
  });
67
107
 
68
108
  export default withStyles(styles)(ImageToolbar);
@@ -30,17 +30,21 @@ export default function ImagePlugin(opts) {
30
30
  supports: node => node.object === 'inline' && node.type === 'image',
31
31
  customToolbar: (node, value, onToolbarDone) => {
32
32
  const alignment = node.data.get('alignment');
33
- const onChange = alignment => {
33
+ const alt = node.data.get('alt');
34
+ const imageLoaded = node.data.get('loaded') !== false;
35
+ const onChange = newValues => {
34
36
  const update = {
35
37
  ...node.data.toObject(),
36
- alignment
38
+ ...newValues
37
39
  };
38
40
 
39
41
  const change = value.change().setNodeByKey(node.key, { data: update });
40
42
  onToolbarDone(change, false);
41
43
  };
42
44
 
43
- const Tb = () => <ImageToolbar alignment={alignment || 'left'} onChange={onChange} />;
45
+ const Tb = () => (
46
+ <ImageToolbar alt={alt} imageLoaded={imageLoaded} alignment={alignment || 'left'} onChange={onChange} />
47
+ );
44
48
  return Tb;
45
49
  },
46
50
  showDone: true
@@ -150,7 +154,8 @@ export const serialization = {
150
154
  height,
151
155
  margin: el.style.margin,
152
156
  justifyContent: el.style.justifyContent,
153
- alignment: el.getAttribute('alignment')
157
+ alignment: el.getAttribute('alignment'),
158
+ alt: el.getAttribute('alt')
154
159
  }
155
160
  };
156
161
  log('return object: ', out);
@@ -166,6 +171,7 @@ export const serialization = {
166
171
  const alignment = data.get('alignment');
167
172
  const margin = data.get('margin');
168
173
  const justifyContent = data.get('margin');
174
+ const alt = data.get('alt');
169
175
  const style = {};
170
176
  if (width) {
171
177
  style.width = `${width}px`;
@@ -203,7 +209,8 @@ export const serialization = {
203
209
  const props = {
204
210
  src,
205
211
  style,
206
- alignment
212
+ alignment,
213
+ alt
207
214
  };
208
215
 
209
216
  return <img {...props} />;
@@ -51,7 +51,6 @@ export default function ResponseAreaPlugin(opts) {
51
51
 
52
52
  if (!lastText) {
53
53
  return;
54
-
55
54
  }
56
55
  const parentNode = value.document.getParent(lastText.key);
57
56
 
@@ -79,7 +79,9 @@ TableCell.propTypes = {
79
79
  };
80
80
 
81
81
  export const moveFocusToBeginningOfTable = change => {
82
- const addedTable = change.value.document.findDescendant(d => !!d.data && !!d.data.get('newTable'));
82
+ const addedTable = change.value.document.findDescendant(
83
+ d => !!d.data && !!d.data.get('newTable')
84
+ );
83
85
 
84
86
  if (!addedTable) {
85
87
  return;