Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: utils package #86

Merged
merged 3 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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