aptechka 0.1.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/.prettierignore +16 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +4 -0
- package/.vscode/launch.json +11 -0
- package/.vscode/settings.json +18 -0
- package/README.md +0 -0
- package/index.html +32 -0
- package/package.json +272 -0
- package/public/vite.svg +1 -0
- package/src/packages/animation/Animated.ts +189 -0
- package/src/packages/animation/Damped.ts +39 -0
- package/src/packages/animation/Tweened.ts +51 -0
- package/src/packages/animation/index.ts +10 -0
- package/src/packages/attribute/index.ts +59 -0
- package/src/packages/canvas-2d/index.ts +137 -0
- package/src/packages/controls/Controls.ts +15 -0
- package/src/packages/controls/KeyboardControls.ts +63 -0
- package/src/packages/controls/LinearControls.ts +27 -0
- package/src/packages/controls/User.ts +20 -0
- package/src/packages/controls/WheelControls.ts +92 -0
- package/src/packages/controls/index.ts +5 -0
- package/src/packages/css-unit-parser/index.ts +32 -0
- package/src/packages/custom-element/index.ts +19 -0
- package/src/packages/device/Device.ts +113 -0
- package/src/packages/device/Viewport.ts +67 -0
- package/src/packages/device/index.ts +2 -0
- package/src/packages/element-constructor/ElementConstructor.ts +577 -0
- package/src/packages/element-constructor/htmlTags.ts +679 -0
- package/src/packages/element-constructor/index.ts +4 -0
- package/src/packages/element-constructor/specialObjects.ts +8 -0
- package/src/packages/element-constructor/svgTags.ts +588 -0
- package/src/packages/en3/attachments/En3SourceManager.ts +116 -0
- package/src/packages/en3/core/en3.ts +306 -0
- package/src/packages/en3/index.ts +52 -0
- package/src/packages/en3/instances/en3LazyLoader.ts +22 -0
- package/src/packages/en3/libs/MeshoptDecoder.js +138 -0
- package/src/packages/en3/loaders/en3GLTFLoader.ts +54 -0
- package/src/packages/en3/loaders/en3TextureLoader.ts +3 -0
- package/src/packages/en3/objects/En3Clip.ts +53 -0
- package/src/packages/en3/objects/En3ClipHelpers.ts +12 -0
- package/src/packages/en3/objects/En3GLTF.ts +35 -0
- package/src/packages/en3/objects/En3Image.ts +18 -0
- package/src/packages/en3/objects/En3ImageLike.ts +101 -0
- package/src/packages/en3/objects/En3SourceConsumer.ts +5 -0
- package/src/packages/en3/objects/En3Video.ts +88 -0
- package/src/packages/en3/test/En3HTML.ts +55 -0
- package/src/packages/en3/test/En3ModifiedMaterial.ts +221 -0
- package/src/packages/en3/test/En3Raycaster.ts +187 -0
- package/src/packages/en3/utils/coverTexture.ts +29 -0
- package/src/packages/en3/utils/dispose.ts +27 -0
- package/src/packages/en3/utils/traverseMaterials.ts +10 -0
- package/src/packages/en3/utils/traverseMeshes.ts +9 -0
- package/src/packages/image/index.ts +19 -0
- package/src/packages/intersector/index.ts +83 -0
- package/src/packages/ladder/index.ts +112 -0
- package/src/packages/layout-box/index.ts +417 -0
- package/src/packages/loading/index.ts +131 -0
- package/src/packages/measurer/CumulativeOffsetLeft.ts +8 -0
- package/src/packages/measurer/CumulativeOffsetTop.ts +8 -0
- package/src/packages/measurer/Meaurer.ts +38 -0
- package/src/packages/measurer/index.ts +3 -0
- package/src/packages/media/index.ts +38 -0
- package/src/packages/morph/Link.ts +32 -0
- package/src/packages/morph/Morph.ts +246 -0
- package/src/packages/morph/index.ts +10 -0
- package/src/packages/notifier/index.ts +41 -0
- package/src/packages/order/index.ts +14 -0
- package/src/packages/resizer/index.ts +55 -0
- package/src/packages/router/Link.ts +33 -0
- package/src/packages/router/Route.ts +152 -0
- package/src/packages/router/RouteElement.ts +34 -0
- package/src/packages/router/Router.ts +190 -0
- package/src/packages/router/index.ts +13 -0
- package/src/packages/scroll/ScrollElement.ts +618 -0
- package/src/packages/scroll/ScrollUserElement.ts +21 -0
- package/src/packages/scroll/ScrollbarElement.ts +170 -0
- package/src/packages/scroll/index.ts +2 -0
- package/src/packages/scroll-entries/index.ts +74 -0
- package/src/packages/source/SourceClass.ts +77 -0
- package/src/packages/source/SourceElement.ts +177 -0
- package/src/packages/source/SourceManager.ts +61 -0
- package/src/packages/source/SourceSet.ts +52 -0
- package/src/packages/source/index.ts +8 -0
- package/src/packages/store/Composed.ts +33 -0
- package/src/packages/store/Derived.ts +24 -0
- package/src/packages/store/DerivedArray.ts +36 -0
- package/src/packages/store/Resource.ts +38 -0
- package/src/packages/store/Store.ts +144 -0
- package/src/packages/store/StoreRegistry.ts +105 -0
- package/src/packages/store/index.ts +23 -0
- package/src/packages/ticker/index.ts +173 -0
- package/src/packages/utils/array.ts +3 -0
- package/src/packages/utils/attributes.ts +19 -0
- package/src/packages/utils/browser.ts +2 -0
- package/src/packages/utils/canvas.ts +46 -0
- package/src/packages/utils/collisions.ts +12 -0
- package/src/packages/utils/coordinates.ts +40 -0
- package/src/packages/utils/decoding.ts +11 -0
- package/src/packages/utils/dev.ts +5 -0
- package/src/packages/utils/dom.ts +48 -0
- package/src/packages/utils/easings.ts +69 -0
- package/src/packages/utils/file.ts +17 -0
- package/src/packages/utils/function.ts +29 -0
- package/src/packages/utils/index.ts +61 -0
- package/src/packages/utils/layout.ts +22 -0
- package/src/packages/utils/math.ts +74 -0
- package/src/packages/utils/number.ts +26 -0
- package/src/packages/utils/object.ts +108 -0
- package/src/packages/utils/string.ts +49 -0
- package/src/packages/utils/ts-shape.ts +25 -0
- package/src/packages/utils/ts-utility.ts +47 -0
- package/src/packages/video/index.ts +39 -0
- package/src/playground/index.ts +0 -0
- package/tsconfig.json +31 -0
- package/vite.config.ts +65 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { define } from '$packages/custom-element'
|
|
2
|
+
import { RESIZE_ORDER } from '$packages/order'
|
|
3
|
+
import { resizer } from '$packages/resizer'
|
|
4
|
+
import { isBrowser } from '$packages/utils'
|
|
5
|
+
import { ScrollUserElement } from './ScrollUserElement'
|
|
6
|
+
|
|
7
|
+
@define('e-scrollbar')
|
|
8
|
+
export class ScrollbarElement extends ScrollUserElement {
|
|
9
|
+
#slotElement: HTMLSlotElement = null!
|
|
10
|
+
#thumbElement: HTMLElement = null!
|
|
11
|
+
|
|
12
|
+
#isHorisontal = false
|
|
13
|
+
|
|
14
|
+
#thumbSize = 0
|
|
15
|
+
#thumbScrollSize = 0
|
|
16
|
+
|
|
17
|
+
#position = 0
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
super()
|
|
21
|
+
|
|
22
|
+
if (isBrowser) {
|
|
23
|
+
const root = this.attachShadow({ mode: 'open' })
|
|
24
|
+
|
|
25
|
+
const styleElement = document.createElement('style')
|
|
26
|
+
styleElement.textContent = `
|
|
27
|
+
:host {
|
|
28
|
+
display: inline-block;
|
|
29
|
+
z-index: 1;
|
|
30
|
+
background-color: #efefef;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:host([axis="y"]) {
|
|
34
|
+
position: absolute;
|
|
35
|
+
right: 0;
|
|
36
|
+
top: 0;
|
|
37
|
+
width: 1vmin;
|
|
38
|
+
height: 100%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:host([axis="x"]) {
|
|
42
|
+
position: absolute;
|
|
43
|
+
left: 0;
|
|
44
|
+
bottom: 0;
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: 1vmin;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.default-thumb {
|
|
50
|
+
background-color: #181818;
|
|
51
|
+
border-radius: 1vmin;
|
|
52
|
+
touch-action: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
::slotted(*) {
|
|
56
|
+
touch-action: none;
|
|
57
|
+
}
|
|
58
|
+
`
|
|
59
|
+
root.appendChild(styleElement)
|
|
60
|
+
|
|
61
|
+
this.#slotElement = document.createElement('slot')
|
|
62
|
+
root.appendChild(this.#slotElement)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public get thumbElement() {
|
|
67
|
+
return this.#thumbElement
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
protected override connectedCallback() {
|
|
71
|
+
super.connectedCallback()
|
|
72
|
+
|
|
73
|
+
const slottedThumb = this.#slotElement.assignedElements()[0]
|
|
74
|
+
|
|
75
|
+
if (slottedThumb instanceof HTMLElement) {
|
|
76
|
+
this.#thumbElement = slottedThumb
|
|
77
|
+
} else {
|
|
78
|
+
this.#thumbElement = document.createElement('div')
|
|
79
|
+
this.#thumbElement.classList.add('default-thumb')
|
|
80
|
+
this.shadowRoot!.appendChild(this.#thumbElement)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.#thumbElement.addEventListener('pointerdown', this.#grabListener)
|
|
84
|
+
|
|
85
|
+
resizer.subscribe(this.#resizeListener, RESIZE_ORDER.SCROLL + 1)
|
|
86
|
+
|
|
87
|
+
this.scrollElement.damped.subscribe(this.#scrollListener)
|
|
88
|
+
|
|
89
|
+
this.scrollElement.axisAttibute.subscribe(this.#axisListener)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
protected disconnectedCallback() {
|
|
93
|
+
this.#thumbElement.removeEventListener('pointerdown', this.#grabListener)
|
|
94
|
+
|
|
95
|
+
resizer.unsubscribe(this.#resizeListener)
|
|
96
|
+
|
|
97
|
+
this.scrollElement.damped.unsubscribe(this.#scrollListener)
|
|
98
|
+
|
|
99
|
+
this.scrollElement.axisAttibute.unsubscribe(this.#axisListener)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#resizeListener = () => {
|
|
103
|
+
this.#isHorisontal = this.offsetWidth > this.offsetHeight
|
|
104
|
+
|
|
105
|
+
const barSize = this.#isHorisontal ? this.offsetWidth : this.offsetHeight
|
|
106
|
+
|
|
107
|
+
this.#thumbSize =
|
|
108
|
+
barSize / ((this.scrollElement.scrollSize + this.scrollElement.viewportSize) / barSize)
|
|
109
|
+
|
|
110
|
+
this.#thumbSize = Math.max(this.#thumbSize, 30)
|
|
111
|
+
|
|
112
|
+
if (this.#isHorisontal) {
|
|
113
|
+
this.#thumbElement.style.width = this.#thumbSize + 'px'
|
|
114
|
+
this.#thumbElement.style.height = '100%'
|
|
115
|
+
} else {
|
|
116
|
+
this.#thumbElement.style.width = '100%'
|
|
117
|
+
this.#thumbElement.style.height = this.#thumbSize + 'px'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.#thumbScrollSize = barSize - this.#thumbSize
|
|
121
|
+
|
|
122
|
+
if (!this.scrollElement.scrollSize) {
|
|
123
|
+
this.style.display = 'none'
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#scrollListener = () => {
|
|
128
|
+
this.#position = this.scrollElement.currentProgress * this.#thumbScrollSize
|
|
129
|
+
|
|
130
|
+
if (this.#isHorisontal) {
|
|
131
|
+
this.#thumbElement.style.transform = `translate3d(${this.#position}px, 0px, 0px)`
|
|
132
|
+
} else {
|
|
133
|
+
this.#thumbElement.style.transform = `translate3d(0px, ${this.#position}px, 0px)`
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#axisListener = () => {
|
|
138
|
+
this.setAttribute('axis', this.scrollElement.axisAttibute.current)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#grabListener = (grabEvent: PointerEvent) => {
|
|
142
|
+
const move = (moveEvent: PointerEvent) => {
|
|
143
|
+
const moveCursor = this.#isHorisontal ? moveEvent.x : moveEvent.y
|
|
144
|
+
|
|
145
|
+
const mult = this.scrollElement.distance / this.#thumbScrollSize
|
|
146
|
+
const delta = (moveCursor - grabCursor) * mult
|
|
147
|
+
|
|
148
|
+
this.scrollElement.damped.set(startValue + delta)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const drop = () => {
|
|
152
|
+
removeEventListener('pointermove', move)
|
|
153
|
+
removeEventListener('pointerup', drop)
|
|
154
|
+
removeEventListener('touchend', drop)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
addEventListener('pointermove', move)
|
|
158
|
+
addEventListener('pointerup', drop)
|
|
159
|
+
addEventListener('touchend', drop)
|
|
160
|
+
|
|
161
|
+
const startValue = this.scrollElement.damped.target
|
|
162
|
+
const grabCursor = this.#isHorisontal ? grabEvent.x : grabEvent.y
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
declare global {
|
|
167
|
+
interface HTMLElementTagNameMap {
|
|
168
|
+
'e-scrollbar': ScrollbarElement
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Axes2D } from '$packages/utils'
|
|
2
|
+
|
|
3
|
+
interface ScrollEntry {
|
|
4
|
+
axis: Axes2D
|
|
5
|
+
value: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class ScrollEntries {
|
|
9
|
+
#elements: Set<HTMLElement> = new Set()
|
|
10
|
+
#entires: WeakMap<HTMLElement, ScrollEntry> = new WeakMap()
|
|
11
|
+
|
|
12
|
+
public register(element: HTMLElement) {
|
|
13
|
+
this.#entires.set(element, {
|
|
14
|
+
axis: 'y',
|
|
15
|
+
value: 0,
|
|
16
|
+
})
|
|
17
|
+
this.#elements.add(element)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public unregister(element: HTMLElement) {
|
|
21
|
+
this.#entires.delete(element)
|
|
22
|
+
this.#elements.delete(element)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public update(element: HTMLElement, axis: Axes2D, value: number) {
|
|
26
|
+
const entry = this.#entires.get(element)!
|
|
27
|
+
entry.axis = axis
|
|
28
|
+
entry.value = value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public hasEntry(element: HTMLElement) {
|
|
32
|
+
return this.#entires.has(element)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public getEntry(element: HTMLElement) {
|
|
36
|
+
return this.#entires.get(element)!
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public getClosest(element: HTMLElement): ScrollEntry | null {
|
|
40
|
+
let parent = element.parentElement
|
|
41
|
+
|
|
42
|
+
while (parent && !this.hasEntry(parent)) {
|
|
43
|
+
parent = parent.parentElement
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (parent && parent !== element) {
|
|
47
|
+
const entry = this.getEntry(parent)
|
|
48
|
+
|
|
49
|
+
if (entry) {
|
|
50
|
+
return entry
|
|
51
|
+
} else {
|
|
52
|
+
return this.getClosest(parent)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public getAll(element: HTMLElement) {
|
|
60
|
+
const elements = Array.from(this.#elements).filter((v) => v !== element && v.contains(element))
|
|
61
|
+
|
|
62
|
+
const entries: Array<ScrollEntry> = []
|
|
63
|
+
|
|
64
|
+
elements.forEach((element) => {
|
|
65
|
+
if (this.#entires.has(element)) {
|
|
66
|
+
entries.push(this.#entires.get(element)!)
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
return entries
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const scrollEnties = new ScrollEntries()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export class Source {
|
|
2
|
+
#url: string
|
|
3
|
+
#name: string
|
|
4
|
+
#density: number
|
|
5
|
+
#query: string
|
|
6
|
+
#queryPx: number
|
|
7
|
+
#queryType: 'max' | 'min'
|
|
8
|
+
#extension: string
|
|
9
|
+
|
|
10
|
+
constructor(url: string) {
|
|
11
|
+
this.#url = url
|
|
12
|
+
|
|
13
|
+
const firstQuestionIndex = url.indexOf('?')
|
|
14
|
+
|
|
15
|
+
if (firstQuestionIndex >= 0) {
|
|
16
|
+
url = url.slice(0, firstQuestionIndex)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const splitted = url.split('.')
|
|
20
|
+
|
|
21
|
+
this.#name = splitted[0]
|
|
22
|
+
|
|
23
|
+
const xpattern = /\d+x/g
|
|
24
|
+
const xmatch = splitted.find((s) => s.match(xpattern))
|
|
25
|
+
this.#density = xmatch ? parseInt(xmatch) : 1
|
|
26
|
+
|
|
27
|
+
const maxpattern = /\d+max/g
|
|
28
|
+
const minpattern = /\d+min/g
|
|
29
|
+
|
|
30
|
+
const maxmatch = splitted.find((s) => s.match(maxpattern))
|
|
31
|
+
const minmatch = splitted.find((s) => s.match(minpattern))
|
|
32
|
+
|
|
33
|
+
if (maxmatch) {
|
|
34
|
+
this.#queryPx = parseInt(maxmatch)
|
|
35
|
+
this.#queryType = 'max'
|
|
36
|
+
this.#query = `(max-width: ${this.#queryPx}px)`
|
|
37
|
+
} else if (minmatch) {
|
|
38
|
+
this.#queryPx = parseInt(minmatch)
|
|
39
|
+
this.#query = `(min-width: ${this.#queryPx}px)`
|
|
40
|
+
this.#queryType = 'min'
|
|
41
|
+
} else {
|
|
42
|
+
this.#queryPx = 0
|
|
43
|
+
this.#query = `(min-width: ${this.#queryPx}px)`
|
|
44
|
+
this.#queryType = 'min'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.#extension = '.' + splitted[splitted.length - 1]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public get url() {
|
|
51
|
+
return this.#url
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public get name() {
|
|
55
|
+
return this.#name
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public get density() {
|
|
59
|
+
return this.#density
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public get query() {
|
|
63
|
+
return this.#query
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public get extension() {
|
|
67
|
+
return this.#extension
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public get queryType() {
|
|
71
|
+
return this.#queryType
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public get queryPx() {
|
|
75
|
+
return this.#queryPx
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { CustomElement } from '$packages/custom-element'
|
|
2
|
+
import { intersector } from '$packages/intersector'
|
|
3
|
+
import { loading } from '$packages/loading'
|
|
4
|
+
import { Notifier } from '$packages/notifier'
|
|
5
|
+
import type { Source } from './SourceClass'
|
|
6
|
+
import { SourceManager } from './SourceManager'
|
|
7
|
+
|
|
8
|
+
let id = 0
|
|
9
|
+
|
|
10
|
+
export interface SourceConsumer extends HTMLElement {
|
|
11
|
+
src: string | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export abstract class SourceElement<T extends SourceConsumer> extends CustomElement {
|
|
15
|
+
#sourceManager: SourceManager = null!
|
|
16
|
+
#consumerElement: T = null!
|
|
17
|
+
#isFirstLoadHappened = false
|
|
18
|
+
#intersectionHappened = false
|
|
19
|
+
#isLazy = false
|
|
20
|
+
#id: string
|
|
21
|
+
#captureEvent = new Notifier()
|
|
22
|
+
#releaseEvent = new Notifier()
|
|
23
|
+
|
|
24
|
+
constructor() {
|
|
25
|
+
super()
|
|
26
|
+
|
|
27
|
+
this.#id = `source-consumer-${++id}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public get consumerElement() {
|
|
31
|
+
return this.#consumerElement
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public get captureEvent() {
|
|
35
|
+
return this.#captureEvent
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public get releaseEvent() {
|
|
39
|
+
return this.#releaseEvent
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected abstract createConsumer(): T
|
|
43
|
+
protected abstract consumeSource(url: string | null): void
|
|
44
|
+
|
|
45
|
+
protected connectedCallback() {
|
|
46
|
+
const srcset = this.getAttribute('srcset')
|
|
47
|
+
|
|
48
|
+
if (!srcset) return
|
|
49
|
+
|
|
50
|
+
this.#consumerElement = this.createConsumer()
|
|
51
|
+
|
|
52
|
+
this.#consumerElement.style.cssText = `
|
|
53
|
+
display: block;
|
|
54
|
+
width: 100%;
|
|
55
|
+
height: 100%;
|
|
56
|
+
`
|
|
57
|
+
|
|
58
|
+
this.#consumerElement.classList.add('source-consumer')
|
|
59
|
+
|
|
60
|
+
Array.from(this.attributes).forEach((attr) => {
|
|
61
|
+
if (attr.nodeName.startsWith('e-')) {
|
|
62
|
+
const originalAttributeName = attr.nodeName.slice(2)
|
|
63
|
+
const value = attr.nodeValue || ''
|
|
64
|
+
|
|
65
|
+
this.#consumerElement.setAttribute(originalAttributeName, value)
|
|
66
|
+
;(this.#consumerElement as any)[originalAttributeName] = value ? value : true
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
this.appendChild(this.#consumerElement)
|
|
71
|
+
|
|
72
|
+
this.#sourceManager = new SourceManager({
|
|
73
|
+
srcset: srcset,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
this.#isLazy = this.hasAttribute('lazy')
|
|
77
|
+
|
|
78
|
+
this.#sourceManager.subscribe((d) => {
|
|
79
|
+
if (!this.#isLazy || (this.#isLazy && this.#intersectionHappened)) {
|
|
80
|
+
this.#loadSource(d.current)
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
intersector.subscribe(this, this.#intersectionListener)
|
|
85
|
+
|
|
86
|
+
this.#sourceManager.connect()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected disconnectedCallback() {
|
|
90
|
+
intersector.unsubscribe(this.#intersectionListener)
|
|
91
|
+
|
|
92
|
+
this.#captureEvent.close()
|
|
93
|
+
this.#releaseEvent.close()
|
|
94
|
+
this.#sourceManager.close()
|
|
95
|
+
|
|
96
|
+
this.#consumerElement.onloadeddata = null
|
|
97
|
+
this.#consumerElement.onload = null
|
|
98
|
+
this.#consumerElement.onerror = null
|
|
99
|
+
|
|
100
|
+
this.#consumerElement.remove()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#loadSource(source: Source | undefined) {
|
|
104
|
+
this.#consumerElement.onloadeddata = null
|
|
105
|
+
this.#consumerElement.onload = null
|
|
106
|
+
this.#consumerElement.onerror = null
|
|
107
|
+
|
|
108
|
+
if (source) {
|
|
109
|
+
const isKeepSourceParameters = this.hasAttribute('keep-source-parameters')
|
|
110
|
+
|
|
111
|
+
this.classList.remove('loaded')
|
|
112
|
+
this.classList.add('loading')
|
|
113
|
+
|
|
114
|
+
const url = isKeepSourceParameters ? source.url : source.name + source.extension
|
|
115
|
+
|
|
116
|
+
this.consumeSource(url)
|
|
117
|
+
|
|
118
|
+
const loadListener = () => {
|
|
119
|
+
this.classList.remove('error')
|
|
120
|
+
this.classList.remove('loading')
|
|
121
|
+
this.classList.add('loaded')
|
|
122
|
+
|
|
123
|
+
if (!this.#isLazy && !this.#isFirstLoadHappened) {
|
|
124
|
+
loading.setLoaded(this.#id, 1)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.#isFirstLoadHappened = true
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const errorListener = () => {
|
|
131
|
+
this.classList.remove('loading')
|
|
132
|
+
this.classList.add('error')
|
|
133
|
+
|
|
134
|
+
if (!this.#isLazy && !this.#isFirstLoadHappened) {
|
|
135
|
+
loading.setError(this.#id, url)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.#isFirstLoadHappened = true
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!this.#isLazy && !this.#isFirstLoadHappened) {
|
|
142
|
+
loading.setTotal(this.#id, 1)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.#consumerElement.onloadeddata = () => {
|
|
146
|
+
loadListener()
|
|
147
|
+
}
|
|
148
|
+
this.#consumerElement.onload = () => {
|
|
149
|
+
loadListener()
|
|
150
|
+
}
|
|
151
|
+
this.#consumerElement.onerror = errorListener
|
|
152
|
+
} else {
|
|
153
|
+
this.consumeSource(null)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#intersectionListener = (entry: IntersectionObserverEntry) => {
|
|
158
|
+
if (this.#isLazy) {
|
|
159
|
+
if (!this.#intersectionHappened && entry.isIntersecting) {
|
|
160
|
+
if (
|
|
161
|
+
this.#sourceManager.current &&
|
|
162
|
+
this.#sourceManager.current !== this.#sourceManager.previous
|
|
163
|
+
) {
|
|
164
|
+
this.#loadSource(this.#sourceManager.current)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this.#intersectionHappened = true
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (entry.isIntersecting) {
|
|
172
|
+
this.#captureEvent.notify()
|
|
173
|
+
} else {
|
|
174
|
+
this.#releaseEvent.notify()
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { RESIZE_ORDER } from '$packages/order'
|
|
2
|
+
import { resizer } from '$packages/resizer'
|
|
3
|
+
import { Store } from '$packages/store'
|
|
4
|
+
import type { Source } from './SourceClass'
|
|
5
|
+
import { SourceSet, SourceSetMediaSources } from './SourceSet'
|
|
6
|
+
|
|
7
|
+
export type SourceManagerSourceSet = string | Array<string>
|
|
8
|
+
|
|
9
|
+
export interface SourceManagerParameters {
|
|
10
|
+
srcset: SourceManagerSourceSet
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class SourceManager extends Store<Source | undefined> {
|
|
14
|
+
#srcset: SourceSet
|
|
15
|
+
|
|
16
|
+
constructor(parameters: SourceManagerParameters) {
|
|
17
|
+
super(undefined)
|
|
18
|
+
|
|
19
|
+
this.#srcset = new SourceSet(parameters.srcset)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public override close() {
|
|
23
|
+
super.close()
|
|
24
|
+
this.disconnect()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public connect() {
|
|
28
|
+
resizer.subscribe(this.#resizeListener, RESIZE_ORDER.SOURCE_MANAGER)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public disconnect() {
|
|
32
|
+
resizer.unsubscribe(this.#resizeListener)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#resizeListener = () => {
|
|
36
|
+
let matchedSources: SourceSetMediaSources | undefined
|
|
37
|
+
|
|
38
|
+
this.#srcset.mediaBuckets.forEach((sources, query) => {
|
|
39
|
+
if (matchMedia(query).matches) {
|
|
40
|
+
matchedSources = sources
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
let matchedSouce: Source | undefined
|
|
45
|
+
|
|
46
|
+
let maxDensity = 0
|
|
47
|
+
|
|
48
|
+
matchedSources?.forEach((s) => {
|
|
49
|
+
if (s.density > maxDensity && s.density <= Math.max(devicePixelRatio, 1)) {
|
|
50
|
+
maxDensity = s.density
|
|
51
|
+
matchedSouce = s
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (matchedSources?.length && !matchedSouce) {
|
|
56
|
+
matchedSouce = matchedSources[0]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.current = matchedSouce
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Source } from './SourceClass'
|
|
2
|
+
|
|
3
|
+
export type SourceSetMediaSources = Array<Source>
|
|
4
|
+
export type SourceSetMediaBucket = Map<string, SourceSetMediaSources>
|
|
5
|
+
|
|
6
|
+
export class SourceSet {
|
|
7
|
+
#mediaBuckets: SourceSetMediaBucket
|
|
8
|
+
|
|
9
|
+
constructor(sourceSet: string | Array<string>) {
|
|
10
|
+
this.#mediaBuckets = new Map()
|
|
11
|
+
|
|
12
|
+
const mediaBuckets =
|
|
13
|
+
typeof sourceSet === 'string'
|
|
14
|
+
? sourceSet
|
|
15
|
+
.trim()
|
|
16
|
+
.split(',')
|
|
17
|
+
.map((u) => u.trim())
|
|
18
|
+
.filter((u) => !!u)
|
|
19
|
+
: sourceSet
|
|
20
|
+
|
|
21
|
+
const tmp: Array<[string, Source]> = []
|
|
22
|
+
|
|
23
|
+
mediaBuckets.forEach((v) => {
|
|
24
|
+
const source = new Source(v)
|
|
25
|
+
tmp.push([source.query, source])
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const max = tmp
|
|
29
|
+
.filter((v) => v[1].queryType === 'max')
|
|
30
|
+
.sort((a, b) => b[1].queryPx - a[1].queryPx)
|
|
31
|
+
|
|
32
|
+
const min = tmp
|
|
33
|
+
.filter((v) => v[1].queryType === 'min' && v[1].queryPx !== 0)
|
|
34
|
+
.sort((a, b) => a[1].queryPx - b[1].queryPx)
|
|
35
|
+
|
|
36
|
+
const defaultMatch = tmp.filter((t) => t[0] === '(min-width: 0px)')
|
|
37
|
+
|
|
38
|
+
const sorted = defaultMatch ? [...defaultMatch, ...max, ...min] : [...max, ...min]
|
|
39
|
+
|
|
40
|
+
sorted.forEach((v) => {
|
|
41
|
+
if (!this.#mediaBuckets.has(v[0])) {
|
|
42
|
+
this.#mediaBuckets.set(v[0], [])
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.#mediaBuckets.get(v[0])!.push(v[1])
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public get mediaBuckets() {
|
|
50
|
+
return this.#mediaBuckets
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Source } from './SourceClass'
|
|
2
|
+
export { SourceSet, type SourceSetMediaSources, type SourceSetMediaBucket } from './SourceSet'
|
|
3
|
+
export {
|
|
4
|
+
SourceManager,
|
|
5
|
+
type SourceManagerSourceSet,
|
|
6
|
+
type SourceManagerParameters,
|
|
7
|
+
} from './SourceManager'
|
|
8
|
+
export { SourceElement, type SourceConsumer } from './SourceElement'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { debounce } from '$packages/utils'
|
|
2
|
+
import { Store, StoreOptions } from './Store'
|
|
3
|
+
|
|
4
|
+
export type ComposedCallback<ComposedType> = () => ComposedType
|
|
5
|
+
|
|
6
|
+
export class Composed<ComposedType> extends Store<ComposedType> {
|
|
7
|
+
#unsubscribers: Array<Function> = []
|
|
8
|
+
|
|
9
|
+
constructor(
|
|
10
|
+
stores: Array<Store<any>>,
|
|
11
|
+
callback: ComposedCallback<ComposedType>,
|
|
12
|
+
parameters?: StoreOptions<ComposedType>
|
|
13
|
+
) {
|
|
14
|
+
super(null!, parameters)
|
|
15
|
+
|
|
16
|
+
const update = debounce(() => {
|
|
17
|
+
this.current = callback()
|
|
18
|
+
}, 0)
|
|
19
|
+
|
|
20
|
+
stores.forEach((store) => {
|
|
21
|
+
this.#unsubscribers.push(
|
|
22
|
+
store.subscribe(() => {
|
|
23
|
+
update()
|
|
24
|
+
})
|
|
25
|
+
)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public override close() {
|
|
30
|
+
super.close()
|
|
31
|
+
this.#unsubscribers.forEach((u) => u())
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Store, StoreOptions } from './Store'
|
|
2
|
+
|
|
3
|
+
export type DerivedCallback<StoreType, DerivedType> = (value: StoreType) => DerivedType
|
|
4
|
+
|
|
5
|
+
export class Derived<DerivedType, StoreType> extends Store<DerivedType> {
|
|
6
|
+
#unsubscriber: Function
|
|
7
|
+
|
|
8
|
+
constructor(
|
|
9
|
+
store: Store<StoreType>,
|
|
10
|
+
callback: DerivedCallback<StoreType, DerivedType>,
|
|
11
|
+
parameters?: StoreOptions<DerivedType>
|
|
12
|
+
) {
|
|
13
|
+
super(null!, parameters)
|
|
14
|
+
|
|
15
|
+
this.#unsubscriber = store.subscribe((e) => {
|
|
16
|
+
this.current = callback(e.current)
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public override close() {
|
|
21
|
+
super.close()
|
|
22
|
+
this.#unsubscriber()
|
|
23
|
+
}
|
|
24
|
+
}
|