├── .babelrc ├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── __tests__ ├── __snapshots__ │ └── email.spec.js.snap └── email.spec.js ├── index.html ├── index.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── rollup.config.js ├── setupTests.js ├── src ├── constants.js └── index.js ├── typings.json └── typings ├── globals ├── core-js │ ├── index.d.ts │ └── typings.json ├── node │ ├── index.d.ts │ └── typings.json └── react │ ├── index.d.ts │ └── typings.json └── index.d.ts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/react"] 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [master] 10 | 11 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 12 | jobs: 13 | npm-publish: 14 | name: npm-publish 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v2 19 | - name: Set up Node.js 20 | uses: actions/setup-node@master 21 | with: 22 | node-version: 10.0.0 23 | - run: npm install 24 | - name: Publish if version has been updated 25 | uses: pascalgn/npm-publish-action@3d228dd9d6c7851b8d24b532dc8d15d74f615043 26 | with: # All of theses inputs are optional 27 | tag_name: "v%s" 28 | tag_message: "v%s" 29 | commit_pattern: "^Release (\\S+)" 30 | workspace: "." 31 | env: # More info about the environment variables in the README 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Leave this as is, it's automatically generated 33 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} # You need to set this in your repo settings 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.iml 4 | .*.haste_cache.* 5 | .DS_Store 6 | .idea 7 | npm-debug.log 8 | node_modules 9 | coverage 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.iml 4 | .*.haste_cache.* 5 | .DS_Store 6 | .idea 7 | .babelrc 8 | .eslintrc 9 | npm-debug.log 10 | lib 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2016 Masoud Ghorbani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Email Autocomplete 2 | 3 | ![GitHub stars](https://img.shields.io/github/stars/msudgh/react-email-autocomplete.svg?style=social) ![NPM Version](https://img.shields.io/npm/v/react-email-autocomplete.svg?style=popout) ![License | MIT](https://img.shields.io/npm/l/react-email-autocomplete.svg?style=popout") ![Travis CI Master branch](https://img.shields.io/travis/msudgh/react-email-autocomplete/master.svg?style=popout) 4 | 5 | An autocomplete React component for email fields inspired by [**`Auto-Email`**](https://github.com/chrisyuska/auto-email) JQuery plugin. 6 | 7 | ## Demo 8 | 9 | [Here](https://msudgh.github.io/react-email-autocomplete) you can see component demo and functionality. 10 | 11 | ## Usage 12 | 13 | To use this component, you should install it by npm: 14 | 15 | ```bash 16 | npm install react-email-autocomplete --save-dev 17 | ``` 18 | 19 | And then use the component like bellow example(Bootstrap control): 20 | 21 | ```javascript 22 | import React, { Component } from 'react'; 23 | import Email from 'react-email-autocomplete'; 24 | 25 | class App extends Component { 26 | render() { 27 | return ( 28 |
29 | 30 | 31 |
32 | ) 33 | } 34 | } 35 | 36 | export default App; 37 | ``` 38 | 39 | Also you can pass a list as your custom domains: 40 | 41 | ```javascript 42 | class App extends Component { 43 | render() { 44 | const customDomains = ['yourdomain.com', 'yourdomain2.com', 'gmail.com', 'yahoo.com'] 45 | return ( 46 |
47 | 48 | 49 |
50 | ) 51 | } 52 | } 53 | ``` 54 | 55 | If you want to use it with [Formik](https://jaredpalmer.com/formik/docs/api/formik) you just need to add the *onChange* prop 56 | 57 | ```javascript 58 | class App extends Component { 59 | render() { 60 | return ( 61 |
62 | 63 | 64 | {(props) => { 65 | const { 66 | handleSubmit, 67 | handleBlur, 68 | } = props; 69 | return ( 70 |
71 | 77 | 78 | ); 79 | }} 80 |
81 |
82 | ) 83 | } 84 | ``` 85 | 86 | # License 87 | 88 | This software is released under the [**`MIT License`**](https://msudgh.mit-license.org/). 89 | -------------------------------------------------------------------------------- /__tests__/__snapshots__/email.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Email Component should render correctly without props 1`] = ` 4 |
7 | 38 |
39 | `; 40 | -------------------------------------------------------------------------------- /__tests__/email.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow, mount } from 'enzyme' 3 | 4 | import Email from '../src/index' 5 | 6 | const customDomains = ['jest.com', 'enzyme.com'] 7 | 8 | describe('Email Component', () => { 9 | it('should render correctly without props', () => { 10 | const component = shallow() 11 | expect(component).toMatchSnapshot() 12 | }) 13 | it('should suggest test@gmail.com', () => { 14 | const component = mount() 15 | 16 | // enter an email address 17 | component 18 | .find('input') 19 | .simulate('change', { target: { value: 'test@gm' } }) 20 | 21 | expect(component.instance().state.value).toEqual('test@gmail.com') 22 | }) 23 | it('should suggest test@jest.com', () => { 24 | const component = mount() 25 | 26 | // enter an email address 27 | component 28 | .find('input') 29 | .simulate('change', { target: { value: 'test@je' } }) 30 | 31 | expect(component.instance().state.value).toEqual('test@jest.com') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | React Email Autocomplete 13 | 97 | 98 | 99 |
100 | 101 | 102 | 103 | 104 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define(["react"],t):(e=e||self).Email=t(e.React)}(this,function(e){"use strict";var r="default"in e?e.default:e;function i(e,t){for(var n=0;n 29 | this.selectText() 30 | ); 31 | } else { 32 | this.setState( 33 | { value: `${emailAddress}${suggest}`, suggestion: suggest }, 34 | () => this.selectText() 35 | ); 36 | } 37 | 38 | if (onChange) { 39 | onChange(event); 40 | } 41 | } 42 | selectText() { 43 | const { suggestion, value } = this.state; 44 | 45 | if (typeof suggestion !== 'undefined' && suggestion.length > 0) { 46 | let startPos = value.lastIndexOf(suggestion); 47 | let endPos = startPos + suggestion.length; 48 | this.textHandler.setSelectionRange(startPos, endPos); 49 | } 50 | } 51 | getSuggest(event) { 52 | if (protectedKeyCodes.indexOf(event.keyCode) >= 0) return; 53 | const { suggestion } = this.state; 54 | const { value } = event.target; 55 | 56 | if (event.keyCode === 8) { 57 | this.setState({ 58 | value: this.replaceLast(value, suggestion, ''), 59 | }); 60 | } 61 | } 62 | suggest(string) { 63 | let strArr = string.split('@'); 64 | if (strArr.length - 1 !== 0) { 65 | string = strArr.pop(); 66 | } else return; 67 | 68 | const { domains } = this.props; 69 | 70 | let match = 71 | domains 72 | .filter((domain) => { 73 | return domain.indexOf(string) === 0; 74 | }) 75 | .shift() || ''; 76 | 77 | return match.replace(string, ''); 78 | } 79 | 80 | componentDidMount() { 81 | const { domains, value } = this.props; 82 | if (typeof domains === 'string') { 83 | console.error('domains props should be array not string!'); 84 | this.setState({ 85 | valid: false, 86 | }); 87 | } 88 | 89 | this.setState({ 90 | value, 91 | }); 92 | } 93 | render() { 94 | const props = this.props; 95 | const { value, valid } = this.state; 96 | 97 | return valid ? ( 98 |
99 | { 108 | this.textHandler = input; 109 | }} 110 | /> 111 |
112 | ) : ( 113 | 'Unable to render component! Please, Check out developer tools of your browser.' 114 | ); 115 | } 116 | } 117 | 118 | Email.defaultProps = { 119 | domains: emailServicesDomains, 120 | }; 121 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDependencies": { 3 | "node": "registry:dt/node#6.0.0+20161121110008", 4 | "react": "registry:dt/react#0.14.0+20161124131900" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /typings/globals/core-js/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/be0ba281b67575b3b626a6bbb15b152add97244e/core-js/core-js.d.ts", 5 | "raw": "registry:dt/core-js#0.0.0+20160914114559", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/be0ba281b67575b3b626a6bbb15b152add97244e/core-js/core-js.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/globals/node/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/fb7fbd28b477f5e239467e69397ed020d92817e7/node/node.d.ts", 5 | "raw": "registry:dt/node#6.0.0+20161121110008", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/fb7fbd28b477f5e239467e69397ed020d92817e7/node/node.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/globals/react/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/05038b3f910f48be272db420d09c84e26dfe6f88/react/react.d.ts 3 | declare namespace __React { 4 | 5 | // 6 | // React Elements 7 | // ---------------------------------------------------------------------- 8 | 9 | type ReactType = string | ComponentClass | StatelessComponent; 10 | 11 | type Key = string | number; 12 | type Ref = string | ((instance: T) => any); 13 | type ComponentState = {} | void; 14 | 15 | interface Attributes { 16 | key?: Key; 17 | } 18 | interface ClassAttributes extends Attributes { 19 | ref?: Ref; 20 | } 21 | 22 | interface ReactElement

{ 23 | type: string | ComponentClass

| SFC

; 24 | props: P; 25 | key?: Key; 26 | } 27 | 28 | interface SFCElement

extends ReactElement

{ 29 | type: SFC

; 30 | } 31 | 32 | type CElement> = ComponentElement; 33 | interface ComponentElement> extends ReactElement

{ 34 | type: ComponentClass

; 35 | ref?: Ref; 36 | } 37 | 38 | type ClassicElement

= CElement>; 39 | 40 | interface DOMElement

extends ReactElement

{ 41 | type: string; 42 | ref: Ref; 43 | } 44 | 45 | interface ReactHTMLElement extends DOMElement { 46 | } 47 | 48 | interface ReactSVGElement extends DOMElement { 49 | } 50 | 51 | // 52 | // Factories 53 | // ---------------------------------------------------------------------- 54 | 55 | interface Factory

{ 56 | (props?: P & Attributes, ...children: ReactNode[]): ReactElement

; 57 | } 58 | 59 | interface SFCFactory

{ 60 | (props?: P & Attributes, ...children: ReactNode[]): SFCElement

; 61 | } 62 | 63 | interface ComponentFactory> { 64 | (props?: P & ClassAttributes, ...children: ReactNode[]): CElement; 65 | } 66 | 67 | type CFactory> = ComponentFactory; 68 | type ClassicFactory

= CFactory>; 69 | 70 | interface DOMFactory

{ 71 | (props?: P & ClassAttributes, ...children: ReactNode[]): DOMElement; 72 | } 73 | 74 | interface HTMLFactory extends DOMFactory { 75 | } 76 | 77 | interface SVGFactory extends DOMFactory { 78 | } 79 | 80 | // 81 | // React Nodes 82 | // http://facebook.github.io/react/docs/glossary.html 83 | // ---------------------------------------------------------------------- 84 | 85 | type ReactText = string | number; 86 | type ReactChild = ReactElement | ReactText; 87 | 88 | // Should be Array but type aliases cannot be recursive 89 | type ReactFragment = {} | Array; 90 | type ReactNode = ReactChild | ReactFragment | boolean; 91 | 92 | // 93 | // Top Level API 94 | // ---------------------------------------------------------------------- 95 | 96 | function createClass(spec: ComponentSpec): ClassicComponentClass

; 97 | 98 | function createFactory

( 99 | type: string): DOMFactory; 100 | function createFactory

(type: SFC

): SFCFactory

; 101 | function createFactory

( 102 | type: ClassType, ClassicComponentClass

>): CFactory>; 103 | function createFactory, C extends ComponentClass

>( 104 | type: ClassType): CFactory; 105 | function createFactory

(type: ComponentClass

| SFC

): Factory

; 106 | 107 | function createElement

( 108 | type: string, 109 | props?: P & ClassAttributes, 110 | ...children: ReactNode[]): DOMElement; 111 | function createElement

( 112 | type: SFC

, 113 | props?: P & Attributes, 114 | ...children: ReactNode[]): SFCElement

; 115 | function createElement

( 116 | type: ClassType, ClassicComponentClass

>, 117 | props?: P & ClassAttributes>, 118 | ...children: ReactNode[]): CElement>; 119 | function createElement, C extends ComponentClass

>( 120 | type: ClassType, 121 | props?: P & ClassAttributes, 122 | ...children: ReactNode[]): CElement; 123 | function createElement

( 124 | type: ComponentClass

| SFC

, 125 | props?: P & Attributes, 126 | ...children: ReactNode[]): ReactElement

; 127 | 128 | function cloneElement

( 129 | element: DOMElement, 130 | props?: P & ClassAttributes, 131 | ...children: ReactNode[]): DOMElement; 132 | function cloneElement

( 133 | element: SFCElement

, 134 | props?: Q, // should be Q & Attributes, but then Q is inferred as {} 135 | ...children: ReactNode[]): SFCElement

; 136 | function cloneElement

>( 137 | element: CElement, 138 | props?: Q, // should be Q & ClassAttributes 139 | ...children: ReactNode[]): CElement; 140 | function cloneElement

( 141 | element: ReactElement

, 142 | props?: Q, // should be Q & Attributes 143 | ...children: ReactNode[]): ReactElement

; 144 | 145 | function isValidElement

(object: {}): object is ReactElement

; 146 | 147 | var DOM: ReactDOM; 148 | var PropTypes: ReactPropTypes; 149 | var Children: ReactChildren; 150 | var version: string; 151 | 152 | // 153 | // Component API 154 | // ---------------------------------------------------------------------- 155 | 156 | type ReactInstance = Component | Element; 157 | 158 | // Base component for plain JS classes 159 | class Component implements ComponentLifecycle { 160 | constructor(props?: P, context?: any); 161 | setState(f: (prevState: S, props: P) => S, callback?: () => any): void; 162 | setState(state: S, callback?: () => any): void; 163 | forceUpdate(callback?: () => any): void; 164 | render(): JSX.Element; 165 | 166 | // React.Props is now deprecated, which means that the `children` 167 | // property is not available on `P` by default, even though you can 168 | // always pass children as variadic arguments to `createElement`. 169 | // In the future, if we can define its call signature conditionally 170 | // on the existence of `children` in `P`, then we should remove this. 171 | props: P & { children?: ReactNode }; 172 | state: S; 173 | context: any; 174 | refs: { 175 | [key: string]: ReactInstance 176 | }; 177 | } 178 | 179 | class PureComponent extends Component {} 180 | 181 | interface ClassicComponent extends Component { 182 | replaceState(nextState: S, callback?: () => any): void; 183 | isMounted(): boolean; 184 | getInitialState?(): S; 185 | } 186 | 187 | interface ChildContextProvider { 188 | getChildContext(): CC; 189 | } 190 | 191 | // 192 | // Class Interfaces 193 | // ---------------------------------------------------------------------- 194 | 195 | type SFC

= StatelessComponent

; 196 | interface StatelessComponent

{ 197 | (props: P, context?: any): ReactElement; 198 | propTypes?: ValidationMap

; 199 | contextTypes?: ValidationMap; 200 | defaultProps?: P; 201 | displayName?: string; 202 | } 203 | 204 | interface ComponentClass

{ 205 | new(props?: P, context?: any): Component; 206 | propTypes?: ValidationMap

; 207 | contextTypes?: ValidationMap; 208 | childContextTypes?: ValidationMap; 209 | defaultProps?: P; 210 | displayName?: string; 211 | } 212 | 213 | interface ClassicComponentClass

extends ComponentClass

{ 214 | new(props?: P, context?: any): ClassicComponent; 215 | getDefaultProps?(): P; 216 | } 217 | 218 | /** 219 | * We use an intersection type to infer multiple type parameters from 220 | * a single argument, which is useful for many top-level API defs. 221 | * See https://github.com/Microsoft/TypeScript/issues/7234 for more info. 222 | */ 223 | type ClassType, C extends ComponentClass

> = 224 | C & 225 | (new() => T) & 226 | (new() => { props: P }); 227 | 228 | // 229 | // Component Specs and Lifecycle 230 | // ---------------------------------------------------------------------- 231 | 232 | interface ComponentLifecycle { 233 | componentWillMount?(): void; 234 | componentDidMount?(): void; 235 | componentWillReceiveProps?(nextProps: P, nextContext: any): void; 236 | shouldComponentUpdate?(nextProps: P, nextState: S, nextContext: any): boolean; 237 | componentWillUpdate?(nextProps: P, nextState: S, nextContext: any): void; 238 | componentDidUpdate?(prevProps: P, prevState: S, prevContext: any): void; 239 | componentWillUnmount?(): void; 240 | } 241 | 242 | interface Mixin extends ComponentLifecycle { 243 | mixins?: Mixin; 244 | statics?: { 245 | [key: string]: any; 246 | }; 247 | 248 | displayName?: string; 249 | propTypes?: ValidationMap; 250 | contextTypes?: ValidationMap; 251 | childContextTypes?: ValidationMap; 252 | 253 | getDefaultProps?(): P; 254 | getInitialState?(): S; 255 | } 256 | 257 | interface ComponentSpec extends Mixin { 258 | render(): ReactElement; 259 | 260 | [propertyName: string]: any; 261 | } 262 | 263 | // 264 | // Event System 265 | // ---------------------------------------------------------------------- 266 | 267 | interface SyntheticEvent { 268 | bubbles: boolean; 269 | cancelable: boolean; 270 | currentTarget: EventTarget; 271 | defaultPrevented: boolean; 272 | eventPhase: number; 273 | isTrusted: boolean; 274 | nativeEvent: Event; 275 | preventDefault(): void; 276 | isDefaultPrevented(): boolean; 277 | stopPropagation(): void; 278 | isPropagationStopped(): boolean; 279 | persist(): void; 280 | target: EventTarget; 281 | timeStamp: Date; 282 | type: string; 283 | } 284 | 285 | interface ClipboardEvent extends SyntheticEvent { 286 | clipboardData: DataTransfer; 287 | } 288 | 289 | interface CompositionEvent extends SyntheticEvent { 290 | data: string; 291 | } 292 | 293 | interface DragEvent extends MouseEvent { 294 | dataTransfer: DataTransfer; 295 | } 296 | 297 | interface FocusEvent extends SyntheticEvent { 298 | relatedTarget: EventTarget; 299 | } 300 | 301 | interface FormEvent extends SyntheticEvent { 302 | } 303 | 304 | interface KeyboardEvent extends SyntheticEvent { 305 | altKey: boolean; 306 | charCode: number; 307 | ctrlKey: boolean; 308 | getModifierState(key: string): boolean; 309 | key: string; 310 | keyCode: number; 311 | locale: string; 312 | location: number; 313 | metaKey: boolean; 314 | repeat: boolean; 315 | shiftKey: boolean; 316 | which: number; 317 | } 318 | 319 | interface MouseEvent extends SyntheticEvent { 320 | altKey: boolean; 321 | button: number; 322 | buttons: number; 323 | clientX: number; 324 | clientY: number; 325 | ctrlKey: boolean; 326 | getModifierState(key: string): boolean; 327 | metaKey: boolean; 328 | pageX: number; 329 | pageY: number; 330 | relatedTarget: EventTarget; 331 | screenX: number; 332 | screenY: number; 333 | shiftKey: boolean; 334 | } 335 | 336 | interface TouchEvent extends SyntheticEvent { 337 | altKey: boolean; 338 | changedTouches: TouchList; 339 | ctrlKey: boolean; 340 | getModifierState(key: string): boolean; 341 | metaKey: boolean; 342 | shiftKey: boolean; 343 | targetTouches: TouchList; 344 | touches: TouchList; 345 | } 346 | 347 | interface UIEvent extends SyntheticEvent { 348 | detail: number; 349 | view: AbstractView; 350 | } 351 | 352 | interface WheelEvent extends MouseEvent { 353 | deltaMode: number; 354 | deltaX: number; 355 | deltaY: number; 356 | deltaZ: number; 357 | } 358 | 359 | interface AnimationEvent extends SyntheticEvent { 360 | animationName: string; 361 | pseudoElement: string; 362 | elapsedTime: number; 363 | } 364 | 365 | interface TransitionEvent extends SyntheticEvent { 366 | propertyName: string; 367 | pseudoElement: string; 368 | elapsedTime: number; 369 | } 370 | 371 | // 372 | // Event Handler Types 373 | // ---------------------------------------------------------------------- 374 | 375 | interface EventHandler { 376 | (event: E): void; 377 | } 378 | 379 | type ReactEventHandler = EventHandler; 380 | 381 | type ClipboardEventHandler = EventHandler; 382 | type CompositionEventHandler = EventHandler; 383 | type DragEventHandler = EventHandler; 384 | type FocusEventHandler = EventHandler; 385 | type FormEventHandler = EventHandler; 386 | type KeyboardEventHandler = EventHandler; 387 | type MouseEventHandler = EventHandler; 388 | type TouchEventHandler = EventHandler; 389 | type UIEventHandler = EventHandler; 390 | type WheelEventHandler = EventHandler; 391 | type AnimationEventHandler = EventHandler; 392 | type TransitionEventHandler = EventHandler; 393 | 394 | // 395 | // Props / DOM Attributes 396 | // ---------------------------------------------------------------------- 397 | 398 | /** 399 | * @deprecated. This was used to allow clients to pass `ref` and `key` 400 | * to `createElement`, which is no longer necessary due to intersection 401 | * types. If you need to declare a props object before passing it to 402 | * `createElement` or a factory, use `ClassAttributes`: 403 | * 404 | * ```ts 405 | * var b: Button; 406 | * var props: ButtonProps & ClassAttributes