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,187 @@
|
|
|
1
|
+
import { Intersection, Object3D, Raycaster, Vector2 } from 'three'
|
|
2
|
+
import { en3 } from '../core/en3'
|
|
3
|
+
|
|
4
|
+
export type En3RaycasterEventType =
|
|
5
|
+
| 'en3-pointerdown'
|
|
6
|
+
| 'en3-pointerup'
|
|
7
|
+
| 'en3-pointermove'
|
|
8
|
+
| 'en3-pointerleave'
|
|
9
|
+
| 'en3-pointerenter'
|
|
10
|
+
| 'en3-pointermove'
|
|
11
|
+
|
|
12
|
+
export type En3RaycasterEvent = {
|
|
13
|
+
type: En3RaycasterEventType
|
|
14
|
+
originalEvent: PointerEvent
|
|
15
|
+
} & Intersection<Object3D>
|
|
16
|
+
|
|
17
|
+
export interface En3RaycasterOptions {
|
|
18
|
+
targetName?: string
|
|
19
|
+
eventDispatcher?: Object3D
|
|
20
|
+
propagation?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface En3RaycasterTargetParameters extends En3RaycasterOptions {
|
|
24
|
+
object3D: Object3D
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type En3RaycasterCallback = (event: En3RaycasterEvent) => void
|
|
28
|
+
|
|
29
|
+
class En3RaycasterTarget {
|
|
30
|
+
#targetName: string | undefined
|
|
31
|
+
#eventDispatcher: Object3D
|
|
32
|
+
#propagation: boolean
|
|
33
|
+
#object3D: Object3D
|
|
34
|
+
#target: () => Object3D
|
|
35
|
+
|
|
36
|
+
public intersection: Intersection<Object3D> | undefined
|
|
37
|
+
|
|
38
|
+
constructor(parameters: En3RaycasterTargetParameters) {
|
|
39
|
+
this.#targetName = parameters.targetName || undefined
|
|
40
|
+
this.#eventDispatcher = parameters.eventDispatcher || parameters.object3D
|
|
41
|
+
this.#propagation = parameters.propagation || false
|
|
42
|
+
this.#object3D = parameters.object3D
|
|
43
|
+
|
|
44
|
+
this.#target = !this.#targetName
|
|
45
|
+
? () => this.#object3D
|
|
46
|
+
: () => this.#object3D.getObjectByName(this.#targetName!) || this.#object3D
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public get object3D() {
|
|
50
|
+
return this.#object3D
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public get eventDispatcher() {
|
|
54
|
+
return this.#eventDispatcher
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public get propagation() {
|
|
58
|
+
return this.#propagation
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public get target() {
|
|
62
|
+
return this.#target()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public dispatch(type: En3RaycasterEventType, originalEvent: PointerEvent) {
|
|
66
|
+
this.eventDispatcher.dispatchEvent<any>({
|
|
67
|
+
originalEvent,
|
|
68
|
+
type,
|
|
69
|
+
...this.intersection,
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class En3Raycaster {
|
|
75
|
+
static #instance: En3Raycaster | undefined
|
|
76
|
+
|
|
77
|
+
#targets: Array<En3RaycasterTarget> = []
|
|
78
|
+
#hits: Array<En3RaycasterTarget> = []
|
|
79
|
+
|
|
80
|
+
#pointer = new Vector2()
|
|
81
|
+
#raycaster = new Raycaster()
|
|
82
|
+
|
|
83
|
+
constructor() {
|
|
84
|
+
if (En3Raycaster.#instance) {
|
|
85
|
+
return En3Raycaster.#instance
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
En3Raycaster.#instance = this
|
|
89
|
+
|
|
90
|
+
en3.containerElement.addEventListener('pointerdown', this.#pointerdownListener)
|
|
91
|
+
en3.containerElement.addEventListener('pointerup', this.#pointerupListener)
|
|
92
|
+
en3.containerElement.addEventListener('pointermove', this.#pointermoveListener)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public destroy() {
|
|
96
|
+
en3.containerElement.removeEventListener('pointerdown', this.#pointerdownListener)
|
|
97
|
+
en3.containerElement.removeEventListener('pointerup', this.#pointerupListener)
|
|
98
|
+
en3.containerElement.removeEventListener('pointermove', this.#pointermoveListener)
|
|
99
|
+
En3Raycaster.#instance = undefined
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public add(object3D: Object3D, options?: En3RaycasterOptions) {
|
|
103
|
+
if (this.#targets.find((t) => t.object3D.uuid === object3D.uuid)) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const target = new En3RaycasterTarget({
|
|
108
|
+
object3D,
|
|
109
|
+
...options,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
this.#targets.push(target)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public remove(object3D: Object3D) {
|
|
116
|
+
this.#targets = this.#targets.filter((t) => t.object3D.uuid !== object3D.uuid)
|
|
117
|
+
this.#hits = this.#hits.filter((h) => h.object3D.uuid !== object3D.uuid)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#pointerdownListener = (event: PointerEvent) => {
|
|
121
|
+
for (let index = 0; index < this.#hits.length; index++) {
|
|
122
|
+
this.#hits[index].dispatch('en3-pointerdown', event)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#pointerupListener = (event: PointerEvent) => {
|
|
127
|
+
for (let index = 0; index < this.#hits.length; index++) {
|
|
128
|
+
this.#hits[index].dispatch('en3-pointerup', event)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#pointermoveListener = (event: PointerEvent) => {
|
|
133
|
+
this.#pointer.x = (event.clientX / en3.width) * 2 - 1
|
|
134
|
+
this.#pointer.y = -(event.clientY / en3.height) * 2 + 1
|
|
135
|
+
|
|
136
|
+
if (en3.camera) {
|
|
137
|
+
this.#raycaster.setFromCamera(this.#pointer, en3.camera)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const hits: Array<En3RaycasterTarget> = []
|
|
141
|
+
|
|
142
|
+
for (const target of this.#targets) {
|
|
143
|
+
const intersection = this.#raycaster.intersectObject(target.target)
|
|
144
|
+
|
|
145
|
+
if (intersection.length) {
|
|
146
|
+
target.intersection = intersection[0]
|
|
147
|
+
hits.push(target)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let isStopPropagation = false
|
|
152
|
+
|
|
153
|
+
const leaveHits = this.#hits.filter(
|
|
154
|
+
(s) => !hits.find((h) => h.object3D.uuid === s.object3D.uuid)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
const finalHits = hits
|
|
158
|
+
.sort((a, b) => b.object3D.position.z - a.object3D.position.z)
|
|
159
|
+
.filter((h) => {
|
|
160
|
+
if (isStopPropagation) {
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
isStopPropagation = !h.propagation
|
|
165
|
+
|
|
166
|
+
return true
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const enterHits = finalHits.filter(
|
|
170
|
+
(s) => !this.#hits.find((h) => h.object3D.uuid === s.object3D.uuid)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
for (let index = 0; index < leaveHits.length; index++) {
|
|
174
|
+
leaveHits[index].dispatch('en3-pointerleave', event)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
for (let index = 0; index < enterHits.length; index++) {
|
|
178
|
+
enterHits[index].dispatch('en3-pointerenter', event)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.#hits = finalHits
|
|
182
|
+
|
|
183
|
+
for (let index = 0; index < this.#hits.length; index++) {
|
|
184
|
+
this.#hits[index].dispatch('en3-pointermove', event)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Dot2D } from '$packages/utils'
|
|
2
|
+
import { Texture } from 'three'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* texture.matrixAutoUpdate must be false
|
|
6
|
+
*/
|
|
7
|
+
export function coverTexture(texture: Texture, planeSize: Dot2D, aspect?: number) {
|
|
8
|
+
const width = planeSize.x
|
|
9
|
+
const height = planeSize.y
|
|
10
|
+
const _aspect = aspect || texture.image.width / texture.image.height
|
|
11
|
+
|
|
12
|
+
let sx = 0
|
|
13
|
+
let sy = 0
|
|
14
|
+
let tx = texture.offset.x
|
|
15
|
+
let ty = texture.offset.y
|
|
16
|
+
let r = texture.rotation
|
|
17
|
+
let cx = texture.center.x
|
|
18
|
+
let cy = texture.center.y
|
|
19
|
+
|
|
20
|
+
if (width / height > _aspect) {
|
|
21
|
+
sx = 1
|
|
22
|
+
sy = (height / width) * _aspect
|
|
23
|
+
} else {
|
|
24
|
+
sy = 1
|
|
25
|
+
sx = width / height / _aspect
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
texture.matrix.setUvTransform(tx, ty, sx, sy, r, cx, cy)
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Material, Mesh, Object3D } from 'three'
|
|
2
|
+
|
|
3
|
+
export function dispose(object3d: Object3D) {
|
|
4
|
+
const cleanMaterial = (material: Material) => {
|
|
5
|
+
material.dispose()
|
|
6
|
+
|
|
7
|
+
for (const key of Object.keys(material)) {
|
|
8
|
+
const value = material[key as keyof Material]
|
|
9
|
+
|
|
10
|
+
if (value && typeof value === 'object' && 'minFilter' in value) {
|
|
11
|
+
value.dispose()
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
object3d.traverse((object: Object3D) => {
|
|
17
|
+
if (!(object instanceof Mesh)) return
|
|
18
|
+
|
|
19
|
+
object.geometry.dispose()
|
|
20
|
+
|
|
21
|
+
if (!Array.isArray(object.material) && object.material.isMaterial) {
|
|
22
|
+
cleanMaterial(object.material)
|
|
23
|
+
} else if (Array.isArray(object.material)) {
|
|
24
|
+
for (const material of object.material) cleanMaterial(material)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Mesh, Material, Object3D } from 'three'
|
|
2
|
+
|
|
3
|
+
export function traverseMaterials(object: Object3D, callback: (material: Material) => void) {
|
|
4
|
+
object.traverse((node: Object3D) => {
|
|
5
|
+
if (node instanceof Mesh && node.material) {
|
|
6
|
+
const materials = Array.isArray(node.material) ? node.material : [node.material]
|
|
7
|
+
materials.forEach(callback)
|
|
8
|
+
}
|
|
9
|
+
})
|
|
10
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { define } from '$packages/custom-element'
|
|
2
|
+
import { SourceElement } from '$packages/source'
|
|
3
|
+
|
|
4
|
+
@define('e-image')
|
|
5
|
+
export class ImageElement extends SourceElement<HTMLImageElement> {
|
|
6
|
+
protected override createConsumer() {
|
|
7
|
+
return document.createElement('img')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
protected override consumeSource(url: string | null) {
|
|
11
|
+
this.consumerElement.src = url || ''
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
interface HTMLElementTagNameMap {
|
|
17
|
+
'e-image': ImageElement
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { isBrowser } from '$packages/utils'
|
|
2
|
+
|
|
3
|
+
export type IntersectorCallback = (entry: IntersectionObserverEntry) => void
|
|
4
|
+
|
|
5
|
+
interface IntersectorSubscriber {
|
|
6
|
+
element: Element
|
|
7
|
+
callback: IntersectorCallback
|
|
8
|
+
entry: IntersectionObserverEntry | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class Intersector {
|
|
12
|
+
#subscribers: Array<IntersectorSubscriber> = []
|
|
13
|
+
#intersectionObserver: IntersectionObserver = null!
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
if (isBrowser) {
|
|
17
|
+
this.#intersectionObserver = new IntersectionObserver(this.#intersectionListener)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public subscribe(elementOrSelector: HTMLElement | string, callback: IntersectorCallback) {
|
|
22
|
+
const element =
|
|
23
|
+
typeof elementOrSelector === 'string'
|
|
24
|
+
? document.querySelector<HTMLElement>(elementOrSelector)
|
|
25
|
+
: elementOrSelector
|
|
26
|
+
|
|
27
|
+
if (!element) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const alreadyObserved = this.#subscribers.find((sub) => sub.element === element)
|
|
32
|
+
|
|
33
|
+
if (!alreadyObserved) {
|
|
34
|
+
this.#intersectionObserver.observe(element)
|
|
35
|
+
} else {
|
|
36
|
+
const alreadyIntersected = this.#subscribers.find((s) => s.element === element && s.entry)
|
|
37
|
+
|
|
38
|
+
if (alreadyIntersected?.entry!.isIntersecting) {
|
|
39
|
+
callback(alreadyIntersected.entry)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.#subscribers.push({
|
|
44
|
+
element,
|
|
45
|
+
callback,
|
|
46
|
+
entry: alreadyObserved?.entry || null,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
this.unsubscribe(callback)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public unsubscribe(callback: IntersectorCallback) {
|
|
55
|
+
const subscriber = this.#subscribers.find((sub) => sub.callback === callback)
|
|
56
|
+
|
|
57
|
+
if (subscriber) {
|
|
58
|
+
this.#subscribers = this.#subscribers.filter((sub) => sub.callback === callback)
|
|
59
|
+
|
|
60
|
+
if (!this.#subscribers.find((sub) => sub.element === subscriber.element)) {
|
|
61
|
+
this.#intersectionObserver.unobserve(subscriber.element)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#intersectionListener: IntersectionObserverCallback = (entries) => {
|
|
67
|
+
const matches = this.#subscribers.map((subscriber) => {
|
|
68
|
+
return {
|
|
69
|
+
subscriber,
|
|
70
|
+
entry: entries.find((entry) => entry.target === subscriber.element),
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
matches.forEach((match) => {
|
|
75
|
+
if (match.entry) {
|
|
76
|
+
match.subscriber.entry = match.entry
|
|
77
|
+
match.subscriber.callback(match.entry)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const intersector = new Intersector()
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Store } from '$packages/store'
|
|
2
|
+
|
|
3
|
+
export type LadderDefaultStepName = number | string
|
|
4
|
+
|
|
5
|
+
export type LadderOperation = '+' | '*' | '/' | '-'
|
|
6
|
+
|
|
7
|
+
export type LadderStep<T> = [LadderOperation, T]
|
|
8
|
+
|
|
9
|
+
export type LadderSteps<K, T> = Map<K, LadderStep<T>>
|
|
10
|
+
|
|
11
|
+
export type LadderDefaultValueType = { [key: string]: number }
|
|
12
|
+
|
|
13
|
+
export class Ladder<
|
|
14
|
+
V extends LadderDefaultValueType = LadderDefaultValueType,
|
|
15
|
+
K extends LadderDefaultStepName = LadderDefaultStepName
|
|
16
|
+
> extends Store<V> {
|
|
17
|
+
#base: V
|
|
18
|
+
#steps: LadderSteps<K, V>
|
|
19
|
+
#bindings: Set<V>
|
|
20
|
+
|
|
21
|
+
constructor(base: V) {
|
|
22
|
+
super(base, {
|
|
23
|
+
equalityCheck: () => false,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
this.#base = { ...base }
|
|
27
|
+
this.#steps = new Map()
|
|
28
|
+
this.#bindings = new Set()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public get base() {
|
|
32
|
+
return this.#base
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public get steps() {
|
|
36
|
+
return this.#steps
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public override close() {
|
|
40
|
+
super.close()
|
|
41
|
+
this.#bindings.clear()
|
|
42
|
+
this.#steps.clear()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public bind(sub: V) {
|
|
46
|
+
this.#bindings.add(sub)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public unbind(sub: V) {
|
|
50
|
+
this.#bindings.delete(sub)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public deleteStep(stepName: K) {
|
|
54
|
+
this.steps.delete(stepName)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public getStepValue(stepName: K) {
|
|
58
|
+
return this.steps.get(stepName)![1]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public setStep(stepName: K, action: LadderOperation, value: Partial<V>) {
|
|
62
|
+
const readyValue = {} as V
|
|
63
|
+
|
|
64
|
+
for (const key in this.current) {
|
|
65
|
+
;(readyValue[key] as any) = value[key] || 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.steps.set(stepName, [action, readyValue])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public calculate() {
|
|
72
|
+
const calculated = {} as V
|
|
73
|
+
|
|
74
|
+
for (const key in this.base) {
|
|
75
|
+
calculated[key] = this.base[key]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const item of this.steps) {
|
|
79
|
+
const step = item[1]
|
|
80
|
+
|
|
81
|
+
if (step[0] === '+') {
|
|
82
|
+
for (const key in this.base) {
|
|
83
|
+
const value = step[1][key] as number
|
|
84
|
+
;(calculated[key] as any) += value
|
|
85
|
+
}
|
|
86
|
+
} else if (step[0] === '*') {
|
|
87
|
+
for (const key in this.base) {
|
|
88
|
+
const value = step[1][key] as number
|
|
89
|
+
;(calculated[key] as any) *= value
|
|
90
|
+
}
|
|
91
|
+
} else if (step[0] === '/') {
|
|
92
|
+
for (const key in this.base) {
|
|
93
|
+
const value = step[1][key] as number
|
|
94
|
+
;(calculated[key] as any) /= value
|
|
95
|
+
}
|
|
96
|
+
} else if (step[0] === '-') {
|
|
97
|
+
for (const key in this.base) {
|
|
98
|
+
const value = step[1][key] as number
|
|
99
|
+
;(calculated[key] as any) -= value
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const sub of this.#bindings) {
|
|
105
|
+
for (const key in calculated) {
|
|
106
|
+
sub[key] = calculated[key]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.current = calculated
|
|
111
|
+
}
|
|
112
|
+
}
|