From 0ff41e1d1daaac21a0e8a91dafc7328e51f25ce9 Mon Sep 17 00:00:00 2001 From: zombiej Date: Sun, 5 May 2019 17:58:46 +0800 Subject: [PATCH 01/32] add mention logic --- assets/index.less | 40 +++++++++++++++++++ examples/basic.js | 13 ++++++- package.json | 5 ++- src/Mentions.tsx | 96 ++++++++++++++++++++++++++++++++++++++++++++-- src/Option.tsx | 7 ++++ src/util.ts | 0 storybook/index.js | 9 ++++- typings/index.d.ts | 2 + 8 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 src/Option.tsx create mode 100644 src/util.ts diff --git a/assets/index.less b/assets/index.less index e020cb6..adae98e 100644 --- a/assets/index.less +++ b/assets/index.less @@ -1 +1,41 @@ @mentionsPrefixCls: rc-mentions; + +.@{mentionsPrefixCls} { + font-size: 100px; + display: inline-block; + position: relative; + + // reset + > textarea, &-measure { + font-size: inherit; + padding: 0; + margin: 0; + line-height: inherit; + vertical-align: top; + font-weight: inherit; + font-family: inherit; + overflow: inherit; + } + + > textarea { + border: none; + } + + &-measure { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + pointer-events: none; + color: red; + // z-index: -1; + } +} + +// Border style +.@{mentionsPrefixCls} { + border: 1px solid #999; + border-radius: 3px; + overflow: hidden; +} \ No newline at end of file diff --git a/examples/basic.js b/examples/basic.js index dbc1986..1f80d36 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -1,12 +1,21 @@ /* eslint no-console: 0 */ import React from 'react'; -// import Mentions from '../src'; +import Mentions from '../src'; import '../assets/index.less'; +const { Option } = Mentions; + class Demo extends React.Component { + state = {}; + render() { - return null; + return ( + + + + + ); } } diff --git a/package.json b/package.json index 7104842..7715cc4 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,10 @@ "typescript": "^3.2.2" }, "dependencies": { - "babel-runtime": "^6.23.0" + "babel-runtime": "^6.23.0", + "classnames": "^2.2.6", + "omit.js": "^1.0.2", + "react-lifecycles-compat": "^3.0.4" }, "pre-commit": [ "lint-staged" diff --git a/src/Mentions.tsx b/src/Mentions.tsx index 20af4de..373d7d7 100644 --- a/src/Mentions.tsx +++ b/src/Mentions.tsx @@ -1,7 +1,97 @@ +import classNames from 'classnames'; +import omit from 'omit.js'; import * as React from 'react'; +import { polyfill } from 'react-lifecycles-compat'; +import Option from './Option'; -export default class Mentions extends React.Component { - render() { - return null; +interface MentionsProps { + value?: string; + onChange?: (text: string) => void; + prefixCls?: string; + prefix?: string; + className?: string; + style?: React.CSSProperties; + autoFocus?: boolean; +} +interface MentionsState { + value: string; + measuring: boolean; + measureText: string; +} +class Mentions extends React.Component { + public static Option = Option; + + public static defaultProps = { + prefixCls: 'rc-mentions', + prefix: '@', + }; + + public static getDerivedStateFromProps(props: MentionsProps, prevState: MentionsState) { + const newState: Partial = {}; + + if ('value' in props && props.value !== prevState.value) { + newState.value = props.value; + } + + return newState; + } + + public state = { + value: '', + measuring: false, + measureText: '', + }; + + public textarea?: HTMLTextAreaElement; + + public onChange: React.ChangeEventHandler = ({ target: { value } }) => { + const { onChange } = this.props; + this.setState({ value }); + if (onChange) { + onChange(value); + } + }; + + // Check if hit the measure keyword + public onKeyDown: React.KeyboardEventHandler = ({ key }) => { + const { value } = this.state; + const { prefix } = this.props; + if (prefix === key) { + const startLoc = this.textarea!.selectionStart; + this.setState({ + measureText: `${value.slice(0, startLoc)}`, + }); + } + }; + + public setTextAreaRef = (element: HTMLTextAreaElement) => { + this.textarea = element; + }; + + public render() { + const { value, measureText } = this.state; + const { prefix, prefixCls, className, style, ...restProps } = this.props; + + const props = omit(restProps, ['onChange']); + + return ( +
+