Skip to content

Commit

Permalink
feat: utils package (#86)
Browse files Browse the repository at this point in the history
* feat: utils package

* [autofix.ci] apply automated fixes

* Delete vite.config.ts
  • Loading branch information
productdevbook authored May 5, 2023
1 parent 052417d commit f48ed78
Show file tree
Hide file tree
Showing 15 changed files with 4,386 additions and 8,823 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"react-dom": "^18.2.0",
"storybook": "^7.0.7",
"storybook-dark-mode": "^3.0.0",
"tsup": "^6.7.0",
"turbo": "^1.9.3",
"typescript": "^5.0.4",
"unocss": "^0.51.8",
Expand Down
1 change: 1 addition & 0 deletions packages/core/use-controllable-ref/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"scripts": {
"clean": "rm -rf dist",
"build": "vite build --mode production",
"dev": "vite build --mode production --watch",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
Expand Down
1 change: 1 addition & 0 deletions packages/core/use-previous/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"scripts": {
"clean": "rm -rf dist",
"build": "vite build --mode production",
"dev": "vite build --mode production --watch",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
Expand Down
1 change: 1 addition & 0 deletions packages/core/use-size/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"scripts": {
"clean": "rm -rf dist",
"build": "vite build --mode production",
"dev": "vite build --mode production --watch",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
Expand Down
10 changes: 10 additions & 0 deletions packages/core/utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# `@oku-ui/utils`

## Installation

```sh
$ pnpm add @oku-ui/utils
```

## Usage
...
40 changes: 40 additions & 0 deletions packages/core/utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@oku-ui/utils",
"type": "module",
"version": "0.0.0",
"license": "MIT",
"source": "src/index.ts",
"funding": "https://github.com/sponsors/productdevbook",
"homepage": "https://oku-ui.com/primitives",
"repository": {
"type": "git",
"url": "git+https://github.com/oku-ui/primitives.git"
},
"bugs": {
"url": "https://github.com/oku-ui/c/issues"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.cjs",
"import": "./dist/index.js"
}
},
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist",
"README.md"
],
"scripts": {
"clean": "rm -rf dist",
"build": "tsup --dts",
"dev": "tsup --watch",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"devDependencies": {
"tsconfig": "workspace:^"
}
}
5 changes: 5 additions & 0 deletions packages/core/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { clamp } from './number'
export { composeEventHandlers } from './primitive'

export { observeElementRect } from './observeElementRect'
export type { Measurable } from './observeElementRect'
5 changes: 5 additions & 0 deletions packages/core/utils/src/number.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function clamp(value: number, [min, max]: [number, number]): number {
return Math.min(max, Math.max(min, value))
}

export { clamp }
107 changes: 107 additions & 0 deletions packages/core/utils/src/observeElementRect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
type Measurable = { getBoundingClientRect(): DOMRect }

type CallbackFn = (rect: DOMRect) => void

type ObservedData = {
rect: DOMRect
callbacks: Array<CallbackFn>
}

let rafId: number
const observedElements: Map<Measurable, ObservedData> = new Map()

/**
* Observes an element's rectangle on screen (getBoundingClientRect)
* This is useful to track elements on the screen and attach other elements
* that might be in different layers, etc.
*/
function observeElementRect(
/** The element whose rect to observe */
elementToObserve: Measurable,
/** The callback which will be called when the rect changes */
callback: CallbackFn,
) {
const observedData = observedElements.get(elementToObserve)

if (observedData === undefined) {
// add the element to the map of observed elements with its first callback
// because this is the first time this element is observed
observedElements.set(elementToObserve, { rect: {} as ClientRect, callbacks: [callback] })

if (observedElements.size === 1) {
// start the internal loop once at least 1 element is observed
rafId = requestAnimationFrame(runLoop)
}
}
else {
// only add a callback for this element as it's already observed
observedData.callbacks.push(callback)
callback(elementToObserve.getBoundingClientRect())
}

return () => {
const observedData = observedElements.get(elementToObserve)
if (observedData === undefined)
return

// start by removing the callback
const index = observedData.callbacks.indexOf(callback)
if (index > -1)
observedData.callbacks.splice(index, 1)

if (observedData.callbacks.length === 0) {
// stop observing this element because there are no
// callbacks registered for it anymore
observedElements.delete(elementToObserve)

if (observedElements.size === 0) {
// stop the internal loop once no elements are observed anymore
cancelAnimationFrame(rafId)
}
}
}
}

// ========================================================================
// module internals

function runLoop() {
const changedRectsData: Array<ObservedData> = []

// process all DOM reads first (getBoundingClientRect)
observedElements.forEach((data, element) => {
const newRect = element.getBoundingClientRect()

// gather all the data for elements whose rects have changed
if (!rectEquals(data.rect, newRect)) {
data.rect = newRect
changedRectsData.push(data)
}
})

// group DOM writes here after the DOM reads (getBoundingClientRect)
// as DOM writes will most likely happen with the callbacks
changedRectsData.forEach((data) => {
data.callbacks.forEach(callback => callback(data.rect))
})

rafId = requestAnimationFrame(runLoop)
}
// ========================================================================

/**
* Returns whether 2 rects are equal in values
*/
function rectEquals(rect1: DOMRect, rect2: DOMRect) {
return (
rect1.width === rect2.width
&& rect1.height === rect2.height
&& rect1.top === rect2.top
&& rect1.right === rect2.right
&& rect1.bottom === rect2.bottom
&& rect1.left === rect2.left
)
}

export { observeElementRect }
export type { Measurable }
14 changes: 14 additions & 0 deletions packages/core/utils/src/primitive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function composeEventHandlers<E>(
originalEventHandler?: (event: E) => void,
ourEventHandler?: (event: E) => void,
{ checkForDefaultPrevented = true } = {},
) {
return function handleEvent(event: E) {
originalEventHandler?.(event)

if (checkForDefaultPrevented === false || !((event as unknown) as Event).defaultPrevented)
return ourEventHandler?.(event)
}
}

export { composeEventHandlers }
8 changes: 8 additions & 0 deletions packages/core/utils/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ref } from 'vue'

function exampleFn() {
const hello = ref('hello')
return 'example'
}

export { exampleFn }
9 changes: 9 additions & 0 deletions packages/core/utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./../../../tsconfig.json",
"compilerOptions": {
"baseUrl": "."
},
"include": [
"./"
]
}
17 changes: 17 additions & 0 deletions packages/core/utils/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Options } from 'tsup'

import pkg from './package.json'

const external = [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
]

export default <Options>{
entryPoints: ['src/index.ts'],
outDir: 'dist',
target: 'node16',
format: ['esm', 'cjs'],
dts: true,
external,
}
1 change: 1 addition & 0 deletions packages/example-package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"scripts": {
"clean": "rm -rf dist",
"build": "vite build --mode production",
"dev": "vite build --mode production --watch",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
Expand Down
Loading

0 comments on commit f48ed78

Please sign in to comment.