@lisergia/core 14.0.1 → 16.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/dist/index.mjs ADDED
@@ -0,0 +1,563 @@
1
+ // src/App.ts
2
+ import { autorun, computed, makeObservable, observable, observe } from "mobx";
3
+
4
+ // src/EventEmitter.ts
5
+ import AutoBind from "auto-bind";
6
+ import { createNanoEvents } from "nanoevents";
7
+ var EventEmitter = class {
8
+ emitter;
9
+ entries = /* @__PURE__ */ new Map();
10
+ constructor() {
11
+ AutoBind(this);
12
+ this.emitter = createNanoEvents();
13
+ }
14
+ on(event, callback) {
15
+ if (!callback) {
16
+ return console.trace("No callback provided");
17
+ }
18
+ const emitter = this.emitter.on(event, callback);
19
+ this.entries.set(callback, emitter);
20
+ return emitter;
21
+ }
22
+ off(event, callback) {
23
+ const unsubscribe = this.entries.get(callback);
24
+ if (unsubscribe) {
25
+ unsubscribe();
26
+ }
27
+ }
28
+ fire(event, ...args) {
29
+ this.emitter.emit(event, ...args);
30
+ }
31
+ destroy() {
32
+ this.entries.forEach((unsubscribe) => unsubscribe());
33
+ }
34
+ };
35
+
36
+ // src/Component.ts
37
+ var Component = class extends EventEmitter {
38
+ application;
39
+ autoListeners;
40
+ autoMount;
41
+ classes;
42
+ selector;
43
+ selectors;
44
+ id;
45
+ element;
46
+ elements = {};
47
+ constructor({
48
+ application,
49
+ autoListeners = true,
50
+ autoMount = true,
51
+ classes,
52
+ element,
53
+ elements,
54
+ id
55
+ }) {
56
+ super();
57
+ this.application = application;
58
+ this.autoListeners = autoListeners;
59
+ this.autoMount = autoMount;
60
+ this.classes = classes;
61
+ this.selector = element;
62
+ this.selectors = elements;
63
+ this.id = id;
64
+ if (this.autoMount) {
65
+ this.create();
66
+ }
67
+ if (this.autoListeners) {
68
+ this.addEventListeners();
69
+ }
70
+ }
71
+ create() {
72
+ if (this.selector) {
73
+ this.initElement(this.selector);
74
+ }
75
+ if (this.selectors) {
76
+ this.initElements(this.selectors);
77
+ }
78
+ }
79
+ initElement(selector) {
80
+ if (selector instanceof HTMLElement) {
81
+ this.element = selector;
82
+ } else {
83
+ this.element = document.querySelector(selector);
84
+ }
85
+ }
86
+ initElements(selectors) {
87
+ for (const key in selectors) {
88
+ const selector = selectors[key];
89
+ if (selector === window) {
90
+ this.elements[key] = window;
91
+ } else if (selector instanceof HTMLElement) {
92
+ this.elements[key] = selector;
93
+ } else if (selector instanceof NodeList) {
94
+ this.elements[key] = selector;
95
+ } else if (Array.isArray(selector)) {
96
+ this.elements[key] = selector;
97
+ } else {
98
+ const elements = this.element.querySelectorAll(selector);
99
+ if (elements.length === 0) {
100
+ const elements2 = document.querySelectorAll(selector);
101
+ if (elements2.length === 0) {
102
+ this.elements[key] = null;
103
+ } else if (elements2.length === 1) {
104
+ this.elements[key] = elements2[0];
105
+ } else {
106
+ this.elements[key] = elements2;
107
+ }
108
+ } else if (elements.length === 1) {
109
+ this.elements[key] = elements[0];
110
+ } else {
111
+ this.elements[key] = elements;
112
+ }
113
+ }
114
+ }
115
+ }
116
+ addEventListeners() {
117
+ }
118
+ removeEventListeners() {
119
+ }
120
+ destroy() {
121
+ super.destroy();
122
+ this.removeEventListeners();
123
+ }
124
+ };
125
+
126
+ // src/Links.ts
127
+ import { reaction } from "mobx";
128
+
129
+ // src/Link.ts
130
+ var Link = class extends Component {
131
+ constructor({ element }) {
132
+ super({ element });
133
+ }
134
+ onClick(event) {
135
+ event.preventDefault();
136
+ this.fire("click", this.element.href);
137
+ }
138
+ addEventListeners() {
139
+ const isLocal = this.element.href.includes(window.location.origin);
140
+ const isLocalPrevented = this.element.dataset.linkOverride === "";
141
+ const isNotEmail = !this.element.href.startsWith("mailto");
142
+ const isNotPhone = !this.element.href.startsWith("tel");
143
+ const isDownload = this.element.hasAttribute("download");
144
+ const isAnchor = this.element.href.includes("#");
145
+ if (isAnchor) {
146
+ const hash = this.element.href.split("#")[1];
147
+ const element = document.querySelector(`#${hash}`);
148
+ if (element) {
149
+ return;
150
+ }
151
+ }
152
+ if (isLocalPrevented || isDownload) {
153
+ return;
154
+ }
155
+ if (isLocal) {
156
+ this.element.onclick = this.onClick;
157
+ } else if (isNotEmail && isNotPhone) {
158
+ this.element.rel = "noopener";
159
+ this.element.setAttribute("target", "_blank");
160
+ }
161
+ }
162
+ removeEventListeners() {
163
+ this.element.onclick = null;
164
+ }
165
+ };
166
+
167
+ // src/Links.ts
168
+ var Links = class extends EventEmitter {
169
+ constructor(application) {
170
+ super();
171
+ this.application = application;
172
+ reaction(
173
+ () => application.currentPage,
174
+ () => this.refresh(),
175
+ { fireImmediately: true }
176
+ );
177
+ }
178
+ addEventListeners() {
179
+ var _a;
180
+ (_a = this.links) == null ? void 0 : _a.forEach((link) => link.destroy());
181
+ const links = document.querySelectorAll("a");
182
+ this.links = Array.from(links).map((element) => {
183
+ const link = new Link({
184
+ element
185
+ });
186
+ link.on("click", this.onLinkClick);
187
+ return link;
188
+ });
189
+ }
190
+ onLinkClick(href) {
191
+ this.application.route = href;
192
+ }
193
+ refresh() {
194
+ this.addEventListeners();
195
+ }
196
+ };
197
+
198
+ // src/App.ts
199
+ var ApplicationManager = class extends Component {
200
+ template = document.documentElement.dataset.template ?? "404";
201
+ constructor() {
202
+ super({
203
+ autoListeners: false,
204
+ element: ".app"
205
+ });
206
+ makeObservable(this, {
207
+ // Application DOM Element.
208
+ element: observable,
209
+ // Components.
210
+ canvas: observable,
211
+ components: observable,
212
+ transition: observable,
213
+ // Page Information.
214
+ currentPage: observable,
215
+ nextPage: observable,
216
+ // Route Information.
217
+ route: observable,
218
+ // Template Information.
219
+ template: observable,
220
+ // Page Scroll.
221
+ scroll: computed
222
+ });
223
+ observe(this.components, this.onComponentChange);
224
+ observe(this, "route", this.onRouteChange);
225
+ autorun(this.onTitleUpdate);
226
+ autorun(this.onTemplateUpdate);
227
+ this.addEventListeners();
228
+ }
229
+ onTitleUpdate() {
230
+ var _a;
231
+ const title = (_a = this.nextPage) == null ? void 0 : _a.title;
232
+ if (title) {
233
+ document.title = title;
234
+ }
235
+ }
236
+ onTemplateUpdate() {
237
+ var _a;
238
+ const template = (_a = this.nextPage) == null ? void 0 : _a.template;
239
+ if (template) {
240
+ document.documentElement.dataset.template = template;
241
+ this.template = template;
242
+ }
243
+ }
244
+ //
245
+ // Components.
246
+ //
247
+ canvas;
248
+ components = [];
249
+ transition;
250
+ initComponents(components) {
251
+ const classes = components.map(
252
+ ({ component: Component2 }) => new Component2({
253
+ application: this
254
+ })
255
+ );
256
+ this.canvas = classes.find((component) => component.id === "canvas");
257
+ this.transition = classes.find((component) => component.id === "transition");
258
+ classes.forEach((component) => {
259
+ this.addComponent(component);
260
+ });
261
+ }
262
+ addComponent(component) {
263
+ this.components.push(component);
264
+ }
265
+ removeComponent(component) {
266
+ component.destroy();
267
+ const index = this.components.indexOf(component);
268
+ if (index !== -1) {
269
+ this.components.splice(index, 1);
270
+ }
271
+ }
272
+ onComponentChange(event) {
273
+ if (event.type === "splice") {
274
+ const { added, removed } = event;
275
+ }
276
+ }
277
+ //
278
+ // Datasets.
279
+ //
280
+ datasets = [];
281
+ initDatasets(datasets) {
282
+ this.datasets = datasets;
283
+ }
284
+ //
285
+ // Routes.
286
+ //
287
+ pages = /* @__PURE__ */ new Map();
288
+ initRoutes(routes) {
289
+ routes.forEach(({ component, template }) => {
290
+ this.pages.set(template, component);
291
+ });
292
+ }
293
+ //
294
+ // Sprites.
295
+ //
296
+ async initSprites(url = "/bundle.svg") {
297
+ const request = await window.fetch(url);
298
+ const response = await request.text();
299
+ const sprite = document.createElement("div");
300
+ sprite.innerHTML = response;
301
+ sprite.style.left = "-999999px";
302
+ sprite.style.opacity = "0";
303
+ sprite.style.position = "absolute";
304
+ sprite.style.top = "0";
305
+ document.body.appendChild(sprite);
306
+ }
307
+ initPage() {
308
+ this.createPage();
309
+ this.createLinks();
310
+ }
311
+ //
312
+ // Links.
313
+ //
314
+ createLinks() {
315
+ this.links = new Links(this);
316
+ }
317
+ //
318
+ // Page.
319
+ //
320
+ currentPage = void 0;
321
+ createPage(template = this.template) {
322
+ const PageClass = this.pages.get(template);
323
+ const page = new PageClass({
324
+ application: this,
325
+ datasets: this.datasets
326
+ });
327
+ this.currentPage = page;
328
+ this.currentPage.create();
329
+ }
330
+ destroyPage() {
331
+ if (this.currentPage) {
332
+ this.currentPage.destroy();
333
+ }
334
+ }
335
+ //
336
+ // Navigate.
337
+ //
338
+ route = window.location.pathname;
339
+ onRouteChange({ oldValue, newValue }) {
340
+ this.onRouteChangeRequest({
341
+ href: newValue,
342
+ pushState: true
343
+ });
344
+ }
345
+ async onRouteChangeRequest({ href, pushState = true }) {
346
+ const request = await window.fetch(href);
347
+ const response = await request.text();
348
+ this.onRequest({
349
+ href,
350
+ response,
351
+ pushState
352
+ });
353
+ }
354
+ //
355
+ // Request.
356
+ //
357
+ nextPage = {};
358
+ async onRequest({ href, response, pushState }) {
359
+ var _a, _b;
360
+ const domParser = new DOMParser();
361
+ const dom = domParser.parseFromString(response, "text/html");
362
+ const html = dom.querySelector("html");
363
+ const app = dom.querySelector(".app");
364
+ this.nextPage = {
365
+ element: app,
366
+ template: html.dataset.template ?? this.template,
367
+ title: dom.title ?? document.title
368
+ };
369
+ if (this.transition) {
370
+ await ((_b = (_a = this.transition).onTransition) == null ? void 0 : _b.call(_a, this));
371
+ } else {
372
+ this.currentPage.element.remove();
373
+ this.currentPage.destroy();
374
+ this.element.appendChild(this.nextPage.element.firstElementChild);
375
+ this.createPage(this.nextPage.template);
376
+ }
377
+ if (pushState) {
378
+ window.history.pushState({}, this.nextPage.title, href);
379
+ }
380
+ }
381
+ //
382
+ // Pop State.
383
+ //
384
+ onPopState() {
385
+ this.onRouteChangeRequest({
386
+ href: document.location.pathname,
387
+ pushState: false
388
+ });
389
+ }
390
+ //
391
+ // Scroll.
392
+ //
393
+ get scroll() {
394
+ var _a;
395
+ return ((_a = this.currentPage) == null ? void 0 : _a.scroll) ?? 0;
396
+ }
397
+ //
398
+ // Listeners.
399
+ //
400
+ addEventListeners() {
401
+ window.addEventListener("popstate", this.onPopState);
402
+ }
403
+ removeEventListeners() {
404
+ window.removeEventListener("popstate", this.onPopState);
405
+ }
406
+ };
407
+ var Application = new ApplicationManager();
408
+
409
+ // src/Page.ts
410
+ import Lenis from "lenis";
411
+ import { makeObservable as makeObservable2, observable as observable2 } from "mobx";
412
+ import Tempus from "tempus";
413
+ var Page = class extends Component {
414
+ datasets = [];
415
+ scroll = 0;
416
+ scrollEnabled = true;
417
+ scrollOptions = {};
418
+ constructor({
419
+ application,
420
+ classes,
421
+ datasets,
422
+ element,
423
+ elements,
424
+ scrollEnabled = true,
425
+ scrollOptions = {}
426
+ }) {
427
+ super({
428
+ autoMount: false,
429
+ autoListeners: false,
430
+ classes,
431
+ element,
432
+ elements
433
+ });
434
+ this.application = application;
435
+ this.datasets = datasets;
436
+ this.scrollEnabled = scrollEnabled;
437
+ this.scrollOptions = scrollOptions;
438
+ makeObservable2(this, {
439
+ element: observable2,
440
+ elements: observable2,
441
+ components: observable2,
442
+ scroll: observable2
443
+ });
444
+ }
445
+ create() {
446
+ super.create();
447
+ this.createResizeObserver();
448
+ this.createComponents();
449
+ this.createScroll();
450
+ this.addEventListeners();
451
+ }
452
+ createResizeObserver() {
453
+ this.resizeObserver = new ResizeObserver(() => {
454
+ this.onResize();
455
+ });
456
+ this.resizeObserver.observe(this.elements.wrapper);
457
+ }
458
+ destroyResizeObserver() {
459
+ this.resizeObserver.disconnect();
460
+ }
461
+ //
462
+ // Components.
463
+ //
464
+ components = /* @__PURE__ */ new Set();
465
+ createComponents() {
466
+ this.datasets.map(({ component: Component2, selector }) => {
467
+ const elements = this.element.querySelectorAll(selector);
468
+ const components = Array.from(elements).map((element) => {
469
+ const component = new Component2({
470
+ application: this.application,
471
+ element
472
+ });
473
+ return component;
474
+ });
475
+ components.forEach((component) => {
476
+ this.application.addComponent(component);
477
+ this.components.add(component);
478
+ });
479
+ });
480
+ }
481
+ destroyComponents() {
482
+ this.components.forEach((component) => {
483
+ this.application.removeComponent(component);
484
+ });
485
+ this.components.clear();
486
+ }
487
+ createScroll() {
488
+ if (!this.scrollEnabled) {
489
+ this.element.addEventListener("scroll", this.onScrollFallback);
490
+ return;
491
+ }
492
+ this.lenis = new Lenis({
493
+ content: this.elements.wrapper,
494
+ wrapper: this.element,
495
+ ...this.scrollOptions
496
+ });
497
+ this.lenis.on("scroll", (event) => {
498
+ this.onScroll(event.scroll);
499
+ this.emitter.emit("scroll", event);
500
+ });
501
+ }
502
+ destroyScroll() {
503
+ var _a;
504
+ if (!this.scrollEnabled) {
505
+ this.element.removeEventListener("scroll", this.onScrollFallback);
506
+ return;
507
+ }
508
+ (_a = this.lenis) == null ? void 0 : _a.destroy();
509
+ }
510
+ //
511
+ // Events.
512
+ //
513
+ onResize() {
514
+ var _a;
515
+ (_a = this.lenis) == null ? void 0 : _a.resize();
516
+ }
517
+ onRAF(time) {
518
+ var _a;
519
+ (_a = this.lenis) == null ? void 0 : _a.raf(time);
520
+ }
521
+ onScroll(scroll) {
522
+ this.scroll = scroll;
523
+ }
524
+ onScrollFallback() {
525
+ this.scroll = this.element.scrollTop;
526
+ this.emitter.emit("scroll", { scroll: this.scroll });
527
+ }
528
+ //
529
+ // Events.
530
+ //
531
+ addEventListeners() {
532
+ super.addEventListeners();
533
+ this.unsubscribeRaf = Tempus.add(this.onRAF);
534
+ }
535
+ removeEventListeners() {
536
+ var _a;
537
+ super.removeEventListeners();
538
+ (_a = this.unsubscribeRaf) == null ? void 0 : _a.call(this);
539
+ this.unsubscribeRaf = void 0;
540
+ }
541
+ //
542
+ // Destroy.
543
+ //
544
+ destroy() {
545
+ super.destroy();
546
+ this.destroyResizeObserver();
547
+ this.destroyComponents();
548
+ this.destroyScroll();
549
+ }
550
+ };
551
+
552
+ // src/index.ts
553
+ import Tempus2 from "tempus";
554
+ export {
555
+ Application,
556
+ ApplicationManager,
557
+ Component,
558
+ EventEmitter,
559
+ Link,
560
+ Links,
561
+ Page,
562
+ Tempus2 as Tempus
563
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lisergia/core",
3
- "version": "14.0.1",
3
+ "version": "16.0.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",
@@ -9,7 +9,7 @@
9
9
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
10
10
  },
11
11
  "dependencies": {
12
- "@lisergia/config-tsconfig": "14.0.0",
12
+ "@lisergia/config-tsconfig": "16.0.0",
13
13
  "auto-bind": "^5.0.1",
14
14
  "gsap": "^3.13.0",
15
15
  "lenis": "^1.3.1",
package/src/App.ts CHANGED
@@ -43,6 +43,7 @@ export class ApplicationManager extends Component {
43
43
 
44
44
  // Route Information.
45
45
  route: observable,
46
+ routeHistory: observable,
46
47
 
47
48
  // Template Information.
48
49
  template: observable,
@@ -204,13 +205,15 @@ export class ApplicationManager extends Component {
204
205
  // Navigate.
205
206
  //
206
207
  route: string = window.location.pathname
208
+ routeHistory: Array<string> = [this.route]
209
+ routePushState: boolean = true
207
210
 
208
211
  onRouteChange({ oldValue, newValue }: IValueDidChange<string>) {
209
212
  const href = newValue.replace(window.location.origin, '')
210
213
 
211
214
  this.onRouteChangeRequest({
212
215
  href,
213
- pushState: true,
216
+ pushState: this.routePushState,
214
217
  })
215
218
  }
216
219
 
@@ -261,6 +264,8 @@ export class ApplicationManager extends Component {
261
264
  if (pushState) {
262
265
  window.history.pushState({}, this.nextPage.title!, href)
263
266
  }
267
+
268
+ this.routeHistory.push(href)
264
269
  }
265
270
 
266
271
  //
@@ -271,17 +276,16 @@ export class ApplicationManager extends Component {
271
276
  return event.preventDefault()
272
277
  }
273
278
 
274
- this.onRouteChangeRequest({
275
- href: document.location.pathname,
276
- pushState: false,
277
- })
279
+ this.routePushState = false
280
+ this.route = document.location.pathname
281
+ this.routePushState = false
278
282
  }
279
283
 
280
284
  //
281
285
  // Scroll.
282
286
  //
283
287
  get scroll() {
284
- return this.currentPage?.scroll ?? 0
288
+ return this.currentPage!.scroll ?? 0
285
289
  }
286
290
 
287
291
  //
package/src/Links.ts CHANGED
@@ -37,7 +37,7 @@ export class Links extends EventEmitter {
37
37
  }
38
38
 
39
39
  onLinkClick(href: string) {
40
- this.application.route = href
40
+ this.application.route = href.replace(window.location.origin, '')
41
41
  }
42
42
 
43
43
  refresh() {