@pie-lib/graphing 4.0.15-next.15 → 4.0.15-next.21

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/lib/mark-label.js CHANGED
@@ -168,13 +168,17 @@ LabelInput.propTypes = {
168
168
  onBlur: _propTypes["default"].func
169
169
  };
170
170
  var MarkLabel = exports.MarkLabel = function MarkLabel(props) {
171
+ // the node is kept in a ref as well as in the state: the state is needed to reposition the label
172
+ // once it has a size, the ref is what the focus is asserted on - it is never a render behind
173
+ var inputNode = (0, _react.useRef)(null);
171
174
  var _useState = (0, _react.useState)(null),
172
175
  _useState2 = (0, _slicedToArray2["default"])(_useState, 2),
173
176
  input = _useState2[0],
174
177
  setInput = _useState2[1];
175
178
  var _ref = (0, _react.useCallback)(function (node) {
176
- return setInput(node);
177
- });
179
+ inputNode.current = node;
180
+ setInput(node);
181
+ }, []);
178
182
  var theme = (0, _styles.useTheme)();
179
183
  var mark = props.mark,
180
184
  graphProps = props.graphProps,
@@ -189,12 +193,20 @@ var MarkLabel = exports.MarkLabel = function MarkLabel(props) {
189
193
  correctnesslabel = mark.correctnesslabel,
190
194
  correctlabel = mark.correctlabel;
191
195
  var isFocused = function isFocused() {
192
- return !!input && typeof document !== 'undefined' && document.activeElement === input;
196
+ return !!inputNode.current && typeof document !== 'undefined' && document.activeElement === inputNode.current;
193
197
  };
198
+
199
+ // A label that has just been added is empty and its input is enabled - label mode has to be on
200
+ // for that to happen and it always starts off, so a mark that was loaded with an empty label
201
+ // cannot grab the focus. This is read from the mark rather than taken from the autoFocus prop
202
+ // because the tool that asked for the label can be remounted by the marks coming back from the
203
+ // host (async model save) before the input ever renders, and its autoFocus goes with it.
204
+ var isNewLabel = (0, _react.useRef)(mark.label === '' && !disabled && !mark.disabled);
194
205
  var onChange = function onChange(e) {
195
206
  return setLabel(e.target.value);
196
207
  };
197
208
  var handleBlur = (0, _react.useCallback)(function () {
209
+ isNewLabel.current = false;
198
210
  if (label === '') {
199
211
  props.onChange('');
200
212
  }
@@ -224,20 +236,31 @@ var MarkLabel = exports.MarkLabel = function MarkLabel(props) {
224
236
  }, [debouncedLabel]);
225
237
 
226
238
  // A label that has just been added has to be focused so that it can be typed into right away.
227
- // This also re-focuses the input if it gets remounted while it is being edited - saving the model
228
- // is done by the host (api call) and the model that comes back can replace the input node.
239
+ // This runs after every render on purpose - there is no reliable single moment to focus at:
240
+ // the input shows up whenever the mark comes back from the (async) model save, and a remount
241
+ // of the input (same save, replaced node) drops the focus without this component being told.
242
+ // Asserting the focus on every render is what makes it stick; it is a no-op once the input has
243
+ // it, so the caret the user placed is left alone.
229
244
  (0, _react.useEffect)(function () {
230
- if (!autoFocus || !input || disabled || isFocused()) {
245
+ var node = inputNode.current;
246
+
247
+ // node.disabled, not the disabled prop: a disabled input silently ignores focus()
248
+ if (!autoFocus && !isNewLabel.current || !node || node.disabled || isFocused()) {
231
249
  return;
232
250
  }
233
- input.focus();
251
+ node.focus();
252
+
253
+ // a new label is focused once, so it cannot fight over the focus with another one
254
+ if (isFocused()) {
255
+ isNewLabel.current = false;
256
+ }
234
257
 
235
258
  // keep the caret at the end, otherwise typing continues in front of the existing label
236
- var caret = (input.value || '').length;
237
- if (input.setSelectionRange) {
238
- input.setSelectionRange(caret, caret);
259
+ var caret = (node.value || '').length;
260
+ if (node.setSelectionRange) {
261
+ node.setSelectionRange(caret, caret);
239
262
  }
240
- }, [autoFocus, input, disabled]);
263
+ });
241
264
  var rect = input ? input.getBoundingClientRect() : {
242
265
  width: 0,
243
266
  height: 0
@@ -1 +1 @@
1
- {"version":3,"file":"mark-label.js","names":["_react","_interopRequireWildcard","require","_propTypes","_interopRequireDefault","_styles","_reactInputAutosize","_useDebounce","_plot","_renderUi","_labelSvgIcon","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ownKeys","keys","getOwnPropertySymbols","filter","enumerable","push","apply","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","DEBOUNCE_DELAY","StyledInputCorrect","styled","_ref2","theme","padding","spacing","borderRadius","fontSize","backgroundColor","color","defaults","WHITE","CORRECT_WITH_ICON","border","concat","StyledInputIncorrect","_ref3","INCORRECT_WITH_ICON","StyledInputMissing","_ref4","MISSING_WITH_ICON","fontWeight","StyledIncorrect","getInputStyles","disabled","markDisabled","fontFamily","typography","PRIMARY_DARK","SECONDARY","WebkitOpacity","undefined","WebkitTextFillColor","getStudentInputStyles","position","exports","graphProps","mark","rect","width","height","scale","domain","range","shift","rightEdge","x","bottomEdge","y","h","max","v","min","coordinates","left","top","LabelInput","_ref5","_ref","externalInputRef","label","inputStyle","onChange","onBlur","createElement","inputRef","value","propTypes","PropTypes","func","string","bool","object","MarkLabel","props","_useState","useState","_useState2","_slicedToArray2","input","setInput","useCallback","node","useTheme","autoFocus","_useState3","_useState4","setLabel","correctness","correctnesslabel","correctlabel","isFocused","document","activeElement","target","handleBlur","debouncedLabel","useDebounce","useEffect","focus","caret","setSelectionRange","getBoundingClientRect","pos","leftTop","style","pointerEvents","display","alignItems","gap","secondLabelStyle","disabledInput","renderInput","labelValue","studentInputStyle","type","Fragment","marginLeft","types","GraphPropsType","_default"],"sources":["../src/mark-label.jsx"],"sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { styled, useTheme } from '@mui/material/styles';\nimport AutosizeInput from 'react-input-autosize';\nimport { useDebounce } from './use-debounce';\nimport { types } from '@pie-lib/plot';\nimport { color } from '@pie-lib/render-ui';\nimport SvgIcon from './label-svg-icon';\n\nconst DEBOUNCE_DELAY = 500;\n\nconst StyledInputCorrect = styled('div')(({ theme }) => ({\n float: 'right',\n padding: theme.spacing(0.5),\n borderRadius: '4px',\n fontSize: '10px',\n backgroundColor: color.defaults.WHITE,\n color: color.defaults.CORRECT_WITH_ICON,\n border: `solid 1px ${color.defaults.CORRECT_WITH_ICON}`,\n}));\n\nconst StyledInputIncorrect = styled('div')(({ theme }) => ({\n float: 'right',\n padding: theme.spacing(0.5),\n borderRadius: '4px',\n fontSize: '10px',\n backgroundColor: color.defaults.WHITE,\n color: color.defaults.INCORRECT_WITH_ICON,\n border: `solid 1px ${color.defaults.INCORRECT_WITH_ICON}`,\n}));\n\nconst StyledInputMissing = styled('div')(({ theme }) => ({\n float: 'right',\n padding: theme.spacing(0.5),\n borderRadius: '4px',\n fontSize: '10px',\n backgroundColor: color.defaults.WHITE,\n color: color.defaults.MISSING_WITH_ICON,\n border: `solid 1px ${color.defaults.MISSING_WITH_ICON}`,\n fontWeight: 'bold',\n}));\n\nconst StyledIncorrect = styled('div')(() => ({\n float: 'right',\n padding: 0,\n borderRadius: '4px',\n fontSize: '10px',\n backgroundColor: color.defaults.WHITE,\n color: color.defaults.INCORRECT_WITH_ICON,\n fontWeight: 'bold',\n}));\n\nconst getInputStyles = (theme, disabled, markDisabled) => ({\n float: 'right',\n padding: theme.spacing(0.5),\n fontFamily: theme.typography.fontFamily,\n fontSize: '10px',\n border: disabled\n ? `solid 1px ${color.defaults.PRIMARY_DARK}`\n : markDisabled\n ? `solid 1px ${color.disabled()}`\n : `solid 1px ${color.defaults.SECONDARY}`,\n borderRadius: '3px',\n color: markDisabled ? color.disabled() : color.defaults.PRIMARY_DARK,\n backgroundColor: color.defaults.WHITE,\n WebkitOpacity: disabled ? '1' : undefined,\n WebkitTextFillColor: markDisabled ? color.disabled() : undefined,\n});\n\nconst getStudentInputStyles = () => ({\n padding: '0',\n border: 'none',\n color: 'inherit',\n fontWeight: 'bold',\n});\n\nexport const position = (graphProps, mark, rect = { width: 0, height: 0 }) => {\n const { scale, domain, range } = graphProps;\n const shift = 5;\n\n const rightEdge = scale.x(mark.x) + rect.width + shift;\n const bottomEdge = scale.y(mark.y) + rect.height + shift;\n\n const h = rightEdge >= scale.x(domain.max) ? 'left' : 'right';\n const v = bottomEdge >= scale.y(range.min) ? 'top' : 'bottom';\n\n return `${v}-${h}`;\n};\n\nexport const coordinates = (graphProps, mark, rect = { width: 0, height: 0 }, position) => {\n const { scale } = graphProps;\n const shift = 5;\n\n switch (position) {\n case 'bottom-right':\n return { left: scale.x(mark.x) + shift, top: scale.y(mark.y) + shift };\n case 'bottom-left':\n return { left: scale.x(mark.x) - shift - rect.width, top: scale.y(mark.y) + shift };\n case 'top-left':\n return { left: scale.x(mark.x) - shift - rect.width, top: scale.y(mark.y) - shift - rect.height };\n case 'top-right':\n return { left: scale.x(mark.x) + shift, top: scale.y(mark.y) - shift - rect.height };\n default:\n return {};\n }\n};\n\nconst LabelInput = ({ _ref, externalInputRef, label, disabled, inputStyle, onChange, onBlur }) => (\n <AutosizeInput\n inputRef={(r) => {\n _ref(r);\n externalInputRef(r);\n }}\n disabled={disabled}\n inputStyle={inputStyle}\n value={label}\n onChange={onChange}\n onBlur={onBlur}\n />\n);\n\nLabelInput.propTypes = {\n _ref: PropTypes.func,\n externalInputRef: PropTypes.func,\n label: PropTypes.string,\n disabled: PropTypes.bool,\n inputStyle: PropTypes.object,\n onChange: PropTypes.func,\n onBlur: PropTypes.func,\n};\n\nexport const MarkLabel = (props) => {\n const [input, setInput] = useState(null);\n const _ref = useCallback((node) => setInput(node));\n const theme = useTheme();\n\n const { mark, graphProps, disabled, autoFocus, inputRef: externalInputRef } = props;\n\n const [label, setLabel] = useState(mark.label);\n const { correctness, correctnesslabel, correctlabel } = mark;\n\n const isFocused = () => !!input && typeof document !== 'undefined' && document.activeElement === input;\n\n const onChange = (e) => setLabel(e.target.value);\n\n const handleBlur = useCallback(() => {\n if (label === '') {\n props.onChange('');\n }\n\n // lets the tool know that this label is not being edited anymore, so it stops auto focusing it\n if (props.onBlur) {\n props.onBlur();\n }\n }, [label, props.onChange, props.onBlur]);\n\n const debouncedLabel = useDebounce(label, DEBOUNCE_DELAY);\n\n // useState only sets the value once, to synch props to state need useEffect.\n // While this input has the focus we keep what the user typed: the mark can come back from an\n // (async) model save with an older label and that would overwrite the characters typed meanwhile.\n useEffect(() => {\n if (isFocused()) {\n return;\n }\n\n setLabel(mark.label);\n }, [mark.label]);\n\n // pick up the change to debouncedLabel and save it\n useEffect(() => {\n if (typeof debouncedLabel === 'string' && debouncedLabel !== mark.label) {\n props.onChange(debouncedLabel);\n }\n }, [debouncedLabel]);\n\n // A label that has just been added has to be focused so that it can be typed into right away.\n // This also re-focuses the input if it gets remounted while it is being edited - saving the model\n // is done by the host (api call) and the model that comes back can replace the input node.\n useEffect(() => {\n if (!autoFocus || !input || disabled || isFocused()) {\n return;\n }\n\n input.focus();\n\n // keep the caret at the end, otherwise typing continues in front of the existing label\n const caret = (input.value || '').length;\n\n if (input.setSelectionRange) {\n input.setSelectionRange(caret, caret);\n }\n }, [autoFocus, input, disabled]);\n\n const rect = input ? input.getBoundingClientRect() : { width: 0, height: 0 };\n const pos = position(graphProps, mark, rect);\n const leftTop = coordinates(graphProps, mark, rect, pos);\n\n const style = {\n position: 'fixed',\n pointerEvents: 'auto',\n display: 'flex',\n alignItems: 'center',\n gap: '3px',\n ...leftTop,\n };\n\n const secondLabelStyle = {\n ...style,\n top: leftTop.top + 25,\n };\n\n const disabledInput = disabled || mark.disabled;\n\n const renderInput = (inputStyle, labelValue) => (\n <LabelInput\n _ref={_ref}\n externalInputRef={externalInputRef}\n label={labelValue}\n disabled={disabledInput}\n inputStyle={inputStyle}\n onChange={onChange}\n onBlur={handleBlur}\n />\n );\n\n const studentInputStyle = getStudentInputStyles();\n\n if (correctness === 'correct' && correctnesslabel === 'correct' && correctlabel) {\n return (\n <StyledInputCorrect style={style}>\n <SvgIcon type=\"correct\" />\n {renderInput(studentInputStyle, correctlabel)}\n </StyledInputCorrect>\n );\n }\n\n if (correctness === 'correct' && correctnesslabel === 'correct' && !correctlabel) {\n return null;\n }\n\n if (correctness === 'correct' && correctnesslabel === 'incorrect') {\n return (\n <>\n <StyledInputIncorrect style={style}>\n <SvgIcon type=\"incorrect\" />\n {label === '' ? (\n <SvgIcon type=\"empty\" style={{ marginLeft: '3px' }} />\n ) : (\n renderInput(studentInputStyle, label)\n )}\n </StyledInputIncorrect>\n <StyledInputMissing style={secondLabelStyle}>{renderInput(studentInputStyle, correctlabel)}</StyledInputMissing>\n </>\n );\n }\n\n if (correctness === 'missing') {\n return <StyledInputMissing style={style}>{renderInput(studentInputStyle, label)}</StyledInputMissing>;\n }\n\n if (correctness === 'incorrect') {\n return <StyledIncorrect style={style}>{renderInput(studentInputStyle, label)}</StyledIncorrect>;\n }\n\n return <div style={style}>{renderInput(getInputStyles(theme, disabled, mark.disabled), label)}</div>;\n};\n\nMarkLabel.propTypes = {\n autoFocus: PropTypes.bool,\n disabled: PropTypes.bool,\n onBlur: PropTypes.func,\n onChange: PropTypes.func,\n graphProps: types.GraphPropsType,\n inputRef: PropTypes.func,\n mark: PropTypes.object,\n};\n\nexport default MarkLabel;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"mark-label.js","names":["_react","_interopRequireWildcard","require","_propTypes","_interopRequireDefault","_styles","_reactInputAutosize","_useDebounce","_plot","_renderUi","_labelSvgIcon","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ownKeys","keys","getOwnPropertySymbols","filter","enumerable","push","apply","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","DEBOUNCE_DELAY","StyledInputCorrect","styled","_ref2","theme","padding","spacing","borderRadius","fontSize","backgroundColor","color","defaults","WHITE","CORRECT_WITH_ICON","border","concat","StyledInputIncorrect","_ref3","INCORRECT_WITH_ICON","StyledInputMissing","_ref4","MISSING_WITH_ICON","fontWeight","StyledIncorrect","getInputStyles","disabled","markDisabled","fontFamily","typography","PRIMARY_DARK","SECONDARY","WebkitOpacity","undefined","WebkitTextFillColor","getStudentInputStyles","position","exports","graphProps","mark","rect","width","height","scale","domain","range","shift","rightEdge","x","bottomEdge","y","h","max","v","min","coordinates","left","top","LabelInput","_ref5","_ref","externalInputRef","label","inputStyle","onChange","onBlur","createElement","inputRef","value","propTypes","PropTypes","func","string","bool","object","MarkLabel","props","inputNode","useRef","_useState","useState","_useState2","_slicedToArray2","input","setInput","useCallback","node","current","useTheme","autoFocus","_useState3","_useState4","setLabel","correctness","correctnesslabel","correctlabel","isFocused","document","activeElement","isNewLabel","target","handleBlur","debouncedLabel","useDebounce","useEffect","focus","caret","setSelectionRange","getBoundingClientRect","pos","leftTop","style","pointerEvents","display","alignItems","gap","secondLabelStyle","disabledInput","renderInput","labelValue","studentInputStyle","type","Fragment","marginLeft","types","GraphPropsType","_default"],"sources":["../src/mark-label.jsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { styled, useTheme } from '@mui/material/styles';\nimport AutosizeInput from 'react-input-autosize';\nimport { useDebounce } from './use-debounce';\nimport { types } from '@pie-lib/plot';\nimport { color } from '@pie-lib/render-ui';\nimport SvgIcon from './label-svg-icon';\n\nconst DEBOUNCE_DELAY = 500;\n\nconst StyledInputCorrect = styled('div')(({ theme }) => ({\n float: 'right',\n padding: theme.spacing(0.5),\n borderRadius: '4px',\n fontSize: '10px',\n backgroundColor: color.defaults.WHITE,\n color: color.defaults.CORRECT_WITH_ICON,\n border: `solid 1px ${color.defaults.CORRECT_WITH_ICON}`,\n}));\n\nconst StyledInputIncorrect = styled('div')(({ theme }) => ({\n float: 'right',\n padding: theme.spacing(0.5),\n borderRadius: '4px',\n fontSize: '10px',\n backgroundColor: color.defaults.WHITE,\n color: color.defaults.INCORRECT_WITH_ICON,\n border: `solid 1px ${color.defaults.INCORRECT_WITH_ICON}`,\n}));\n\nconst StyledInputMissing = styled('div')(({ theme }) => ({\n float: 'right',\n padding: theme.spacing(0.5),\n borderRadius: '4px',\n fontSize: '10px',\n backgroundColor: color.defaults.WHITE,\n color: color.defaults.MISSING_WITH_ICON,\n border: `solid 1px ${color.defaults.MISSING_WITH_ICON}`,\n fontWeight: 'bold',\n}));\n\nconst StyledIncorrect = styled('div')(() => ({\n float: 'right',\n padding: 0,\n borderRadius: '4px',\n fontSize: '10px',\n backgroundColor: color.defaults.WHITE,\n color: color.defaults.INCORRECT_WITH_ICON,\n fontWeight: 'bold',\n}));\n\nconst getInputStyles = (theme, disabled, markDisabled) => ({\n float: 'right',\n padding: theme.spacing(0.5),\n fontFamily: theme.typography.fontFamily,\n fontSize: '10px',\n border: disabled\n ? `solid 1px ${color.defaults.PRIMARY_DARK}`\n : markDisabled\n ? `solid 1px ${color.disabled()}`\n : `solid 1px ${color.defaults.SECONDARY}`,\n borderRadius: '3px',\n color: markDisabled ? color.disabled() : color.defaults.PRIMARY_DARK,\n backgroundColor: color.defaults.WHITE,\n WebkitOpacity: disabled ? '1' : undefined,\n WebkitTextFillColor: markDisabled ? color.disabled() : undefined,\n});\n\nconst getStudentInputStyles = () => ({\n padding: '0',\n border: 'none',\n color: 'inherit',\n fontWeight: 'bold',\n});\n\nexport const position = (graphProps, mark, rect = { width: 0, height: 0 }) => {\n const { scale, domain, range } = graphProps;\n const shift = 5;\n\n const rightEdge = scale.x(mark.x) + rect.width + shift;\n const bottomEdge = scale.y(mark.y) + rect.height + shift;\n\n const h = rightEdge >= scale.x(domain.max) ? 'left' : 'right';\n const v = bottomEdge >= scale.y(range.min) ? 'top' : 'bottom';\n\n return `${v}-${h}`;\n};\n\nexport const coordinates = (graphProps, mark, rect = { width: 0, height: 0 }, position) => {\n const { scale } = graphProps;\n const shift = 5;\n\n switch (position) {\n case 'bottom-right':\n return { left: scale.x(mark.x) + shift, top: scale.y(mark.y) + shift };\n case 'bottom-left':\n return { left: scale.x(mark.x) - shift - rect.width, top: scale.y(mark.y) + shift };\n case 'top-left':\n return { left: scale.x(mark.x) - shift - rect.width, top: scale.y(mark.y) - shift - rect.height };\n case 'top-right':\n return { left: scale.x(mark.x) + shift, top: scale.y(mark.y) - shift - rect.height };\n default:\n return {};\n }\n};\n\nconst LabelInput = ({ _ref, externalInputRef, label, disabled, inputStyle, onChange, onBlur }) => (\n <AutosizeInput\n inputRef={(r) => {\n _ref(r);\n externalInputRef(r);\n }}\n disabled={disabled}\n inputStyle={inputStyle}\n value={label}\n onChange={onChange}\n onBlur={onBlur}\n />\n);\n\nLabelInput.propTypes = {\n _ref: PropTypes.func,\n externalInputRef: PropTypes.func,\n label: PropTypes.string,\n disabled: PropTypes.bool,\n inputStyle: PropTypes.object,\n onChange: PropTypes.func,\n onBlur: PropTypes.func,\n};\n\nexport const MarkLabel = (props) => {\n // the node is kept in a ref as well as in the state: the state is needed to reposition the label\n // once it has a size, the ref is what the focus is asserted on - it is never a render behind\n const inputNode = useRef(null);\n const [input, setInput] = useState(null);\n const _ref = useCallback((node) => {\n inputNode.current = node;\n setInput(node);\n }, []);\n const theme = useTheme();\n\n const { mark, graphProps, disabled, autoFocus, inputRef: externalInputRef } = props;\n\n const [label, setLabel] = useState(mark.label);\n const { correctness, correctnesslabel, correctlabel } = mark;\n\n const isFocused = () =>\n !!inputNode.current && typeof document !== 'undefined' && document.activeElement === inputNode.current;\n\n // A label that has just been added is empty and its input is enabled - label mode has to be on\n // for that to happen and it always starts off, so a mark that was loaded with an empty label\n // cannot grab the focus. This is read from the mark rather than taken from the autoFocus prop\n // because the tool that asked for the label can be remounted by the marks coming back from the\n // host (async model save) before the input ever renders, and its autoFocus goes with it.\n const isNewLabel = useRef(mark.label === '' && !disabled && !mark.disabled);\n\n const onChange = (e) => setLabel(e.target.value);\n\n const handleBlur = useCallback(() => {\n isNewLabel.current = false;\n\n if (label === '') {\n props.onChange('');\n }\n\n // lets the tool know that this label is not being edited anymore, so it stops auto focusing it\n if (props.onBlur) {\n props.onBlur();\n }\n }, [label, props.onChange, props.onBlur]);\n\n const debouncedLabel = useDebounce(label, DEBOUNCE_DELAY);\n\n // useState only sets the value once, to synch props to state need useEffect.\n // While this input has the focus we keep what the user typed: the mark can come back from an\n // (async) model save with an older label and that would overwrite the characters typed meanwhile.\n useEffect(() => {\n if (isFocused()) {\n return;\n }\n\n setLabel(mark.label);\n }, [mark.label]);\n\n // pick up the change to debouncedLabel and save it\n useEffect(() => {\n if (typeof debouncedLabel === 'string' && debouncedLabel !== mark.label) {\n props.onChange(debouncedLabel);\n }\n }, [debouncedLabel]);\n\n // A label that has just been added has to be focused so that it can be typed into right away.\n // This runs after every render on purpose - there is no reliable single moment to focus at:\n // the input shows up whenever the mark comes back from the (async) model save, and a remount\n // of the input (same save, replaced node) drops the focus without this component being told.\n // Asserting the focus on every render is what makes it stick; it is a no-op once the input has\n // it, so the caret the user placed is left alone.\n useEffect(() => {\n const node = inputNode.current;\n\n // node.disabled, not the disabled prop: a disabled input silently ignores focus()\n if ((!autoFocus && !isNewLabel.current) || !node || node.disabled || isFocused()) {\n return;\n }\n\n node.focus();\n\n // a new label is focused once, so it cannot fight over the focus with another one\n if (isFocused()) {\n isNewLabel.current = false;\n }\n\n // keep the caret at the end, otherwise typing continues in front of the existing label\n const caret = (node.value || '').length;\n\n if (node.setSelectionRange) {\n node.setSelectionRange(caret, caret);\n }\n });\n\n const rect = input ? input.getBoundingClientRect() : { width: 0, height: 0 };\n const pos = position(graphProps, mark, rect);\n const leftTop = coordinates(graphProps, mark, rect, pos);\n\n const style = {\n position: 'fixed',\n pointerEvents: 'auto',\n display: 'flex',\n alignItems: 'center',\n gap: '3px',\n ...leftTop,\n };\n\n const secondLabelStyle = {\n ...style,\n top: leftTop.top + 25,\n };\n\n const disabledInput = disabled || mark.disabled;\n\n const renderInput = (inputStyle, labelValue) => (\n <LabelInput\n _ref={_ref}\n externalInputRef={externalInputRef}\n label={labelValue}\n disabled={disabledInput}\n inputStyle={inputStyle}\n onChange={onChange}\n onBlur={handleBlur}\n />\n );\n\n const studentInputStyle = getStudentInputStyles();\n\n if (correctness === 'correct' && correctnesslabel === 'correct' && correctlabel) {\n return (\n <StyledInputCorrect style={style}>\n <SvgIcon type=\"correct\" />\n {renderInput(studentInputStyle, correctlabel)}\n </StyledInputCorrect>\n );\n }\n\n if (correctness === 'correct' && correctnesslabel === 'correct' && !correctlabel) {\n return null;\n }\n\n if (correctness === 'correct' && correctnesslabel === 'incorrect') {\n return (\n <>\n <StyledInputIncorrect style={style}>\n <SvgIcon type=\"incorrect\" />\n {label === '' ? (\n <SvgIcon type=\"empty\" style={{ marginLeft: '3px' }} />\n ) : (\n renderInput(studentInputStyle, label)\n )}\n </StyledInputIncorrect>\n <StyledInputMissing style={secondLabelStyle}>{renderInput(studentInputStyle, correctlabel)}</StyledInputMissing>\n </>\n );\n }\n\n if (correctness === 'missing') {\n return <StyledInputMissing style={style}>{renderInput(studentInputStyle, label)}</StyledInputMissing>;\n }\n\n if (correctness === 'incorrect') {\n return <StyledIncorrect style={style}>{renderInput(studentInputStyle, label)}</StyledIncorrect>;\n }\n\n return <div style={style}>{renderInput(getInputStyles(theme, disabled, mark.disabled), label)}</div>;\n};\n\nMarkLabel.propTypes = {\n autoFocus: PropTypes.bool,\n disabled: PropTypes.bool,\n onBlur: PropTypes.func,\n onChange: PropTypes.func,\n graphProps: types.GraphPropsType,\n inputRef: PropTypes.func,\n mark: PropTypes.object,\n};\n\nexport default MarkLabel;\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "4.0.15-next.15+668601546",
6
+ "version": "4.0.15-next.21+0d03f1bc7",
7
7
  "description": "Graphing components",
8
8
  "keywords": [
9
9
  "react",
@@ -54,6 +54,6 @@
54
54
  "peerDependencies": {
55
55
  "react": "^18.2.0"
56
56
  },
57
- "gitHead": "668601546296f52a69187c0c4b2b693d8225e0c5",
57
+ "gitHead": "0d03f1bc79fbefc2ffb174ad84521d19b542cb26",
58
58
  "scripts": {}
59
59
  }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render } from '@pie-lib/test-utils';
2
+ import { act, fireEvent, render } from '@pie-lib/test-utils';
3
3
  import { graphProps } from './utils';
4
4
  import Graph, { removeBuildingToolIfCurrentToolDiffers } from '../graph';
5
5
  import { toolsArr } from '../tools';
@@ -719,3 +719,59 @@ describe('Graph', () => {
719
719
  });
720
720
  });
721
721
  });
722
+
723
+ describe('label focus', () => {
724
+ // the marks are owned by the host and come back from a model save asynchronously; a mark that
725
+ // shows up in front of the labelled one changes its key, so the tool that handled the click is
726
+ // remounted and the editingLabel it set is gone before the label input ever renders
727
+ const Host = ({ shiftMarks }) => {
728
+ const [marks, setMarks] = React.useState([{ type: 'point', x: 2, y: 2 }]);
729
+
730
+ return (
731
+ <Graph
732
+ tools={toolsArr}
733
+ marks={marks}
734
+ labelModeEnabled={true}
735
+ onChangeMarks={(next) =>
736
+ setTimeout(() => setMarks(shiftMarks ? [{ type: 'point', x: 5, y: 5 }, ...next] : next), 30)
737
+ }
738
+ domain={{ min: 0, max: 10, step: 1 }}
739
+ range={{ min: 0, max: 10, step: 1 }}
740
+ size={{ width: 400, height: 400 }}
741
+ {...graphProps(0, 10, 0, 10)}
742
+ />
743
+ );
744
+ };
745
+
746
+ const clickPoint = (container) => {
747
+ const circle = container.querySelector('circle');
748
+
749
+ fireEvent.mouseDown(circle, { button: 0 });
750
+ fireEvent.mouseUp(circle, { button: 0 });
751
+ fireEvent.click(circle, { button: 0 });
752
+ act(() => jest.advanceTimersByTime(200));
753
+ };
754
+
755
+ beforeEach(() => jest.useFakeTimers());
756
+ afterEach(() => jest.useRealTimers());
757
+
758
+ it('focuses the label that was just added', () => {
759
+ const { container } = render(<Host />);
760
+
761
+ clickPoint(container);
762
+
763
+ const input = container.querySelector('foreignObject input');
764
+ expect(input).not.toBe(null);
765
+ expect(document.activeElement).toBe(input);
766
+ });
767
+
768
+ it('focuses the label even if the tool was remounted before it rendered', () => {
769
+ const { container } = render(<Host shiftMarks={true} />);
770
+
771
+ clickPoint(container);
772
+
773
+ const input = container.querySelector('foreignObject input');
774
+ expect(input).not.toBe(null);
775
+ expect(document.activeElement).toBe(input);
776
+ });
777
+ });
@@ -75,8 +75,8 @@ describe('MarkLabel - editing', () => {
75
75
  expect(input.selectionEnd).toBe(2);
76
76
  });
77
77
 
78
- it('does not focus the input when autoFocus is not set', () => {
79
- const { input } = renderComponent();
78
+ it('does not focus the input of an existing label when autoFocus is not set', () => {
79
+ const { input } = renderComponent({ mark: { x: 1, y: 1, label: 'A' } });
80
80
 
81
81
  expect(document.activeElement).not.toBe(input);
82
82
  });
@@ -87,6 +87,38 @@ describe('MarkLabel - editing', () => {
87
87
  expect(document.activeElement).not.toBe(input);
88
88
  });
89
89
 
90
+ it('does not focus the input of a disabled mark', () => {
91
+ const { input } = renderComponent({ autoFocus: true, mark: { x: 1, y: 1, label: '', disabled: true } });
92
+
93
+ expect(document.activeElement).not.toBe(input);
94
+ });
95
+
96
+ // the focus can go away without this component hearing about it - the input node gets replaced
97
+ // by a render higher up, so no blur is fired and autoFocus is still on
98
+ it('takes the focus back on the next render while it is still being edited', () => {
99
+ const { input, rerenderWith } = renderComponent({ autoFocus: true, mark: { x: 1, y: 1, label: 'A' } });
100
+
101
+ expect(document.activeElement).toBe(input);
102
+
103
+ input.blur();
104
+ expect(document.activeElement).not.toBe(input);
105
+
106
+ rerenderWith({ mark: { x: 1, y: 1, label: 'A' } });
107
+
108
+ expect(document.activeElement).toBe(input);
109
+ });
110
+
111
+ it('leaves the caret where the user put it while the input has the focus', () => {
112
+ const { input, rerenderWith } = renderComponent({ autoFocus: true, mark: { x: 1, y: 1, label: 'ABC' } });
113
+
114
+ input.setSelectionRange(1, 1);
115
+
116
+ rerenderWith({ mark: { x: 1, y: 1, label: 'ABC' } });
117
+
118
+ expect(document.activeElement).toBe(input);
119
+ expect(input.selectionStart).toBe(1);
120
+ });
121
+
90
122
  it('focuses the input once it is no longer disabled', () => {
91
123
  const { rerenderWith, container } = renderComponent({ autoFocus: true, disabled: true });
92
124
 
@@ -118,6 +150,68 @@ describe('MarkLabel - editing', () => {
118
150
  });
119
151
  });
120
152
 
153
+ // the tool asks for the focus with autoFocus, but its state can be gone before the input renders:
154
+ // the marks come back from the host asynchronously and can remount the tool. An empty label in
155
+ // label mode is a label the user has just added, so the input can tell on its own.
156
+ describe('a label that was just added', () => {
157
+ it('focuses itself without the tool asking', () => {
158
+ const { input } = renderComponent({ mark: { x: 1, y: 1, label: '' } });
159
+
160
+ expect(document.activeElement).toBe(input);
161
+ });
162
+
163
+ it('focuses itself again when it is remounted before it was typed into', () => {
164
+ const props = {
165
+ onChange,
166
+ onBlur,
167
+ inputRef: jest.fn(),
168
+ mark: { x: 1, y: 1, label: '' },
169
+ graphProps: getGraphProps(0, 10, 0, 10),
170
+ };
171
+ const { container, rerender } = render(<MarkLabel key="first" {...props} />);
172
+ const first = input(container);
173
+
174
+ rerender(<MarkLabel key="second" {...props} />);
175
+ const second = input(container);
176
+
177
+ expect(second).not.toBe(first);
178
+ expect(document.activeElement).toBe(second);
179
+ });
180
+
181
+ it('stops chasing the focus once it has been left', () => {
182
+ const { input, rerenderWith } = renderComponent({ mark: { x: 1, y: 1, label: '' } });
183
+
184
+ expect(document.activeElement).toBe(input);
185
+
186
+ input.blur();
187
+ rerenderWith({ mark: { x: 1, y: 1, label: '' } });
188
+
189
+ expect(document.activeElement).not.toBe(input);
190
+ });
191
+
192
+ // outside of label mode the input is disabled, which is how an empty label that was loaded with
193
+ // the item is told apart from one the user has just added - label mode always starts off
194
+ it('is not focused while label mode is off', () => {
195
+ const { input } = renderComponent({ disabled: true, mark: { x: 1, y: 1, label: '' } });
196
+
197
+ expect(document.activeElement).not.toBe(input);
198
+ });
199
+
200
+ it('is not focused when label mode is turned on afterwards', () => {
201
+ const { rerenderWith, container } = renderComponent({ disabled: true, mark: { x: 1, y: 1, label: '' } });
202
+
203
+ rerenderWith({ disabled: false });
204
+
205
+ expect(document.activeElement).not.toBe(input(container));
206
+ });
207
+
208
+ it('is not focused when the mark is disabled', () => {
209
+ const { input } = renderComponent({ mark: { x: 1, y: 1, label: '', disabled: true } });
210
+
211
+ expect(document.activeElement).not.toBe(input);
212
+ });
213
+ });
214
+
121
215
  describe('syncing mark.label into the input', () => {
122
216
  it('picks up a new mark.label while the input does not have the focus', () => {
123
217
  const { rerenderWith, container } = renderComponent({ mark: { x: 1, y: 1, label: 'A' } });
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useState } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { styled, useTheme } from '@mui/material/styles';
4
4
  import AutosizeInput from 'react-input-autosize';
@@ -130,8 +130,14 @@ LabelInput.propTypes = {
130
130
  };
131
131
 
132
132
  export const MarkLabel = (props) => {
133
+ // the node is kept in a ref as well as in the state: the state is needed to reposition the label
134
+ // once it has a size, the ref is what the focus is asserted on - it is never a render behind
135
+ const inputNode = useRef(null);
133
136
  const [input, setInput] = useState(null);
134
- const _ref = useCallback((node) => setInput(node));
137
+ const _ref = useCallback((node) => {
138
+ inputNode.current = node;
139
+ setInput(node);
140
+ }, []);
135
141
  const theme = useTheme();
136
142
 
137
143
  const { mark, graphProps, disabled, autoFocus, inputRef: externalInputRef } = props;
@@ -139,11 +145,21 @@ export const MarkLabel = (props) => {
139
145
  const [label, setLabel] = useState(mark.label);
140
146
  const { correctness, correctnesslabel, correctlabel } = mark;
141
147
 
142
- const isFocused = () => !!input && typeof document !== 'undefined' && document.activeElement === input;
148
+ const isFocused = () =>
149
+ !!inputNode.current && typeof document !== 'undefined' && document.activeElement === inputNode.current;
150
+
151
+ // A label that has just been added is empty and its input is enabled - label mode has to be on
152
+ // for that to happen and it always starts off, so a mark that was loaded with an empty label
153
+ // cannot grab the focus. This is read from the mark rather than taken from the autoFocus prop
154
+ // because the tool that asked for the label can be remounted by the marks coming back from the
155
+ // host (async model save) before the input ever renders, and its autoFocus goes with it.
156
+ const isNewLabel = useRef(mark.label === '' && !disabled && !mark.disabled);
143
157
 
144
158
  const onChange = (e) => setLabel(e.target.value);
145
159
 
146
160
  const handleBlur = useCallback(() => {
161
+ isNewLabel.current = false;
162
+
147
163
  if (label === '') {
148
164
  props.onChange('');
149
165
  }
@@ -175,22 +191,33 @@ export const MarkLabel = (props) => {
175
191
  }, [debouncedLabel]);
176
192
 
177
193
  // A label that has just been added has to be focused so that it can be typed into right away.
178
- // This also re-focuses the input if it gets remounted while it is being edited - saving the model
179
- // is done by the host (api call) and the model that comes back can replace the input node.
194
+ // This runs after every render on purpose - there is no reliable single moment to focus at:
195
+ // the input shows up whenever the mark comes back from the (async) model save, and a remount
196
+ // of the input (same save, replaced node) drops the focus without this component being told.
197
+ // Asserting the focus on every render is what makes it stick; it is a no-op once the input has
198
+ // it, so the caret the user placed is left alone.
180
199
  useEffect(() => {
181
- if (!autoFocus || !input || disabled || isFocused()) {
200
+ const node = inputNode.current;
201
+
202
+ // node.disabled, not the disabled prop: a disabled input silently ignores focus()
203
+ if ((!autoFocus && !isNewLabel.current) || !node || node.disabled || isFocused()) {
182
204
  return;
183
205
  }
184
206
 
185
- input.focus();
207
+ node.focus();
208
+
209
+ // a new label is focused once, so it cannot fight over the focus with another one
210
+ if (isFocused()) {
211
+ isNewLabel.current = false;
212
+ }
186
213
 
187
214
  // keep the caret at the end, otherwise typing continues in front of the existing label
188
- const caret = (input.value || '').length;
215
+ const caret = (node.value || '').length;
189
216
 
190
- if (input.setSelectionRange) {
191
- input.setSelectionRange(caret, caret);
217
+ if (node.setSelectionRange) {
218
+ node.setSelectionRange(caret, caret);
192
219
  }
193
- }, [autoFocus, input, disabled]);
220
+ });
194
221
 
195
222
  const rect = input ? input.getBoundingClientRect() : { width: 0, height: 0 };
196
223
  const pos = position(graphProps, mark, rect);