@zyrab/domo 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,36 +1,39 @@
1
- {
2
- "name": "@zyrab/domo",
3
- "version": "1.1.1",
4
- "description": "Minimalist DOM builder and chaining-friendly micro-framework with router support.",
5
- "main": "index.js",
6
- "type": "module",
7
- "exports": {
8
- ".": "./index.js"
9
- },
10
- "files": [
11
- "index.js",
12
- "src/"
13
- ],
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/zyrab/domo.git"
17
- },
18
- "keywords": [
19
- "dom",
20
- "builder",
21
- "declarative",
22
- "chaining",
23
- "vanilla-js",
24
- "router",
25
- "components",
26
- "event-delegation",
27
- "zyrab"
28
- ],
29
- "author": "Zyrab",
30
- "license": "MIT",
31
- "bugs": {
32
- "url": "https://github.com/zyrab/domo/issues"
33
- },
34
- "homepage": "https://github.com/zyrab/domo#readme",
35
- "logo": "https://github.com/zyrab/domo/blob/main/assets/logo.png"
36
- }
1
+ {
2
+ "name": "@zyrab/domo",
3
+ "version": "1.2.1",
4
+ "description": "Minimalist DOM builder and chaining-friendly micro-framework with router support.",
5
+ "main": "./src/domo.js",
6
+ "type": "module",
7
+ "exports": {
8
+ "import": "./src/domo.js",
9
+ "default": "./src/domo.js"
10
+ },
11
+ "author": "Zyrab",
12
+ "license": "MIT",
13
+ "files": [
14
+ "src/"
15
+ ],
16
+ "keywords": [
17
+ "dom",
18
+ "builder",
19
+ "declarative",
20
+ "chaining",
21
+ "vanilla-js",
22
+ "router",
23
+ "components",
24
+ "event-delegation",
25
+ "zyrab"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/zyrab/domo.git"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/zyrab/domo/issues"
33
+ },
34
+ "homepage": "https://github.com/zyrab/domo#readme",
35
+ "logo": "https://github.com/zyrab/domo/blob/main/assets/logo.png",
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }
package/src/base.js ADDED
@@ -0,0 +1,80 @@
1
+ // Base.js
2
+ const isServer = typeof document === "undefined";
3
+
4
+ /**
5
+ * @class Base
6
+ * @description The foundational class for creating and managing DOM or virtual elements.
7
+ * Handles the basic setup and provides access to the underlying element.
8
+ */
9
+ class Base {
10
+ /**
11
+ * @private
12
+ * @property {boolean} _virtual - True if running in a server-side (virtual DOM) environment.
13
+ */
14
+ _virtual;
15
+
16
+ /**
17
+ * @property {HTMLElement|object} element - The actual DOM element or its virtual representation.
18
+ */
19
+ element;
20
+
21
+ /**
22
+ * Creates an instance of Base.
23
+ * @param {string} [el="div"] - The HTML tag name for the element to create (e.g., 'div', 'span', 'button').
24
+ */
25
+ constructor(el = "div") {
26
+ this._virtual = isServer;
27
+ this.element = this._virtual ? this._vel(el) : this.el(el);
28
+ }
29
+
30
+ /**
31
+ * Creates a real HTML DOM element.
32
+ * @param {string} el - The HTML tag name (e.g., 'div', 'p').
33
+ * @returns {HTMLElement} The created DOM element.
34
+ * @example
35
+ * // Internally used:
36
+ * // this.el('div'); // Creates a <div> element
37
+ */
38
+ el(el) {
39
+ return document.createElement(String(el || "div").toLowerCase());
40
+ }
41
+
42
+ /**
43
+ * Creates a simple JavaScript object representing a virtual element for server-side rendering.
44
+ * @private
45
+ * @param {string} el - The HTML tag name.
46
+ * @returns {object} A plain object structure representing the virtual element.
47
+ * @example
48
+ * // Internally used for server-side:
49
+ * // this._vel('div'); // Returns { _tag: 'div', _cls: [], ... }
50
+ */
51
+ _vel(el) {
52
+ return {
53
+ _tag: el,
54
+ _cls: [],
55
+ _data: {},
56
+ _attr: {},
57
+ _css: {},
58
+ _child: [],
59
+ _events: [],
60
+ };
61
+ }
62
+
63
+ /**
64
+ * In browser context, passes the actual DOM element to the callback.
65
+ *
66
+ * @param {function(HTMLElement|string): void} callback -
67
+ * Receives the actual DOM element (in browser).
68
+ *
69
+ * @returns {this} The current Domo instance for chaining.
70
+
71
+ **/
72
+ ref(callback) {
73
+ if (this._virtual) return this;
74
+ if (typeof callback === "function") callback(this.element);
75
+
76
+ return this;
77
+ }
78
+ }
79
+
80
+ export default Base;
package/src/builder.js ADDED
@@ -0,0 +1,43 @@
1
+ class Builder {
2
+ /**
3
+ * Builds and returns the final DOM element or its HTML string representation.
4
+ * If running in a browser environment, it returns the actual HTML element.
5
+ * If running in a server-side (virtual DOM) environment, it generates an HTML string.
6
+ * @returns {HTMLElement|string} The constructed DOM element or an HTML string.
7
+ * @example
8
+ * // In a browser:
9
+ * const myDiv = Domo('div').txt('Hello').build();
10
+ * document.body.appendChild(myDiv); // myDiv is an actual <div> element
11
+ *
12
+ * @example
13
+ * // In a server-side environment:
14
+ * // const htmlString = Domo('p').txt('Server-rendered content').build();
15
+ * // console.log(htmlString); // Outputs: "<p>Server-rendered content</p>"
16
+ */
17
+ build() {
18
+ if (!this._virtual) return this.element; // If not virtual, return the actual DOM element
19
+
20
+ const tag = this.element._tag;
21
+ const cls = this.element._cls.join(" ");
22
+ const toKebab = (s) => s.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
23
+ const css = Object.entries(this.element._css)
24
+ .map(([k, v]) => `${toKebab(k)}:${v}`)
25
+ .join(";");
26
+
27
+ const attrs = Object.entries(this.element._attr).map(([k, v]) =>
28
+ v === true ? k : `${k}="${String(v).replace(/"/g, "&quot;")}"`
29
+ );
30
+
31
+ const data = Object.entries(this.element._data).map(([k, v]) => `data-${k}="${String(v).replace(/"/g, "&quot;")}"`);
32
+
33
+ if (cls) attrs.push(`class="${cls}"`);
34
+ if (css) attrs.push(`style="${css}"`);
35
+
36
+ const attrStr = attrs.concat(data).join(" ");
37
+ const children = this.element._child.map((c) => (typeof c === "string" ? c : c.build?.() || "")).join("");
38
+
39
+ return `<${tag}${attrStr ? " " + attrStr : ""}>${children}</${tag}>`;
40
+ }
41
+ }
42
+
43
+ export default Builder;
@@ -0,0 +1,188 @@
1
+ import { DomoClass } from "./domo.js";
2
+
3
+ class Children {
4
+ /**
5
+ * Internally handles various types of input to ensure they become valid DOM Nodes
6
+ * or a string representation for virtual DOM.
7
+ * @private
8
+ * @param {*} element - The input to process (Domo instance, Node, string, number).
9
+ * @returns {Node|string|Domo} A DOM Node (HTMLElement, TextNode, DocumentFragment),
10
+ * a string for virtual children, or a Domo instance for virtual children.
11
+ */
12
+ _handleElementInstance(element) {
13
+ // Check if Domo class exists (it's imported in main.domo.js for final class)
14
+ // To avoid circular dependency in JSDoc parsing, we'll check runtime type.
15
+ // For JSDoc type checking, we assume Domo is available in the context of main.domo.js.
16
+ // @ts-ignore - Ignore potential 'Domo is not defined' for JSDoc tool
17
+ if (element instanceof DomoClass) return element.build();
18
+
19
+ if (element instanceof DocumentFragment) return element;
20
+ if (element instanceof Node) return element;
21
+ if (typeof element === "string" || typeof element === "number") {
22
+ return document.createTextNode(element); // Convert strings/numbers to text nodes
23
+ }
24
+ return document.createTextNode(`⚠ Invalid child: ${String(element)}`); // Fallback for invalid input
25
+ }
26
+
27
+ /**
28
+ * Appends one or more children to the element.
29
+ * This method accepts a wide range of child types, including other Domo instances,
30
+ * native DOM nodes, DocumentFragments, and simple text/numbers, even in nested arrays.
31
+ * @param {(Node|string|number|Domo|DocumentFragment|Array<any>)[]} [children=[]] - An array of children to append. Can contain mixed types and nested arrays.
32
+ * @returns {this} The current Domo instance for chaining.
33
+ * @example
34
+ * Domo('div').child([
35
+ * Domo('span').txt('Hello'),
36
+ * document.createElement('br'),
37
+ * 'World!',
38
+ * [Domo('em').txt('Nested')]
39
+ * ]);
40
+ */
41
+ child(children = []) {
42
+ const flattenedChildren = children.flat(); // Handle nested arrays of children
43
+ flattenedChildren.forEach((child) => {
44
+ if (this._virtual) {
45
+ // In virtual mode, store Domo instances or their string representation
46
+ // @ts-ignore - Ignore potential 'Domo is not defined' for JSDoc tool
47
+ this.element._child.push(child instanceof DomoClass ? child : String(child));
48
+ } else this.element.appendChild(this._handleElementInstance(child));
49
+ });
50
+ return this;
51
+ }
52
+
53
+ /**
54
+ * Appends one or more children to the element.
55
+ * This is an alias for the `child` method, offering a more common naming convention.
56
+ * @param {(Node|string|number|Domo|DocumentFragment|Array<any>)[]} [children=[]] - An array of children to append. Can contain mixed types and nested arrays.
57
+ * @returns {this} The current Domo instance for chaining.
58
+ * @example
59
+ * Domo('ul').append([
60
+ * Domo('li').txt('Item 1'),
61
+ * Domo('li').txt('Item 2')
62
+ * ]);
63
+ */
64
+ append(children = []) {
65
+ return this.child(children);
66
+ }
67
+
68
+ /**
69
+ * Appends the current Domo element to a specified target element in the DOM.
70
+ * @param {HTMLElement|Domo|DocumentFragment} target - The element to which the current Domo element will be appended. Can be a native HTMLElement, another Domo instance, or a DocumentFragment.
71
+ * @returns {this} The current Domo instance for chaining.
72
+ * @example
73
+ * const myDiv = Domo('div').txt('My Content');
74
+ * myDiv.appendTo(document.body); // Appends the div to the body
75
+ *
76
+ * const container = Domo('main');
77
+ * myDiv.appendTo(container); // Appends to another Domo instance
78
+ */
79
+ appendTo(target) {
80
+ // @ts-ignore - Ignore potential 'Domo is not defined' for JSDoc tool
81
+ const targetNode = target instanceof DomoClass ? target.element : target; // Get native element if Domo instance
82
+ if (targetNode instanceof Node) {
83
+ // Ensure it's a DOM Node
84
+ targetNode.appendChild(this.element);
85
+ }
86
+ return this;
87
+ }
88
+
89
+ /**
90
+ * Appends the current Domo element to a specified target parent.
91
+ * This is an alias for the `appendTo` method.
92
+ * @param {HTMLElement|Domo|DocumentFragment} target - The parent element to which the current Domo element will be appended.
93
+ * @returns {this} The current Domo instance for chaining.
94
+ * @example
95
+ * Domo('img').attr({ src: 'image.jpg' }).parent(document.getElementById('image-gallery'));
96
+ */
97
+ parent(target) {
98
+ return this.appendTo(target);
99
+ }
100
+
101
+ /**
102
+ * Removes all child nodes from the element, effectively emptying its content.
103
+ * @returns {this} The current Domo instance for chaining.
104
+ * @example
105
+ * Domo('ul').child([Domo('li'), Domo('li')]).clear(); // Removes both <li>s
106
+ */
107
+ clear() {
108
+ this.element.replaceChildren();
109
+ return this;
110
+ }
111
+
112
+ /**
113
+ * Replaces an existing child element with a new one, or replaces the Domo element itself if it's the target.
114
+ * @param {Node} child - The old child element (or the Domo element itself) to be replaced.
115
+ * @param {(Node|string|number|Domo|DocumentFragment|Array<any>)} newChild - The new element (or content) to insert. Can be a Domo instance, native Node, string, or number.
116
+ * @returns {this} The current Domo instance for chaining (or the new Domo instance if the original element was replaced).
117
+ * @example
118
+ * const oldParagraph = document.getElementById('old-p');
119
+ * const newDiv = Domo('div').txt('New Content');
120
+ * Domo('body').replace(oldParagraph, newDiv);
121
+ *
122
+ * // To replace the Domo element itself:
123
+ * // const myButton = Domo('button').id('myBtn');
124
+ * // myButton.replace(myButton.element, Domo('a').txt('Link'));
125
+ */
126
+ replace(child, newChild) {
127
+ const resolvedNew = this._handleElementInstance(newChild);
128
+ const resolvedOld = child; // Child is expected to be a Node already based on context
129
+
130
+ if (resolvedOld === this.element) {
131
+ this.element.replaceWith(resolvedNew);
132
+ this.element = resolvedNew; // Update the internal reference to the new element
133
+ } else if (this.element.contains(resolvedOld)) {
134
+ resolvedOld.replaceWith(resolvedNew);
135
+ }
136
+
137
+ return this;
138
+ }
139
+
140
+ /**
141
+ * Shows or hides the element by controlling its CSS `display` property.
142
+ * @param {boolean} [visible=true] - If `true`, the element is shown. If `false`, it's hidden.
143
+ * @param {string} [displayValue='block'] - The CSS `display` value to use when showing the element (e.g., 'block', 'flex', 'inline-block').
144
+ * @returns {this} The current Domo instance for chaining.
145
+ * @example
146
+ * Domo('div').show(false); // Hides the div
147
+ * Domo('span').show(true, 'inline'); // Shows the span as inline
148
+ */
149
+ show(visible = true, displayValue = "block") {
150
+ this.element.style.display = visible ? displayValue : "none";
151
+ return this;
152
+ }
153
+
154
+ /**
155
+ * Conditionally returns the current Domo instance or a hidden placeholder element.
156
+ * This allows for conditional rendering based on a boolean condition. If the condition is false,
157
+ * a hidden 'if' element is returned, ensuring method chaining can continue without rendering
158
+ * the actual element.
159
+ * @param {boolean} condition - The condition to evaluate.
160
+ * @returns {this|Domo} The current Domo instance if the condition is true,
161
+ * or a new hidden Domo instance representing the 'if' placeholder if false.
162
+ * @example
163
+ * const dataExists = true;
164
+ * Domo('div')
165
+ * .if(dataExists)
166
+ * .txt('Data is here!'); // This will render
167
+ *
168
+ * const userIsAdmin = false;
169
+ * Domo('button')
170
+ * .if(userIsAdmin)
171
+ * .txt('Admin Panel'); // This button will not be rendered (a hidden placeholder is returned)
172
+ */
173
+ if(condition) {
174
+ // @ts-ignore - Ignore potential 'Domo is not defined' for JSDoc tool
175
+ if (!condition) {
176
+ return new DomoClass("if")
177
+ .attr({
178
+ hidden: true,
179
+ })
180
+ .data({
181
+ if: !this._virtual ? this.element.tagName.toLowerCase() : this.element._tag.toLowerCase(),
182
+ });
183
+ }
184
+ return this;
185
+ }
186
+ }
187
+
188
+ export default Children;
package/src/classes.js ADDED
@@ -0,0 +1,62 @@
1
+ class Classes {
2
+ /**
3
+ * Normalizes various inputs into a clean array of class names.
4
+ * Handles strings (space-separated) and arrays of strings.
5
+ * @private
6
+ * @param {string|string[]} input - A string of space-separated class names, or an array of class names.
7
+ * @returns {string[]} An array of cleaned class names.
8
+ */
9
+ _parseClassList(input) {
10
+ return Array.isArray(input) ? input.filter(Boolean) : String(input).split(" ").filter(Boolean);
11
+ }
12
+
13
+ /**
14
+ * Adds one or more CSS classes to the element.
15
+ * @param {string|string[]} classes - A single class name, a string of space-separated class names, or an array of class names.
16
+ * @returns {this} The current Domo instance for chaining.
17
+ * @example
18
+ * Domo('div').cls('active highlight');
19
+ * Domo('button').cls(['btn', 'btn-primary']);
20
+ */
21
+ cls(classes) {
22
+ if (!classes) return this;
23
+ const clsList = this._parseClassList(classes);
24
+ if (this._virtual) this.element._cls.push(...clsList);
25
+ else this.element.classList.add(...clsList);
26
+ return this;
27
+ }
28
+
29
+ /**
30
+ * Removes one or more CSS classes from the element.
31
+ * @param {string|string[]} classes - A single class name, a string of space-separated class names, or an array of class names.
32
+ * @returns {this} The current Domo instance for chaining.
33
+ * @example
34
+ * Domo('div').rmvCls('active');
35
+ * Domo('p').rmvCls(['old-style', 'unused']);
36
+ */
37
+ rmvCls(classes) {
38
+ if (classes) {
39
+ this.element.classList.remove(...this._parseClassList(classes));
40
+ }
41
+ return this;
42
+ }
43
+
44
+ /**
45
+ * Toggles a single CSS class on or off for the element.
46
+ * @param {string} className - The class name to toggle.
47
+ * @param {boolean} [force] - If `true`, the class is added. If `false`, it's removed.
48
+ * If omitted, the class's presence is flipped.
49
+ * @returns {this} The current Domo instance for chaining.
50
+ * @example
51
+ * // Toggle 'active' class:
52
+ * Domo('button').tgglCls('active');
53
+ * // Force add 'highlight' class:
54
+ * Domo('div').tgglCls('highlight', true);
55
+ */
56
+ tgglCls(className, force) {
57
+ this.element.classList.toggle(className, force);
58
+ return this;
59
+ }
60
+ }
61
+
62
+ export default Classes;
package/src/domo.js ADDED
@@ -0,0 +1,77 @@
1
+ import Base from "./base.js";
2
+ import Properties from "./properties.js";
3
+ import Classes from "./classes.js";
4
+ import Events from "./events.js";
5
+ import Children from "./children.js";
6
+ import Builder from "./builder.js";
7
+
8
+ /**
9
+ * @class Domo
10
+ * @augments Base
11
+ * @description The main class for creating and manipulating DOM elements,
12
+ * combining functionalities from various modules (Properties, Classes, Events, Children, Builder).
13
+ * This class provides a fluent, chainable API for building and interacting with HTML elements.
14
+ */
15
+ class DomoClass extends Base {
16
+ // Ensure the constructor explicitly calls the parent's constructor.
17
+ // This makes sure `this._virtual` and `this.element` are initialized by Base.
18
+ constructor(el = "div") {
19
+ super(el); // Calls Base's constructor
20
+ }
21
+ }
22
+
23
+ // Apply all mixin methods to the Domo prototype
24
+ // These lines integrate the methods from other classes into the Domo class's prototype,
25
+ // making them available on any Domo instance.
26
+ Object.getOwnPropertyNames(Properties.prototype)
27
+ .filter((p) => p !== "constructor")
28
+ .forEach((name) => {
29
+ DomoClass.prototype[name] = Properties.prototype[name];
30
+ });
31
+ Object.getOwnPropertyNames(Classes.prototype)
32
+ .filter((p) => p !== "constructor")
33
+ .forEach((name) => {
34
+ DomoClass.prototype[name] = Classes.prototype[name];
35
+ });
36
+ Object.getOwnPropertyNames(Events.prototype)
37
+ .filter((p) => p !== "constructor")
38
+ .forEach((name) => {
39
+ DomoClass.prototype[name] = Events.prototype[name];
40
+ });
41
+ Object.getOwnPropertyNames(Children.prototype)
42
+ .filter((p) => p !== "constructor")
43
+ .forEach((name) => {
44
+ DomoClass.prototype[name] = Children.prototype[name];
45
+ });
46
+ Object.getOwnPropertyNames(Builder.prototype)
47
+ .filter((p) => p !== "constructor")
48
+ .forEach((name) => {
49
+ DomoClass.prototype[name] = Builder.prototype[name];
50
+ });
51
+
52
+ /**
53
+ * A factory function to create a new Domo element instance.
54
+ * This is the primary way to start building an element, avoiding the need for the `new` keyword.
55
+ * @param {string} [el="div"] - The HTML tag name for the element to create (e.g., 'div', 'span', 'button').
56
+ * @returns {Domo} A new Domo instance, ready for method chaining.
57
+ * @example
58
+ * // Create a div and add text and a class:
59
+ * const myDiv = Domo('div').txt('Hello World').cls('container');
60
+ *
61
+ * // Create a button and attach an event:
62
+ * const myButton = Domo('button')
63
+ * .txt('Click Me')
64
+ * .on('click', () => alert('Button clicked!'));
65
+ *
66
+ * // Append to the document body:
67
+ * myButton.appendTo(document.body);
68
+ */
69
+ function Domo(el = "div") {
70
+ return new DomoClass(el);
71
+ }
72
+
73
+ // Export the factory function as the default export
74
+ export default Domo;
75
+
76
+ // Export the Domo Class itself
77
+ export { DomoClass };