ziko 0.49.5 → 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.
package/dist/ziko.js CHANGED
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  Project: ziko.js
4
4
  Author: Zakaria Elalaoui
5
- Date : Wed Nov 26 2025 11:58:54 GMT+0100 (UTC+01:00)
5
+ Date : Sat Nov 29 2025 19:48:11 GMT+0100 (UTC+01:00)
6
6
  Git-Repo : https://github.com/zakarialaoui10/ziko.js
7
7
  Git-Wiki : https://github.com/zakarialaoui10/ziko.js/wiki
8
8
  Released under MIT License
@@ -1184,132 +1184,180 @@
1184
1184
  };
1185
1185
 
1186
1186
  class UseChannel {
1187
+ #channel;
1188
+ #eventData;
1189
+ #handlers;
1190
+ #uuid;
1191
+ #subscribers;
1192
+ #currentRooms;
1187
1193
  constructor(name = "") {
1188
- this.channel = new BroadcastChannel(name);
1189
- this.eventData = new Map();
1190
- this.handlers = new Map();
1191
- this.uuid = "ziko-channel:" + Random.string(10);
1192
- this.subscribers = new Set([this.uuid]);
1193
-
1194
- this.channel.addEventListener("message", (e) => {
1195
- const { last_sent_event, userId, eventData } = e.data;
1196
-
1197
- // ignore own events
1198
- if (userId === this.uuid) return;
1199
-
1200
- this.subscribers.add(userId);
1201
-
1202
- // sync data
1203
- this.eventData = new Map(eventData);
1204
-
1205
- const data = this.eventData.get(last_sent_event);
1206
- const handlers = this.handlers.get(last_sent_event);
1207
-
1208
- if (handlers) {
1209
- handlers.forEach(fn => fn(data));
1210
- }
1194
+ this.#channel = new BroadcastChannel(name);
1195
+ this.#eventData = new Map();
1196
+ this.#handlers = new Map(); // Map<event, Array<{fn, rooms}>>
1197
+ this.#uuid = "ziko-channel:" + Random.string(10);
1198
+ this.#subscribers = new Set([this.#uuid]);
1199
+ this.#currentRooms = new Set();
1200
+ this.#channel.addEventListener("message", (e) => {
1201
+ const { last_sent_event, userId, eventData, rooms } = e.data;
1202
+ if (userId === this.#uuid) return; // ignore own messages
1203
+ // broadcast if no rooms, else check intersection
1204
+ if (rooms && rooms.length && !rooms.some(r => this.#currentRooms.has(r))) return;
1205
+ this.#subscribers.add(userId);
1206
+ this.#eventData = new Map(eventData);
1207
+ const handlersList = this.#handlers.get(last_sent_event);
1208
+ if (!handlersList) return;
1209
+ handlersList.forEach(({ fn, rooms: handlerRooms }) => {
1210
+ // trigger if listener has no room filter, or intersects subscriber rooms
1211
+ if (!handlerRooms || handlerRooms.length === 0 ||
1212
+ !rooms || rooms.some(r => handlerRooms.includes(r))) {
1213
+ fn(this.#eventData.get(last_sent_event));
1214
+ }
1215
+ });
1211
1216
  });
1212
1217
  }
1213
1218
 
1214
- emit(event, data) {
1215
- this.eventData.set(event, data);
1216
-
1217
- this.channel.postMessage({
1218
- eventData: this.eventData,
1219
+ emit(event, data, rooms) {
1220
+ this.#eventData.set(event, data);
1221
+ if(typeof rooms === 'string') rooms = [rooms];
1222
+ this.#channel.postMessage({
1223
+ eventData: Array.from(this.#eventData.entries()),
1219
1224
  last_sent_event: event,
1220
- userId: this.uuid
1225
+ userId: this.#uuid,
1226
+ rooms: rooms && rooms.length ? rooms : undefined
1221
1227
  });
1222
1228
  return this;
1223
1229
  }
1224
-
1225
- on(event, handler = console.log) {
1226
- if (!this.handlers.has(event)) {
1227
- this.handlers.set(event, []);
1228
- }
1229
- this.handlers.get(event).push(handler);
1230
+ on(event, handler = console.log, rooms) {
1231
+ if (!this.#handlers.has(event)) this.#handlers.set(event, []);
1232
+ if(typeof rooms === 'string') rooms = [rooms];
1233
+ this.#handlers.get(event).push({ fn: handler, rooms });
1230
1234
  return this;
1231
1235
  }
1232
-
1233
- close() {
1234
- this.channel.close();
1236
+ off(event, handler) {
1237
+ if (!this.#handlers.has(event)) return this;
1238
+ this.#handlers.set(
1239
+ event,
1240
+ this.#handlers.get(event).filter(h => h.fn !== handler)
1241
+ );
1235
1242
  return this;
1236
1243
  }
1237
-
1238
- get broadcast() {
1244
+ once(event, handler, rooms) {
1245
+ const wrapper = (data) => {
1246
+ handler(data);
1247
+ this.off(event, wrapper);
1248
+ };
1249
+ this.on(event, wrapper, rooms);
1250
+ return this;
1251
+ }
1252
+ join(...rooms) {
1253
+ rooms.forEach(r => this.#currentRooms.add(r));
1254
+ return this;
1255
+ }
1256
+ leave(...rooms) {
1257
+ if (!rooms.length) this.#currentRooms.clear();
1258
+ else rooms.forEach(r => this.#currentRooms.delete(r));
1259
+ return this;
1260
+ }
1261
+ close() {
1262
+ this.#channel.close();
1239
1263
  return this;
1240
1264
  }
1241
1265
  }
1242
1266
 
1243
1267
  const useChannel = (name) => new UseChannel(name);
1244
1268
 
1245
- // To do : remove old items
1246
- class UseStorage{
1247
- constructor(storage, globalKey, initialValue){
1248
- this.cache={
1269
+ class UseStorage {
1270
+ constructor(storage, globalKey, initialValue, use_channel = true) {
1271
+ this.cache = {
1249
1272
  storage,
1250
1273
  globalKey,
1251
- channel:useChannel(`Ziko:useStorage-${globalKey}`),
1252
- oldItemKeys:new Set()
1274
+ channel: use_channel ? useChannel(`Ziko:useStorage-${globalKey}`) : null,
1275
+ oldItemKeys: new Set()
1253
1276
  };
1254
- this.#init(initialValue);
1255
- this.#maintain();
1277
+
1278
+ this.#init(initialValue, use_channel);
1256
1279
  }
1257
- get items(){
1258
- return JSON.parse(this.cache.storage[this.cache.globalKey]??null);
1280
+
1281
+ get items() {
1282
+ const raw = this.cache.storage.getItem(this.cache.globalKey);
1283
+ if (!raw) return {};
1284
+ try {
1285
+ return JSON.parse(raw);
1286
+ } catch {
1287
+ return {};
1288
+ }
1259
1289
  }
1290
+
1260
1291
  #maintain() {
1261
- for(let i in this.items)Object.assign(this, { [[i]]: this.items[i] });
1262
- }
1263
- #init(initialValue){
1264
- this.cache.channel=useChannel(`Ziko:useStorage-${this.cache.globalKey}`);
1265
- this.cache.channel.on("Ziko-Storage-Updated",()=>this.#maintain());
1266
- if(!initialValue)return;
1267
- if(this.cache.storage[this.cache.globalKey]){
1268
- Object.keys(this.items).forEach(key=>this.cache.oldItemKeys.add(key));
1269
- // console.group("Ziko:useStorage")
1270
- // console.warn(`Storage key '${this.cache.globalKey}' already exists. we will not overwrite it.`);
1271
- // console.info(`%cWe'll keep the existing data.`,"background-color:#2222dd; color:gold;");
1272
- // console.group("")
1292
+ const items = this.items;
1293
+ this.cache.oldItemKeys.forEach(k => delete this[k]);
1294
+ for (const key in items) {
1295
+ this[key] = items[key];
1296
+ this.cache.oldItemKeys.add(key);
1297
+ }
1298
+ }
1299
+ #init(initialValue, use_channel) {
1300
+ if (use_channel && this.cache.channel) this.cache.channel.on("Ziko-Storage-Updated", () => this.#maintain());
1301
+ if (!initialValue) {
1302
+ this.#maintain();
1303
+ return;
1273
1304
  }
1305
+ if (this.cache.storage.getItem(this.cache.globalKey)) {
1306
+ const existing = this.items;
1307
+ Object.keys(existing).forEach(k => this.cache.oldItemKeys.add(k));
1308
+ this.#maintain();
1309
+ }
1274
1310
  else this.set(initialValue);
1275
1311
  }
1276
- set(data){
1277
- this.cache.storage.setItem(this.cache.globalKey,JSON.stringify(data));
1278
- this.cache.channel.emit("Ziko-Storage-Updated",{});
1279
- Object.keys(data).forEach(key=>this.cache.oldItemKeys.add(key));
1312
+
1313
+ set(data) {
1314
+ this.cache.storage.setItem(this.cache.globalKey, JSON.stringify(data));
1315
+ if (this.cache.channel) this.cache.channel.emit("Ziko-Storage-Updated", data);
1280
1316
  this.#maintain();
1281
- return this
1317
+ return this;
1282
1318
  }
1283
- add(data){
1284
- const db={
1319
+
1320
+ add(data) {
1321
+ this.set({
1285
1322
  ...this.items,
1286
1323
  ...data
1287
- };
1288
- this.cache.storage.setItem(this.cache.globalKey,JSON.stringify(db));
1289
- this.#maintain();
1324
+ });
1290
1325
  return this;
1291
1326
  }
1292
- remove(...keys){
1293
- const db={...this.items};
1294
- for(let i=0;i<keys.length;i++){
1295
- delete db[keys[i]];
1296
- delete this[keys[i]];
1297
- }
1298
- this.set(db);
1327
+ remove(...keys) {
1328
+ const items = { ...this.items };
1329
+ keys.forEach(key => {
1330
+ delete items[key];
1331
+ delete this[key];
1332
+ this.cache.oldItemKeys.delete(key);
1333
+ });
1334
+ this.set(items);
1299
1335
  return this;
1300
1336
  }
1301
- get(key){
1337
+ get(key) {
1302
1338
  return this.items[key];
1303
1339
  }
1304
- clear(){
1340
+ clear() {
1305
1341
  this.cache.storage.removeItem(this.cache.globalKey);
1342
+ this.cache.oldItemKeys.forEach(k => delete this[k]);
1343
+ this.cache.oldItemKeys.clear();
1306
1344
  this.#maintain();
1307
1345
  return this;
1308
1346
  }
1309
-
1347
+ onStorageUpdated(callback) {
1348
+ if (this.cache.channel) {
1349
+ this.cache.channel.on("Ziko-Storage-Updated", callback);
1350
+ }
1351
+ return this;
1352
+ }
1310
1353
  }
1311
- const useLocaleStorage=(key,initialValue)=>new UseStorage(localStorage,key,initialValue);
1312
- const useSessionStorage=(key,initialValue)=>new UseStorage(sessionStorage,key,initialValue);
1354
+
1355
+ // factory functions
1356
+ const useLocaleStorage = (key, initialValue, use_channel = true) =>
1357
+ new UseStorage(localStorage, key, initialValue, use_channel);
1358
+
1359
+ const useSessionStorage = (key, initialValue, use_channel = true) =>
1360
+ new UseStorage(sessionStorage, key, initialValue, use_channel);
1313
1361
 
1314
1362
  const __State__ = {
1315
1363
  store : new Map(),
@@ -4761,76 +4809,79 @@
4761
4809
  };
4762
4810
 
4763
4811
  class UseEventEmitter {
4764
- constructor() {
4765
- this.events = {};
4766
- this.maxListeners = 10;
4812
+ constructor(maxListeners = 10) {
4813
+ this.events = {};
4814
+ this.maxListeners = maxListeners;
4767
4815
  }
4816
+
4768
4817
  on(event, listener) {
4769
- if (!this.events[event]) {
4770
- this.events[event] = [];
4771
- }
4772
- this.events[event].push(listener);
4773
- if (this.events[event].length > this.maxListeners) {
4774
- console.warn(`Warning: Possible memory leak. Event '${event}' has more than ${this.maxListeners} listeners.`);
4775
- }
4818
+ if (!this.events[event]) this.events[event] = [];
4819
+ this.events[event].push(listener);
4820
+ if (this.events[event].length > this.maxListeners) {
4821
+ console.warn(`Warning: Possible memory leak. Event '${event}' has more than ${this.maxListeners} listeners.`);
4822
+ }
4823
+ return this;
4776
4824
  }
4825
+
4777
4826
  once(event, listener) {
4778
- const onceListener = (data) => {
4779
- this.off(event, onceListener); // Remove the listener after it's been called
4780
- listener(data);
4781
- };
4782
- this.on(event, onceListener);
4827
+ const wrapper = (...args) => {
4828
+ this.off(event, wrapper);
4829
+ listener(...args);
4830
+ };
4831
+ return this.on(event, wrapper);
4783
4832
  }
4784
-
4833
+
4785
4834
  off(event, listener) {
4786
- const listeners = this.events[event];
4787
- if (listeners) {
4835
+ const listeners = this.events[event];
4836
+ if (!listeners) return this;
4837
+
4788
4838
  const index = listeners.indexOf(listener);
4789
4839
  if (index !== -1) {
4790
- listeners.splice(index, 1);
4840
+ listeners.splice(index, 1);
4791
4841
  }
4792
- }
4842
+
4843
+ return this;
4793
4844
  }
4794
-
4845
+
4795
4846
  emit(event, data) {
4796
- const listeners = this.events[event];
4797
- if (listeners) {
4798
- listeners.forEach(listener => {
4799
- listener(data);
4847
+ const listeners = this.events[event];
4848
+ if (!listeners) return false;
4849
+
4850
+ // Make a copy so removing listeners inside callbacks doesn't affect iteration
4851
+ [...listeners].forEach(listener => {
4852
+ try {
4853
+ listener(data);
4854
+ } catch (e) {
4855
+ console.error(`Error in listener for '${event}':`, e);
4856
+ }
4800
4857
  });
4801
- }
4802
- }
4803
-
4804
- clear(event) {
4805
- if (event) {
4806
- delete this.events[event];
4807
- } else {
4808
- this.events = {};
4809
- }
4858
+
4859
+ return true;
4810
4860
  }
4811
-
4812
- setMaxListener(event, max) {
4813
- this.maxListeners = max;
4861
+ remove(event){
4862
+ delete this.events[event];
4863
+ return this;
4814
4864
  }
4815
-
4816
- removeAllListeners(event) {
4817
- if (event) {
4818
- this.events[event] = [];
4819
- } else {
4865
+ clear() {
4820
4866
  this.events = {};
4821
- }
4867
+ return this;
4822
4868
  }
4823
- }
4824
4869
 
4825
- const useEventEmitter=()=>new UseEventEmitter();
4870
+ setMaxListeners(max) {
4871
+ this.maxListeners = max;
4872
+ return this;
4873
+ }
4874
+ }
4875
+
4876
+ const useEventEmitter = (maxListeners) => new UseEventEmitter(maxListeners);
4826
4877
 
4827
4878
  class ZikoUseFavIcon{
4828
- constructor(FavIcon,useEventEmitter=true){
4879
+ constructor(FavIcon,withEmitter=true){
4829
4880
  this.#init();
4830
4881
  this.cache={
4831
4882
  Emitter:null
4832
4883
  };
4833
- if(useEventEmitter)this.useEventEmitter();
4884
+ if(withEmitter)this.useEventEmitter();
4834
4885
  this.set(FavIcon);
4835
4886
  }
4836
4887
  #init(){
@@ -4859,7 +4910,7 @@
4859
4910
  }
4860
4911
 
4861
4912
  }
4862
- const useFavIcon=(FavIcon,useEventEmitter)=>new ZikoUseFavIcon(FavIcon,useEventEmitter);
4913
+ const useFavIcon=(FavIcon,withEmitter)=>new ZikoUseFavIcon(FavIcon,withEmitter);
4863
4914
 
4864
4915
  class ZikoMeta{
4865
4916
  constructor({viewport,charset,description,author,keywords}){
@@ -4939,7 +4990,7 @@
4939
4990
  return this;
4940
4991
  }
4941
4992
  }
4942
- const useTitle=(title, useEventEmitter)=>new ZikoUseTitle(title, useEventEmitter);
4993
+ const useTitle$1=(title, useEventEmitter)=>new ZikoUseTitle(title, useEventEmitter);
4943
4994
 
4944
4995
  // import {useLink} from "./";
4945
4996
  class ZikoHead{
@@ -4947,7 +4998,7 @@
4947
4998
  this.html = globalThis?.document?.documentElement;
4948
4999
  this.head = globalThis?.document?.head;
4949
5000
 
4950
- title && useTitle(title);
5001
+ title && useTitle$1(title);
4951
5002
  lang && this.setLang(lang);
4952
5003
  icon && useFavIcon(icon);
4953
5004
  meta && useMeta(meta);
@@ -5184,7 +5235,7 @@
5184
5235
  const subscribers = new Set();
5185
5236
 
5186
5237
  sources.forEach(source => {
5187
- const srcValue = source(); // getValue()
5238
+ const srcValue = source();
5188
5239
  srcValue._subscribe(() => {
5189
5240
  {
5190
5241
  const newVal = deriveFn(...sources.map(s => s().value));
@@ -5213,121 +5264,155 @@
5213
5264
  nested_value
5214
5265
  );
5215
5266
 
5216
- class UseThreed {
5217
- #workerContent;
5267
+ class UseThread {
5268
+ #worker;
5269
+ #callbacks = new Map();
5270
+ #idCounter = 0;
5271
+
5218
5272
  constructor() {
5219
- this.#workerContent = (
5220
- function (msg) {
5221
- try {
5222
- const func = new Function("return " + msg.data.fun)();
5223
- let result = func();
5224
- postMessage({ result });
5225
- } catch (error) {
5226
- postMessage({ error: error.message });
5227
- } finally {
5228
- if (msg.data.close) self.close();
5229
- }
5273
+ const workerCode = `
5274
+ this.onmessage = function(e) {
5275
+ const { id, funStr, args, close } = e.data;
5276
+ try {
5277
+ const func = new Function("return " + funStr)();
5278
+ const result = func(...args);
5279
+ postMessage({ id, result });
5280
+ } catch (error) {
5281
+ postMessage({ id, error: error.message });
5282
+ } finally {
5283
+ if (close) self.close();
5230
5284
  }
5231
- ).toString();
5232
- this.blob = new Blob(["this.onmessage = " + this.#workerContent], { type: "text/javascript" });
5233
- this.worker = new Worker(window.URL.createObjectURL(this.blob));
5285
+ }
5286
+ `;
5287
+ const blob = new Blob([workerCode], { type: "text/javascript" });
5288
+ this.#worker = new Worker(URL.createObjectURL(blob));
5289
+
5290
+ this.#worker.addEventListener("message", (e) => {
5291
+ const { id, result, error } = e.data;
5292
+ const callback = this.#callbacks.get(id);
5293
+ if (!callback) return;
5294
+
5295
+ callback(result, error);
5296
+ this.#callbacks.delete(id);
5297
+ });
5234
5298
  }
5235
- call(func, callback, close = true) {
5236
- this.worker.postMessage({
5237
- fun: func.toString(),
5299
+ call(func, callback, args = [], close = true) {
5300
+ if (typeof func !== "function") throw new TypeError("func must be a function");
5301
+ const id = ++this.#idCounter;
5302
+ this.#callbacks.set(id, callback);
5303
+
5304
+ this.#worker.postMessage({
5305
+ id,
5306
+ funStr: func.toString(),
5307
+ args,
5238
5308
  close
5239
5309
  });
5240
- this.worker.onmessage = function (e) {
5241
- if (e.data.error) {
5242
- console.error(e.data.error);
5243
- } else {
5244
- callback(e.data.result);
5245
- }
5246
- };
5310
+
5247
5311
  return this;
5248
5312
  }
5249
- }
5250
5313
 
5251
- const useThread = (func, callback , close) => {
5252
- const T = new UseThreed();
5253
- if (func) {
5254
- T.call(func, callback , close);
5314
+ terminate() {
5315
+ this.#worker.terminate();
5255
5316
  }
5256
- return T;
5257
- };
5317
+ }
5258
5318
 
5259
- class UseRoot {
5260
- constructor(PropsMap, {namespace = 'Ziko', register, ValidateCssProps = false} = {}){
5261
- this.currentPropsMap = PropsMap;
5262
- this.namespace = namespace;
5263
- this.ValidateCssProps = ValidateCssProps;
5264
- // this.pairs = {};
5265
- // this.#maintain()
5266
- this.use(PropsMap);
5267
- }
5268
- use(PropsMap){
5269
- if(this.ValidateCssProps) ValidateCssProps(PropsMap);
5270
- this.currentPropsMap = PropsMap;
5271
- this.#maintain();
5272
- return this;
5319
+ /*
5320
+ [
5321
+ {
5322
+ query: '(min-width: 600px)',
5323
+ callback: () => console.log(1)
5324
+ },
5325
+ {
5326
+ query: '(max-width: 300px)',
5327
+ callback: () => console.log(2)
5273
5328
  }
5274
- #maintain(){
5275
- const root = globalThis?.document?.documentElement?.style;
5276
- for(let prop in this.currentPropsMap){
5277
- const cssProp = this.namespace ? `--${this.namespace}-${prop}` : `--${prop}`;
5278
- root.setProperty(
5279
- cssProp,
5280
- this.currentPropsMap[prop]
5281
- );
5282
- // Object.assign(this.pairs, {
5283
- // [prop] : `var(--${this.namespace}-${prop})`
5284
- // })
5285
- Object.defineProperty(this, prop, {
5286
- value: `var(${cssProp})`,
5287
- writable: true,
5288
- configurable: true,
5289
- enumerable: false
5290
- });
5291
- }
5329
+ ]
5330
+ */
5331
+
5332
+ class UseMediaQuery {
5333
+ #mediaQueryRules;
5334
+ #fallback;
5335
+ #lastCalledCallback = null;
5336
+
5337
+ constructor(mediaQueryRules = [], fallback = () => {}) {
5338
+ this.#mediaQueryRules = mediaQueryRules;
5339
+ this.#fallback = fallback;
5340
+
5341
+ this.#init();
5292
5342
  }
5293
- }
5294
5343
 
5295
- function ValidateCssProps(PropsMap){
5296
- const validProps = new Set(Object.keys(document.documentElement.style));
5297
- for (let key in PropsMap) {
5298
- if (!validProps.has(key)) {
5299
- throw new Error(`Invalid CSS property: "${key}"`);
5300
- }
5344
+ // PRIVATE: check if ANY rule matches
5345
+ #checkAllRules() {
5346
+ return this.#mediaQueryRules.some(
5347
+ ({ query }) => globalThis.matchMedia(query).matches
5348
+ );
5301
5349
  }
5302
- }
5303
5350
 
5304
- const useRoot = (PropsMap, {namespace, register, ValidateCssProps} = {}) => new UseRoot(PropsMap, {namespace, register, ValidateCssProps});
5351
+ // PRIVATE: installs listeners and initial checks
5352
+ #init() {
5353
+ this.#mediaQueryRules.forEach(({ query, callback }) => {
5354
+ const mediaQueryList = globalThis.matchMedia(query);
5305
5355
 
5306
- // Usage
5356
+ const checkMatches = () => {
5357
+ const anyMatch = this.#checkAllRules();
5307
5358
 
5308
- /*
5309
- const Styles = {
5310
- S1 : {
5311
- background : 'white',
5312
- color : 'darkblue'
5313
- border : '2px darkblue solid"'
5314
- },
5315
- S2 : {
5316
- background : 'darkblue',
5317
- color : 'white'
5318
- border : '2px green solid"'
5319
- }
5359
+ if (mediaQueryList.matches) {
5360
+ callback();
5361
+ this.#lastCalledCallback = callback;
5362
+ } else if (!anyMatch && this.#lastCalledCallback !== this.#fallback) {
5363
+ this.#fallback();
5364
+ this.#lastCalledCallback = this.#fallback;
5365
+ }
5366
+ };
5367
+
5368
+ checkMatches();
5369
+ mediaQueryList.addEventListener("change", checkMatches);
5370
+ });
5371
+ }
5320
5372
  }
5321
- const {use, border, background, color} = useRoot(Style.S1)
5322
5373
 
5323
- tags.p("Test useRoot ").style({
5324
- border,
5325
- color,
5326
- background,
5327
- padding : '10px'
5328
- })
5374
+ const useMediaQuery = (mediaQueryRules, fallback) =>
5375
+ new UseMediaQuery(mediaQueryRules, fallback);
5376
+
5377
+ class UseTitle {
5378
+ constructor(title = document.title, withEmitter = true) {
5379
+ this.cache = {
5380
+ emitter: null
5381
+ };
5329
5382
 
5330
- */
5383
+ if (withEmitter) this.useEventEmitter();
5384
+ this.set(title);
5385
+ }
5386
+
5387
+ useEventEmitter() {
5388
+ this.cache.emitter = useEventEmitter();
5389
+ return this;
5390
+ }
5391
+
5392
+ setTitle(title) {
5393
+ if (title !== document.title) {
5394
+ document.title = title;
5395
+
5396
+ if (this.cache.emitter) {
5397
+ this.cache.emitter.emit("ziko:title-changed", title);
5398
+ }
5399
+ }
5400
+ return this;
5401
+ }
5402
+
5403
+ get current() {
5404
+ return document.title;
5405
+ }
5406
+
5407
+ onChange(callback) {
5408
+ if (this.cache.emitter) {
5409
+ this.cache.emitter.on("ziko:title-changed", callback);
5410
+ }
5411
+ return this;
5412
+ }
5413
+ }
5414
+
5415
+ const useTitle = (title, withEmitter = true) => new UseTitle(title, withEmitter);
5331
5416
 
5332
5417
  let {sqrt, cos, sin, exp, log, cosh, sinh} = Math;
5333
5418
  // Math.abs = new Proxy(Math.abs, {
@@ -5422,7 +5507,7 @@
5422
5507
  exports.UISVGWrapper = UISVGWrapper;
5423
5508
  exports.UISwitch = UISwitch;
5424
5509
  exports.UIView = UIView;
5425
- exports.UseRoot = UseRoot;
5510
+ exports.UseThread = UseThread;
5426
5511
  exports.Utils = Utils;
5427
5512
  exports.View = View;
5428
5513
  exports.ZikoApp = ZikoApp;
@@ -5594,14 +5679,15 @@
5594
5679
  exports.timeTaken = timeTaken;
5595
5680
  exports.time_memory_Taken = time_memory_Taken;
5596
5681
  exports.timeout = timeout;
5682
+ exports.useChannel = useChannel;
5597
5683
  exports.useDerived = useDerived;
5598
5684
  exports.useEventEmitter = useEventEmitter;
5599
5685
  exports.useLocaleStorage = useLocaleStorage;
5686
+ exports.useMediaQuery = useMediaQuery;
5600
5687
  exports.useReactive = useReactive;
5601
- exports.useRoot = useRoot;
5602
5688
  exports.useSessionStorage = useSessionStorage;
5603
5689
  exports.useState = useState;
5604
- exports.useThread = useThread;
5690
+ exports.useTitle = useTitle;
5605
5691
  exports.wait = wait;
5606
5692
  exports.waitForUIElm = waitForUIElm;
5607
5693
  exports.waitForUIElmSync = waitForUIElmSync;