@lisergia/core 1.0.0 → 2.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,552 @@
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
+ this.links?.forEach((link) => link.destroy());
180
+ const links = document.querySelectorAll("a");
181
+ this.links = Array.from(links).map((element) => {
182
+ const link = new Link({
183
+ element
184
+ });
185
+ link.on("click", this.onLinkClick);
186
+ return link;
187
+ });
188
+ }
189
+ onLinkClick(href) {
190
+ this.application.route = href;
191
+ }
192
+ refresh() {
193
+ this.addEventListeners();
194
+ }
195
+ };
196
+
197
+ // src/App.ts
198
+ var ApplicationManager = class extends Component {
199
+ template = document.documentElement.dataset.template ?? "404";
200
+ constructor() {
201
+ super({
202
+ autoListeners: false,
203
+ element: ".app"
204
+ });
205
+ makeObservable(this, {
206
+ // Application DOM Element.
207
+ element: observable,
208
+ // Components.
209
+ canvas: observable,
210
+ components: observable,
211
+ transition: observable,
212
+ // Page Information.
213
+ currentPage: observable,
214
+ nextPage: observable,
215
+ // Route Information.
216
+ route: observable,
217
+ // Template Information.
218
+ template: observable,
219
+ // Page Scroll.
220
+ scroll: computed
221
+ });
222
+ observe(this.components, this.onComponentChange);
223
+ observe(this, "route", this.onRouteChange);
224
+ autorun(this.onTitleUpdate);
225
+ autorun(this.onTemplateUpdate);
226
+ this.addEventListeners();
227
+ }
228
+ onTitleUpdate() {
229
+ const title = this.nextPage?.title;
230
+ if (title) {
231
+ document.title = title;
232
+ }
233
+ }
234
+ onTemplateUpdate() {
235
+ const template = this.nextPage?.template;
236
+ if (template) {
237
+ document.documentElement.dataset.template = template;
238
+ this.template = template;
239
+ }
240
+ }
241
+ //
242
+ // Components.
243
+ //
244
+ canvas;
245
+ components = [];
246
+ transition;
247
+ initComponents(components) {
248
+ const classes = components.map(
249
+ ({ component: Component2 }) => new Component2({
250
+ application: this
251
+ })
252
+ );
253
+ this.canvas = classes.find((component) => component.id === "canvas");
254
+ this.transition = classes.find((component) => component.id === "transition");
255
+ classes.forEach((component) => {
256
+ this.addComponent(component);
257
+ });
258
+ }
259
+ addComponent(component) {
260
+ this.components.push(component);
261
+ }
262
+ removeComponent(component) {
263
+ component.destroy();
264
+ const index = this.components.indexOf(component);
265
+ if (index !== -1) {
266
+ this.components.splice(index, 1);
267
+ }
268
+ }
269
+ onComponentChange(event) {
270
+ if (event.type === "splice") {
271
+ const { added, removed } = event;
272
+ }
273
+ }
274
+ //
275
+ // Datasets.
276
+ //
277
+ datasets = [];
278
+ initDatasets(datasets) {
279
+ this.datasets = datasets;
280
+ }
281
+ //
282
+ // Routes.
283
+ //
284
+ pages = /* @__PURE__ */ new Map();
285
+ initRoutes(routes) {
286
+ routes.forEach(({ component, template }) => {
287
+ this.pages.set(template, component);
288
+ });
289
+ }
290
+ //
291
+ // Sprites.
292
+ //
293
+ async initSprites(url = "/bundle.svg") {
294
+ const request = await window.fetch(url);
295
+ const response = await request.text();
296
+ const sprite = document.createElement("div");
297
+ sprite.innerHTML = response;
298
+ sprite.style.left = "-999999px";
299
+ sprite.style.opacity = "0";
300
+ sprite.style.position = "absolute";
301
+ sprite.style.top = "0";
302
+ document.body.appendChild(sprite);
303
+ }
304
+ initPage() {
305
+ this.createPage();
306
+ this.createLinks();
307
+ }
308
+ //
309
+ // Links.
310
+ //
311
+ createLinks() {
312
+ this.links = new Links(this);
313
+ }
314
+ //
315
+ // Page.
316
+ //
317
+ currentPage = void 0;
318
+ createPage(template = this.template) {
319
+ const PageClass = this.pages.get(template);
320
+ const page = new PageClass({
321
+ application: this,
322
+ datasets: this.datasets
323
+ });
324
+ this.currentPage = page;
325
+ this.currentPage.create();
326
+ }
327
+ destroyPage() {
328
+ if (this.currentPage) {
329
+ this.currentPage.destroy();
330
+ }
331
+ }
332
+ //
333
+ // Navigate.
334
+ //
335
+ route = window.location.pathname;
336
+ onRouteChange({ oldValue, newValue }) {
337
+ this.onRouteChangeRequest({
338
+ href: newValue,
339
+ pushState: true
340
+ });
341
+ }
342
+ async onRouteChangeRequest({ href, pushState = true }) {
343
+ const request = await window.fetch(href);
344
+ const response = await request.text();
345
+ this.onRequest({
346
+ href,
347
+ response,
348
+ pushState
349
+ });
350
+ }
351
+ //
352
+ // Request.
353
+ //
354
+ nextPage = {};
355
+ async onRequest({ href, response, pushState }) {
356
+ const domParser = new DOMParser();
357
+ const dom = domParser.parseFromString(response, "text/html");
358
+ const html = dom.querySelector("html");
359
+ const app = dom.querySelector(".app");
360
+ this.nextPage = {
361
+ element: app,
362
+ template: html.dataset.template ?? this.template,
363
+ title: dom.title ?? document.title
364
+ };
365
+ if (this.transition) {
366
+ await this.transition.onTransition?.(this);
367
+ } else {
368
+ this.currentPage.element.remove();
369
+ this.currentPage.destroy();
370
+ this.element.appendChild(this.nextPage.element.firstElementChild);
371
+ this.createPage(this.nextPage.template);
372
+ }
373
+ if (pushState) {
374
+ window.history.pushState({}, this.nextPage.title, href);
375
+ }
376
+ }
377
+ //
378
+ // Pop State.
379
+ //
380
+ onPopState() {
381
+ this.onRouteChangeRequest({
382
+ href: document.location.pathname,
383
+ pushState: false
384
+ });
385
+ }
386
+ //
387
+ // Scroll.
388
+ //
389
+ get scroll() {
390
+ return this.currentPage?.scroll ?? 0;
391
+ }
392
+ //
393
+ // Listeners.
394
+ //
395
+ addEventListeners() {
396
+ window.addEventListener("popstate", this.onPopState);
397
+ }
398
+ removeEventListeners() {
399
+ window.removeEventListener("popstate", this.onPopState);
400
+ }
401
+ };
402
+ var Application = new ApplicationManager();
403
+
404
+ // src/Page.ts
405
+ import Lenis from "lenis";
406
+ import { makeObservable as makeObservable2, observable as observable2 } from "mobx";
407
+ import Tempus from "tempus";
408
+ var Page = class extends Component {
409
+ datasets = [];
410
+ scroll = 0;
411
+ scrollEnabled = true;
412
+ scrollOptions = {};
413
+ constructor({
414
+ application,
415
+ classes,
416
+ datasets,
417
+ element,
418
+ elements,
419
+ scrollEnabled = true,
420
+ scrollOptions = {}
421
+ }) {
422
+ super({
423
+ autoMount: false,
424
+ autoListeners: false,
425
+ classes,
426
+ element,
427
+ elements
428
+ });
429
+ this.application = application;
430
+ this.datasets = datasets;
431
+ this.scrollEnabled = scrollEnabled;
432
+ this.scrollOptions = scrollOptions;
433
+ makeObservable2(this, {
434
+ components: observable2,
435
+ scroll: observable2
436
+ });
437
+ }
438
+ create() {
439
+ super.create();
440
+ this.createResizeObserver();
441
+ this.createComponents();
442
+ this.createScroll();
443
+ this.addEventListeners();
444
+ }
445
+ createResizeObserver() {
446
+ this.resizeObserver = new ResizeObserver(() => {
447
+ this.onResize();
448
+ });
449
+ this.resizeObserver.observe(this.elements.wrapper);
450
+ }
451
+ destroyResizeObserver() {
452
+ this.resizeObserver.disconnect();
453
+ }
454
+ //
455
+ // Components.
456
+ //
457
+ components = /* @__PURE__ */ new Set();
458
+ createComponents() {
459
+ this.datasets.map(({ component: Component2, selector }) => {
460
+ const elements = this.element.querySelectorAll(selector);
461
+ const components = Array.from(elements).map((element) => {
462
+ const component = new Component2({
463
+ application: this.application,
464
+ element
465
+ });
466
+ return component;
467
+ });
468
+ components.forEach((component) => {
469
+ this.application.addComponent(component);
470
+ this.components.add(component);
471
+ });
472
+ });
473
+ }
474
+ destroyComponents() {
475
+ this.components.forEach((component) => {
476
+ this.application.removeComponent(component);
477
+ });
478
+ this.components.clear();
479
+ }
480
+ createScroll() {
481
+ if (!this.scrollEnabled) {
482
+ this.element.addEventListener("scroll", this.onScrollFallback);
483
+ return;
484
+ }
485
+ this.lenis = new Lenis({
486
+ content: this.elements.wrapper,
487
+ wrapper: this.element,
488
+ ...this.scrollOptions
489
+ });
490
+ this.lenis.on("scroll", (event) => {
491
+ this.onScroll(event.scroll);
492
+ this.emitter.emit("scroll", event);
493
+ });
494
+ }
495
+ destroyScroll() {
496
+ if (!this.scrollEnabled) {
497
+ this.element.removeEventListener("scroll", this.onScrollFallback);
498
+ return;
499
+ }
500
+ this.lenis?.destroy();
501
+ }
502
+ //
503
+ // Events.
504
+ //
505
+ onResize() {
506
+ this.lenis?.resize();
507
+ }
508
+ onRAF(time) {
509
+ this.lenis?.raf(time);
510
+ }
511
+ onScroll(scroll) {
512
+ this.scroll = scroll;
513
+ }
514
+ onScrollFallback() {
515
+ this.scroll = this.element.scrollTop;
516
+ this.emitter.emit("scroll", { scroll: this.scroll });
517
+ }
518
+ //
519
+ // Events.
520
+ //
521
+ addEventListeners() {
522
+ super.addEventListeners();
523
+ this.unsubscribeRaf = Tempus.add(this.onRAF);
524
+ }
525
+ removeEventListeners() {
526
+ super.removeEventListeners();
527
+ this.unsubscribeRaf?.();
528
+ this.unsubscribeRaf = void 0;
529
+ }
530
+ //
531
+ // Destroy.
532
+ //
533
+ destroy() {
534
+ super.destroy();
535
+ this.destroyResizeObserver();
536
+ this.destroyComponents();
537
+ this.destroyScroll();
538
+ }
539
+ };
540
+
541
+ // src/index.ts
542
+ import Tempus2 from "tempus";
543
+ export {
544
+ Application,
545
+ ApplicationManager,
546
+ Component,
547
+ EventEmitter,
548
+ Link,
549
+ Links,
550
+ Page,
551
+ Tempus2 as Tempus
552
+ };
package/package.json CHANGED
@@ -1,18 +1,20 @@
1
1
  {
2
2
  "name": "@lisergia/core",
3
- "version": "1.0.0",
4
- "exports": {
5
- "./*": "./src/*.ts"
3
+ "version": "2.0.0",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.mjs",
6
+ "types": "./dist/index.d.ts",
7
+ "type": "module",
8
+ "scripts": {
9
+ "build": "tsup src/index.ts --format cjs,esm --dts",
10
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
6
11
  },
7
- "main": "./src/index.ts",
8
- "files": [
9
- "src"
10
- ],
11
12
  "dependencies": {
13
+ "@lisergia/config-tsconfig": "2.0.0",
12
14
  "auto-bind": "^5.0.1",
13
15
  "lenis": "^1.3.1",
14
16
  "mobx": "^6.13.7",
15
17
  "nanoevents": "^9.1.0",
16
- "tempus": "1.0.0-dev.10"
18
+ "tempus": "^1.0.0-dev.10"
17
19
  }
18
20
  }
package/tsconfig.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "@lisergia/config-tsconfig/base.json"
3
+ }