From 190e4dd074870f768a4bdbdd71cb8b8fb921f282 Mon Sep 17 00:00:00 2001 From: Antonio Stoilkov Date: Thu, 23 Nov 2023 17:05:33 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20initial=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.ts | 10 ++++++ package.json | 26 ++++++++++++--- readme.md | 55 +++++++++++++++++++++++++----- src/getBorderBox.ts | 3 ++ src/getContentBox.ts | 22 ++++++++++++ src/getElementBox.ts | 68 ++++++++++++++++++++++++++++++++++++++ src/getMarginBox.ts | 14 ++++++++ src/getPaddingBox.ts | 16 +++++++++ src/getWindowBox.ts | 3 ++ test.ts | 79 ++++++++++++++++++++++++++++++++++++++++++-- 10 files changed, 280 insertions(+), 16 deletions(-) create mode 100644 src/getBorderBox.ts create mode 100644 src/getContentBox.ts create mode 100644 src/getElementBox.ts create mode 100644 src/getMarginBox.ts create mode 100644 src/getPaddingBox.ts create mode 100644 src/getWindowBox.ts diff --git a/index.ts b/index.ts index e69de29..decce3d 100644 --- a/index.ts +++ b/index.ts @@ -0,0 +1,10 @@ +// core +export { default as getContentBox } from "./src/getContentBox"; +export { default as getPaddingBox } from "./src/getPaddingBox"; +export { default as getBorderBox } from "./src/getBorderBox"; +export { default as getMarginBox } from "./src/getMarginBox"; + +// extra +export { default as getWindowBox } from "./src/getWindowBox"; +export { default as getElementBox } from "./src/getElementBox"; +export type { ElementBoxOptions } from "./src/getElementBox"; diff --git a/package.json b/package.json index 218a9b8..cc74e8b 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name": "{{data.name}}", + "name": "element-box", "version": "0.1.0", - "description": "{{data.description}}", + "description": "Work with elements dimension and position more easily", "license": "MIT", - "repository": "astoilkov/{{data.name}}", + "repository": "astoilkov/element-box", "funding": "https://github.com/sponsors/astoilkov", "author": { "name": "Antonio Stoilkov", @@ -11,7 +11,22 @@ "url": "https://astoilkov.com" }, "keywords": [ - "{{data.keywords}}" + "Element", + "HTMLElement", + "DOM", + "getBoundingClientRect()", + "rect", + "offsetWidth", + "offsetHeight", + "border", + "padding", + "margin", + "width", + "height", + "top", + "left", + "bottom", + "right" ], "type": "module", "exports": { @@ -34,7 +49,8 @@ "src/*.d.ts" ], "devDependencies": { - "@vitest/coverage-c8": "^0.33.0", + "@vitest/coverage-v8": "^0.33.0", + "jsdom": "^22.1.0", "np": "^8.0.4", "prettier": "^3.0.3", "typescript": "^5.2.2", diff --git a/readme.md b/readme.md index d750182..f71ef9f 100644 --- a/readme.md +++ b/readme.md @@ -1,22 +1,61 @@ -# `{{data.name}}` +# element-box -> {{data.description}} +> Work with elements dimension and position more easily -[![Gzipped Size](https://img.shields.io/bundlephobia/minzip/{{data.name}})](https://bundlephobia.com/result?p={{data.name}}) -[![Build Status](https://img.shields.io/github/actions/workflow/status/astoilkov/{{data.name}}/main.yml?branch=main)](https://github.com/astoilkov/{{data.name}}/actions/workflows/main.yml) +[![Gzipped Size](https://img.shields.io/bundlephobia/minzip/element-box)](https://bundlephobia.com/result?p=element-box) +[![Build Status](https://img.shields.io/github/actions/workflow/status/astoilkov/element-box/main. +yml?branch=main)](https://github.com/astoilkov/element-box/actions/workflows/main.yml) ## Install ```bash -npm install {{data.name}} +npm install element-box ``` ## Why -## Usage +I work with elements' dimension and position rarely enough to forget the details but often enough to frustrate me that it takes away my flow/productivity. + +It's hard to recall what the difference between `clientWidth` and `offsetWidth` and does `element.getBoundingClientRect()` include the borders or not. + +This library provides a friendly API that let's you think about the topic more easily while adding functions that are otherwise hard to ## API -## Alternatives +### `getContentBox(element: HTMLElement): DOMRect` + +Element's dimensions without border, padding, and margin — `element.getBoundingClientRect() - border - padding`. + +### `getPaddingBox(element: HTMLElement): DOMRect` + +Element's dimensions with padding but without border and margin — `element.getBoundingClientRect() - border`. + +### `getBorderBox(element: HTMLElement): DOMRect` + +Equivalent to `element.getBoundingClientRect()`. -## Related +### `getMarginBox(element: HTMLElement): DOMRect` + +Element's dimensions with padding, border, and margin — `element.getBoundingClientRect() + margin`. + +### `getWindowBox(): DOMRect` + +Something like `window.getBoundingClientRect()` if it existed. If you are wondering, `document.documentElement.getBoundingClientRect()` won't probably do what you want. + +### `getElementBox(element: HTMLElement, options): DOMRect` + +Would be easier to show you examples: +```ts +// like that: +getElementBox(element, { + includePadding: true, + includeBorder: false, + includeMargin: false +}) + +// or like that: +getElementBox(element, 'content-box') +getElementBox(element, 'padding-box') +getElementBox(element, 'border-box') +getElementBox(element, 'margin-box') +``` diff --git a/src/getBorderBox.ts b/src/getBorderBox.ts new file mode 100644 index 0000000..43637ae --- /dev/null +++ b/src/getBorderBox.ts @@ -0,0 +1,3 @@ +export default function getBorderBox(element: Element): DOMRect { + return element.getBoundingClientRect(); +} diff --git a/src/getContentBox.ts b/src/getContentBox.ts new file mode 100644 index 0000000..94636d2 --- /dev/null +++ b/src/getContentBox.ts @@ -0,0 +1,22 @@ +export default function getContentBox(element: Element): DOMRect { + const clientRect = element.getBoundingClientRect(); + const computedStyle = getComputedStyle(element); + return new DOMRect( + clientRect.x + + Number.parseFloat(computedStyle.borderLeftWidth) + + Number.parseFloat(computedStyle.paddingLeft), + clientRect.y + + Number.parseFloat(computedStyle.borderTopWidth) + + Number.parseFloat(computedStyle.paddingTop), + clientRect.width - + Number.parseFloat(computedStyle.borderLeftWidth) - + Number.parseFloat(computedStyle.borderRightWidth) - + Number.parseFloat(computedStyle.paddingLeft) - + Number.parseFloat(computedStyle.paddingRight), + clientRect.height - + Number.parseFloat(computedStyle.borderTopWidth) - + Number.parseFloat(computedStyle.borderBottomWidth) - + Number.parseFloat(computedStyle.paddingTop) - + Number.parseFloat(computedStyle.paddingBottom), + ); +} diff --git a/src/getElementBox.ts b/src/getElementBox.ts new file mode 100644 index 0000000..00a2cb5 --- /dev/null +++ b/src/getElementBox.ts @@ -0,0 +1,68 @@ +import getMarginBox from "./getMarginBox"; +import getBorderBox from "./getBorderBox"; +import getPaddingBox from "./getPaddingBox"; +import getContentBox from "./getContentBox"; + +export type ElementBoxOptions = + | "content-box" + | { + includeMargin: false; + includeBorder: false; + includePadding: false; + } + | "padding-box" + | { + includeMargin: false; + includeBorder: false; + includePadding: true; + } + | "border-box" // (what getBoundingClientRect() returns) + | { + includeMargin: false; + includeBorder: true; + includePadding: true; + } + | "margin-box" + | { + includeMargin: true; + includeBorder: true; + includePadding: true; + }; + +export default function getElementBox( + element: Element, + options: ElementBoxOptions = "border-box", +) { + const boxType = + typeof options === "string" + ? options + : options.includeMargin === false && + options.includeBorder === false && + options.includePadding === false + ? "content-box" + : options.includeMargin === false && + options.includeBorder === false && + options.includePadding === true + ? "padding-box" + : options.includeMargin === false && + options.includeBorder === true && + options.includePadding === true + ? "border-box" + : options.includeMargin === true && + options.includeBorder === true && + options.includePadding === true + ? "margin-box" + : "border-box"; + + if (boxType === "content-box") { + return getContentBox(element); + } else if (boxType === "padding-box") { + return getPaddingBox(element); + } else if (boxType === "border-box") { + return getBorderBox(element); + } else if (boxType === "margin-box") { + return getMarginBox(element); + } + + return getBorderBox(element); +} diff --git a/src/getMarginBox.ts b/src/getMarginBox.ts new file mode 100644 index 0000000..de7d4c6 --- /dev/null +++ b/src/getMarginBox.ts @@ -0,0 +1,14 @@ +export default function getMarginBox(element: Element): DOMRect { + const clientRect = element.getBoundingClientRect(); + const computedStyle = getComputedStyle(element); + return new DOMRect( + clientRect.x - Number.parseFloat(computedStyle.marginLeft), + clientRect.y - Number.parseFloat(computedStyle.marginTop), + clientRect.width + + Number.parseFloat(computedStyle.marginLeft) + + Number.parseFloat(computedStyle.marginRight), + clientRect.height + + Number.parseFloat(computedStyle.marginTop) + + Number.parseFloat(computedStyle.marginBottom), + ); +} diff --git a/src/getPaddingBox.ts b/src/getPaddingBox.ts new file mode 100644 index 0000000..2a97744 --- /dev/null +++ b/src/getPaddingBox.ts @@ -0,0 +1,16 @@ +export default function getPaddingBox(element: Element): DOMRect { + const clientRect = element.getBoundingClientRect(); + const computedStyle = getComputedStyle(element); + return new DOMRect( + clientRect.x + Number.parseFloat(computedStyle.borderLeftWidth), + clientRect.y + Number.parseFloat(computedStyle.borderTopWidth), + + clientRect.width - + Number.parseFloat(computedStyle.borderLeftWidth) - + Number.parseFloat(computedStyle.borderRightWidth), + + clientRect.height - + Number.parseFloat(computedStyle.borderTopWidth) - + Number.parseFloat(computedStyle.borderBottomWidth), + ); +} diff --git a/src/getWindowBox.ts b/src/getWindowBox.ts new file mode 100644 index 0000000..0525a81 --- /dev/null +++ b/src/getWindowBox.ts @@ -0,0 +1,3 @@ +export default function getWindowBox(): DOMRect { + return new DOMRect(0, 0, window.innerWidth, window.innerHeight); +} diff --git a/test.ts b/test.ts index 6231077..80ccd77 100644 --- a/test.ts +++ b/test.ts @@ -1,7 +1,80 @@ import { describe, expect, test } from "vitest"; +import { + getBorderBox, + getContentBox, + getElementBox, + getMarginBox, + getPaddingBox, + getWindowBox, +} from "./index"; -describe("{{data.name}}", () => { - test("", () => { - expect(true).toBe(true); +describe("element-box", () => { + const div = document.createElement("div"); + + test("getContentBox()", () => { + expect(() => getContentBox(div)).not.toThrow(); + }); + + test("getPaddingBox()", () => { + expect(() => getPaddingBox(div)).not.toThrow(); + }); + + test("getBorderBox()", () => { + expect(() => getBorderBox(div)).not.toThrow(); + }); + + test("getMarginBox()", () => { + expect(() => getMarginBox(div)).not.toThrow(); + }); + + test("getElementBox(), content-box", () => { + expect(() => + getElementBox(div, { + includeMargin: false, + includeBorder: false, + includePadding: false, + }), + ).not.toThrow(); + }); + + test("getElementBox(), padding-box", () => { + expect(() => + getElementBox(div, { + includeMargin: false, + includeBorder: false, + includePadding: true, + }), + ).not.toThrow(); + }); + + test("getElementBox(), border-box", () => { + expect(() => + getElementBox(div, { + includeMargin: false, + includeBorder: true, + includePadding: true, + }), + ).not.toThrow(); + }); + + test("getElementBox(), margin-box", () => { + expect(() => + getElementBox(div, { + includeMargin: true, + includeBorder: true, + includePadding: true, + }), + ).not.toThrow(); + }); + + test("getElementBox(), string", () => { + expect(() => getElementBox(div, "border-box")).not.toThrow(); + expect(() => getElementBox(div, "content-box")).not.toThrow(); + expect(() => getElementBox(div, "padding-box")).not.toThrow(); + expect(() => getElementBox(div, "margin-box")).not.toThrow(); + }); + + test("getWindowBox()", () => { + expect(() => getWindowBox()).not.toThrow(); }); });