diff --git a/package-lock.json b/package-lock.json index 9255ad6fc..7f16cbbcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4060,6 +4060,10 @@ "resolved": "packages/sui-pde", "link": true }, + "node_modules/@s-ui/performance": { + "resolved": "packages/sui-performance", + "link": true + }, "node_modules/@s-ui/polyfills": { "resolved": "packages/sui-polyfills", "link": true @@ -12867,6 +12871,14 @@ "postcss": "^8.1.0" } }, + "node_modules/idlefy": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/idlefy/-/idlefy-1.1.1.tgz", + "integrity": "sha512-iV1kFRoiaN7XSFjErCLT5IB384kRhMv3AqPV7hZYpy2b62O/117QyIdBNemuq56flt/1wtaSCP0pFOFrk5oWiA==", + "funding": { + "url": "https://github.com/sponsors/harshkc" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -24746,9 +24758,19 @@ "node": ">= 10" } }, + "packages/sui-performance": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "idlefy": "1.1.1" + }, + "devDependencies": { + "@s-ui/test": "8" + } + }, "packages/sui-polyfills": { "name": "@s-ui/polyfills", - "version": "1.21.0", + "version": "1.22.0", "license": "MIT", "dependencies": { "core-js": "3", diff --git a/packages/sui-performance/CHANGELOG.md b/packages/sui-performance/CHANGELOG.md new file mode 100644 index 000000000..505d21df1 --- /dev/null +++ b/packages/sui-performance/CHANGELOG.md @@ -0,0 +1 @@ +# CHANGELOG \ No newline at end of file diff --git a/packages/sui-performance/README.md b/packages/sui-performance/README.md new file mode 100644 index 000000000..939e388b6 --- /dev/null +++ b/packages/sui-performance/README.md @@ -0,0 +1,57 @@ +# @s-ui/performance + +> Performance utilities to make your web application go fast ⚡️ + +## Installation + +```sh +npm install @s-ui/performance +``` + +## Usage + +### Delay code execution + +Use this function to delay the execution of an expensive operation and prioritize user actions. Keep in mind that it only delays the response by a maximum of 1 frame, an average of 8ms, which is too little for a human to notice for the types of major actions where you’d use this function. + +```jsx +import {delayTask} from '@s-ui/performance'; + +export default function Example() { + const [counter, setCounter] = useState(0) + + const handleClick = async () => { + setCounter(counter => counter + 1) + + await delayTask() + + work() // expensive work + } + + return +} +``` + +### Delay code execution until urgent + +Use this function to delay the execution of an expensive operation while the main thread is idle, using [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback), and to prioritize user actions. This method ensures the execution is completed before the user leaves the page. It is especially useful for delaying tracking execution. + +The `delayTaskUntilUrgent` function optionally receives an options object. The documentation can be found [here](https://github.com/redbus-labs/idlefy/tree/main?tab=readme-ov-file#methods) ([idlefy](https://github.com/redbus-labs/idlefy/tree/main) is used under the hood). + +```jsx +import {delayTaskUntilUrgent} from '@s-ui/performance'; + +export default function Example() { + const [counter, setCounter] = useState(0) + + const handleClick = async () => { + setCounter(counter => counter + 1) + + await delayTaskUntilUrgent() + + track() // expensive work + } + + return +} +``` \ No newline at end of file diff --git a/packages/sui-performance/package.json b/packages/sui-performance/package.json new file mode 100644 index 000000000..bb438f55f --- /dev/null +++ b/packages/sui-performance/package.json @@ -0,0 +1,26 @@ +{ + "name": "@s-ui/performance", + "version": "1.0.0", + "description": "", + "type": "module", + "main": "lib/index.js", + "scripts": { + "lib": "babel --presets sui ./src --out-dir ./lib", + "prepublishOnly": "npm run lib", + "test:browser:watch": "NODE_ENV=test npm run test:browser -- --watch", + "test:browser": "NODE_ENV=test sui-test browser", + "test:server:watch": "npm run test:server -- --watch", + "test:server": "NODE_ENV=test sui-test server", + "test": "npm run test:server && npm run test:browser" + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "idlefy": "1.1.1" + }, + "peerDependencies": {}, + "devDependencies": { + "@s-ui/test": "8" + } +} diff --git a/packages/sui-performance/src/index.js b/packages/sui-performance/src/index.js new file mode 100644 index 000000000..6ef0e8728 --- /dev/null +++ b/packages/sui-performance/src/index.js @@ -0,0 +1,18 @@ +import {IdleQueue} from 'idlefy' + +const queue = new IdleQueue({ensureTasksRun: true}) + +export function delayTask() { + return new Promise(resolve => { + setTimeout(resolve, 100) + requestAnimationFrame(() => { + setTimeout(resolve, 0) + }) + }) +} + +export function delayTaskUntilUrgent(options) { + return new Promise(resolve => { + queue.pushTask(resolve, options) + }) +} diff --git a/packages/sui-performance/test/browser/indexSpec.js b/packages/sui-performance/test/browser/indexSpec.js new file mode 100644 index 000000000..36701fe47 --- /dev/null +++ b/packages/sui-performance/test/browser/indexSpec.js @@ -0,0 +1,26 @@ +import {expect} from 'chai' +import sinon from 'sinon' + +import {waitFor} from '@testing-library/react' + +import {delayTask, delayTaskUntilUrgent} from '../../src/index.js' + +describe('delayTask', () => { + it('should execute function', async () => { + const callback = sinon.spy() + + delayTask().then(callback) + + await waitFor(() => expect(callback.called).to.be.true) + }) +}) + +describe('delayTaskUntilUrgent', () => { + it('should execute function', async () => { + const callback = sinon.spy() + + delayTaskUntilUrgent().then(callback) + + await waitFor(() => expect(callback.called).to.be.true) + }) +})