@lisergia/core 24.0.0 → 25.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/src/App.ts DELETED
@@ -1,308 +0,0 @@
1
- import { autorun, computed, IArrayDidChange, IValueDidChange, makeObservable, observable, observe } from 'mobx'
2
-
3
- import { Component, ComponentParameters } from './Component'
4
- import { Links } from './Links'
5
- import { Page, PageParameters } from './Page'
6
-
7
- export interface ApplicationComponentData {
8
- component: new (params?: ComponentParameters) => Component
9
- }
10
-
11
- export interface ApplicationComponentDatasetData extends ApplicationComponentData {
12
- selector: string
13
- }
14
-
15
- export interface ApplicationRoute {
16
- component: new (params?: PageParameters) => Page
17
- template: string
18
- }
19
-
20
- export class ApplicationManager extends Component {
21
- declare element: HTMLElement
22
-
23
- template: string = document.documentElement.dataset.template ?? '404'
24
-
25
- constructor() {
26
- super({
27
- autoListeners: false,
28
- element: '.app',
29
- })
30
-
31
- makeObservable(this, {
32
- // Application DOM Element.
33
- element: observable,
34
-
35
- // Components.
36
- canvas: observable,
37
- components: observable,
38
- transition: observable,
39
-
40
- // Page Information.
41
- currentPage: observable,
42
- nextPage: observable,
43
-
44
- // Route Information.
45
- route: observable,
46
- routeHistory: observable,
47
-
48
- // Template Information.
49
- template: observable,
50
-
51
- // Page Scroll.
52
- scroll: computed,
53
- })
54
-
55
- observe(this.components, this.onComponentChange)
56
- observe(this, 'route', this.onRouteChange)
57
-
58
- autorun(this.onTitleUpdate)
59
- autorun(this.onTemplateUpdate)
60
-
61
- this.addEventListeners()
62
- }
63
-
64
- onTitleUpdate() {
65
- const title = this.nextPage?.title
66
-
67
- if (title) {
68
- document.title = title
69
- }
70
- }
71
-
72
- onTemplateUpdate() {
73
- const template = this.nextPage?.template
74
-
75
- if (template) {
76
- document.documentElement.dataset.template = template
77
-
78
- this.template = template
79
- }
80
- }
81
-
82
- //
83
- // Components.
84
- //
85
- canvas?: Component
86
- components: Array<Component> = []
87
- transition?: Component & { onTransition?: (args: any) => Promise<void> }
88
-
89
- initComponents(components: Array<ApplicationComponentData>) {
90
- const classes = components.map(
91
- ({ component: Component }) =>
92
- new Component({
93
- application: this,
94
- }),
95
- )
96
-
97
- this.canvas = classes.find((component) => component.id === 'canvas')
98
- this.transition = classes.find((component) => component.id === 'transition')
99
-
100
- classes.forEach((component) => {
101
- this.addComponent(component)
102
- })
103
- }
104
-
105
- addComponent(component: Component) {
106
- this.components.push(component)
107
- }
108
-
109
- removeComponent(component: Component) {
110
- component.destroy()
111
-
112
- const index = this.components.indexOf(component)
113
-
114
- if (index !== -1) {
115
- this.components.splice(index, 1)
116
- }
117
- }
118
-
119
- onComponentChange(event: IArrayDidChange<Component>) {
120
- if (event.type === 'splice') {
121
- const { added, removed } = event
122
- }
123
- }
124
-
125
- //
126
- // Datasets.
127
- //
128
- datasets: Array<ApplicationComponentDatasetData> = []
129
-
130
- initDatasets(datasets: Array<ApplicationComponentDatasetData>) {
131
- this.datasets = datasets
132
- }
133
-
134
- //
135
- // Routes.
136
- //
137
- pages: Map<string, new (args: PageParameters) => Page> = new Map()
138
-
139
- initRoutes(routes: Array<ApplicationRoute>) {
140
- routes.forEach(({ component, template }) => {
141
- this.pages.set(template, component)
142
- })
143
- }
144
-
145
- //
146
- // Sprites.
147
- //
148
- async initSprites(url = '/bundle.svg') {
149
- const request = await window.fetch(url)
150
- const response = await request.text()
151
-
152
- const sprite = document.createElement('div')
153
-
154
- sprite.innerHTML = response
155
-
156
- sprite.style.left = '-999999px'
157
- sprite.style.opacity = '0'
158
- sprite.style.position = 'absolute'
159
- sprite.style.top = '0'
160
-
161
- document.body.appendChild(sprite)
162
- }
163
-
164
- //
165
- // Initialization.
166
- //
167
- IS_LINKS_ENABLED = true
168
-
169
- declare links: Links
170
-
171
- initPage() {
172
- this.createPage()
173
- this.createLinks()
174
- }
175
-
176
- //
177
- // Links.
178
- //
179
- createLinks() {
180
- if (!this.IS_LINKS_ENABLED) {
181
- return
182
- }
183
-
184
- this.links = new Links(this)
185
- }
186
-
187
- //
188
- // Page.
189
- //
190
- currentPage?: Page = undefined
191
-
192
- createPage(template = this.template) {
193
- const PageClass = this.pages.get(template)!
194
-
195
- const page = new PageClass({
196
- application: this,
197
- datasets: this.datasets,
198
- })
199
-
200
- this.currentPage = page
201
- this.currentPage.create()
202
- }
203
-
204
- destroyPage() {
205
- if (this.currentPage) {
206
- this.currentPage.destroy()
207
- }
208
- }
209
-
210
- //
211
- // Navigate.
212
- //
213
- route: string = window.location.pathname
214
- routeHistory: Array<string> = [this.route]
215
- routePushState: boolean = true
216
-
217
- onRouteChange({ oldValue, newValue }: IValueDidChange<string>) {
218
- const href = newValue.replace(window.location.origin, '')
219
-
220
- this.onRouteChangeRequest({
221
- href,
222
- pushState: this.routePushState,
223
- })
224
- }
225
-
226
- async onRouteChangeRequest({ href, pushState = true }: { href: string; pushState: boolean }) {
227
- const request = await window.fetch(href)
228
- const response = await request.text()
229
-
230
- this.onRequest({
231
- href,
232
- response,
233
- pushState,
234
- })
235
- }
236
-
237
- //
238
- // Request.
239
- //
240
- nextPage: {
241
- element?: Element
242
- template?: string
243
- title?: string
244
- } = {}
245
-
246
- async onRequest({ href, response, pushState }: { href: string; response: string; pushState: boolean }) {
247
- let domParser: DOMParser | null = new DOMParser()
248
- let dom: Document | null = domParser.parseFromString(response, 'text/html')
249
-
250
- const html = dom!.querySelector('html')!
251
- const app = dom!.querySelector('.app')!
252
-
253
- this.nextPage = {
254
- element: app,
255
- template: html.dataset.template ?? this.template,
256
- title: dom!.title ?? document.title,
257
- }
258
-
259
- if (this.transition) {
260
- await this.transition.onTransition?.(this)
261
- } else {
262
- this.currentPage!.element.remove()
263
- this.currentPage!.destroy()
264
-
265
- this.element.appendChild(this.nextPage.element!.firstElementChild!)
266
-
267
- this.createPage(this.nextPage.template)
268
- }
269
-
270
- if (pushState) {
271
- window.history.pushState({}, this.nextPage.title!, href)
272
- }
273
-
274
- this.routeHistory.push(href)
275
-
276
- domParser = null
277
- dom = null
278
- }
279
-
280
- //
281
- // Pop State.
282
- //
283
- onPopState(event: PopStateEvent) {
284
- this.routePushState = false
285
- this.route = document.location.pathname
286
- this.routePushState = true
287
- }
288
-
289
- //
290
- // Scroll.
291
- //
292
- get scroll() {
293
- return this.currentPage!.scroll ?? 0
294
- }
295
-
296
- //
297
- // Listeners.
298
- //
299
- addEventListeners() {
300
- window.addEventListener('popstate', this.onPopState)
301
- }
302
-
303
- removeEventListeners() {
304
- window.removeEventListener('popstate', this.onPopState)
305
- }
306
- }
307
-
308
- export const Application = new ApplicationManager()
package/src/Component.ts DELETED
@@ -1,144 +0,0 @@
1
- import { ApplicationManager } from './App'
2
- import { EventEmitter } from './EventEmitter'
3
-
4
- export interface ComponentClasses {
5
- [key: string]: string
6
- }
7
-
8
- export interface ComponentElements {
9
- [key: string]: Array<any> | Element | Array<Element> | HTMLElement | Array<HTMLElement> | NodeList | Window | null
10
- }
11
-
12
- export type ComponentSelector = string | HTMLElement
13
-
14
- export interface ComponentSelectors {
15
- [key: string]: string | Element | Array<Element> | HTMLElement | Array<HTMLElement> | NodeList | Window
16
- }
17
-
18
- export interface ComponentParameters {
19
- application?: ApplicationManager
20
- autoListeners?: boolean
21
- autoMount?: boolean
22
- classes?: ComponentClasses
23
- element?: ComponentSelector
24
- elements?: ComponentSelectors
25
- id?: string
26
- }
27
-
28
- export class Component extends EventEmitter {
29
- application?: ApplicationManager
30
- autoListeners: boolean
31
- autoMount: boolean
32
- classes?: ComponentClasses
33
- selector?: ComponentSelector
34
- selectors?: ComponentSelectors
35
-
36
- id?: string
37
-
38
- element?: HTMLElement
39
- elements: ComponentElements = {}
40
-
41
- constructor({
42
- application,
43
- autoListeners = true,
44
- autoMount = true,
45
- classes,
46
- element,
47
- elements,
48
- id,
49
- }: ComponentParameters) {
50
- super()
51
-
52
- this.application = application
53
- this.autoListeners = autoListeners
54
- this.autoMount = autoMount
55
-
56
- this.classes = classes
57
-
58
- this.selector = element
59
- this.selectors = elements
60
-
61
- this.id = id
62
-
63
- if (this.autoMount) {
64
- this.create()
65
- }
66
-
67
- if (this.autoListeners) {
68
- this.addEventListeners()
69
- }
70
- }
71
-
72
- create() {
73
- if (this.selector) {
74
- this.initElement(this.selector)
75
- }
76
-
77
- if (this.selectors) {
78
- this.initElements(this.selectors)
79
- }
80
- }
81
-
82
- initElement(selector: ComponentSelector) {
83
- if (selector instanceof HTMLElement) {
84
- this.element = selector
85
- } else {
86
- this.element = document.querySelector(selector)!
87
- }
88
- }
89
-
90
- destroyElement() {
91
- this.element = undefined
92
- }
93
-
94
- initElements(selectors?: ComponentSelectors) {
95
- for (const key in selectors) {
96
- const selector = selectors[key]
97
-
98
- if (selector === window) {
99
- this.elements[key] = window
100
- } else if (selector instanceof HTMLElement) {
101
- this.elements[key] = selector
102
- } else if (selector instanceof NodeList) {
103
- this.elements[key] = selector
104
- } else if (Array.isArray(selector)) {
105
- this.elements[key] = selector
106
- } else {
107
- const elements = this.element!.querySelectorAll(selector as string)
108
-
109
- if (elements.length === 0) {
110
- const elements = document.querySelectorAll(selector as string)
111
-
112
- if (elements.length === 0) {
113
- this.elements[key] = null
114
- } else if (elements.length === 1) {
115
- this.elements[key] = elements[0] as HTMLElement
116
- } else {
117
- this.elements[key] = elements
118
- }
119
- } else if (elements.length === 1) {
120
- this.elements[key] = elements[0] as HTMLElement
121
- } else {
122
- this.elements[key] = elements
123
- }
124
- }
125
- }
126
- }
127
-
128
- destroyElements() {
129
- this.elements = {}
130
- }
131
-
132
- addEventListeners() {}
133
-
134
- removeEventListeners() {}
135
-
136
- destroy() {
137
- super.destroy()
138
-
139
- this.removeEventListeners()
140
-
141
- this.destroyElements()
142
- this.destroyElement()
143
- }
144
- }
@@ -1,44 +0,0 @@
1
- import AutoBind from 'auto-bind'
2
- import { Emitter, Unsubscribe, createNanoEvents } from 'nanoevents'
3
-
4
- export class EventEmitter {
5
- emitter: Emitter
6
- entries: Map<Function, Unsubscribe> = new Map()
7
-
8
- constructor() {
9
- AutoBind(this)
10
-
11
- this.emitter = createNanoEvents()
12
- }
13
-
14
- on(event: string, callback: (...args: any) => void) {
15
- if (!callback) {
16
- return console.trace('No callback provided')
17
- }
18
-
19
- const emitter = this.emitter.on(event, callback)
20
-
21
- this.entries.set(callback, emitter)
22
-
23
- return emitter
24
- }
25
-
26
- off(event: string, callback: (...args: any) => void) {
27
- const unsubscribe = this.entries.get(callback)
28
-
29
- if (unsubscribe) {
30
- unsubscribe()
31
- }
32
-
33
- this.entries.delete(callback)
34
- }
35
-
36
- fire(event: string, ...args: any[]) {
37
- this.emitter.emit(event, ...args)
38
- }
39
-
40
- destroy() {
41
- this.entries.forEach((unsubscribe) => unsubscribe())
42
- this.entries.clear()
43
- }
44
- }
package/src/Link.ts DELETED
@@ -1,51 +0,0 @@
1
- import { Component } from './Component'
2
-
3
- export class Link extends Component {
4
- declare element: HTMLLinkElement
5
-
6
- constructor({ element }: { element: HTMLAnchorElement }) {
7
- super({ element })
8
- }
9
-
10
- onClick(event: MouseEvent) {
11
- event.preventDefault()
12
-
13
- this.fire('click', this.element.href)
14
- }
15
-
16
- addEventListeners() {
17
- const isLocal = this.element.href.includes(window.location.origin)
18
- const isLocalPrevented = this.element.dataset.linkOverride === ''
19
- const isNotEmail = !this.element.href.startsWith('mailto')
20
- const isNotPhone = !this.element.href.startsWith('tel')
21
- const isDownload = this.element.hasAttribute('download')
22
- const isAnchor = this.element.href.includes('#')
23
-
24
- if (isAnchor) {
25
- const hash = this.element.href.split('#')[1]
26
-
27
- if (hash) {
28
- const element = document.querySelector(`#${hash}`)
29
-
30
- if (element) {
31
- return
32
- }
33
- }
34
- }
35
-
36
- if (isLocalPrevented || isDownload) {
37
- return
38
- }
39
-
40
- if (isLocal) {
41
- this.element.onclick = this.onClick
42
- } else if (isNotEmail && isNotPhone) {
43
- this.element.rel = 'noopener'
44
- this.element.setAttribute('target', '_blank')
45
- }
46
- }
47
-
48
- removeEventListeners() {
49
- this.element.onclick = null
50
- }
51
- }
package/src/Links.ts DELETED
@@ -1,46 +0,0 @@
1
- import { reaction } from 'mobx'
2
-
3
- import { ApplicationManager } from './App'
4
- import { Link } from './Link'
5
- import { EventEmitter } from './EventEmitter'
6
-
7
- export class Links extends EventEmitter {
8
- declare application: ApplicationManager
9
- declare links: Array<Link>
10
-
11
- constructor(application: ApplicationManager) {
12
- super()
13
-
14
- this.application = application
15
-
16
- reaction(
17
- () => application.currentPage,
18
- () => this.refresh(),
19
- { fireImmediately: true },
20
- )
21
- }
22
-
23
- addEventListeners() {
24
- this.links?.forEach((link) => link.destroy())
25
-
26
- const links = document.querySelectorAll('a')
27
-
28
- this.links = Array.from(links).map((element) => {
29
- const link = new Link({
30
- element,
31
- })
32
-
33
- link.on('click', this.onLinkClick)
34
-
35
- return link
36
- })
37
- }
38
-
39
- onLinkClick(href: string) {
40
- this.application.route = href.replace(window.location.origin, '')
41
- }
42
-
43
- refresh() {
44
- this.addEventListeners()
45
- }
46
- }