diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..af090c2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,19 @@ +name: test +on: [push] + +jobs: + test: + name: Node.js ${{ matrix.node_version }} + runs-on: ubuntu-latest + strategy: + matrix: + node_version: [10, 12] + + steps: + - uses: actions/checkout@master + - name: Use Node.js ${{ matrix.node_version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node_version }} + - run: npm install + - run: npm test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f3fa8cd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: node_js -node_js: - - '10' - - '8' diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index d21917b..0000000 --- a/index.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Type definitions ported from DefinitelyTyped -// Ported by: M4rk9696 -// Original Definitions by: Łukasz Ostrowski - -import { Chalk } from 'chalk'; -import type {SpinnerName} from 'cli-spinners'; -import type { Component } from 'react'; - -type StringifyPartial = { - [P in keyof T]?: string; -}; - -type BooleansPartial = { - [P in keyof T]?: boolean; -}; - -type TupleOfNumbersPartial = { - [P in keyof T]?: [number, number, number]; -}; -// Omit taken from https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html -type Omit = Pick>; - -type ChalkColorModels = Pick; -type ChalkKeywordsAndHexes = Pick; -type ChalkCommons = Omit; - -interface SpinnerProps { - type?: SpinnerName; -} - -type ChalkProps = BooleansPartial - & StringifyPartial - & TupleOfNumbersPartial; - -declare class Spinner extends Component { } - -export = Spinner; diff --git a/index.test-d.tsx b/index.test-d.tsx deleted file mode 100644 index a7f69d9..0000000 --- a/index.test-d.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import * as React from "react"; -import Spinner = require("."); -import { render } from "ink"; - -render( - <> - - - - - -); diff --git a/package.json b/package.json index 8fb72c1..5d5c79c 100644 --- a/package.json +++ b/package.json @@ -9,20 +9,17 @@ "email": "vdemedes@gmail.com", "url": "github.com/vadimdemedes" }, - "main": "build/index.js", - "types": "index.d.ts", + "main": "build", + "types": "build/index.d.ts", "engines": { - "node": ">=8" + "node": ">=10" }, "scripts": { - "pretest": "npm run build", - "test": "tsc --noEmit --jsx react --module commonjs index.test-d.tsx && xo && ava", - "build": "babel src --out-dir=build", - "prepare": "npm run build" + "test": "tsc && xo && ava", + "prepare": "tsc" }, "files": [ - "build", - "index.d.ts" + "build" ], "keywords": [ "ink", @@ -35,54 +32,52 @@ "react" ], "dependencies": { - "cli-spinners": "^1.0.0", - "prop-types": "^15.5.10" + "@types/react": "^16.9.38", + "cli-spinners": "^2.3.0" }, "devDependencies": { "@ava/babel": "^1.0.1", - "@babel/cli": "^7.2.3", - "@babel/core": "^7.3.3", - "@babel/plugin-proposal-class-properties": "^7.3.3", - "@babel/preset-react": "^7.0.0", - "@babel/register": "^7.10.3", + "@babel/preset-react": "^7.10.1", + "@sindresorhus/tsconfig": "^0.7.0", + "@vdemedes/prettier-config": "^1.0.0", "ava": "^3.9.0", - "babel-eslint": "^10.1.0", "delay": "^4.1.0", "eslint-config-xo-react": "^0.23.0", "eslint-plugin-react": "^7.20.0", "eslint-plugin-react-hooks": "^4.0.4", - "ink": "^2.0.0", - "ink-testing-library": "^1.0.0", + "husky": "^4.2.5", + "ink": "^3.0.0-4", + "ink-testing-library": "^2.0.0", + "prettier": "^2.0.5", + "pretty-quick": "^2.0.1", "react": "^16.8.2", "typescript": "^3.9.5", "xo": "^0.32.0" }, "peerDependencies": { "react": "^16.8.2", - "ink": "^2.0.0" - }, - "babel": { - "plugins": [ - "@babel/plugin-proposal-class-properties" - ], - "presets": [ - "@babel/preset-react" - ] + "ink": "^3.0.0-4" }, "ava": { - "babel": true, - "require": [ - "@babel/register" - ] + "babel": { + "testOptions": { + "presets": [ + "@babel/preset-react" + ] + } + } }, "xo": { - "parser": "babel-eslint", - "extends": [ - "xo-react" - ], - "ignores": [ - "index.d.ts", - "index.test-d.tsx" - ] + "extends": "xo-react", + "prettier": true, + "rules": { + "react/prop-types": "off" + } + }, + "prettier": "@vdemedes/prettier-config", + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } } } diff --git a/readme.md b/readme.md index 3a71b7a..717ba0f 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,8 @@ -# ink-spinner [![Build Status](https://travis-ci.org/vadimdemedes/ink-spinner.svg?branch=master)](https://travis-ci.org/vadimdemedes/ink-spinner) +# ink-spinner ![test](https://github.com/vadimdemedes/ink-spinner/workflows/test/badge.svg) > Spinner component for [Ink](https://github.com/vadimdemedes/ink). Uses [cli-spinners](https://github.com/sindresorhus/cli-spinners) for the collection of spinners. +Looking for a version compatible with Ink 2.x? Check out [previous release](https://github.com/vadimdemedes/ink-spinner/tree/v3.1.0). ## Install @@ -9,25 +10,25 @@ $ npm install ink-spinner ``` - ## Usage -```js -import React, {Fragment} from 'react'; -import {render, Color} from 'ink'; +```jsx +import React from 'react'; +import { render, Text } from 'ink'; import Spinner from 'ink-spinner'; render( - - + + + + {' Loading'} - + ); ``` - ## Props ### type @@ -36,8 +37,3 @@ Type: `string`
Defaults: `dots` Type of a spinner. See [cli-spinners](https://github.com/sindresorhus/cli-spinners) for available spinners. - - -## License - -MIT © [Vadim Demedes](https://github.com/vadimdemedes) diff --git a/source/index.tsx b/source/index.tsx new file mode 100644 index 0000000..350ac08 --- /dev/null +++ b/source/index.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import type { FC } from 'react'; +import { Text } from 'ink'; +import spinners from 'cli-spinners'; +import type { SpinnerName } from 'cli-spinners'; + +interface Props { + /** + * Type of a spinner. + * See [cli-spinners](https://github.com/sindresorhus/cli-spinners) for available spinners. + * + * @default dots + */ + type?: SpinnerName; +} + +/** + * Spinner. + */ +const Spinner: FC = ({ type = 'dots' }) => { + const [frame, setFrame] = useState(0); + const spinner = spinners[type]; + + useEffect(() => { + const timer = setInterval(() => { + setFrame(previousFrame => { + const isLastFrame = previousFrame === spinner.frames.length - 1; + return isLastFrame ? 0 : previousFrame + 1; + }); + }, spinner.interval); + + return () => { + clearInterval(timer); + }; + }, [spinner]); + + return {spinner.frames[frame]}; +}; + +export default Spinner; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index bb22e0a..0000000 --- a/src/index.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict'; -const React = require('react'); -const PropTypes = require('prop-types'); -const {Box} = require('ink'); -const spinners = require('cli-spinners'); - -class Spinner extends React.Component { - static propTypes = { - type: PropTypes.string - } - - static defaultProps = { - type: 'dots' - } - - state = { - frame: 0 - } - - render() { - const spinner = this.getSpinner(); - - return ( - - {spinner.frames[this.state.frame]} - - ); - } - - componentDidMount() { - const spinner = this.getSpinner(); - this.timer = setInterval(this.switchFrame, spinner.interval); - } - - componentWillUnmount() { - clearInterval(this.timer); - } - - getSpinner() { - return spinners[this.props.type] || spinners.dots; - } - - switchFrame = () => { - const {frame} = this.state; - const spinner = this.getSpinner(); - const isLastFrame = frame === spinner.frames.length - 1; - const nextFrame = isLastFrame ? 0 : frame + 1; - - this.setState({ - frame: nextFrame - }); - } -} - -module.exports = Spinner; -module.exports.default = Spinner; diff --git a/test.js b/test.js index 823be42..d1d1731 100644 --- a/test.js +++ b/test.js @@ -1,5 +1,5 @@ import React from 'react'; -import {render} from 'ink-testing-library'; +import { render } from 'ink-testing-library'; import spinners from 'cli-spinners'; import test from 'ava'; import delay from 'delay'; @@ -7,7 +7,7 @@ import Spinner from '.'; test('render spinner', async t => { const spinner = spinners.dots; - const {frames, unmount} = render(); + const { frames, unmount } = render(); await delay(spinner.frames.length * spinner.interval); unmount(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e950093 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@sindresorhus/tsconfig", + "compilerOptions": { + "outDir": "build", + "target": "es2018", + "lib": ["es2018"] + } +}