├── example ├── .npmignore ├── index.html ├── tsconfig.json ├── package.json └── index.tsx ├── .gitignore ├── src ├── index.ts ├── inputType.ts ├── Input.tsx ├── machine.ts └── InlineEdit.tsx ├── test └── blah.test.tsx ├── tsconfig.json ├── .github └── workflows │ └── main.yml ├── LICENSE ├── package.json └── README.md /example/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | dist 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import InlineEdit from './InlineEdit' 2 | import InputType from './inputType' 3 | 4 | export { InputType } 5 | export default InlineEdit 6 | -------------------------------------------------------------------------------- /src/inputType.ts: -------------------------------------------------------------------------------- 1 | enum InputType { 2 | Text = 'text', 3 | Number = 'number', 4 | Email = 'email', 5 | Password = 'password', 6 | Date = 'date', 7 | Range = 'range', 8 | TextArea = 'textarea', 9 | Select = 'select', 10 | } 11 | 12 | export default InputType 13 | -------------------------------------------------------------------------------- /test/blah.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import * as ReactDOM from 'react-dom' 3 | import InlineEdit from '../src' 4 | 5 | describe('it', () => { 6 | it('renders without crashing', () => { 7 | const div = document.createElement('div') 8 | ReactDOM.render( fetch('')} />, div) 9 | ReactDOM.unmountComponentAtNode(div) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Playground 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": false, 4 | "target": "es5", 5 | "module": "commonjs", 6 | "jsx": "react", 7 | "moduleResolution": "node", 8 | "noImplicitAny": false, 9 | "noUnusedLocals": false, 10 | "noUnusedParameters": false, 11 | "removeComments": true, 12 | "strictNullChecks": true, 13 | "preserveConstEnums": true, 14 | "sourceMap": true, 15 | "lib": ["es2015", "es2016", "dom"], 16 | "baseUrl": ".", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "parcel index.html", 8 | "build": "parcel build index.html" 9 | }, 10 | "dependencies": { 11 | "react-app-polyfill": "^1.0.6" 12 | }, 13 | "alias": { 14 | "react": "../node_modules/react", 15 | "react-dom": "../node_modules/react-dom/profiling", 16 | "scheduler/tracing": "../node_modules/scheduler/tracing-profiling" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^16.9.49", 20 | "@types/react-dom": "^16.9.8", 21 | "parcel": "^1.12.4", 22 | "typescript": "^4.0.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types"], 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "lib": ["dom", "esnext"], 6 | "importHelpers": true, 7 | "declaration": true, 8 | "sourceMap": true, 9 | "rootDir": "./src", 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "strictFunctionTypes": true, 14 | "strictPropertyInitialization": true, 15 | "noImplicitThis": true, 16 | "alwaysStrict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noImplicitReturns": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "moduleResolution": "node", 22 | "baseUrl": "./", 23 | "paths": { 24 | "*": ["src/*", "node_modules/*"] 25 | }, 26 | "jsx": "react", 27 | "esModuleInterop": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | 7 | steps: 8 | - name: Begin CI... 9 | uses: actions/checkout@v2 10 | 11 | - name: Use Node 12 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: 12.x 15 | 16 | - name: Use cached node_modules 17 | uses: actions/cache@v1 18 | with: 19 | path: node_modules 20 | key: nodeModules-${{ hashFiles('**/yarn.lock') }} 21 | restore-keys: | 22 | nodeModules- 23 | 24 | - name: Install dependencies 25 | run: yarn install --frozen-lockfile 26 | env: 27 | CI: true 28 | 29 | - name: Lint 30 | run: yarn lint 31 | env: 32 | CI: true 33 | 34 | - name: Test 35 | run: yarn test --ci --coverage --maxWorkers=2 36 | env: 37 | CI: true 38 | 39 | - name: Build 40 | run: yarn build 41 | env: 42 | CI: true 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 F2 .net engineering s.r.l. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "riec", 3 | "version": "0.1.9", 4 | "author": "Matteo Frana ", 5 | "description": "Modern React component for inline edit of text/select values, written in Typescript", 6 | "keywords": [ 7 | "react inline edit", 8 | "react edit", 9 | "react", 10 | "inline", 11 | "edit", 12 | "riek" 13 | ], 14 | "license": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/ReactBricks/react-inline-edit-async.git" 18 | }, 19 | "main": "dist/index.js", 20 | "typings": "dist/index.d.ts", 21 | "files": [ 22 | "dist", 23 | "src" 24 | ], 25 | "engines": { 26 | "node": ">=10" 27 | }, 28 | "scripts": { 29 | "start": "tsdx watch", 30 | "build": "tsdx build", 31 | "test": "tsdx test --passWithNoTests", 32 | "lint": "tsdx lint", 33 | "prepare": "tsdx build" 34 | }, 35 | "peerDependencies": { 36 | "react": ">=16" 37 | }, 38 | "husky": { 39 | "hooks": { 40 | "pre-commit": "tsdx lint" 41 | } 42 | }, 43 | "prettier": { 44 | "printWidth": 80, 45 | "semi": false, 46 | "singleQuote": true, 47 | "trailingComma": "es5" 48 | }, 49 | "module": "dist/react-inline-edit-async.esm.js", 50 | "devDependencies": { 51 | "@types/jest": "^26.0.13", 52 | "@types/react": "^16.9.49", 53 | "@types/react-dom": "^16.9.8", 54 | "husky": "^4.2.5", 55 | "react": "^16.13.1", 56 | "react-dom": "^16.13.1", 57 | "tsdx": "^0.13.3", 58 | "tslib": "^2.0.1", 59 | "typescript": "^4.0.2" 60 | }, 61 | "dependencies": { 62 | "@xstate/react": "^0.8.1", 63 | "xstate": "^4.13.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Input.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useCallback } from 'react' 2 | import { useRef } from 'react' 3 | import InputType from './inputType' 4 | 5 | interface InputProps { 6 | value: any 7 | type: InputType 8 | editProps?: { 9 | [key: string]: any 10 | } 11 | editClassProp: { className: string } | {} 12 | options: any[] 13 | valueKey: string 14 | labelKey: string 15 | handleChange: (value: any) => void 16 | handleKeyDown: React.KeyboardEventHandler 17 | handleBlur: React.FocusEventHandler 18 | } 19 | 20 | const Input: React.FC = ({ 21 | value, 22 | type, 23 | editProps, 24 | editClassProp, 25 | options, 26 | valueKey, 27 | labelKey, 28 | handleChange, 29 | handleKeyDown, 30 | handleBlur, 31 | }) => { 32 | //========================== 33 | // Focus input as it mounts 34 | // ========================= 35 | const inputRef = useRef(null) 36 | const textareaRef = useRef(null) 37 | const selectRef = useRef(null) 38 | 39 | const getRef = useCallback(() => { 40 | if (type === InputType.Select) { 41 | return selectRef 42 | } 43 | if (type === InputType.TextArea) { 44 | return textareaRef 45 | } 46 | return inputRef 47 | }, [type]) 48 | 49 | useEffect(() => { 50 | const controlRef = getRef() 51 | 52 | if (controlRef.current) { 53 | setTimeout(function() { 54 | if (controlRef.current) { 55 | // Focus input 56 | controlRef.current.focus() 57 | if (controlRef === inputRef || controlRef === textareaRef) { 58 | // If it is not a Select => select input content 59 | controlRef.current.select() 60 | } 61 | } 62 | }, 10) 63 | } 64 | }, [getRef]) 65 | 66 | //========================== 67 | // Select 68 | // ========================= 69 | if (type === InputType.Select) { 70 | return ( 71 | 86 | ) 87 | } 88 | 89 | //========================== 90 | // TextArea 91 | // ========================= 92 | if (type === InputType.TextArea) { 93 | return ( 94 |