oberry
Version:
A lightweight and modern jQuery alternative for DOM manipulation, user interactions, and reactive data binding
568 lines (563 loc) • 13.8 kB
JavaScript
// src/core/reactivity/index.ts
import { computed, effect, effectScope, signal } from "alien-signals";
var $ref = signal;
var $computed = computed;
var $effect = effect;
var $effectScope = effectScope;
// src/core/selector/index.ts
function $(selector) {
let elements = [];
if (typeof selector === "string") {
if (selector.includes(" ")) {
elements = document.querySelectorAll(selector);
} else if (selector.startsWith("#")) {
const el = document.getElementById(selector.slice(1));
if (el !== null) {
elements = [el];
}
} else if (selector.startsWith(".")) {
elements = Array.from(
document.getElementsByClassName(selector.slice(1))
);
} else if (selector.includes(".") || selector.includes("#")) {
elements = document.querySelectorAll(selector);
} else {
elements = Array.from(
document.getElementsByTagName(selector)
);
}
} else if (selector instanceof HTMLElement) {
elements = [selector];
} else if (selector instanceof NodeList || Array.isArray(selector)) {
elements = selector;
} else {
throw new Error("Invalid selector type");
}
return new ElementWrapper(elements);
}
// src/core/wrapper/index.ts
var ElementWrapper = class _ElementWrapper {
constructor(elements) {
if (elements instanceof NodeList) {
this.elements = Array.from(elements).filter(
(node) => node.nodeType === Node.ELEMENT_NODE
);
} else {
this.elements = elements;
}
}
class(name, mode) {
if (name === void 0) {
return Array.from(this.elements[0]?.classList ?? []);
}
if (mode === void 0 || mode === "toggle") {
for (const el of this.elements) {
el.classList.toggle(name);
}
return this;
}
if (mode === "add") {
for (const el of this.elements) {
el.classList.add(name);
}
return this;
}
if (mode === "remove") {
for (const el of this.elements) {
el.classList.remove(name);
}
return this;
}
return this;
}
id(id) {
if (id === void 0) {
const el = this.elements[0];
return el?.id || void 0;
}
if (this.elements[0]) {
this.elements[0].id = id;
}
return this;
}
/**
* Modify the style of all elements.
*/
css(styles) {
for (const el of this.elements) {
Object.assign(el.style, styles);
}
return this;
}
html(content) {
if (content === void 0) {
return this.elements[0]?.innerHTML;
}
for (const el of this.elements) {
el.innerHTML = content;
}
return this;
}
text(content) {
if (content === void 0) {
return this.elements[0]?.innerText;
}
for (const el of this.elements) {
el.innerText = content;
}
return this;
}
/**
* Get the array of elements.
*/
getArray() {
return Array.from(this.elements);
}
value(newValue) {
const el = this.elements[0];
if (newValue === void 0) {
if (!el) {
return;
}
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
return el.value;
}
return;
}
if (!el) {
return this;
}
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
el.value = newValue;
}
return this;
}
append(content) {
for (const el of this.elements) {
if (typeof content === "string") {
el.innerHTML += content;
} else if (content instanceof HTMLElement) {
el.appendChild(content);
} else if (content instanceof _ElementWrapper) {
for (const child of content.elements) {
el.appendChild(child.cloneNode(true));
}
}
}
return this;
}
prepend(content) {
for (const el of this.elements) {
if (typeof content === "string") {
el.innerHTML = content + el.innerHTML;
} else if (content instanceof HTMLElement) {
el.insertBefore(content.cloneNode(true), el.firstChild);
} else if (content instanceof _ElementWrapper) {
content.elements.forEach((childEl, index) => {
el.insertBefore(childEl.cloneNode(true), el.children[index] || null);
});
}
}
return this;
}
after(content) {
for (const el of this.elements) {
const parent = el.parentNode;
if (!parent) {
continue;
}
if (typeof content === "string") {
const temp = document.createElement("div");
temp.innerHTML = content;
while (temp.firstChild) {
parent.insertBefore(temp.firstChild, el.nextSibling);
}
} else if (content instanceof HTMLElement) {
parent.insertBefore(content.cloneNode(true), el.nextSibling);
} else if (content instanceof _ElementWrapper) {
for (const childEl of content.elements) {
parent.insertBefore(childEl.cloneNode(true), el.nextSibling);
}
}
}
return this;
}
before(content) {
for (const el of this.elements) {
const parent = el.parentNode;
if (!parent) {
continue;
}
if (typeof content === "string") {
const temp = document.createElement("div");
temp.innerHTML = content;
while (temp.firstChild) {
parent.insertBefore(temp.firstChild, el);
}
} else if (content instanceof HTMLElement) {
parent.insertBefore(content.cloneNode(true), el);
} else if (content instanceof _ElementWrapper) {
for (const childEl of content.elements) {
parent.insertBefore(childEl.cloneNode(true), el);
}
}
}
return this;
}
/**
* Remove all the elements from DOM.
*/
remove() {
for (const el of this.elements) {
el.parentNode?.removeChild(el);
}
this.elements = [];
return this;
}
/**
* Find descendants matching the selector within all elements.
*/
find(selector) {
const foundElements = [];
for (const el of this.elements) {
const found = el.querySelectorAll(selector);
foundElements.push(
...Array.from(found).filter(
(node) => node.nodeType === Node.ELEMENT_NODE
)
);
}
return new _ElementWrapper(foundElements);
}
/**
* Find the closest ancestor matching the selector for the first element.
*/
closest(selector) {
const el = this.elements[0];
if (!el) {
return null;
}
const closest = el.closest(selector);
if (!closest) {
return null;
}
return new _ElementWrapper([closest]);
}
/**
* Get the parent wrapper of the first element.
*/
parent() {
const el = this.elements[0];
if (!el) {
return null;
}
const parent = el.parentElement;
if (!parent) {
return null;
}
return $([parent]);
}
/**
* Get the children wrapper of the first element.
*/
children() {
const el = this.elements[0];
if (!el) {
return null;
}
const children = el.children;
if (!children) {
return null;
}
return $(Array.from(children));
}
/**
* Get the children wrapper of the all elements.
*/
allChildren() {
const children = [];
for (const el of this.elements) {
children.push(...Array.from(el.children));
}
if (!children) {
return null;
}
return $(Array.from(children));
}
/**
* Bind the value of a ref into the element's inner HTML.
*/
bindHTML(ref) {
$effect(() => {
this.html(String(ref()));
});
return this;
}
/**
* Bind the value of a ref into the element's text content.
*/
bind(ref) {
$effect(() => {
this.text(String(ref()));
});
return this;
}
/**
* Bind the input value of the first element into a ref value.
*/
bindInput(ref) {
const el = this.elements[0];
if (!el) {
return this;
}
el.addEventListener("input", () => {
ref(el.value);
});
return this;
}
attr(key, value) {
if (value === void 0) {
const el = this.elements[0];
if (!el) {
return null;
}
return el.getAttribute(key);
}
for (const el of this.elements) {
el.setAttribute(key, value);
}
return this;
}
data(key, value) {
if (value === void 0) {
const el = this.elements[0];
if (!el) {
return;
}
return el.dataset[key];
}
for (const el of this.elements) {
el.dataset[key] = value;
}
return this;
}
on(event, callback) {
for (const el of this.elements) {
el.addEventListener(event, callback);
}
return this;
}
onClick(callback) {
return this.on("click", callback);
}
onMouseOver(callback) {
return this.on("mouseover", callback);
}
onMouseOut(callback) {
return this.on("mouseout", callback);
}
onChange(callback) {
return this.on("change", callback);
}
onInput(callback) {
return this.on("input", callback);
}
onSubmit(callback) {
return this.on("submit", callback);
}
onFocus(callback) {
return this.on("focus", callback);
}
onBlur(callback) {
return this.on("blur", callback);
}
onKeyDown(callback) {
return this.on("keydown", callback);
}
onKeyUp(callback) {
return this.on("keyup", callback);
}
onKeyPress(callback) {
return this.on("keypress", callback);
}
onDblClick(callback) {
return this.on("dblclick", callback);
}
onContextMenu(callback) {
return this.on("contextmenu", callback);
}
onScroll(callback) {
return this.on("scroll", callback);
}
onResize(callback) {
return this.on("resize", callback);
}
onLoad(callback) {
return this.on("load", callback);
}
onDrag(callback) {
return this.on("drag", callback);
}
onDrop(callback) {
return this.on("drop", callback);
}
/**
* Get the elements at even positions (1-based indexing).
*/
even() {
const elements = [];
for (let i = 0; i < this.elements.length; i++) {
if ((i + 1) % 2 === 0 && this.elements[i]) {
elements.push(this.elements[i]);
}
}
return new _ElementWrapper(elements);
}
/**
* Get the elements at odd positions (1-based indexing).
*/
odd() {
const elements = [];
for (let i = 0; i < this.elements.length; i++) {
if ((i + 1) % 2 === 1 && this.elements[i]) {
elements.push(this.elements[i]);
}
}
return new _ElementWrapper(elements);
}
/**
* Hide all elements.
*/
hide() {
for (const el of this.elements) {
if (!el.dataset.originalDisplay) {
const computedStyle = window.getComputedStyle(el);
el.dataset.originalDisplay = computedStyle.display;
}
el.style.display = "none";
}
return this;
}
/**
* Show all elements.
*/
show() {
for (const el of this.elements) {
const originalDisplay = el.dataset.originalDisplay || "";
el.style.display = originalDisplay === "none" ? "block" : originalDisplay;
delete el.dataset.originalDisplay;
}
return this;
}
/**
* Toggle visibility of all elements.
*/
toggle() {
for (const el of this.elements) {
const isVisible = window.getComputedStyle(el).display !== "none";
if (isVisible) {
if (!el.dataset.originalDisplay) {
el.dataset.originalDisplay = window.getComputedStyle(el).display;
}
el.style.display = "none";
} else {
const originalDisplay = el.dataset.originalDisplay || "block";
el.style.display = originalDisplay;
delete el.dataset.originalDisplay;
}
}
return this;
}
/**
* Get the nth element (0-based indexing).
*/
eq(idx) {
const el = this.elements[idx];
return new _ElementWrapper(el ? [el] : []);
}
/**
* Get the first element.
*/
first() {
return this.eq(0);
}
/**
* Get the last element.
*/
last() {
if (this.elements.length === 0) {
return new _ElementWrapper([]);
}
return this.eq(this.elements.length - 1);
}
/**
* Filter elements based on a predicate function or CSS selector.
*/
filter(predicate) {
let filteredElements;
if (typeof predicate === "string") {
filteredElements = this.elements.filter((el) => el.matches(predicate));
} else {
filteredElements = this.elements.filter(
(el, index) => predicate(el, index)
);
}
return new _ElementWrapper(filteredElements);
}
forEach(callback) {
for (const el of this.elements) {
callback(new _ElementWrapper([el]));
}
}
length() {
return this.elements.length;
}
isEmpty() {
return this.elements.length === 0;
}
static extend(name, func) {
_ElementWrapper.prototype[name] = func;
}
};
// src/core/creator/index.ts
function $new(tag) {
const el = document.createElement(tag);
const wrapper = new ElementWrapper([el]);
return wrapper;
}
// src/core/plugins/index.ts
var Plugin = class {
constructor(name, install) {
this.name = name;
this.install = install;
}
getInstaller() {
return this.install;
}
};
var installedPlugins = /* @__PURE__ */ new Set();
function use(plugin) {
if (installedPlugins.has(plugin.name)) {
console.warn(`Plugin "${plugin.name}" is already installed`);
return;
}
try {
const extendFunction = (name, func) => {
ElementWrapper.extend(name, func);
};
plugin.getInstaller()(extendFunction);
installedPlugins.add(plugin.name);
} catch (error) {
console.error(`Failed to install plugin "${plugin.name}":`, error);
}
}
export {
$,
$computed,
$effect,
$effectScope,
$new,
$ref,
Plugin,
use
};