@lamartinecabral/freedom 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Lamartine Cabral
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # @lamartinecabral/freedom
2
+
3
+ - [What is it?](#what-is-it)
4
+ - [Installation](#installation)
5
+ - [Usage example](#usage-example)
6
+ - [Reference](#reference)
7
+
8
+ # What is it?
9
+
10
+ A really small set of utilities to write `HTML` and `CSS` using only pure javascript.
11
+
12
+ ### But how?
13
+
14
+ It uses browser native APIs under the hood and get type checking and suggestions thanks to [`typescript`](https://github.com/microsoft/TypeScript/blob/main/src/lib/dom.generated.d.ts).
15
+
16
+ # Installation
17
+
18
+ In a browser:
19
+
20
+ ```html
21
+ <script src="https://unpkg.com/@lamartinecabral/freedom/dist/index.js"></script>
22
+ <script>
23
+ const { elem, style } = freedom;
24
+ </script>
25
+ ```
26
+
27
+ ```html
28
+ <script type="module">
29
+ import "https://unpkg.com/@lamartinecabral/freedom/dist/index.js";
30
+ const { elem, style } = freedom;
31
+ </script>
32
+ ```
33
+
34
+ Using npm:
35
+
36
+ ```sh
37
+ $ npm install @lamartinecabral/freedom
38
+ ```
39
+
40
+ ```javascript
41
+ require("@lamartinecabral/freedom");
42
+ const { elem, style } = freedom;
43
+ ```
44
+
45
+ ```javascript
46
+ import "@lamartinecabral/freedom";
47
+ const { elem, style } = freedom;
48
+ ```
49
+
50
+ # Usage example
51
+
52
+ The code below...
53
+
54
+ ```html
55
+ <html>
56
+ <head>
57
+ <style>
58
+ #app {
59
+ text-align: center;
60
+ width: fit-content;
61
+ border: 1px solid;
62
+ padding: 1em;
63
+ }
64
+ h1 {
65
+ color: red;
66
+ }
67
+ * {
68
+ font-family: monospace;
69
+ }
70
+ </style>
71
+ </head>
72
+ <body>
73
+ <div id="app">
74
+ <h1>Hello World!</h1>
75
+ <button onclick="count()">click me</button>
76
+ <p>you clicked <span id="counter">0</span> times.</p>
77
+ <button id="clr" onclick="reset()">reset</button>
78
+ </div>
79
+ <script>
80
+ let counter = +document.getElementById("counter").innerText;
81
+ function count() {
82
+ document.getElementById("counter").innerText = `${++counter}`;
83
+ document.getElementById("clr").disabled = false;
84
+ }
85
+ function reset() {
86
+ document.getElementById("counter").innerText = `${(counter = 0)}`;
87
+ document.getElementById("clr").disabled = true;
88
+ }
89
+ </script>
90
+ </body>
91
+ </html>
92
+ ```
93
+
94
+ ... can be rewritten like this:
95
+
96
+ ```html
97
+ <body>
98
+ <script src="https://unpkg.com/@lamartinecabral/freedom/dist/index.js"></script>
99
+ <script>
100
+ const { elem, style, getElem } = freedom;
101
+
102
+ style("#app", {
103
+ textAlign: "center",
104
+ width: "fit-content",
105
+ border: "1px solid",
106
+ padding: "1em",
107
+ });
108
+ style("h1", {
109
+ color: "red",
110
+ });
111
+ style("*", {
112
+ fontFamily: "monospace",
113
+ });
114
+
115
+ document.body.append(
116
+ elem("div", { id: "app" }, [
117
+ elem("h1", {}, ["Hello World!"]),
118
+ elem("button", { onclick: () => count() }, ["click me"]),
119
+ elem("p", {}, [
120
+ " you clicked ",
121
+ elem("span", { id: "counter" }, ["0"]),
122
+ " times. ",
123
+ ]),
124
+ elem("button", { id: "clr", onclick: () => reset(), disabled: true }, [
125
+ "reset",
126
+ ]),
127
+ ])
128
+ );
129
+
130
+ let counter = +getElem("counter").innerText;
131
+ function count() {
132
+ getElem("counter").innerText = `${++counter}`;
133
+ getElem("clr").disabled = false;
134
+ }
135
+ function reset() {
136
+ getElem("counter").innerText = `${(counter = 0)}`;
137
+ getElem("clr").disabled = true;
138
+ }
139
+ </script>
140
+ </body>
141
+ ```
142
+
143
+ # Reference
144
+
145
+ ### `elem()`
146
+
147
+ It abstracts the [`Document.createElement`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement), [`Element.setAttribute`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute) and [`Element.append`](https://developer.mozilla.org/en-US/docs/Web/API/Element/append) methods. It returns the element created.
148
+
149
+ Signature:
150
+
151
+ ```typescript
152
+ function elem<T extends keyof HTMLElementTagNameMap>(
153
+ tag: T,
154
+ attributes: Partial<HTMLElementTagNameMap[T]>,
155
+ children: Array<HTMLElement | string>
156
+ ): HTMLElementTagNameMap[T];
157
+ ```
158
+
159
+ ### `style()`
160
+
161
+ It creates and returns a global [CSSRule](https://developer.mozilla.org/en-US/docs/Web/API/CSSRule) and abstracts the [`CSSStyleDeclaration.setProperty`](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/setProperty).
162
+
163
+ Signature:
164
+
165
+ ```typescript
166
+ function style(
167
+ selector: string,
168
+ properties: Partial<CSSStyleDeclaration>
169
+ ): CSSStyleRule;
170
+ ```
@@ -0,0 +1,55 @@
1
+ type Tags = keyof HTMLElementTagNameMap;
2
+ type Elem<T extends Tags> = HTMLElementTagNameMap[T];
3
+ type DeepPartial<T extends object> = T extends Function | Array<unknown> ? T : {
4
+ [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
5
+ };
6
+ type PickByType<T, U> = {
7
+ [P in keyof T as T[P] extends U ? P : never]: T[P];
8
+ };
9
+ type ElemAttributes<T extends Tags> = DeepPartial<Elem<T>>;
10
+ type ElemText = string | number;
11
+ type ElemChildren = Array<HTMLElement | ElemText>;
12
+ type TagObj<T extends Tags> = {
13
+ tag: T;
14
+ id?: string;
15
+ };
16
+ type TagLike<T extends Tags> = T | TagObj<T>;
17
+ type Stringable = {
18
+ toString: () => string;
19
+ };
20
+ type StyleProps = Partial<PickByType<CSSStyleDeclaration, string>> & {
21
+ [property: string]: string;
22
+ };
23
+ declare function elem<T extends Tags>(tag: TagLike<T>): Elem<T>;
24
+ declare function elem<T extends Tags>(tag: TagLike<T>, attributes: ElemAttributes<T>): Elem<T>;
25
+ declare function elem<T extends Tags>(tag: TagLike<T>, attributes: ElemAttributes<T>, children: ElemChildren): Elem<T>;
26
+ declare function elem<T extends Tags>(tag: TagLike<T>, children: ElemChildren): Elem<T>;
27
+ declare function elem<T extends Tags>(tag: TagLike<T>, text: ElemText): Elem<T>;
28
+ declare function elem<T extends Tags>(tag: TagLike<T>, attributes: ElemAttributes<T>, text: ElemText): Elem<T>;
29
+ declare function style(selector: string | Stringable, properties: StyleProps): CSSStyleRule | null;
30
+ declare function media(condition: string, styleRules: Record<string, StyleProps>): CSSMediaRule | null;
31
+ declare function queryElem<T extends Tags>(selector: string, tag?: T): "main" extends T ? HTMLElement : Elem<T>;
32
+ declare function getElem<T extends Tags>(id: string, tag?: T): "main" extends T ? HTMLElement : Elem<T>;
33
+ declare function getChild<T extends Tags>(id: string, tag?: T): "main" extends T ? HTMLElement : Elem<T>;
34
+ declare function getParent<T extends Tags>(id: string, tag?: T): "main" extends T ? HTMLElement : Elem<T>;
35
+ declare const thisModule: Readonly<{
36
+ elem: typeof elem;
37
+ style: typeof style;
38
+ media: typeof media;
39
+ getElem: typeof getElem;
40
+ queryElem: typeof queryElem;
41
+ getChild: typeof getChild;
42
+ getParent: typeof getParent;
43
+ refElem: <T extends Tags>(tag: T) => {
44
+ (): Elem<T>;
45
+ id: string;
46
+ tag: T;
47
+ selector: string;
48
+ toString(): string;
49
+ };
50
+ version: string;
51
+ }>;
52
+ declare global {
53
+ var freedom: typeof thisModule;
54
+ }
55
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,274 @@
1
+ !(function(){
2
+ "use strict";
3
+ var __assign = (this && this.__assign) || function () {
4
+ __assign = Object.assign || function(t) {
5
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
6
+ s = arguments[i];
7
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
8
+ t[p] = s[p];
9
+ }
10
+ return t;
11
+ };
12
+ return __assign.apply(this, arguments);
13
+ };
14
+ var __rest = (this && this.__rest) || function (s, e) {
15
+ var t = {};
16
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
17
+ t[p] = s[p];
18
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
19
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
20
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
21
+ t[p[i]] = s[p[i]];
22
+ }
23
+ return t;
24
+ };
25
+ function setInlineStyle(element, style) {
26
+ try {
27
+ for (var prop in style) {
28
+ try {
29
+ var value = typeof style[prop] === "string"
30
+ ? style[prop].replace("!important", "")
31
+ : style[prop];
32
+ var important = value === style[prop] ? "" : "important";
33
+ if (prop in element.style && !important)
34
+ element.style[prop] = value;
35
+ else
36
+ element.style.setProperty(prop in element.style ? kebab(prop) : prop, value, important);
37
+ }
38
+ catch (e) {
39
+ console.error(e);
40
+ }
41
+ }
42
+ }
43
+ catch (e) {
44
+ throw e;
45
+ }
46
+ }
47
+ function setAttribute(element, name, value) {
48
+ try {
49
+ if (name === "style")
50
+ setInlineStyle(element, value);
51
+ else if (name in element)
52
+ element[name] = value;
53
+ else
54
+ element.setAttribute(name, value);
55
+ }
56
+ catch (e) {
57
+ throw e;
58
+ }
59
+ }
60
+ function createElem(tag, attributes) {
61
+ if (tag === String("")) {
62
+ return document.createDocumentFragment();
63
+ }
64
+ else {
65
+ var el = document.createElement(tag);
66
+ for (var attr in attributes)
67
+ setAttribute(el, attr, attributes[attr]);
68
+ return el;
69
+ }
70
+ }
71
+ function appendChildren(parent, children) {
72
+ for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
73
+ var child = children_1[_i];
74
+ if (typeof child === "boolean" || typeof child === "function")
75
+ continue;
76
+ if (child === null || child === undefined)
77
+ continue;
78
+ if (Array.isArray(child)) {
79
+ appendChildren(parent, child);
80
+ continue;
81
+ }
82
+ try {
83
+ child !== "" &&
84
+ parent.appendChild(typeof child === "object"
85
+ ? child
86
+ : document.createTextNode(String(child)));
87
+ }
88
+ catch (err) {
89
+ console.error(err);
90
+ }
91
+ }
92
+ }
93
+ function elemArgs(args) {
94
+ var _a = typeof args[0] === "string" ? [args[0], ""] : [args[0].tag, args[0].id], tag = _a[0], id = _a[1];
95
+ var attributes = {};
96
+ var children = [];
97
+ if (args[1]) {
98
+ if (typeof args[1] === "string" || typeof args[1] === "number")
99
+ children = [args[1]];
100
+ else if (Array.isArray(args[1]))
101
+ children = args[1];
102
+ else if (args[1])
103
+ attributes = args[1];
104
+ }
105
+ if (args.length >= 3) {
106
+ children = args.slice(2);
107
+ }
108
+ else {
109
+ if (Array.isArray(attributes.children))
110
+ children = attributes.children;
111
+ }
112
+ if ("children" in attributes) {
113
+ var _ = attributes.children, rest = __rest(attributes, ["children"]);
114
+ attributes = rest;
115
+ }
116
+ if (id)
117
+ attributes = __assign(__assign({}, attributes), { id: id });
118
+ return [tag, attributes, children];
119
+ }
120
+ function elem() {
121
+ var args = [];
122
+ for (var _i = 0; _i < arguments.length; _i++) {
123
+ args[_i] = arguments[_i];
124
+ }
125
+ try {
126
+ if (isComponent(args[0]))
127
+ return getComponent(args);
128
+ var _a = elemArgs(args), tag = _a[0], attributes = _a[1], children = _a[2];
129
+ var el = createElem(tag, attributes);
130
+ appendChildren(el, children);
131
+ return el;
132
+ }
133
+ catch (e) {
134
+ console.error(e);
135
+ return "";
136
+ }
137
+ }
138
+ var getStyleSheet = (function () {
139
+ var styleElement;
140
+ return function getStyleSheet() {
141
+ if (!styleElement) {
142
+ styleElement = document.createElement("style");
143
+ document.head.appendChild(styleElement);
144
+ }
145
+ if (!styleElement.sheet)
146
+ throw new Error("Unable to add style rule.");
147
+ return styleElement.sheet;
148
+ };
149
+ })();
150
+ function initRule(sheet, selector, content) {
151
+ var index = sheet.cssRules.length;
152
+ sheet.insertRule(selector + (content ? " { content: ".concat(content, "; }") : " {}"), index);
153
+ var rule = sheet.cssRules.item(index);
154
+ return rule;
155
+ }
156
+ function style(selector, properties) {
157
+ try {
158
+ var rule = initRule(getStyleSheet(), selector, properties.content);
159
+ setInlineStyle(rule, properties);
160
+ return rule;
161
+ }
162
+ catch (e) {
163
+ console.error(e);
164
+ return null;
165
+ }
166
+ }
167
+ function media(condition, styleRules) {
168
+ try {
169
+ var mediaRule = initRule(getStyleSheet(), "@media ".concat(condition));
170
+ try {
171
+ for (var selector in styleRules) {
172
+ var properties = styleRules[selector];
173
+ var rule = initRule(mediaRule, selector, properties.content);
174
+ setInlineStyle(rule, properties);
175
+ }
176
+ }
177
+ catch (e) {
178
+ console.error(e);
179
+ }
180
+ return mediaRule;
181
+ }
182
+ catch (e) {
183
+ console.error(e);
184
+ return null;
185
+ }
186
+ }
187
+ function assertElement(el, tag) {
188
+ if (!el)
189
+ throw new ReferenceError("Element not found.");
190
+ if (tag && el.tagName.toLowerCase() !== tag)
191
+ throw new TypeError("tag parameter and element's tag do not match.");
192
+ return el;
193
+ }
194
+ function queryElem(selector, tag) {
195
+ var el = document.querySelector(selector);
196
+ return assertElement(el, tag);
197
+ }
198
+ function getElem(id, tag) {
199
+ var el = document.getElementById(id);
200
+ return assertElement(el, tag);
201
+ }
202
+ function getChild(id, tag) {
203
+ var _a;
204
+ var el = (_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.firstElementChild;
205
+ return assertElement(el, tag);
206
+ }
207
+ function getParent(id, tag) {
208
+ var _a;
209
+ var el = (_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.parentElement;
210
+ return assertElement(el, tag);
211
+ }
212
+ var refElem = (function () {
213
+ var refCount = 0;
214
+ return function refElem(tag) {
215
+ var id = "e" + (refCount++).toString(36);
216
+ var ref = function () {
217
+ return getElem(id, tag);
218
+ };
219
+ ref.id = id;
220
+ ref.tag = tag;
221
+ ref.selector = tag + "#" + id;
222
+ ref.toString = function () {
223
+ return this.selector;
224
+ };
225
+ return Object.freeze(ref);
226
+ };
227
+ })();
228
+ function isRef(value) {
229
+ return (Object.isFrozen(value) &&
230
+ "selector" in value &&
231
+ value.selector === String(value));
232
+ }
233
+ function isComponent(value) {
234
+ return typeof value === "function" && !isRef(value);
235
+ }
236
+ function getComponent(args) {
237
+ try {
238
+ // this is for jsx compatibility
239
+ return args[0](__assign({ children: args.slice(2) }, args[1]));
240
+ }
241
+ catch (err) {
242
+ console.error(err);
243
+ return;
244
+ }
245
+ }
246
+ function kebab(name) {
247
+ if (name.indexOf("-") !== -1)
248
+ return name;
249
+ return name.replace(/([A-Z])/g, function (a) {
250
+ return "-" + a.toLowerCase();
251
+ });
252
+ }
253
+ var thisModule = Object.freeze({
254
+ elem: elem,
255
+ style: style,
256
+ media: media,
257
+ getElem: getElem,
258
+ queryElem: queryElem,
259
+ getChild: getChild,
260
+ getParent: getParent,
261
+ refElem: refElem,
262
+ version: "1.0.0",
263
+ });
264
+ Object.defineProperty(window, "freedom", {
265
+ value: thisModule,
266
+ enumerable: true,
267
+ });
268
+ // this is for jsx compatibility
269
+ "React" in window ||
270
+ Object.defineProperty(window, "React", {
271
+ value: { createElement: elem, Fragment: "" },
272
+ enumerable: true,
273
+ });
274
+ })()
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@lamartinecabral/freedom",
3
+ "version": "1.0.0",
4
+ "description": "A really small set of utilities to build web apps.",
5
+ "repository": "github:lamartinecabral/freedom",
6
+ "homepage": "https://www.github.com/lamartinecabral/freedom",
7
+ "keywords": [
8
+ "web",
9
+ "javascript",
10
+ "typescript",
11
+ "browser",
12
+ "library"
13
+ ],
14
+ "author": "lamartinecabral",
15
+ "license": "MIT",
16
+ "main": "dist/index.js",
17
+ "files": [
18
+ "dist/index.js",
19
+ "dist/index.d.ts"
20
+ ],
21
+ "typings": "dist/index.d.ts",
22
+ "scripts": {
23
+ "start": "tsc",
24
+ "build": "npm install && npm start && node post-build.js",
25
+ "tag": "v=$(node -p \"'v'+require('./package.json').version\") && git tag $v && git push origin $v"
26
+ },
27
+ "devDependencies": {
28
+ "typescript": "5.8.3"
29
+ }
30
+ }