update to 2.0

I overreached on the getter / setters sharing the same names, and everything
_seemed_ to work OK at first, but then when I tried to import it...
This commit is contained in:
2025-09-06 21:46:50 -04:00
parent d11e4e963c
commit 35058fe201
16 changed files with 1801 additions and 676 deletions

View File

@@ -3,8 +3,8 @@
[*] [*]
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
indent_size = 4 indent_size = tab
indent_style = space indent_style = tab
insert_final_newline = true insert_final_newline = true
[*.md] [*.md]

View File

@@ -2,6 +2,7 @@
"cSpell.words": [ "cSpell.words": [
"btih", "btih",
"Hocevar", "Hocevar",
"itsericwoodward",
"ohtml", "ohtml",
"unlisten", "unlisten",
"unplugin" "unplugin"

1
.yarnrc.yml Normal file
View File

@@ -0,0 +1 @@
nodeLinker: node-modules

View File

@@ -11,6 +11,14 @@ When you don't need a whole framework (or JSX), but also don't want to spend all
3. Import it as an ESM module (`import $d from "./src/fluent-dom-esm.js"`). 3. Import it as an ESM module (`import $d from "./src/fluent-dom-esm.js"`).
4. Profit! 4. Profit!
### With JSR
This package can also be installed from [JSR](https://jsr.io/):
```
yarn add jsr:@itsericwoodward/fluent-dom-esm
```
### Occasionally Asked Questions ### Occasionally Asked Questions
- _How can I use this in Node?_ You can't, AFAIK. It requires access to the HTML `Document` object from the browser. - _How can I use this in Node?_ You can't, AFAIK. It requires access to the HTML `Document` object from the browser.

View File

@@ -1,25 +1,25 @@
// @license magnet:?xt=urn:btih:723febf9f6185544f57f0660a41489c7d6b4931b&dn=wtfpl.txt // @license magnet:?xt=urn:btih:723febf9f6185544f57f0660a41489c7d6b4931b&dn=wtfpl.txt
const version = "1.1.0";
const isFluentDomObject = (value) => !!value.fluentDom; const isFluentDomObject = (value) => !!value.fluentDom;
const isHTMLElement = (value) => !!value.nodeType; const isHTMLElement = (value) => !!value.nodeType;
const isNumber = (value) => typeof value === "number"; const isNumber = (value) => typeof value === "number";
const isString = (value) => typeof value === "string"; const isString = (value) => typeof value === "string";
/** /**
* fluent-dom-esm v1.1.0 * fluent-dom-esm v2.0.0
* *
* Fluent DOM Manipulation, adapted to ESM and cranked up to v1.1(.0). * Fluent DOM Manipulation, adapted to ESM and cranked up to v2.0(.0).
* *
* https://git.itsericwoodward.com/eric/fluent-dom-esm * https://git.itsericwoodward.com/eric/fluent-dom-esm
* *
* v1.1.0 Copyright (c) 2025 Eric Woodward * v2.0.0 Copyright (c) 2025 Eric Woodward
* Original copyright (c) 2009 Tommy Montgomery (https://glacius.tmont.com/articles/fluent-dom-manipulation-in-javascript) * Original copyright (c) 2009 Tommy Montgomery (https://glacius.tmont.com/articles/fluent-dom-manipulation-in-javascript)
* *
* Released under the WTFPL (Do What the Fuck You Want to Public License) * Released under the WTFPL (Do What the Fuck You Want to Public License)
* *
* @author Eric Woodward (v1.1.0 update) * @author Eric Woodward (v2.0.0 update)
* @author Tommy Montgomery (original) * @author Tommy Montgomery (original)
* @license http://sam.zoy.org/wtfpl/ * @license http://sam.zoy.org/wtfpl/
*/ */
const APP_VERSION = "2.0.0";
const fluentDomEsm = (function() { const fluentDomEsm = (function() {
const FluentDom = function(node) { const FluentDom = function(node) {
return new FluentDomInternal(node); return new FluentDomInternal(node);
@@ -31,12 +31,11 @@ const fluentDomEsm = (function() {
}; };
const FluentDomInternal = function(node) { const FluentDomInternal = function(node) {
let root = node || null; let root = node || null;
this.fluentDom = version; this.fluentDom = APP_VERSION;
this.a = this.attr = function(name, value) { this.a = this.attr = function(name, value) {
if (!root || value && !root.setAttribute) { if (!root || typeof name === "undefined" || typeof value === "undefined") {
throw new Error("Cannot set an attribute on a non-element"); throw new Error("Cannot set an attribute on a non-element");
} }
if (!value) return root.getAttribute(name);
root.setAttribute(name, value); root.setAttribute(name, value);
return this; return this;
}; };
@@ -76,8 +75,8 @@ const fluentDomEsm = (function() {
root = null; root = null;
return this; return this;
}; };
this.h = this.href = function(link) { this.h = this.href = function(url) {
return this.attr("href", link); return this.attr("href", url);
}; };
this.html = function(content) { this.html = function(content) {
if (!root) { if (!root) {
@@ -85,7 +84,6 @@ const fluentDomEsm = (function() {
"Cannot get or set innerHTML for a non-element" "Cannot get or set innerHTML for a non-element"
); );
} }
if (!content) return root.innerHTML;
root.innerHTML = content; root.innerHTML = content;
return this; return this;
}; };
@@ -99,26 +97,14 @@ const fluentDomEsm = (function() {
root.addEventListener(...props); root.addEventListener(...props);
return this; return this;
}; };
this.ohtml = function(content) { function styleFunction(prop, value) {
if (!root) {
throw new Error(
"Cannot get or set outerHTML for a non-element"
);
}
if (!content) return root.outerHTML;
root.outerHTML = content;
return this;
};
this.s = this.style = function(prop, value) {
if (!root) { if (!root) {
throw new Error("Cannot get or set style for a non-element"); throw new Error("Cannot get or set style for a non-element");
} }
if (typeof prop === "undefined") return root.style; if (typeof prop === "string" && typeof value !== "undefined") {
if (typeof value !== "undefined") {
root.style[prop] = value; root.style[prop] = value;
return this; return this;
} }
if (typeof prop === "string") return root.style[prop];
if (typeof prop !== "object") { if (typeof prop !== "object") {
throw new Error( throw new Error(
`Invalid argument: "prop" must be string or object (found ${typeof prop})` `Invalid argument: "prop" must be string or object (found ${typeof prop})`
@@ -128,23 +114,23 @@ const fluentDomEsm = (function() {
root.style[key] = prop[key]; root.style[key] = prop[key];
}); });
return this; return this;
}; }
this.s = this.style = styleFunction;
this.t = this.text = function(text) { this.t = this.text = function(text) {
if (!root) { if (!root) {
throw new Error( throw new Error(
"Cannot get or set innerText for a non-element" "Cannot get or set innerText for a non-element"
); );
} }
if (typeof text === "undefined") return root.innerText;
return this.append(text); return this.append(text);
}; };
this.title = function(value) { this.title = function(title) {
if (!root) { if (!root) {
throw new Error( throw new Error(
"Cannot get or set outerHTML for a non-element" "Cannot get or set outerHTML for a non-element"
); );
} }
return this.attr("title", value); return this.attr("title", title);
}; };
this.toDom = function() { this.toDom = function() {
return root; return root;

26
dist/fluent-dom-esm.types.d.ts vendored Normal file
View File

@@ -0,0 +1,26 @@
export interface FluentDomObject {
fluentDom: string;
a: (name: string, value: string) => FluentDomObject;
app: (obj: FluentDomObject | HTMLElement | string) => FluentDomObject;
append: (obj: FluentDomObject | HTMLElement | string) => FluentDomObject;
attr: (name: string, value: string) => FluentDomObject;
c: (tagName: string) => FluentDomObject;
create: (tagName: string) => FluentDomObject;
className: (className: string) => FluentDomObject;
clear: () => FluentDomObject;
cls: (className: string) => FluentDomObject;
clr: () => FluentDomObject;
h: (url: string) => FluentDomObject;
href: (url: string) => FluentDomObject;
html: (content: string) => FluentDomObject;
id: (id: string) => FluentDomObject;
l: (type: keyof HTMLElementEventMap, listener: () => {}, optionsOrUseCapture?: boolean | object) => FluentDomObject;
listen: (type: keyof HTMLElementEventMap, listener: () => {}, optionsOrUseCapture?: boolean | object) => FluentDomObject;
s: ((prop: CSSStyleDeclaration) => FluentDomObject) | ((prop: string, value: string) => FluentDomObject);
style: ((prop: CSSStyleDeclaration) => FluentDomObject) | ((prop: string, value: string) => FluentDomObject);
t: (text: string) => FluentDomObject;
text: (text: string) => FluentDomObject;
title: (title: string) => FluentDomObject;
toDom: () => HTMLElement | null;
unlisten: (type: keyof HTMLElementEventMap, listener: () => {}, optionsOrUseCapture?: boolean | object) => FluentDomObject;
}

View File

@@ -1,35 +0,0 @@
declare const _default: {
"name": "fluent-dom-esm",
"version": "1.1.0",
"description": "",
"license": "WTFPL",
"exports": {
".": {
"import": "./dist/fluent-dom-esm.js"
}
},
"type": "module",
"files": [
"dist"
],
"module": "./dist/fluent-dom-esm.js",
"scripts": {
"add-license": "cat ./src/license-begin.txt ./dist/fluent-dom-esm.js ./src/license-end.txt > .temp && mv .temp ./dist/fluent-dom-esm.js",
"build": "tsc && vite build && yarn add-license",
"ci": "yarn build",
"dev": "vite",
"preview": "vite preview",
"prepublishOnly": "yarn build",
"postpublish": "git push && git push --tags",
"test": "echo 'No tests yet!'"
},
"devDependencies": {
"prettier": "^3.6.2",
"typescript": "^5.9.2",
"unplugin-dts": "1.0.0-beta.6",
"vite": "^7.1.4"
}
}
;
export default _default;

View File

@@ -1,27 +0,0 @@
export interface FluentDomObject {
fluentDom: string;
a: (name: string, value?: string) => FluentDomObject | string | null;
app: (obj: FluentDomObject | HTMLElement | string) => FluentDomObject;
append: (obj: FluentDomObject | HTMLElement | string) => FluentDomObject;
attr: (name: string, value?: string) => FluentDomObject | string | null;
c: (tagName: string) => FluentDomObject;
create: (tagName: string) => FluentDomObject;
className: (className?: string) => FluentDomObject | string | null;
clear: () => FluentDomObject;
cls: (className?: string) => FluentDomObject | string | null;
clr: () => FluentDomObject;
h: (url?: string) => FluentDomObject | string | null;
href: (url?: string) => FluentDomObject | string | null;
html: (content?: string) => string | FluentDomObject;
id: (id?: string) => FluentDomObject | string | null;
l: (type: keyof HTMLElementEventMap, listener: () => {}, optionsOrUseCapture?: boolean | object) => FluentDomObject;
listen: (type: keyof HTMLElementEventMap, listener: () => {}, optionsOrUseCapture?: boolean | object) => FluentDomObject;
ohtml: (content?: string) => string | FluentDomObject;
s: (prop?: CSSStyleDeclaration | string, value?: string) => FluentDomObject | CSSStyleDeclaration | string | undefined;
style: (prop?: CSSStyleDeclaration | string, value?: string) => FluentDomObject | CSSStyleDeclaration | string | undefined;
t: (text?: string) => FluentDomObject | string;
text: (text?: string) => FluentDomObject | string;
title: (title?: string) => FluentDomObject | string | null;
toDom: () => HTMLElement | null;
unlisten: (type: keyof HTMLElementEventMap, listener: () => {}, optionsOrUseCapture?: boolean | object) => FluentDomObject;
}

View File

@@ -134,7 +134,6 @@
.c("button") .c("button")
.cls("themeToggle-button") .cls("themeToggle-button")
.id("themeToggle-button") .id("themeToggle-button")
// .t("☀︎ / ☾")
.listen("click", () => { .listen("click", () => {
document.body.classList.toggle("invertedTheme"); document.body.classList.toggle("invertedTheme");
}) })

View File

@@ -1,5 +1,5 @@
{ {
"name": "@itsericwoodward/fluent-dom-esm", "name": "@itsericwoodward/fluent-dom-esm",
"version": "1.1.0", "version": "2.0.0",
"exports": "./src/fluent-dom-esm.ts" "exports": "./src/fluent-dom-esm.ts"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "fluent-dom-esm", "name": "fluent-dom-esm",
"version": "1.1.0", "version": "2.0.0",
"description": "", "description": "",
"license": "WTFPL", "license": "WTFPL",
"exports": { "exports": {
@@ -28,5 +28,6 @@
"typescript": "^5.9.2", "typescript": "^5.9.2",
"unplugin-dts": "1.0.0-beta.6", "unplugin-dts": "1.0.0-beta.6",
"vite": "^7.1.4" "vite": "^7.1.4"
} },
"packageManager": "yarn@4.9.4"
} }

View File

@@ -1,23 +1,22 @@
/** /**
* fluent-dom-esm v1.1.0 * fluent-dom-esm v2.0.0
* *
* Fluent DOM Manipulation, adapted to ESM and cranked up to v1.1(.0). * Fluent DOM Manipulation, adapted to ESM and cranked up to v2.0(.0).
* *
* https://git.itsericwoodward.com/eric/fluent-dom-esm * https://git.itsericwoodward.com/eric/fluent-dom-esm
* *
* v1.1.0 Copyright (c) 2025 Eric Woodward * v2.0.0 Copyright (c) 2025 Eric Woodward
* Original copyright (c) 2009 Tommy Montgomery (https://glacius.tmont.com/articles/fluent-dom-manipulation-in-javascript) * Original copyright (c) 2009 Tommy Montgomery (https://glacius.tmont.com/articles/fluent-dom-manipulation-in-javascript)
* *
* Released under the WTFPL (Do What the Fuck You Want to Public License) * Released under the WTFPL (Do What the Fuck You Want to Public License)
* *
* @author Eric Woodward (v1.1.0 update) * @author Eric Woodward (v2.0.0 update)
* @author Tommy Montgomery (original) * @author Tommy Montgomery (original)
* @license http://sam.zoy.org/wtfpl/ * @license http://sam.zoy.org/wtfpl/
*/ */
import type { FluentDomObject } from "./fluent-dom-esm.types"; import type { FluentDomObject } from "./fluent-dom-esm.types";
import { version } from "../package.json" with { type: "json" };
import { import {
isFluentDomObject, isFluentDomObject,
isHTMLElement, isHTMLElement,
@@ -25,6 +24,8 @@ import {
isString, isString,
} from "./fluent-dom-esm.type-guards"; } from "./fluent-dom-esm.type-guards";
const APP_VERSION = "2.0.0";
/** /**
* IIFE that creates the FluentDomObject as default export * IIFE that creates the FluentDomObject as default export
*/ */
@@ -55,18 +56,20 @@ export default (function () {
let root = node || null; let root = node || null;
// adds several new features // adds several new features
this.fluentDom = version; this.fluentDom = APP_VERSION;
/** /**
* Gets or sets the named attribute on the wrapped HTMLElement * Sets the named attribute on the wrapped HTMLElement
*/ */
this.a = this.attr = function (name, value) { this.a = this.attr = function (name, value) {
if (!root || (value && !root.setAttribute)) { if (
!root ||
typeof name === "undefined" ||
typeof value === "undefined"
) {
throw new Error("Cannot set an attribute on a non-element"); throw new Error("Cannot set an attribute on a non-element");
} }
if (!value) return root.getAttribute(name);
root.setAttribute(name, value); root.setAttribute(name, value);
return this; return this;
}; };
@@ -111,10 +114,10 @@ export default (function () {
}; };
/** /**
* Gets or sets the "class" attribute on the wrapped HTMLElement * Sets the "class" attribute on the wrapped HTMLElement
*/ */
this.className = this.cls = function (value) { this.className = this.cls = function (value) {
return this.attr("class", value); return this.attr("class", value as string);
}; };
/** /**
@@ -126,30 +129,28 @@ export default (function () {
}; };
/** /**
* Gets or sets the "href" attribute on the wrapped HTMLElement * Sets the "href" attribute on the wrapped HTMLElement
*/ */
this.h = this.href = function (link) { this.h = this.href = function (url) {
return this.attr("href", link); return this.attr("href", url);
}; };
/** /**
* Gets or sets the wrapped HTMLElement's "innerHTML" property * Sets the wrapped HTMLElement's "innerHTML" property
*/ */
this.html = function (content) { this.html = function (content: string) {
if (!root) { if (!root) {
throw new Error( throw new Error(
"Cannot get or set innerHTML for a non-element", "Cannot get or set innerHTML for a non-element",
); );
} }
if (!content) return root.innerHTML;
root.innerHTML = content; root.innerHTML = content;
return this; return this;
}; };
/** /**
* Gets or sets the "id" attribute on the wrapped HTMLElement * Sets the "id" attribute on the wrapped HTMLElement
*/ */
this.id = function (value) { this.id = function (value) {
return this.attr("id", value); return this.attr("id", value);
@@ -167,50 +168,47 @@ export default (function () {
return this; return this;
}; };
/**
* Gets or sets the wrapped HTMLElement's "outerHTML" property
*/
this.ohtml = function (content) {
if (!root) {
throw new Error(
"Cannot get or set outerHTML for a non-element",
);
}
if (!content) return root.outerHTML;
root.outerHTML = content;
return this;
};
/** /**
* Gets or sets a one or more style attributes on the wrapped HTMLElement * Gets or sets a one or more style attributes on the wrapped HTMLElement
*/ */
this.s = this.style = function (prop, value) { function styleFunction(
this: FluentDomObject,
prop: CSSStyleDeclaration,
): FluentDomObject;
function styleFunction(
this: FluentDomObject,
prop: string,
value: string,
): FluentDomObject;
function styleFunction(
this: FluentDomObject,
prop: string | CSSStyleDeclaration,
value?: string,
): FluentDomObject {
if (!root) { if (!root) {
throw new Error("Cannot get or set style for a non-element"); throw new Error("Cannot get or set style for a non-element");
} }
if (typeof prop === "undefined") return root.style; if (typeof prop === "string" && typeof value !== "undefined") {
if (typeof value !== "undefined") {
root.style[prop as any] = value; root.style[prop as any] = value;
return this; return this;
} }
if (typeof prop === "string") return root.style[prop as any];
if (typeof prop !== "object") { if (typeof prop !== "object") {
throw new Error( throw new Error(
`Invalid argument: "prop" must be string or object (found ${typeof prop})`, `Invalid argument: "prop" must be string or object (found ${typeof prop})`,
); );
} }
Object.keys(prop).forEach((key) => { Object.keys(prop as CSSStyleDeclaration).forEach((key) => {
(root as HTMLElement).style[key as any] = prop[key as any]; (root as HTMLElement).style[key as any] = prop[
key as any
] as string;
}); });
return this; return this;
}; }
this.s = this.style = styleFunction;
/** /**
* Gets or sets the wrapped HTMLElement's "innerText" property * Gets or sets the wrapped HTMLElement's "innerText" property
@@ -222,22 +220,20 @@ export default (function () {
); );
} }
if (typeof text === "undefined") return root.innerText;
return this.append(text); return this.append(text);
}; };
/** /**
* Gets or sets the "title" attribute on the wrapped HTMLElement * Gets or sets the "title" attribute on the wrapped HTMLElement
*/ */
this.title = function (value) { this.title = function (title) {
if (!root) { if (!root) {
throw new Error( throw new Error(
"Cannot get or set outerHTML for a non-element", "Cannot get or set outerHTML for a non-element",
); );
} }
return this.attr("title", value); return this.attr("title", title);
}; };
/** /**

View File

@@ -1,20 +1,20 @@
export interface FluentDomObject { export interface FluentDomObject {
fluentDom: string; fluentDom: string;
a: (name: string, value?: string) => FluentDomObject | string | null; a: (name: string, value: string) => FluentDomObject;
app: (obj: FluentDomObject | HTMLElement | string) => FluentDomObject; app: (obj: FluentDomObject | HTMLElement | string) => FluentDomObject;
append: (obj: FluentDomObject | HTMLElement | string) => FluentDomObject; append: (obj: FluentDomObject | HTMLElement | string) => FluentDomObject;
attr: (name: string, value?: string) => FluentDomObject | string | null; attr: (name: string, value: string) => FluentDomObject;
c: (tagName: string) => FluentDomObject; c: (tagName: string) => FluentDomObject;
create: (tagName: string) => FluentDomObject; create: (tagName: string) => FluentDomObject;
className: (className?: string) => FluentDomObject | string | null; className: (className: string) => FluentDomObject;
clear: () => FluentDomObject; clear: () => FluentDomObject;
cls: (className?: string) => FluentDomObject | string | null; cls: (className: string) => FluentDomObject;
clr: () => FluentDomObject; clr: () => FluentDomObject;
h: (url?: string) => FluentDomObject | string | null; h: (url: string) => FluentDomObject;
href: (url?: string) => FluentDomObject | string | null; href: (url: string) => FluentDomObject;
html: (content?: string) => string | FluentDomObject; html: (content: string) => FluentDomObject;
id: (id?: string) => FluentDomObject | string | null; id: (id: string) => FluentDomObject;
l: ( l: (
type: keyof HTMLElementEventMap, type: keyof HTMLElementEventMap,
listener: () => {}, listener: () => {},
@@ -25,18 +25,16 @@ export interface FluentDomObject {
listener: () => {}, listener: () => {},
optionsOrUseCapture?: boolean | object, optionsOrUseCapture?: boolean | object,
) => FluentDomObject; ) => FluentDomObject;
ohtml: (content?: string) => string | FluentDomObject; s:
s: ( | ((prop: CSSStyleDeclaration) => FluentDomObject)
prop?: CSSStyleDeclaration | string, | ((prop: string, value: string) => FluentDomObject);
value?: string, style:
) => FluentDomObject | CSSStyleDeclaration | string | undefined; | ((prop: CSSStyleDeclaration) => FluentDomObject)
style: ( | ((prop: string, value: string) => FluentDomObject);
prop?: CSSStyleDeclaration | string,
value?: string, t: (text: string) => FluentDomObject;
) => FluentDomObject | CSSStyleDeclaration | string | undefined; text: (text: string) => FluentDomObject;
t: (text?: string) => FluentDomObject | string; title: (title: string) => FluentDomObject;
text: (text?: string) => FluentDomObject | string;
title: (title?: string) => FluentDomObject | string | null;
toDom: () => HTMLElement | null; toDom: () => HTMLElement | null;
unlisten: ( unlisten: (
type: keyof HTMLElementEventMap, type: keyof HTMLElementEventMap,

2191
yarn.lock

File diff suppressed because it is too large Load Diff