ziko 0.49.6 → 0.49.7

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.
@@ -1,5 +1,13 @@
1
+ // STATES
1
2
  export * from './use-state.js';
2
3
  export * from './use-derived.js';
3
4
  export * from './use-reactive.js';
5
+
6
+ //
4
7
  export * from './use-channel.js'
5
- export * from './use-storage.js'
8
+ export * from './use-storage.js'
9
+ export * from './use-thread.js'
10
+ export * from './use-event-emitter.js'
11
+ export * from './use-media-query.js'
12
+ export * from './use-title.js'
13
+ export * from './use-root.js'
@@ -1,36 +1,28 @@
1
1
  import { Random } from "../math/random/index.js";
2
2
 
3
3
  class UseChannel {
4
- // private fields
5
4
  #channel;
6
5
  #eventData;
7
6
  #handlers;
8
7
  #uuid;
9
8
  #subscribers;
10
- #currentRooms; // now a Set for multiple rooms
11
-
9
+ #currentRooms;
12
10
  constructor(name = "") {
13
11
  this.#channel = new BroadcastChannel(name);
14
12
  this.#eventData = new Map();
15
13
  this.#handlers = new Map(); // Map<event, Array<{fn, rooms}>>
16
14
  this.#uuid = "ziko-channel:" + Random.string(10);
17
15
  this.#subscribers = new Set([this.#uuid]);
18
- this.#currentRooms = new Set(); // multiple rooms
19
-
16
+ this.#currentRooms = new Set();
20
17
  this.#channel.addEventListener("message", (e) => {
21
18
  const { last_sent_event, userId, eventData, rooms } = e.data;
22
-
23
19
  if (userId === this.#uuid) return; // ignore own messages
24
-
25
20
  // broadcast if no rooms, else check intersection
26
21
  if (rooms && rooms.length && !rooms.some(r => this.#currentRooms.has(r))) return;
27
-
28
22
  this.#subscribers.add(userId);
29
23
  this.#eventData = new Map(eventData);
30
-
31
24
  const handlersList = this.#handlers.get(last_sent_event);
32
25
  if (!handlersList) return;
33
-
34
26
  handlersList.forEach(({ fn, rooms: handlerRooms }) => {
35
27
  // trigger if listener has no room filter, or intersects subscriber rooms
36
28
  if (!handlerRooms || handlerRooms.length === 0 ||
@@ -44,24 +36,20 @@ class UseChannel {
44
36
  emit(event, data, rooms) {
45
37
  this.#eventData.set(event, data);
46
38
  if(typeof rooms === 'string') rooms = [rooms]
47
-
48
39
  this.#channel.postMessage({
49
40
  eventData: Array.from(this.#eventData.entries()),
50
41
  last_sent_event: event,
51
42
  userId: this.#uuid,
52
43
  rooms: rooms && rooms.length ? rooms : undefined
53
44
  });
54
-
55
45
  return this;
56
46
  }
57
-
58
47
  on(event, handler = console.log, rooms) {
59
48
  if (!this.#handlers.has(event)) this.#handlers.set(event, []);
60
49
  if(typeof rooms === 'string') rooms = [rooms]
61
50
  this.#handlers.get(event).push({ fn: handler, rooms });
62
51
  return this;
63
52
  }
64
-
65
53
  off(event, handler) {
66
54
  if (!this.#handlers.has(event)) return this;
67
55
  this.#handlers.set(
@@ -70,7 +58,6 @@ class UseChannel {
70
58
  );
71
59
  return this;
72
60
  }
73
-
74
61
  once(event, handler, rooms) {
75
62
  const wrapper = (data) => {
76
63
  handler(data);
@@ -79,23 +66,19 @@ class UseChannel {
79
66
  this.on(event, wrapper, rooms);
80
67
  return this;
81
68
  }
82
-
83
69
  join(...rooms) {
84
70
  rooms.forEach(r => this.#currentRooms.add(r));
85
71
  return this;
86
72
  }
87
-
88
73
  leave(...rooms) {
89
74
  if (!rooms.length) this.#currentRooms.clear();
90
75
  else rooms.forEach(r => this.#currentRooms.delete(r));
91
76
  return this;
92
77
  }
93
-
94
78
  close() {
95
79
  this.#channel.close();
96
80
  return this;
97
81
  }
98
-
99
82
  }
100
83
 
101
84
  const useChannel = (name) => new UseChannel(name);
@@ -0,0 +1,68 @@
1
+ class UseEventEmitter {
2
+ constructor(maxListeners = 10) {
3
+ this.events = {};
4
+ this.maxListeners = maxListeners;
5
+ }
6
+
7
+ on(event, listener) {
8
+ if (!this.events[event]) this.events[event] = [];
9
+ this.events[event].push(listener);
10
+ if (this.events[event].length > this.maxListeners) {
11
+ console.warn(`Warning: Possible memory leak. Event '${event}' has more than ${this.maxListeners} listeners.`);
12
+ }
13
+ return this;
14
+ }
15
+
16
+ once(event, listener) {
17
+ const wrapper = (...args) => {
18
+ this.off(event, wrapper);
19
+ listener(...args);
20
+ };
21
+ return this.on(event, wrapper);
22
+ }
23
+
24
+ off(event, listener) {
25
+ const listeners = this.events[event];
26
+ if (!listeners) return this;
27
+
28
+ const index = listeners.indexOf(listener);
29
+ if (index !== -1) {
30
+ listeners.splice(index, 1);
31
+ }
32
+
33
+ return this;
34
+ }
35
+
36
+ emit(event, data) {
37
+ const listeners = this.events[event];
38
+ if (!listeners) return false;
39
+
40
+ // Make a copy so removing listeners inside callbacks doesn't affect iteration
41
+ [...listeners].forEach(listener => {
42
+ try {
43
+ listener(data);
44
+ } catch (e) {
45
+ console.error(`Error in listener for '${event}':`, e);
46
+ }
47
+ });
48
+
49
+ return true;
50
+ }
51
+ remove(event){
52
+ delete this.events[event];
53
+ return this;
54
+ }
55
+ clear() {
56
+ this.events = {};
57
+ return this;
58
+ }
59
+
60
+ setMaxListeners(max) {
61
+ this.maxListeners = max;
62
+ return this;
63
+ }
64
+ }
65
+
66
+ const useEventEmitter = (maxListeners) => new UseEventEmitter(maxListeners);
67
+
68
+ export { useEventEmitter };
@@ -0,0 +1,59 @@
1
+ import { useEventEmitter } from "./use-event-emitter.js";
2
+
3
+ class UseFavIcon {
4
+ constructor(FavIcon, withEmitter = true) {
5
+ this.#init();
6
+ this.cache = {
7
+ emitter: null
8
+ };
9
+ if (withEmitter) this.useEventEmitter();
10
+ if (FavIcon) this.set(FavIcon);
11
+ }
12
+
13
+ #init() {
14
+ let link = document.querySelector("link[rel*='icon']");
15
+ let created = false;
16
+
17
+ if (!link) {
18
+ link = document.createElement("link");
19
+ created = true;
20
+ }
21
+
22
+ link.type = "image/x-icon";
23
+ link.rel = "shortcut icon";
24
+
25
+ if (created) document.head.appendChild(link);
26
+
27
+ this.__FavIcon__ = link;
28
+ }
29
+
30
+ setFavicon(href) {
31
+ if (href !== this.__FavIcon__.href) {
32
+ this.__FavIcon__.href = href;
33
+
34
+ if (this.cache.emitter)
35
+ this.cache.emitter.emit("ziko:favicon-changed", href);
36
+ }
37
+ return this;
38
+ }
39
+
40
+ get current() {
41
+ return this.__FavIcon__.href;
42
+ }
43
+
44
+ onChange(callback) {
45
+ if (this.cache.emitter)
46
+ this.cache.emitter.on("ziko:favicon-changed", callback);
47
+ return this;
48
+ }
49
+
50
+ useEventEmitter() {
51
+ this.cache.emitter = useEventEmitter();
52
+ return this;
53
+ }
54
+
55
+ }
56
+
57
+ const useFavIcon = (FavIcon, withEmitter = true) => new UseFavIcon(FavIcon, withEmitter);
58
+
59
+ export { useFavIcon };
@@ -0,0 +1,59 @@
1
+ /*
2
+ [
3
+ {
4
+ query: '(min-width: 600px)',
5
+ callback: () => console.log(1)
6
+ },
7
+ {
8
+ query: '(max-width: 300px)',
9
+ callback: () => console.log(2)
10
+ }
11
+ ]
12
+ */
13
+
14
+ class UseMediaQuery {
15
+ #mediaQueryRules;
16
+ #fallback;
17
+ #lastCalledCallback = null;
18
+
19
+ constructor(mediaQueryRules = [], fallback = () => {}) {
20
+ this.#mediaQueryRules = mediaQueryRules;
21
+ this.#fallback = fallback;
22
+
23
+ this.#init();
24
+ }
25
+
26
+ // PRIVATE: check if ANY rule matches
27
+ #checkAllRules() {
28
+ return this.#mediaQueryRules.some(
29
+ ({ query }) => globalThis.matchMedia(query).matches
30
+ );
31
+ }
32
+
33
+ // PRIVATE: installs listeners and initial checks
34
+ #init() {
35
+ this.#mediaQueryRules.forEach(({ query, callback }) => {
36
+ const mediaQueryList = globalThis.matchMedia(query);
37
+
38
+ const checkMatches = () => {
39
+ const anyMatch = this.#checkAllRules();
40
+
41
+ if (mediaQueryList.matches) {
42
+ callback();
43
+ this.#lastCalledCallback = callback;
44
+ } else if (!anyMatch && this.#lastCalledCallback !== this.#fallback) {
45
+ this.#fallback();
46
+ this.#lastCalledCallback = this.#fallback;
47
+ }
48
+ };
49
+
50
+ checkMatches();
51
+ mediaQueryList.addEventListener("change", checkMatches);
52
+ });
53
+ }
54
+ }
55
+
56
+ const useMediaQuery = (mediaQueryRules, fallback) =>
57
+ new UseMediaQuery(mediaQueryRules, fallback);
58
+
59
+ export { useMediaQuery };
@@ -0,0 +1,73 @@
1
+ class UseRoot {
2
+ constructor(PropsMap, { namespace = 'Ziko', ValidateCssProps = false } = {}) {
3
+ this.currentPropsMap = PropsMap;
4
+ this.namespace = namespace;
5
+ this.ValidateCssProps = ValidateCssProps;
6
+ this.use(PropsMap);
7
+ }
8
+
9
+ use(PropsMap) {
10
+ if (this.ValidateCssProps) ValidateCssPropsFn(PropsMap);
11
+ this.currentPropsMap = PropsMap;
12
+ this.#maintain();
13
+ return this;
14
+ }
15
+
16
+ #maintain() {
17
+ const root = globalThis?.document?.documentElement?.style;
18
+ for (const prop in this.currentPropsMap) {
19
+ const cssProp = this.namespace ? `--${this.namespace}-${prop}` : `--${prop}`;
20
+ root.setProperty(cssProp, this.currentPropsMap[prop]);
21
+
22
+ Object.defineProperty(this, prop, {
23
+ value: `var(${cssProp})`,
24
+ writable: true,
25
+ configurable: true,
26
+ enumerable: false
27
+ });
28
+ }
29
+ }
30
+ }
31
+
32
+ function ValidateCssPropsFn(PropsMap) {
33
+ const validProps = new Set(Object.keys(document.documentElement.style));
34
+ for (const key in PropsMap) {
35
+ if (!validProps.has(key)) {
36
+ throw new Error(`Invalid CSS property: "${key}"`);
37
+ }
38
+ }
39
+ }
40
+
41
+ const useRoot = (PropsMap, options = {}) => new UseRoot(PropsMap, options);
42
+
43
+ export {
44
+ UseRoot,
45
+ useRoot
46
+ };
47
+
48
+
49
+ // Usage
50
+
51
+ /*
52
+ const Styles = {
53
+ S1 : {
54
+ background : 'white',
55
+ color : 'darkblue'
56
+ border : '2px darkblue solid"'
57
+ },
58
+ S2 : {
59
+ background : 'darkblue',
60
+ color : 'white'
61
+ border : '2px green solid"'
62
+ }
63
+ }
64
+ const {use, border, background, color} = useRoot(Style.S1)
65
+
66
+ tags.p("Test useRoot ").style({
67
+ border,
68
+ color,
69
+ background,
70
+ padding : '10px'
71
+ })
72
+
73
+ */
@@ -1,73 +1,99 @@
1
- // To do : remove old items
2
1
  import { useChannel } from "./use-channel.js";
3
- class UseStorage{
4
- constructor(storage, globalKey, initialValue){
5
- this.cache={
2
+
3
+ class UseStorage {
4
+ constructor(storage, globalKey, initialValue, use_channel = true) {
5
+ this.cache = {
6
6
  storage,
7
7
  globalKey,
8
- channel:useChannel(`Ziko:useStorage-${globalKey}`),
9
- oldItemKeys:new Set()
10
- }
11
- this.#init(initialValue);
12
- this.#maintain();
8
+ channel: use_channel ? useChannel(`Ziko:useStorage-${globalKey}`) : null,
9
+ oldItemKeys: new Set()
10
+ };
11
+
12
+ this.#init(initialValue, use_channel);
13
13
  }
14
- get items(){
15
- return JSON.parse(this.cache.storage[this.cache.globalKey]??null);
14
+
15
+ get items() {
16
+ const raw = this.cache.storage.getItem(this.cache.globalKey);
17
+ if (!raw) return {};
18
+ try {
19
+ return JSON.parse(raw);
20
+ } catch {
21
+ return {};
22
+ }
16
23
  }
24
+
17
25
  #maintain() {
18
- for(let i in this.items)Object.assign(this, { [[i]]: this.items[i] });
26
+ const items = this.items;
27
+ this.cache.oldItemKeys.forEach(k => delete this[k]);
28
+ for (const key in items) {
29
+ this[key] = items[key];
30
+ this.cache.oldItemKeys.add(key);
31
+ }
19
32
  }
20
- #init(initialValue){
21
- this.cache.channel=useChannel(`Ziko:useStorage-${this.cache.globalKey}`);
22
- this.cache.channel.on("Ziko-Storage-Updated",()=>this.#maintain());
23
- if(!initialValue)return;
24
- if(this.cache.storage[this.cache.globalKey]){
25
- Object.keys(this.items).forEach(key=>this.cache.oldItemKeys.add(key));
26
- // console.group("Ziko:useStorage")
27
- // console.warn(`Storage key '${this.cache.globalKey}' already exists. we will not overwrite it.`);
28
- // console.info(`%cWe'll keep the existing data.`,"background-color:#2222dd; color:gold;");
29
- // console.group("")
33
+ #init(initialValue, use_channel) {
34
+ if (use_channel && this.cache.channel) this.cache.channel.on("Ziko-Storage-Updated", () => this.#maintain());
35
+ if (!initialValue) {
36
+ this.#maintain();
37
+ return;
30
38
  }
39
+ if (this.cache.storage.getItem(this.cache.globalKey)) {
40
+ const existing = this.items;
41
+ Object.keys(existing).forEach(k => this.cache.oldItemKeys.add(k));
42
+ this.#maintain();
43
+ }
31
44
  else this.set(initialValue);
32
45
  }
33
- set(data){
34
- this.cache.storage.setItem(this.cache.globalKey,JSON.stringify(data));
35
- this.cache.channel.emit("Ziko-Storage-Updated",{});
36
- Object.keys(data).forEach(key=>this.cache.oldItemKeys.add(key));
46
+
47
+ set(data) {
48
+ this.cache.storage.setItem(this.cache.globalKey, JSON.stringify(data));
49
+ if (this.cache.channel) this.cache.channel.emit("Ziko-Storage-Updated", data);
37
50
  this.#maintain();
38
- return this
51
+ return this;
39
52
  }
40
- add(data){
41
- const db={
53
+
54
+ add(data) {
55
+ this.set({
42
56
  ...this.items,
43
57
  ...data
44
- }
45
- this.cache.storage.setItem(this.cache.globalKey,JSON.stringify(db));
46
- this.#maintain();
58
+ });
47
59
  return this;
48
60
  }
49
- remove(...keys){
50
- const db={...this.items};
51
- for(let i=0;i<keys.length;i++){
52
- delete db[keys[i]];
53
- delete this[keys[i]];
54
- }
55
- this.set(db);
61
+ remove(...keys) {
62
+ const items = { ...this.items };
63
+ keys.forEach(key => {
64
+ delete items[key];
65
+ delete this[key];
66
+ this.cache.oldItemKeys.delete(key);
67
+ });
68
+ this.set(items);
56
69
  return this;
57
70
  }
58
- get(key){
71
+ get(key) {
59
72
  return this.items[key];
60
73
  }
61
- clear(){
74
+ clear() {
62
75
  this.cache.storage.removeItem(this.cache.globalKey);
76
+ this.cache.oldItemKeys.forEach(k => delete this[k]);
77
+ this.cache.oldItemKeys.clear();
63
78
  this.#maintain();
64
79
  return this;
65
80
  }
66
-
81
+ onStorageUpdated(callback) {
82
+ if (this.cache.channel) {
83
+ this.cache.channel.on("Ziko-Storage-Updated", callback);
84
+ }
85
+ return this;
86
+ }
67
87
  }
68
- const useLocaleStorage=(key,initialValue)=>new UseStorage(localStorage,key,initialValue);
69
- const useSessionStorage=(key,initialValue)=>new UseStorage(sessionStorage,key,initialValue);
70
- export{
88
+
89
+ // factory functions
90
+ const useLocaleStorage = (key, initialValue, use_channel = true) =>
91
+ new UseStorage(localStorage, key, initialValue, use_channel);
92
+
93
+ const useSessionStorage = (key, initialValue, use_channel = true) =>
94
+ new UseStorage(sessionStorage, key, initialValue, use_channel);
95
+
96
+ export {
71
97
  useLocaleStorage,
72
98
  useSessionStorage
73
- }
99
+ };
@@ -0,0 +1,55 @@
1
+ class UseThread {
2
+ #worker;
3
+ #callbacks = new Map();
4
+ #idCounter = 0;
5
+
6
+ constructor() {
7
+ const workerCode = `
8
+ this.onmessage = function(e) {
9
+ const { id, funStr, args, close } = e.data;
10
+ try {
11
+ const func = new Function("return " + funStr)();
12
+ const result = func(...args);
13
+ postMessage({ id, result });
14
+ } catch (error) {
15
+ postMessage({ id, error: error.message });
16
+ } finally {
17
+ if (close) self.close();
18
+ }
19
+ }
20
+ `;
21
+ const blob = new Blob([workerCode], { type: "text/javascript" });
22
+ this.#worker = new Worker(URL.createObjectURL(blob));
23
+
24
+ this.#worker.addEventListener("message", (e) => {
25
+ const { id, result, error } = e.data;
26
+ const callback = this.#callbacks.get(id);
27
+ if (!callback) return;
28
+
29
+ callback(result, error);
30
+ this.#callbacks.delete(id);
31
+ });
32
+ }
33
+ call(func, callback, args = [], close = true) {
34
+ if (typeof func !== "function") throw new TypeError("func must be a function");
35
+ const id = ++this.#idCounter;
36
+ this.#callbacks.set(id, callback);
37
+
38
+ this.#worker.postMessage({
39
+ id,
40
+ funStr: func.toString(),
41
+ args,
42
+ close
43
+ });
44
+
45
+ return this;
46
+ }
47
+
48
+ terminate() {
49
+ this.#worker.terminate();
50
+ }
51
+ }
52
+
53
+ const useThread = (func, callback, args = [], close = true) => new UseThread().call(func, callback, args, close);
54
+
55
+ export { UseThread, useThread };
@@ -0,0 +1,43 @@
1
+ import { useEventEmitter } from "./use-event-emitter.js";
2
+
3
+ class UseTitle {
4
+ constructor(title = document.title, withEmitter = true) {
5
+ this.cache = {
6
+ emitter: null
7
+ };
8
+
9
+ if (withEmitter) this.useEventEmitter();
10
+ this.set(title);
11
+ }
12
+
13
+ useEventEmitter() {
14
+ this.cache.emitter = useEventEmitter();
15
+ return this;
16
+ }
17
+
18
+ setTitle(title) {
19
+ if (title !== document.title) {
20
+ document.title = title;
21
+
22
+ if (this.cache.emitter) {
23
+ this.cache.emitter.emit("ziko:title-changed", title);
24
+ }
25
+ }
26
+ return this;
27
+ }
28
+
29
+ get current() {
30
+ return document.title;
31
+ }
32
+
33
+ onChange(callback) {
34
+ if (this.cache.emitter) {
35
+ this.cache.emitter.on("ziko:title-changed", callback);
36
+ }
37
+ return this;
38
+ }
39
+ }
40
+
41
+ const useTitle = (title, withEmitter = true) => new UseTitle(title, withEmitter);
42
+
43
+ export { useTitle };
@@ -1,11 +1,11 @@
1
- import { useEventEmitter } from "../../../use/use-event-emmiter.js";
1
+ import { useEventEmitter } from "../../../hooks/use-event-emitter.js";
2
2
  class ZikoUseFavIcon{
3
- constructor(FavIcon,useEventEmitter=true){
3
+ constructor(FavIcon,withEmitter=true){
4
4
  this.#init();
5
5
  this.cache={
6
6
  Emitter:null
7
7
  }
8
- if(useEventEmitter)this.useEventEmitter();
8
+ if(withEmitter)this.useEventEmitter();
9
9
  this.set(FavIcon);
10
10
  }
11
11
  #init(){
@@ -34,5 +34,5 @@ class ZikoUseFavIcon{
34
34
  }
35
35
 
36
36
  }
37
- const useFavIcon=(FavIcon,useEventEmitter)=>new ZikoUseFavIcon(FavIcon,useEventEmitter);
37
+ const useFavIcon=(FavIcon,withEmitter)=>new ZikoUseFavIcon(FavIcon,withEmitter);
38
38
  export{ useFavIcon }
@@ -1,4 +1,4 @@
1
- import { useEventEmitter } from "../../../use/use-event-emmiter.js";
1
+ import { useEventEmitter } from "../../../hooks/use-event-emitter.js";
2
2
  class ZikoUseTitle{
3
3
  constructor(title=document.title,useEventEmitter=true){
4
4
  this.cache={
package/src/use/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // export * from './use-channel.js';
2
- export * from './use-event-emmiter.js';
2
+ // export * from './use-event-emmiter.js';
3
3
  export * from './use-thread.js';
4
4
 
5
5
  export * from './use-root.js';
@@ -1,7 +1,7 @@
1
1
  class UseEventEmitter {
2
- constructor() {
2
+ constructor(maxListeners = 10) {
3
3
  this.events = {};
4
- this.maxListeners = 10;
4
+ this.maxListeners = maxListeners;
5
5
  }
6
6
  on(event, listener) {
7
7
  if (!this.events[event]) {
@@ -60,5 +60,5 @@ class UseEventEmitter {
60
60
  }
61
61
  }
62
62
 
63
- const useEventEmitter=()=>new UseEventEmitter()
63
+ const useEventEmitter=(maxListeners)=>new UseEventEmitter(maxListeners)
64
64
  export{useEventEmitter}
@@ -1,4 +1,11 @@
1
1
  export type * from './use-channel.d.ts'
2
+ export type * from './use-storage.d.ts'
2
3
  export type * from './use-state.d.ts'
3
4
  export type * from './use-derived.d.ts'
4
- export type * from './use-reactive.d.ts'
5
+ export type * from './use-reactive.d.ts'
6
+ export type * from './use-Thread.d.ts'
7
+ export type * from './use-event-emitter.d.ts'
8
+ export type * from './use-media-query.d.ts'
9
+ export type * from './use-title.d.ts'
10
+ export type * from './use-favicon.d.ts'
11
+ export type * from './use-root.d.ts'