@pap-it/system-utils
Advanced tools
Comparing version 0.0.0 to 1.0.1
@@ -7,2 +7,13 @@ export interface QueryOption<T> { | ||
export declare function query<T extends Element = HTMLElement>(options: queryParam<T>): (target: HTMLElement, propertyKey: string) => void; | ||
export interface ContextOption { | ||
name?: string; | ||
attribute?: string; | ||
rerender?: boolean; | ||
verbose?: boolean; | ||
onUpdate?: string; | ||
} | ||
export declare function context(options?: ContextOption): (target: any, propertyKey: string) => void; | ||
export declare enum Spread { | ||
BREAKOUT = 1 | ||
} | ||
export interface PropertyOption { | ||
@@ -13,3 +24,5 @@ type: Function; | ||
onUpdate?: string; | ||
context?: boolean; | ||
verbose?: boolean; | ||
spread?: string | Spread | boolean; | ||
set?: (value: any) => any; | ||
@@ -16,0 +29,0 @@ get?: (value: any) => any; |
@@ -0,1 +1,2 @@ | ||
import { NextParent } from '../functions/helpers'; | ||
export function query(options) { | ||
@@ -58,3 +59,84 @@ const selector = typeof options === "string" ? options : options.selector; | ||
} | ||
const DefaultOptions = { | ||
const DefaultContextOption = { | ||
rerender: true, | ||
verbose: false, | ||
}; | ||
export function context(options) { | ||
const _options = JSON.parse(JSON.stringify(options === undefined ? DefaultContextOption : { ...DefaultContextOption, ...options })); | ||
return function (target, propertyKey) { | ||
// assign default values | ||
if (!_options.name) | ||
_options.name = propertyKey; | ||
if (!_options.attribute) | ||
_options.attribute = propertyKey; | ||
// Storing original connectedCallback if it exists | ||
const originalConnectedCallback = target.connectedCallback; | ||
// Override the connectedCallback to set up context subscription | ||
target.connectedCallback = function () { | ||
const me = this; | ||
// Call original connectedCallback if it exists | ||
if (originalConnectedCallback) | ||
originalConnectedCallback.call(me); | ||
if (_options.verbose) | ||
console.log("connected-callback", _options); | ||
let parent = NextParent(me); | ||
while (parent) { | ||
// check if parent is our selector | ||
if (_options.verbose) | ||
console.log('finding parent', parent); | ||
if (_options.name in parent) { | ||
break; | ||
} | ||
if (_options.attribute && parent.hasAttribute(_options.attribute)) { | ||
break; | ||
} | ||
if (_options.verbose) { | ||
console.log('did not find', _options.name, 'in', parent); | ||
} | ||
if (parent === document.documentElement) { | ||
parent = null; | ||
break; | ||
} | ||
parent = NextParent(parent); | ||
} | ||
if (_options.verbose) | ||
console.log('final parent', parent); | ||
if (parent) { | ||
const operation = () => { | ||
if (_options.verbose) | ||
console.log('context-update', _options.name, _options.attribute); | ||
const old = me[_options.name]; | ||
if (_options.name in parent) { | ||
me[_options.name] = parent[_options.name]; | ||
} | ||
else if (_options.attribute && parent.hasAttribute(_options.attribute)) { | ||
me[_options.name] = parent.getAttribute(_options.attribute); | ||
} | ||
if (_options.onUpdate) { | ||
me[_options.onUpdate + "_attempts"] = 0; | ||
tryupdate.call(me, _options.onUpdate, me[_options.name], old, !!_options.verbose); | ||
} | ||
if (_options.rerender) { | ||
me.debouncedRequestUpdate(); | ||
} | ||
}; | ||
// init value | ||
operation(); | ||
// Subscribe to context changes | ||
parent.addEventListener(`context-${_options.name}`, operation); | ||
// if (DEV_MODE) parent.addEventListener('context-manual-change', operation); | ||
parent.addEventListener('context-manual-change-banana', operation); | ||
} | ||
else { | ||
console.warn(`Context provider for '${_options.name}' not found.`); | ||
} | ||
}; | ||
}; | ||
} | ||
// property.ts | ||
export var Spread; | ||
(function (Spread) { | ||
Spread[Spread["BREAKOUT"] = 1] = "BREAKOUT"; | ||
})(Spread || (Spread = {})); | ||
const DefaultPropertyOptions = { | ||
type: String, | ||
@@ -66,9 +148,23 @@ attribute: true, | ||
export function property(options) { | ||
const _options = options === undefined ? DefaultOptions : { ...DefaultOptions, ...options }; | ||
const _options = options === undefined ? DefaultPropertyOptions : { ...DefaultPropertyOptions, ...options }; | ||
return function (target, propertyKey) { | ||
const attributeName = (typeof _options.attribute === "string" ? _options.attribute : propertyKey).toLowerCase(); | ||
const spreadAttributeNames = {}; | ||
let internal = false; | ||
// Observe attributes | ||
const observedAttributes = target.constructor.observedAttributes || []; | ||
observedAttributes.push(attributeName); | ||
// NOTE spread is not working, at this point the objedct havent been defined yet, | ||
// we need to keep track if we defined this and in the setter we should do this work | ||
// if not done already - so keep track if we did this setup (setup being to add to 'observedAttributes') | ||
if (_options.spread) { | ||
console.log('spread yes!', target.constructor[propertyKey]); | ||
spreadAttributes(_options.spread === Spread.BREAKOUT ? "" : typeof _options.spread === "string" ? _options.spread : attributeName, target.constructor[propertyKey], (name) => { | ||
console.log('adding spread', name); | ||
observedAttributes.push(name); | ||
spreadAttributeNames[name] = true; | ||
}); | ||
} | ||
else { | ||
observedAttributes.push(attributeName); | ||
} | ||
target.constructor.observedAttributes = observedAttributes; | ||
@@ -78,5 +174,25 @@ // Handle attributeChangedCallback | ||
target.attributeChangedCallback = function (name, oldValue, newValue) { | ||
// how many times is the same code going to be called? | ||
attributeChanged.call(this, name, oldValue, newValue); | ||
if (name === attributeName && !internal && newValue !== oldValue) { | ||
this[propertyKey] = convertFromString(newValue, _options.type); | ||
if ((name === attributeName || spreadAttributeNames[name]) && !internal && newValue !== oldValue) { | ||
if (_options.verbose) | ||
console.log('attribute is set', attributeName); | ||
// NOTE spread is not working, perhaps with the attribute changes updated this part should work | ||
if (_options.spread) { | ||
const keys = name.split("-"); | ||
let newobject = this[propertyKey]; | ||
let target = newobject; | ||
for (let i = 0; i < keys.length; i++) { | ||
if (i !== keys.length - 1) { | ||
target = target[keys[i]]; | ||
} | ||
else { | ||
target[keys[i]] = convertFromStringPrimitive(newValue); | ||
} | ||
} | ||
this[propertyKey] = newobject; | ||
} | ||
else { | ||
this[propertyKey] = convertFromString(newValue, _options.type); | ||
} | ||
} | ||
@@ -99,5 +215,17 @@ }; | ||
const operation = () => { | ||
if (_options.attribute) { | ||
// we want to use spread over attribute (I guess?) | ||
if (_options.spread) { | ||
if (_options.verbose) | ||
console.log('property is set, setting attribute', attributeName); | ||
// NOTE for spread we need to assign each attribute | ||
} | ||
else if (_options.attribute) { | ||
internal = true; | ||
this.setAttribute(attributeName, valuestring); | ||
if (value === undefined) { | ||
// TODO need to check if this would cause issues with type:boolean = true values - is value true or undefined? | ||
this.removeAttribute(attributeName); | ||
} | ||
else { | ||
this.setAttribute(attributeName, valuestring); | ||
} | ||
internal = false; | ||
@@ -112,2 +240,7 @@ } | ||
} | ||
if (_options.verbose) | ||
console.log('update'); | ||
if (_options.context) { | ||
this.dispatchEvent(new Event(`context-${propertyKey}`)); | ||
} | ||
}; | ||
@@ -145,2 +278,12 @@ if (!this.connected) { | ||
} | ||
function convertFromStringPrimitive(value) { | ||
if (value === null) | ||
return null; | ||
if (["true", "false"].includes(value.toLowerCase())) | ||
return Boolean(value); | ||
const number_value = Number(value); | ||
if (!Number.isNaN(number_value)) | ||
return number_value; | ||
return value; | ||
} | ||
function convertFromString(value, type) { | ||
@@ -172,2 +315,19 @@ switch (type.name) { | ||
} | ||
function spreadAttributes(parentKey, object, callback) { | ||
console.log('spread object?', object); | ||
for (let key in object) { | ||
console.log('spead jey', key); | ||
let nextname = parentKey === "" ? key : `${parentKey}-${key}`; | ||
if (object[key] instanceof Array) { | ||
// like wtf.. | ||
throw new Error("[LAZY] since I dont know yet what case this could be I will throw a error until I meet it and fix it then... your're welcome future Henry!"); | ||
} | ||
else if (object[key] instanceof Object) { | ||
spreadAttributes(nextname, object[key], callback); | ||
} | ||
else { | ||
callback(nextname); | ||
} | ||
} | ||
} | ||
//# sourceMappingURL=decorators.js.map |
import { Devices } from "../types"; | ||
export declare function suspense<T extends (...args: any[]) => any>(execute: T, delay?: number): (...args: Parameters<T>) => void; | ||
export declare function debounce<T extends (...args: any[]) => any>(execute: T, delay?: number): (...args: Parameters<T>) => void; | ||
export declare function generateUUID(): string; | ||
export declare function NextParent<T = HTMLElement>(element: HTMLElement): HTMLElement | T | null; | ||
@@ -4,0 +5,0 @@ export declare function CumulativeOffset(element: HTMLElement): { |
@@ -1,2 +0,2 @@ | ||
export function suspense(execute, delay = 300) { | ||
export function debounce(execute, delay = 300) { | ||
let timer = null; | ||
@@ -13,2 +13,13 @@ return function (...args) { | ||
} | ||
export function generateUUID() { | ||
let d = new Date().getTime(); | ||
if (typeof performance !== 'undefined' && typeof performance.now === 'function') { | ||
d += performance.now(); //use high-precision timer if available | ||
} | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
const r = (d + Math.random() * 16) % 16 | 0; | ||
d = Math.floor(d / 16); | ||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); | ||
}); | ||
} | ||
export function NextParent(element) { | ||
@@ -15,0 +26,0 @@ if (element.parentElement) |
export declare function findComments(element: Node): ChildNode[]; | ||
export declare function html(strings: TemplateStringsArray, ...values: any[]): DocumentFragment; | ||
export declare function ReapplyEvents(element: Element, clone: Element): void; | ||
export declare function ReapplyEventsDeep(element: Element, clone: Element): void; | ||
type EventObject = { | ||
listener: EventListener; | ||
options?: EventListenerOptions; | ||
}; | ||
declare global { | ||
interface Window { | ||
eventListenersBeenInitialized: boolean; | ||
} | ||
interface Element { | ||
__eventListeners: Record<string, EventObject[]>; | ||
getEventListeners(): Record<string, EventObject[]>; | ||
} | ||
} | ||
export {}; |
@@ -0,1 +1,2 @@ | ||
// import { prepareElement } from './html-helper'; | ||
export function findComments(element) { | ||
@@ -57,3 +58,10 @@ let arr = []; | ||
} | ||
// Check if the value is undefined | ||
else if (values[i] === undefined) { | ||
// Find and exclude the attribute from the string part | ||
const attributePattern = /\w+="$/; | ||
result += strings[i].replace(attributePattern, ''); | ||
} | ||
else { | ||
// Normal concatenation for defined values | ||
result += strings[i] + values[i]; | ||
@@ -86,2 +94,3 @@ } | ||
}); | ||
InitEventListeners(); | ||
content.querySelectorAll("*").forEach(element => { | ||
@@ -93,2 +102,5 @@ Array.from(element.attributes).forEach((attr) => { | ||
element.removeAttribute(attr.name); | ||
// prepareElement(element); | ||
// if (!element.eventListeners) element.eventListeners = []; | ||
// element.eventListeners.push({eventName, callback: values[indexValue]}); | ||
element.addEventListener(eventName, values[indexValue]); | ||
@@ -101,2 +113,75 @@ } | ||
} | ||
export function ReapplyEvents(element, clone) { | ||
if (!clone.__eventListeners) { | ||
for (const eventname in element.__eventListeners) { | ||
for (const event of element.__eventListeners[eventname]) { | ||
clone.addEventListener(eventname, event.listener, event.options); | ||
} | ||
} | ||
} | ||
} | ||
export function ReapplyEventsDeep(element, clone) { | ||
ReapplyEvents(element, clone); | ||
if (element instanceof HTMLSlotElement) { | ||
// NOTE this seems not to do anything for us.. | ||
// if (clone instanceof HTMLSlotElement) { | ||
// const elementslotchildren = element.assignedElements(); | ||
// const cloneslotchildren = clone.assignedElements(); | ||
// // console.log(element, clone, { elementslotchildren, cloneslotchildren, nodes: clone.assignedNodes() }) | ||
// for (let i = 0; i < elementslotchildren.length; i++) { | ||
// ReapplyEventsDeep(elementslotchildren[i], cloneslotchildren[i]); | ||
// } | ||
// } | ||
// else { | ||
// throw new Error("[ERROR]: element is slot but clone is not..") | ||
// } | ||
} | ||
else { | ||
for (let i = 0; i < element.children.length; i++) { | ||
ReapplyEventsDeep(element.children[i], clone.children[i]); | ||
} | ||
} | ||
} | ||
function InitEventListeners() { | ||
if (window.eventListenersBeenInitialized) | ||
return; | ||
window.eventListenersBeenInitialized = true; | ||
const originalAddEventListener = Element.prototype.addEventListener; | ||
const originalRemoveEventListener = Element.prototype.removeEventListener; | ||
const originalCloneNode = Element.prototype.cloneNode; | ||
Element.prototype.addEventListener = function (name, listener, options) { | ||
this.__eventListeners = this.__eventListeners || {}; | ||
const event_obj = { listener, options }; | ||
this.__eventListeners[name] = this.__eventListeners[name] || []; | ||
// Find the index of the existing event listener | ||
const existingListenerIndex = this.__eventListeners[name].findIndex(l => l.listener === listener && JSON.stringify(l.options) === JSON.stringify(options)); | ||
if (existingListenerIndex !== -1) { | ||
originalRemoveEventListener.call(this, name, this.__eventListeners[name][existingListenerIndex].listener, this.__eventListeners[name][existingListenerIndex].options); | ||
this.__eventListeners[name].splice(existingListenerIndex, 1); | ||
} | ||
this.__eventListeners[name].push(event_obj); | ||
// this.__eventListeners[name].push(listener); | ||
originalAddEventListener.call(this, name, listener, options); | ||
}; | ||
Element.prototype.removeEventListener = function (name, listener, options) { | ||
if (this.__eventListeners && this.__eventListeners[name]) { | ||
const index = this.__eventListeners[name].findIndex(data => data.listener === listener && options === data.options); | ||
if (index !== -1) { | ||
this.__eventListeners[name].splice(index, 1); | ||
} | ||
} | ||
originalRemoveEventListener.call(this, name, listener, options); | ||
}; | ||
Element.prototype.cloneNode = function (deep) { | ||
const clone = originalCloneNode.call(this, deep); | ||
if (deep) { | ||
// need to iterate the children and reapply the events | ||
ReapplyEventsDeep(this, clone); | ||
} | ||
return clone; | ||
}; | ||
Element.prototype.getEventListeners = function () { | ||
return this.__eventListeners; | ||
}; | ||
} | ||
//# sourceMappingURL=html.js.map |
export * from './decorators'; | ||
export * from './helpers'; | ||
export * from './html'; | ||
export * from './directives'; |
export * from './decorators'; | ||
export * from './helpers'; | ||
export * from './html'; | ||
export * from './directives'; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@pap-it/system-utils", | ||
"version": "0.0.0", | ||
"version": "1.0.1", | ||
"scripts": { | ||
@@ -32,4 +32,4 @@ "init": "sh .scripts/init.sh", | ||
"eslint": "^8.53.0", | ||
"@pap-it/system-showcase": "^0.0.0", | ||
"@pap-it/typography": "^0.0.0", | ||
"@pap-it/system-showcase": "1.0.0", | ||
"@pap-it/typography": "1.0.0", | ||
"typescript": "^5.0.4" | ||
@@ -40,4 +40,2 @@ }, | ||
"typescript", | ||
"demoing", | ||
"testing", | ||
"sass" | ||
@@ -44,0 +42,0 @@ ], |
@@ -1,6 +0,6 @@ | ||
# UtilsTool | ||
# UtilsSystem | ||
Atomic Type: tools | ||
Atomic Type: system | ||
Version: 0.0.0 | ||
Version: 1.0.1 | ||
@@ -7,0 +7,0 @@ ## Development |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
87745
30
759
1