Introduce React-Trello in place of React-Kanban
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
12
client/src/components/trello-board/widgets/DeleteButton.jsx
Normal file
12
client/src/components/trello-board/widgets/DeleteButton.jsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from "react";
|
||||
import { DelButton, DeleteWrapper } from "../styles/Elements";
|
||||
|
||||
const DeleteButton = (props) => {
|
||||
return (
|
||||
<DeleteWrapper {...props}>
|
||||
<DelButton>✖</DelButton>
|
||||
</DeleteWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteButton;
|
||||
87
client/src/components/trello-board/widgets/EditableLabel.jsx
Normal file
87
client/src/components/trello-board/widgets/EditableLabel.jsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
class EditableLabel extends React.Component {
|
||||
constructor({value}) {
|
||||
super()
|
||||
this.state = {value: value}
|
||||
}
|
||||
|
||||
getText = el => {
|
||||
return el.innerText
|
||||
}
|
||||
|
||||
onTextChange = ev => {
|
||||
const value = this.getText(ev.target)
|
||||
this.setState({value: value})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.autoFocus) {
|
||||
this.refDiv.focus()
|
||||
}
|
||||
}
|
||||
|
||||
onBlur = () => {
|
||||
this.props.onChange(this.state.value)
|
||||
}
|
||||
|
||||
onPaste = ev => {
|
||||
ev.preventDefault()
|
||||
const value = ev.clipboardData.getData('text')
|
||||
document.execCommand('insertText', false, value)
|
||||
}
|
||||
|
||||
getClassName = () => {
|
||||
const placeholder = this.state.value === '' ? 'comPlainTextContentEditable--has-placeholder' : ''
|
||||
return `comPlainTextContentEditable ${placeholder}`
|
||||
}
|
||||
|
||||
onKeyDown = e => {
|
||||
if (e.keyCode === 13) {
|
||||
this.props.onChange(this.state.value)
|
||||
this.refDiv.blur()
|
||||
e.preventDefault()
|
||||
}
|
||||
if (e.keyCode === 27) {
|
||||
this.refDiv.value = this.props.value
|
||||
this.setState({value: this.props.value})
|
||||
// this.refDiv.blur()
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const placeholder = this.props.value.length > 0 ? false : this.props.placeholder
|
||||
return (
|
||||
<div
|
||||
ref={ref => (this.refDiv = ref)}
|
||||
contentEditable="true"
|
||||
className={this.getClassName()}
|
||||
onPaste={this.onPaste}
|
||||
onBlur={this.onBlur}
|
||||
onInput={this.onTextChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
EditableLabel.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
autoFocus: PropTypes.bool,
|
||||
inline: PropTypes.bool,
|
||||
value: PropTypes.string
|
||||
}
|
||||
|
||||
EditableLabel.defaultProps = {
|
||||
onChange: () => {},
|
||||
placeholder: '',
|
||||
autoFocus: false,
|
||||
inline: false,
|
||||
value: ''
|
||||
}
|
||||
export default EditableLabel
|
||||
106
client/src/components/trello-board/widgets/InlineInput.jsx
Normal file
106
client/src/components/trello-board/widgets/InlineInput.jsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { InlineInput } from "../styles/Base";
|
||||
import autosize from "autosize";
|
||||
|
||||
const InlineInputController = ({ onSave, border, placeholder, value, autoFocus, resize, onCancel }) => {
|
||||
const inputRef = useRef(null);
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
|
||||
// Effect for autosizing and initial autoFocus
|
||||
useEffect(() => {
|
||||
if (inputRef.current && resize !== "none") {
|
||||
autosize(inputRef.current);
|
||||
}
|
||||
if (inputRef.current && autoFocus) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [resize, autoFocus]);
|
||||
|
||||
// Effect to update value when props change
|
||||
useEffect(() => {
|
||||
setInputValue(value);
|
||||
}, [value]);
|
||||
|
||||
const handleFocus = (e) => e.target.select();
|
||||
|
||||
const handleMouseDown = (e) => {
|
||||
if (document.activeElement !== e.target) {
|
||||
e.preventDefault();
|
||||
inputRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
updateValue();
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
// Enter
|
||||
inputRef.current.blur();
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode === 27) {
|
||||
// Escape
|
||||
setInputValue(value); // Reset to initial value
|
||||
inputRef.current.blur();
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode === 9) {
|
||||
// Tab
|
||||
if (inputValue.length === 0) {
|
||||
onCancel();
|
||||
}
|
||||
inputRef.current.blur();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const updateValue = () => {
|
||||
if (inputValue !== value) {
|
||||
onSave(inputValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<InlineInput
|
||||
ref={inputRef}
|
||||
border={border}
|
||||
onMouseDown={handleMouseDown}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={inputValue.length === 0 ? undefined : placeholder}
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
dataGramm="false"
|
||||
rows={1}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
InlineInputController.propTypes = {
|
||||
onSave: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
border: PropTypes.bool,
|
||||
placeholder: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
autoFocus: PropTypes.bool,
|
||||
resize: PropTypes.oneOf(["none", "vertical", "horizontal"])
|
||||
};
|
||||
|
||||
InlineInputController.defaultProps = {
|
||||
onSave: () => {},
|
||||
onCancel: () => {},
|
||||
placeholder: "",
|
||||
value: "",
|
||||
border: false,
|
||||
autoFocus: false,
|
||||
resize: "none"
|
||||
};
|
||||
|
||||
export default InlineInputController;
|
||||
@@ -0,0 +1,94 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { InlineInput } from "../styles/Base";
|
||||
import autosize from "autosize";
|
||||
|
||||
class NewLaneTitleEditor extends React.Component {
|
||||
onKeyDown = (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
this.refInput.blur();
|
||||
this.props.onSave();
|
||||
e.preventDefault();
|
||||
}
|
||||
if (e.keyCode === 27) {
|
||||
this.cancel();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (e.keyCode === 9) {
|
||||
if (this.getValue().length === 0) {
|
||||
this.cancel();
|
||||
} else {
|
||||
this.props.onSave();
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
cancel = () => {
|
||||
this.setValue("");
|
||||
this.props.onCancel();
|
||||
this.refInput.blur();
|
||||
};
|
||||
|
||||
getValue = () => this.refInput.value;
|
||||
setValue = (value) => (this.refInput.value = value);
|
||||
|
||||
saveValue = () => {
|
||||
if (this.getValue() !== this.props.value) {
|
||||
this.props.onSave(this.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
focus = () => this.refInput.focus();
|
||||
|
||||
setRef = (ref) => {
|
||||
this.refInput = ref;
|
||||
if (this.props.resize !== "none") {
|
||||
autosize(this.refInput);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { autoFocus, resize, border, autoResize, value, placeholder } = this.props;
|
||||
|
||||
return (
|
||||
<InlineInput
|
||||
style={{ resize: resize }}
|
||||
ref={this.setRef}
|
||||
border={border}
|
||||
onKeyDown={this.onKeyDown}
|
||||
placeholder={value.length === 0 ? undefined : placeholder}
|
||||
defaultValue={value}
|
||||
rows={3}
|
||||
autoResize={autoResize}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NewLaneTitleEditor.propTypes = {
|
||||
onSave: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
border: PropTypes.bool,
|
||||
placeholder: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
autoFocus: PropTypes.bool,
|
||||
autoResize: PropTypes.bool,
|
||||
resize: PropTypes.oneOf(["none", "vertical", "horizontal"])
|
||||
};
|
||||
|
||||
NewLaneTitleEditor.defaultProps = {
|
||||
inputRef: () => {},
|
||||
onSave: () => {},
|
||||
onCancel: () => {},
|
||||
placeholder: "",
|
||||
value: "",
|
||||
border: false,
|
||||
autoFocus: false,
|
||||
autoResize: false,
|
||||
resize: "none"
|
||||
};
|
||||
|
||||
export default NewLaneTitleEditor;
|
||||
9
client/src/components/trello-board/widgets/index.jsx
Normal file
9
client/src/components/trello-board/widgets/index.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import DeleteButton from "./DeleteButton";
|
||||
import EditableLabel from "./EditableLabel";
|
||||
import InlineInput from "./InlineInput";
|
||||
|
||||
export default {
|
||||
DeleteButton,
|
||||
EditableLabel,
|
||||
InlineInput
|
||||
};
|
||||
Reference in New Issue
Block a user