Skip to content

Instantly share code, notes, and snippets.

@dw2
Created November 7, 2018 23:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dw2/c4c0485e76623af35004e87ae8f21d2f to your computer and use it in GitHub Desktop.
Save dw2/c4c0485e76623af35004e87ae8f21d2f to your computer and use it in GitHub Desktop.
Example of React Components for Masking Offensive Input
import React from 'react';
import styled from 'styled-components';
const Wrapper = styled.div`
overflow: hidden;
position: relative;
`;
const Textarea = styled.textarea`
background: transparent;
border: 1px solid gray;
color: transparent;
font-family: sans-serif;
font-size: 18px;
font-weight: 400;
line-height: 1.5;
overflow-y: scroll;
padding: 8px;
pointer-events: auto;
position: relative;
vertical-align: top;
z-index: 2;
@supports (-webkit-overflow-scrolling: touch) {
margin-left: -3px;
}
&::placeholder {
@supports (-webkit-overflow-scrolling: touch) {
text-indent: -3px;
}
}
`;
const Mask = styled.div`
color: black;
display: inline-block;
font-family: sans-serif;
font-size: 18px;
font-weight: 400;
line-height: 1.5;
margin: 0;
max-width: 100%;
overflow-x: hidden;
overflow-y: scroll;
padding: 8px 8px 2px;
position: absolute;
text-align: left;
vertical-align: top;
width: calc(100% - 2px);
word-wrap: break-word;
z-index: 1;
span,
mark {
background: transparent;
white-space: pre-wrap;
}
span { color: black; }
mark { color: red; }
@supports (-webkit-overflow-scrolling: touch) {
width: calc(100% - 6px);
}
`;
export class MaskedTextarea extends React.Component<Props> {
state = { userInput: '' };
badWords = ['asshat', 'dookie'];
inputRef = React.createRef();
onChange = event => {
console.log('event >>', event);
if (!event.target || typeof event.target.value === 'undefined') return;
this.setState({ userInput: event.target.value });
};
onScroll = event => {
const el = this.inputRef?.current;
if (!el) return;
const scrollTop = event.target?.scrollTop || 0;
el.scrollTop = scrollTop;
};
render() {
const { userInput } = this.state;
const words = userInput.split(/([^\w-]+)/gi);
const maskedValues = [];
let key = 0;
words.forEach(word => {
word.split(/([\r\n]+)/gi).forEach(wordOrNewline => {
key += 1;
if (!wordOrNewline.length) return;
if (wordOrNewline.match(/[\r\n]/)) {
maskedValues.push(
...Array(wordOrNewline.length).fill(<br />),
);
return;
}
maskedValues.push(
this.badWords.includes(wordOrNewline.toLowerCase())
? <mark key={`word-${key}`}>{wordOrNewline}</mark>
: <span key={`word-${key}`}>{wordOrNewline}</span>
);
});
});
return (
<Wrapper>
<Mask>{maskedValues}</Mask>
<Textarea ref={this.inputRef} onChange={this.onChange} />
</Wrapper>
);
}
}
export default MaskedTextarea;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment