Skip to content

Commit

Permalink
✨ add startAnimation options
Browse files Browse the repository at this point in the history
  • Loading branch information
almond-bongbong committed Mar 27, 2023
1 parent 1bcddfa commit b4f6c83
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 17 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended"
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"globals": {
"Atomics": "readonly",
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/auto_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Auto Release

on:
push:
branches:
- main

jobs:
create_release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Get version from package.json
id: version
run: echo "::set-output name=version::$(node -p "require('./package.json').version")"

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ steps.version.outputs.version }}
release_name: Release v${{ steps.version.outputs.version }}
body: Auto-generated release
draft: false
prerelease: false
46 changes: 46 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Publish Package

on:
release:
types: [created]

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: 14

- name: Install dependencies
run: npm ci

- name: Build package
run: npm run build

- name: Install jq
run: sudo apt-get install -y jq

- name: Update package.json for npm
run: |
jq '.name = "react-slot-counter"' package.json > package.temp.json
mv package.temp.json package.json
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Update package.json for GitHub Packages
run: |
jq '.name = "@almond-bongbong/react-slot-counter"' package.json > package.temp.json
mv package.temp.json package.json
- name: Publish to GitHub Packages
run: npm publish --registry=https://npm.pkg.github.com/
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ You can access the SlotCounter component using a ref. This ref can be used to st
| ---------------- | ------------------------------------ |
| `startAnimation` | Start the animation of the component |

The `startAnimation` method accepts an optional object with the following properties:

- `duration`: The duration of the animation in seconds. Overrides the `duration` prop.
- `dummyCharacterCount`: The number of dummy characters to be displayed in the animation before reaching the target character. Overrides the `dummyCharacterCount` prop.


Example:

```jsx
Expand Down
25 changes: 22 additions & 3 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function App() {
<h3>Using the `ref` to Start Animation</h3>
<h4>startAnimation</h4>
<p>
This method starts the animation of the `SlotCounter` component.
This method starts the animation of the `SlotCounter` component with optional parameters.
</p>
<div className="playground">
<SlotCounter
Expand All @@ -210,7 +210,16 @@ function App() {
className="example-button"
onClick={() => counterRef1.current?.startAnimation()}
>
start animation
start animation (default)
</button>
<button
className="example-button"
onClick={() => counterRef1.current?.startAnimation({
duration: 1.5,
dummyCharacterCount: 10
})}
>
start animation (with options)
</button>
</div>
<CommonHighlighter>
Expand All @@ -228,7 +237,17 @@ function App() {
className="example-button"
onClick={() => counterRef.current?.startAnimation()}
>
start animation
start animation (default)
</button>
<button
className="example-button"
onClick={() => counterRef.current?.startAnimation({
duration: 1.5,
dummyCharacterCount: 10
})}
>
start animation (with options)
</button>`}
</CommonHighlighter>
</div>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-slot-counter",
"version": "1.2.0",
"version": "1.3.0",
"description": "React Slot Counter Component",
"author": "almond-bongbong",
"license": "MIT",
Expand Down
48 changes: 36 additions & 12 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ import styles from './index.module.scss';
import { mergeClassNames, random, range, shuffle } from './utils';
import useIsomorphicLayoutEffect from './hooks/useIsomorphicLayoutEffect';

export interface SlotCounterRef {
startAnimation: () => void;
}

interface Props {
value: string | number | string[];
duration?: number;
Expand All @@ -27,6 +23,15 @@ interface Props {
separatorClassName?: string;
}

export interface SlotCounterRef {
startAnimation: (options?: {
duration?: number;
dummyCharacterCount?: number;
}) => void;
}

type StartAnimationOptions = Parameters<SlotCounterRef['startAnimation']>[0];

const SEPARATOR = [',', '.'];

function SlotCounter(
Expand All @@ -45,20 +50,40 @@ function SlotCounter(
const [active, setActive] = useState(false);
const [localValue, setLocalValue] = useState(value);
const [fontHeight, setFontHeight] = useState(0);
const [startAnimationOptions, setStartAnimationOptions] =
useState<StartAnimationOptions>();
const numbersRef = useRef<HTMLDivElement>(null);
const effectiveDummyCharacterCount = useMemo(
() => startAnimationOptions?.dummyCharacterCount ?? dummyCharacterCount,
[startAnimationOptions?.dummyCharacterCount, dummyCharacterCount],
);
const effectiveDuration = useMemo(
() => startAnimationOptions?.duration ?? duration,
[startAnimationOptions?.duration, duration],
);
const dummyList = useMemo(
() =>
range(0, dummyCharacterCount - 1).map((i) => {
range(0, effectiveDummyCharacterCount - 1).map((i) => {
if (!dummyCharacters) return random(0, 10);

const index =
i >= dummyCharacters.length ? random(0, dummyCharacters.length) : i;
return dummyCharacters[index];
}),
[dummyCharacters, dummyCharacterCount],
[dummyCharacters, effectiveDummyCharacterCount],
);
const valueList = useMemo(
() =>
Array.isArray(localValue) ? localValue : localValue.toString().split(''),
[localValue],
);
const calculatedInterval = useMemo(() => {
const MAX_INTERVAL = 0.1;
return Math.min(MAX_INTERVAL, effectiveDuration / valueList.length);
}, [effectiveDuration, valueList.length]);

const startAnimation = useCallback(() => {
const startAnimation = useCallback((options?: StartAnimationOptions) => {
setStartAnimationOptions(options);
setActive(false);
setTimeout(() => setActive(true), 20);
}, []);
Expand Down Expand Up @@ -98,10 +123,7 @@ function SlotCounter(
active && styles.active,
)}
>
{(Array.isArray(localValue)
? localValue
: localValue.toString().split('')
).map((v, i) => {
{valueList.map((v, i) => {
if (SEPARATOR.includes(v)) {
return (
<div
Expand All @@ -128,7 +150,9 @@ function SlotCounter(
transform: `translateY(-${
fontHeight * (dummyList.length + 1)
}px)`,
transition: `transform ${duration}s ${i * 0.1}s ease-in-out`,
transition: `transform ${effectiveDuration}s ${
i * calculatedInterval
}s ease-in-out`,
}),
}}
>
Expand Down

0 comments on commit b4f6c83

Please sign in to comment.