feat: animation system
This commit is contained in:
parent
406e71575f
commit
7534a613cb
22 changed files with 19733 additions and 19122 deletions
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||

|
||||
|
|
@ -17,16 +17,18 @@
|
|||
"license": "MIT",
|
||||
"author": "Asaki Yuki",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"main": "dist/js/index.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsc --watch",
|
||||
"test": "bun test/app.ts",
|
||||
"prefetch": "bun scripts/prefetch",
|
||||
"vanilla:defs": "bun scripts/vanilladefs",
|
||||
"gen:enums": "bun scripts/enum",
|
||||
"gen:items": "bun scripts/items",
|
||||
"gen:autocomplete": "bun scripts/autocomplete-build"
|
||||
"gen:autocomplete": "bun scripts/autocomplete-build",
|
||||
"full-build": "bun run prefetch;\nbun run vanilla:defs;\nbun run gen:enums;\nbun run gen:items;\nbun run gen:autocomplete;\nbun run build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.3",
|
||||
|
|
|
|||
BIN
resources/logo.png
Normal file
BIN
resources/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
|
|
@ -10,7 +10,12 @@ const intelliSense: string[] = [
|
|||
'import { Type as T } from "../enums/Type.js"\n',
|
||||
"export type Namespace = keyof IntelliSense;",
|
||||
"export type Element<T extends Namespace> = Extract<keyof IntelliSense[T], string>",
|
||||
"export type VanillaType<T extends Namespace, K extends Element<T>> = IntelliSense[T][K]\n",
|
||||
"export type VanillaElementInfo<T extends Namespace, K extends Element<T>> = IntelliSense[T][K]",
|
||||
"// @ts-ignore",
|
||||
'export type VanillaType<T extends Namespace, K extends Element<T>> = VanillaElementInfo<T, K>["type"]',
|
||||
"// @ts-ignore",
|
||||
'export type VanillaElementChilds<T extends Namespace, K extends Element<T>> = VanillaElementInfo<T, K>["children"]',
|
||||
"\n",
|
||||
"export type IntelliSense = {",
|
||||
]
|
||||
|
||||
|
|
@ -27,9 +32,12 @@ for (const [namespace, element] of Object.entries(data)) {
|
|||
ePaths: string[] = [` "${namespace}": {`]
|
||||
|
||||
for (const [ePath, info] of Object.entries(<any>element)) {
|
||||
const { file, type, extend } = <any>info
|
||||
const { file, type, extend, children } = <any>info
|
||||
|
||||
const childType = children?.map((v: string) => `'${v}'`).join(" | ") || "string"
|
||||
|
||||
eType.push(`"${ePath}"`)
|
||||
eType3.push(` "${ePath}": T.${type.toUpperCase()},`)
|
||||
eType3.push(` "${ePath}": { type: T.${type.toUpperCase()}, children: ${childType} },`)
|
||||
ePaths.push(` "${ePath}": "${file}",`)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ const vanilla: NamespaceMap = new Map()
|
|||
function readControls(namespace: string, file: string, elements: ElementMap, data: any[], prefix: string) {
|
||||
prefix += "/"
|
||||
|
||||
const childs: string[] = []
|
||||
|
||||
for (const element of data) {
|
||||
const [fullname, properties] = Object.entries(element)[0]
|
||||
|
||||
|
|
@ -17,6 +19,7 @@ function readControls(namespace: string, file: string, elements: ElementMap, dat
|
|||
const [name, $2] = fullname.split("@")
|
||||
|
||||
if (name.startsWith("$")) continue
|
||||
childs.push(name)
|
||||
|
||||
if ($2 && !$2.startsWith("$")) {
|
||||
const [$3, $4] = $2.split(".")
|
||||
|
|
@ -37,9 +40,12 @@ function readControls(namespace: string, file: string, elements: ElementMap, dat
|
|||
|
||||
const controls = (<any>properties).controls
|
||||
if (controls) {
|
||||
readControls(namespace, file, elements, controls, prefix + name)
|
||||
const childs = readControls(namespace, file, elements, controls, prefix + name)
|
||||
if (childs.length) data.children = childs
|
||||
}
|
||||
}
|
||||
|
||||
return childs
|
||||
}
|
||||
|
||||
function readData(namespace: string, file: string, elements: ElementMap, data: any) {
|
||||
|
|
@ -73,7 +79,8 @@ function readData(namespace: string, file: string, elements: ElementMap, data: a
|
|||
|
||||
const controls = (<any>properties).controls
|
||||
if (controls) {
|
||||
readControls(namespace, file, elements, controls, name)
|
||||
const childs = readControls(namespace, file, elements, controls, name)
|
||||
if (childs.length) data.children = childs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -148,6 +155,7 @@ interface VanillaElement {
|
|||
namespace: string
|
||||
}
|
||||
anim_type?: string
|
||||
children?: string[]
|
||||
type: string
|
||||
file: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import { AnimationKeyframe } from "../components/AnimationKeyframe.js"
|
||||
import { AnimType } from "../types/enums/AnimType.js"
|
||||
import { KeyframeAnimationProperties } from "../types/properties/element/Animation.js"
|
||||
import { Binding } from "../types/properties/value.js"
|
||||
|
||||
export function FormatProperties(properties: any) {
|
||||
|
|
@ -27,3 +30,11 @@ export function FormatProperties(properties: any) {
|
|||
|
||||
return properties
|
||||
}
|
||||
|
||||
export function FormatAnimationProperties(properties: KeyframeAnimationProperties<AnimType>) {
|
||||
if (properties.next instanceof AnimationKeyframe) {
|
||||
properties.next = `${properties.next}`
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { AnimationKeyframe } from "../components/AnimationKeyframe.js"
|
||||
import { Class } from "../components/Class.js"
|
||||
import { UI } from "../components/UI.js"
|
||||
import { AnimType } from "../types/enums/AnimType.js"
|
||||
import { Renderer } from "../types/enums/Renderer.js"
|
||||
import { Type } from "../types/enums/Type.js"
|
||||
|
||||
type Element = UI<Type, Renderer | null>
|
||||
type Element = UI<Type, Renderer | null> | AnimationKeyframe<AnimType>
|
||||
interface FileInterface {
|
||||
namespace: string
|
||||
elements: Element[]
|
||||
|
|
@ -13,7 +15,7 @@ type Files = Map<string, FileInterface>
|
|||
export class Memory extends Class {
|
||||
protected static files: Files = new Map<string, FileInterface>()
|
||||
|
||||
public static add(element: UI<Type, Renderer | null>) {
|
||||
public static add(element: UI<Type, Renderer | null> | AnimationKeyframe<AnimType>) {
|
||||
let file = Memory.files.get(element.path)
|
||||
|
||||
if (!file) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { Memory } from "./Memory.js"
|
||||
|
||||
process.on("beforeExit", () => {
|
||||
// console.log(Memory.build())
|
||||
})
|
||||
const isBuildMode = process.argv.includes("--build")
|
||||
|
||||
if (isBuildMode) {
|
||||
process.on("beforeExit", () => {
|
||||
console.log(JSON.stringify(Memory.build(), null, 2))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,63 @@
|
|||
import { AnimType } from "../types/enums/AnimType.js"
|
||||
import { AnimationProperties } from "../types/properties/element/Animation.js"
|
||||
import { Class } from "./Class.js"
|
||||
import { KeyframeController } from "./KeyframeController.js"
|
||||
|
||||
export class Animation extends Class {}
|
||||
export class Animation<T extends AnimType> extends Class {
|
||||
protected keyframes: KeyframeController<AnimType>[] = []
|
||||
protected loop = false
|
||||
|
||||
constructor(
|
||||
readonly type: T,
|
||||
...keyframes: (AnimationProperties<T> | number)[]
|
||||
) {
|
||||
super()
|
||||
if (type === AnimType.WAIT) console.warn("Why are you create a wait animation?")
|
||||
this.addKeyframes(...keyframes)
|
||||
}
|
||||
|
||||
protected lastKey() {
|
||||
return this.keyframes[this.keyframes.length - 1]
|
||||
}
|
||||
|
||||
protected firstKey() {
|
||||
return this.keyframes[0]
|
||||
}
|
||||
|
||||
addKeyframes(...keyframes: (AnimationProperties<T> | number)[]) {
|
||||
for (const $ of keyframes) {
|
||||
let keyframe: AnimationProperties<AnimType>, animType: AnimType
|
||||
|
||||
if (typeof $ === "number") {
|
||||
keyframe = { duration: $ }
|
||||
animType = AnimType.WAIT
|
||||
} else {
|
||||
keyframe = $
|
||||
animType = this.type
|
||||
}
|
||||
|
||||
const keyframeController = new KeyframeController(animType, keyframe as AnimationProperties<T>)
|
||||
|
||||
const prevKeyframe = this.lastKey()
|
||||
if (prevKeyframe) prevKeyframe.setNext(keyframeController)
|
||||
|
||||
if (this.loop) keyframeController.setNext(this.firstKey() as KeyframeController<T>)
|
||||
|
||||
this.keyframes.push(keyframeController)
|
||||
}
|
||||
}
|
||||
|
||||
setLoop(boolean: boolean) {
|
||||
this.loop = boolean
|
||||
|
||||
if (this.loop) {
|
||||
const prevKeyframe = this.lastKey()
|
||||
if (prevKeyframe) prevKeyframe.setNext(this.firstKey() as KeyframeController<T>)
|
||||
} else {
|
||||
const prevKeyframe = this.lastKey()
|
||||
if (prevKeyframe) prevKeyframe.clearNext()
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,61 @@
|
|||
import { FormatAnimationProperties } from "../compilers/FormatProperties.js"
|
||||
import { Memory } from "../compilers/Memory.js"
|
||||
import { AnimType } from "../types/enums/AnimType.js"
|
||||
import { KeyframeAnimationProperties } from "../types/properties/element/Animation.js"
|
||||
import { Class } from "./Class.js"
|
||||
import { RandomString } from "./Utils.js"
|
||||
|
||||
export class AnimationKeyframe extends Class {}
|
||||
import util from "node:util"
|
||||
|
||||
export class AnimationKeyframe<T extends AnimType> extends Class {
|
||||
readonly path: string
|
||||
readonly name: string
|
||||
readonly namespace: string
|
||||
|
||||
constructor(
|
||||
readonly type: T,
|
||||
readonly properties: KeyframeAnimationProperties<T>,
|
||||
name?: string,
|
||||
namespace?: string,
|
||||
path?: string,
|
||||
) {
|
||||
super()
|
||||
|
||||
if (name === "namespace") {
|
||||
console.error("The 'namespace' cannot be used as a name")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (namespace && !/^\w+$/.test(namespace)) {
|
||||
console.error(`The '${namespace}' cannot be used as a namespace`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
this.name = name || RandomString(16)
|
||||
this.namespace = namespace || RandomString(16)
|
||||
this.path = path || `@/${this.namespace}`
|
||||
|
||||
Memory.add(this)
|
||||
}
|
||||
|
||||
protected toJsonUI() {
|
||||
return FormatAnimationProperties(this.properties)
|
||||
}
|
||||
|
||||
protected toJSON() {
|
||||
return {
|
||||
anim_type: this.type,
|
||||
...this.toJsonUI(),
|
||||
}
|
||||
}
|
||||
|
||||
protected toString() {
|
||||
return `@${this.namespace}.${this.name}`
|
||||
}
|
||||
|
||||
protected [util.inspect.custom]($: any, opts: any) {
|
||||
return `\x1b[33mAnimationKeyFrame\x1b[0m<\x1b[92m${
|
||||
this.type
|
||||
}\x1b[0m> \x1b[92m"${this}\x1b[92m"\x1b[0m ${util.inspect(this.toJsonUI(), opts)}\n`
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
src/components/KeyframeController.ts
Normal file
19
src/components/KeyframeController.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { AnimType } from "../types/enums/AnimType.js"
|
||||
import { KeyframeAnimationProperties } from "../types/properties/element/Animation.js"
|
||||
import { AnimationKeyframe } from "./AnimationKeyframe.js"
|
||||
|
||||
export class KeyframeController<T extends AnimType> extends AnimationKeyframe<T> {
|
||||
constructor(type: T, properties: KeyframeAnimationProperties<T>, name?: string, namespace?: string, path?: string) {
|
||||
super(type, properties, name, namespace, path)
|
||||
}
|
||||
|
||||
setNext(keyframe: AnimationKeyframe<AnimType>) {
|
||||
this.properties.next = keyframe
|
||||
return this
|
||||
}
|
||||
|
||||
clearNext() {
|
||||
delete this.properties.next
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +1,31 @@
|
|||
import { isCompileBinding } from "../compilers/bindings/Checker.js"
|
||||
import { Parser } from "../compilers/bindings/Parser.js"
|
||||
import { FormatProperties } from "../compilers/FormatProperties.js"
|
||||
import { Memory } from "../compilers/Memory.js"
|
||||
import { BindingType } from "../types/enums/BindingType.js"
|
||||
import { ArrayName } from "../types/enums/ArrayName.js"
|
||||
import { Operation } from "../types/enums/Operation.js"
|
||||
import { Renderer } from "../types/enums/Renderer.js"
|
||||
import { Type } from "../types/enums/Type.js"
|
||||
import { Properties } from "../types/properties/components.js"
|
||||
import { BindingItem } from "../types/properties/value.js"
|
||||
import { BindingItem, ButtonMapping, ModificationItem, VariableItem, Variables } from "../types/properties/value.js"
|
||||
import { Class } from "./Class.js"
|
||||
import { ExtendsOf, RandomString } from "./Utils.js"
|
||||
import { ExtendsOf, RandomString, ResolveBinding } from "./Utils.js"
|
||||
|
||||
import util from "node:util"
|
||||
|
||||
export class UI<T extends Type, K extends Renderer | null = null> extends Class {
|
||||
readonly path: string
|
||||
|
||||
readonly name: string
|
||||
readonly namespace: string
|
||||
readonly extend?: UI<Type, Renderer | null>
|
||||
|
||||
readonly extend?: UI<Type, Renderer | null>
|
||||
readonly extendable: boolean
|
||||
|
||||
readonly controls = new Map<string, [UI<Type, Renderer | null>, Properties<Type, Renderer | null>]>()
|
||||
readonly bindings: BindingItem[] = []
|
||||
readonly extendType?: Type
|
||||
properties: Properties<T, K> = <any>{}
|
||||
protected readonly controls = new Map<string, [UI<Type, Renderer | null>, Properties<Type, Renderer | null>]>()
|
||||
protected readonly bindings: BindingItem[] = []
|
||||
protected readonly variables: VariableItem[] = []
|
||||
protected readonly buttonMappings: ButtonMapping[] = []
|
||||
protected readonly extendType?: Type
|
||||
|
||||
protected properties: Properties<T, K> = <any>{}
|
||||
|
||||
constructor(
|
||||
public type?: T,
|
||||
|
|
@ -55,60 +56,72 @@ export class UI<T extends Type, K extends Renderer | null = null> extends Class
|
|||
Memory.add(this)
|
||||
}
|
||||
|
||||
protected UI_JSON() {
|
||||
const obj: any = {
|
||||
...FormatProperties(this.properties),
|
||||
}
|
||||
|
||||
if (this.type) {
|
||||
obj.type = this.type
|
||||
}
|
||||
|
||||
if (this.bindings.length) {
|
||||
obj.bindings = this.bindings
|
||||
}
|
||||
|
||||
if (this.controls.size) {
|
||||
obj.controls = []
|
||||
this.controls.forEach((e, key) => obj.controls.push({ [key + e[0]]: e[1] }))
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties for this element
|
||||
* @param properties
|
||||
* @returns
|
||||
*/
|
||||
setProperties(properties: Properties<T, K>) {
|
||||
this.properties = { ...this.properties, ...properties }
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind data (coming from the code) to this UI element to use.
|
||||
* @param bindings
|
||||
* @returns
|
||||
*/
|
||||
addBindings(...bindings: BindingItem[]) {
|
||||
for (const binding of bindings) {
|
||||
if (binding.source_property_name) {
|
||||
if (isCompileBinding(binding.source_property_name)) {
|
||||
const { gen, out } = new Parser(binding.source_property_name.slice(1, -1)).out()
|
||||
if (gen) this.bindings.push(...gen)
|
||||
binding.source_property_name = out
|
||||
this.bindings.push(...ResolveBinding(...bindings))
|
||||
return this
|
||||
}
|
||||
|
||||
binding.binding_type = BindingType.VIEW
|
||||
|
||||
if (!binding.target_property_name) throw new Error("Binding must have a target property name")
|
||||
}
|
||||
this.bindings.push(binding)
|
||||
}
|
||||
/**
|
||||
* Changes variables values if conditions are met.
|
||||
* @param variables
|
||||
* @returns
|
||||
*/
|
||||
addVariables(variables: Variables) {
|
||||
Object.entries(variables).forEach(([key, value]) => {
|
||||
this.variables.push({
|
||||
requires: key,
|
||||
...value,
|
||||
})
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
addChild<T extends Type, K extends Renderer | null>(child: UI<T, K>, properties?: Properties<T, K>, name?: string) {
|
||||
if (this === <any>child) {
|
||||
throw new Error("Cannot add a child to itself")
|
||||
/**
|
||||
* Add button mappings for this element
|
||||
* @param mappings
|
||||
* @returns
|
||||
*/
|
||||
addButtonMappings(...mappings: ButtonMapping[]) {
|
||||
this.buttonMappings.push(...mappings)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Children of the UI element.
|
||||
* @param child
|
||||
* @param properties
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
addChild<T extends Type, K extends Renderer | null>(child: UI<T, K>, properties?: Properties<T, K>, name?: string) {
|
||||
if (this === <any>child) throw new Error("Cannot add a child to itself")
|
||||
this.controls.set(name || RandomString(16), [child, properties || {}])
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a extend of this element
|
||||
* @param properties
|
||||
* @param name
|
||||
* @param namespace
|
||||
* @returns
|
||||
*/
|
||||
clone(properties?: Properties<T, K>, name?: string, namespace?: string) {
|
||||
return ExtendsOf(this, properties, name, namespace)
|
||||
}
|
||||
|
|
@ -117,40 +130,264 @@ export class UI<T extends Type, K extends Renderer | null = null> extends Class
|
|||
return `@${this.namespace}.${this.name}`
|
||||
}
|
||||
|
||||
protected toJSON() {
|
||||
return this.UI_JSON()
|
||||
}
|
||||
|
||||
protected [util.inspect.custom]($: any, opts: any) {
|
||||
protected toJsonUI() {
|
||||
const obj: any = {
|
||||
...FormatProperties(this.properties),
|
||||
}
|
||||
|
||||
if (this.bindings.length) {
|
||||
obj.bindings = this.bindings
|
||||
}
|
||||
if (this.type) obj.type = this.type
|
||||
|
||||
if (this.bindings.length) obj.bindings = this.bindings
|
||||
if (this.variables.length) obj.variables = this.variables
|
||||
if (this.buttonMappings.length) obj.button_mappings = this.buttonMappings
|
||||
|
||||
if (this.controls.size) {
|
||||
obj.controls = []
|
||||
this.controls.forEach((e, key) => obj.controls.push({ [key + e[0]]: e[1] }))
|
||||
}
|
||||
|
||||
const elementType = this.type || (this.extend ? `${this.extendType || "unknown"}:${this.extend}` : "unknown")
|
||||
|
||||
return `\x1b[33mUI\x1b[0m<\x1b[92m${
|
||||
elementType
|
||||
}\x1b[0m> \x1b[92m"${this}\x1b[92m"\x1b[0m ${util.inspect(obj, opts)}\n`
|
||||
}
|
||||
}
|
||||
|
||||
export class ModifyUI<T extends Type> extends UI<T, null> {
|
||||
constructor(namespace: string, name: string, path: string) {
|
||||
if (!path) throw new Error("ModifyUI cannot have a path")
|
||||
super(undefined, name, namespace, path)
|
||||
return obj
|
||||
}
|
||||
|
||||
protected toJSON() {
|
||||
const obj = this.UI_JSON()
|
||||
return obj
|
||||
return this.toJsonUI()
|
||||
}
|
||||
|
||||
protected [util.inspect.custom]($: any, opts: any) {
|
||||
return `\x1b[33mUI\x1b[0m<\x1b[92m${
|
||||
this.type || (this.extend ? `${this.extendType || "unknown"}:${this.extend}` : "unknown")
|
||||
}\x1b[0m> \x1b[92m"${this}\x1b[92m"\x1b[0m ${util.inspect(this.toJsonUI(), opts)}\n`
|
||||
}
|
||||
}
|
||||
|
||||
export class ModifyUI<T extends Type = Type.PANEL, S extends string = string> extends UI<T, null> {
|
||||
private isClearBinding: boolean = false
|
||||
private isClearVariables: boolean = false
|
||||
private isClearButtonMappings: boolean = false
|
||||
|
||||
protected modifications: ModificationItem[] = []
|
||||
|
||||
constructor(namespace: string, name: string, path: string) {
|
||||
super(undefined, name, namespace, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all bindings of this modify element
|
||||
* @returns
|
||||
*/
|
||||
clearBinding() {
|
||||
this.isClearBinding = true
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all variables of this modfy element
|
||||
* @returns
|
||||
*/
|
||||
clearVariables() {
|
||||
this.isClearVariables = true
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all button mappings of this element
|
||||
* @returns
|
||||
*/
|
||||
clearButtonMappings() {
|
||||
this.isClearButtonMappings = true
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to modify the UI elements from resource packs below this one
|
||||
* @returns
|
||||
*/
|
||||
addModifications(...modifications: ModificationItem[]) {
|
||||
this.modifications.push(...modifications)
|
||||
return this
|
||||
}
|
||||
|
||||
insertBackChild<T extends Type, K extends Renderer | null>(
|
||||
child: UI<T, K>,
|
||||
properties?: Properties<T, K>,
|
||||
name?: string,
|
||||
) {
|
||||
if (this === <any>child) throw new Error("Cannot add a child to itself")
|
||||
if (!name) name = RandomString(16)
|
||||
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.CONTROLS,
|
||||
operation: Operation.INSERT_BACK,
|
||||
value: {
|
||||
[`${name}${child}`]: properties || {},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
insertFrontChild<T extends Type, K extends Renderer | null>(
|
||||
child: UI<T, K>,
|
||||
properties?: Properties<T, K>,
|
||||
name?: string,
|
||||
) {
|
||||
if (this === <any>child) throw new Error("Cannot add a child to itself")
|
||||
if (!name) name = RandomString(16)
|
||||
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.CONTROLS,
|
||||
operation: Operation.INSERT_FRONT,
|
||||
value: {
|
||||
[`${name}${child}`]: properties || {},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
insertAfterChild<T extends Type, K extends Renderer | null>(
|
||||
where: S,
|
||||
child: UI<T, K>,
|
||||
properties?: Properties<T, K>,
|
||||
name?: string,
|
||||
) {
|
||||
if (this === <any>child) throw new Error("Cannot add a child to itself")
|
||||
if (!name) name = RandomString(16)
|
||||
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.CONTROLS,
|
||||
operation: Operation.INSERT_AFTER,
|
||||
control_name: where!,
|
||||
value: {
|
||||
[`${name}${child}`]: properties || {},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
insertBeforeChild<T extends Type, K extends Renderer | null>(
|
||||
where: S,
|
||||
child: UI<T, K>,
|
||||
properties?: Properties<T, K>,
|
||||
name?: string,
|
||||
) {
|
||||
if (this === <any>child) throw new Error("Cannot add a child to itself")
|
||||
if (!name) name = RandomString(16)
|
||||
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.CONTROLS,
|
||||
operation: Operation.INSERT_BEFORE,
|
||||
control_name: where!,
|
||||
value: {
|
||||
[`${name}${child}`]: properties || {},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
insertChild<T extends Type, K extends Renderer | null>(child: UI<T, K>, properties?: Properties<T, K>) {
|
||||
return this.insertBackChild(child, properties)
|
||||
}
|
||||
|
||||
replaceChild<T extends Type, K extends Renderer | null>(where: S, child: UI<T, K>, properties?: Properties<T, K>) {
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.CONTROLS,
|
||||
operation: Operation.REPLACE,
|
||||
control_name: where!,
|
||||
value: properties || {},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a child of this element
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
removeChild(name: S) {
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.CONTROLS,
|
||||
operation: Operation.REMOVE,
|
||||
control_name: name,
|
||||
})
|
||||
}
|
||||
|
||||
insertBackBindings(...bindings: BindingItem[]) {
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.BINDINGS,
|
||||
operation: Operation.INSERT_BACK,
|
||||
value: ResolveBinding(...bindings),
|
||||
})
|
||||
}
|
||||
|
||||
insertFrontBindings(...bindings: BindingItem[]) {
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.BINDINGS,
|
||||
operation: Operation.INSERT_FRONT,
|
||||
value: ResolveBinding(...bindings),
|
||||
})
|
||||
}
|
||||
|
||||
insertBindings(...bindings: BindingItem[]) {
|
||||
return this.insertBackBindings(...bindings)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a binding of this element
|
||||
* @param binding
|
||||
*/
|
||||
removeBinding(binding: BindingItem) {
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.BINDINGS,
|
||||
operation: Operation.REMOVE,
|
||||
where: binding,
|
||||
})
|
||||
}
|
||||
|
||||
insertBackButtonMappings(...buttonMappings: ButtonMapping[]) {
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.BUTTON_MAPPINGS,
|
||||
operation: Operation.INSERT_BACK,
|
||||
value: buttonMappings,
|
||||
})
|
||||
}
|
||||
|
||||
insertFrontButtonMappings(...buttonMappings: ButtonMapping[]) {
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.BUTTON_MAPPINGS,
|
||||
operation: Operation.INSERT_FRONT,
|
||||
value: buttonMappings,
|
||||
})
|
||||
}
|
||||
|
||||
insertButtonMappings(...buttonMappings: ButtonMapping[]) {
|
||||
return this.insertBackButtonMappings(...buttonMappings)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a button mapping of this element
|
||||
* @param buttonMapping
|
||||
* @returns
|
||||
*/
|
||||
removeButtonMapping(buttonMapping: ButtonMapping) {
|
||||
return this.addModifications({
|
||||
array_name: ArrayName.BUTTON_MAPPINGS,
|
||||
operation: Operation.REMOVE,
|
||||
where: buttonMapping,
|
||||
})
|
||||
}
|
||||
|
||||
protected toJsonUIModify() {
|
||||
const obj = this.toJsonUI()
|
||||
|
||||
if (this.isClearBinding) obj.bindings = []
|
||||
if (this.isClearVariables) obj.variables = []
|
||||
if (this.isClearButtonMappings) obj.button_mappings = []
|
||||
|
||||
if (this.modifications.length) obj.modifications = this.modifications
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
protected toJSON() {
|
||||
const obj = this.toJsonUIModify()
|
||||
return obj
|
||||
}
|
||||
|
||||
protected [util.inspect.custom]($: any, opts: any) {
|
||||
return `\x1b[33mUI\x1b[0m<\x1b[92mmodify\x1b[0m> \x1b[92m"${this}\x1b[92m"\x1b[0m ${util.inspect(this.toJsonUIModify(), opts)}\n`
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Type } from "../types/enums/Type.js"
|
||||
import { Array3, Binding } from "../types/properties/value.js"
|
||||
import { Array3, Binding, BindingItem } from "../types/properties/value.js"
|
||||
import { ModifyUI, UI } from "./UI.js"
|
||||
|
||||
import { Renderer } from "../types/enums/Renderer.js"
|
||||
|
|
@ -27,8 +27,14 @@ import {
|
|||
SliderBox,
|
||||
} from "../types/properties/components.js"
|
||||
import { ItemAuxID } from "../types/enums/Items.js"
|
||||
import { Element, Namespace, VanillaType } from "../types/vanilla/intellisense.js"
|
||||
import { Element, Namespace, VanillaElementChilds, VanillaType } from "../types/vanilla/intellisense.js"
|
||||
import { paths } from "../types/vanilla/paths.js"
|
||||
import { isCompileBinding } from "../compilers/bindings/Checker.js"
|
||||
import { Parser } from "../compilers/bindings/Parser.js"
|
||||
import { BindingType } from "../types/enums/BindingType.js"
|
||||
import { AnimType } from "../types/enums/AnimType.js"
|
||||
import { AnimationKeyframe } from "./AnimationKeyframe.js"
|
||||
import { KeyframeAnimationProperties } from "../types/properties/element/Animation.js"
|
||||
|
||||
const CHARS = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
type CompileBinding = `[${string}]`
|
||||
|
|
@ -60,6 +66,27 @@ export function Color(hex: string | number): Array3<number> {
|
|||
}
|
||||
}
|
||||
|
||||
export function ResolveBinding(...bindings: BindingItem[]) {
|
||||
const result: BindingItem[] = []
|
||||
|
||||
for (const binding of bindings) {
|
||||
if (binding.source_property_name) {
|
||||
if (isCompileBinding(binding.source_property_name)) {
|
||||
const { gen, out } = new Parser(binding.source_property_name.slice(1, -1)).out()
|
||||
if (gen) result.push(...gen)
|
||||
binding.source_property_name = out
|
||||
}
|
||||
|
||||
binding.binding_type = BindingType.VIEW
|
||||
|
||||
if (!binding.target_property_name) throw new Error("Binding must have a target property name")
|
||||
}
|
||||
result.push(binding)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function RandomString(length: number, base: number = 32) {
|
||||
const chars = CHARS.slice(0, base)
|
||||
const out = new Array<string>(length)
|
||||
|
|
@ -112,7 +139,7 @@ export function b(input: string): CompileBinding {
|
|||
// Quick Elements
|
||||
export function Modify<T extends Namespace, K extends Element<T>>(namespace: T, name: K) {
|
||||
// @ts-ignore -- TS cannot prove this, but runtime guarantees it
|
||||
return new ModifyUI<VanillaType<T, K>>(name, namespace, paths[namespace][name])
|
||||
return new ModifyUI<VanillaType<T, K>, VanillaElementChilds<T, K>>(namespace, name, paths[namespace][name])
|
||||
}
|
||||
|
||||
export function Panel(properties?: Panel, namespace?: string, name?: string) {
|
||||
|
|
@ -219,3 +246,72 @@ export function ExtendsOf<T extends Type, K extends Renderer | null>(
|
|||
ui.extendType = element.type || element.extendType
|
||||
return ui as typeof element
|
||||
}
|
||||
|
||||
// Quick Keyframe
|
||||
export function KeyframeOffset(
|
||||
properties?: KeyframeAnimationProperties<AnimType.OFFSET>,
|
||||
namespace?: string,
|
||||
name?: string,
|
||||
) {
|
||||
return new AnimationKeyframe(AnimType.OFFSET, properties || {}, name, namespace)
|
||||
}
|
||||
|
||||
export function KeyframeSize(
|
||||
properties?: KeyframeAnimationProperties<AnimType.SIZE>,
|
||||
namespace?: string,
|
||||
name?: string,
|
||||
) {
|
||||
return new AnimationKeyframe(AnimType.SIZE, properties || {}, name, namespace)
|
||||
}
|
||||
|
||||
export function KeyframeUV(properties?: KeyframeAnimationProperties<AnimType.UV>, namespace?: string, name?: string) {
|
||||
return new AnimationKeyframe(AnimType.UV, properties || {}, name, namespace)
|
||||
}
|
||||
|
||||
export function KeyframeClip(
|
||||
properties?: KeyframeAnimationProperties<AnimType.CLIP>,
|
||||
namespace?: string,
|
||||
name?: string,
|
||||
) {
|
||||
return new AnimationKeyframe(AnimType.CLIP, properties || {}, name, namespace)
|
||||
}
|
||||
|
||||
export function KeyframeColor(
|
||||
properties?: KeyframeAnimationProperties<AnimType.COLOR>,
|
||||
namespace?: string,
|
||||
name?: string,
|
||||
) {
|
||||
return new AnimationKeyframe(AnimType.COLOR, properties || {}, name, namespace)
|
||||
}
|
||||
|
||||
export function KeyframeAlpha(
|
||||
properties?: KeyframeAnimationProperties<AnimType.ALPHA>,
|
||||
namespace?: string,
|
||||
name?: string,
|
||||
) {
|
||||
return new AnimationKeyframe(AnimType.ALPHA, properties || {}, name, namespace)
|
||||
}
|
||||
|
||||
export function KeyframeWait(
|
||||
properties?: KeyframeAnimationProperties<AnimType.WAIT>,
|
||||
namespace?: string,
|
||||
name?: string,
|
||||
) {
|
||||
return new AnimationKeyframe(AnimType.WAIT, properties || {}, name, namespace)
|
||||
}
|
||||
|
||||
export function KeyframeFlipBook(
|
||||
properties?: KeyframeAnimationProperties<AnimType.FLIP_BOOK>,
|
||||
namespace?: string,
|
||||
name?: string,
|
||||
) {
|
||||
return new AnimationKeyframe(AnimType.FLIP_BOOK, properties || {}, name, namespace)
|
||||
}
|
||||
|
||||
export function KeyframeAsepriteFlipBook(
|
||||
properties?: KeyframeAnimationProperties<AnimType.ASEPRITE_FLIP_BOOK>,
|
||||
namespace?: string,
|
||||
name?: string,
|
||||
) {
|
||||
return new AnimationKeyframe(AnimType.ASEPRITE_FLIP_BOOK, properties || {}, name, namespace)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import "./compilers/PreCompile.js"
|
||||
import "./compilers/RunEnd.js"
|
||||
|
||||
export * from "./components/Animation.js"
|
||||
export * from "./components/UI.js"
|
||||
export { Animation } from "./components/Animation.js"
|
||||
export { AnimationKeyframe } from "./components/AnimationKeyframe.js"
|
||||
export { ModifyUI, UI } from "./components/UI.js"
|
||||
export * from "./components/Utils.js"
|
||||
|
||||
export * from "./types/enums/index.js"
|
||||
|
|
|
|||
5
src/types/enums/ArrayName.ts
Normal file
5
src/types/enums/ArrayName.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export enum ArrayName {
|
||||
CONTROLS = "controls",
|
||||
BINDINGS = "bindings",
|
||||
BUTTON_MAPPINGS = "button_mappings",
|
||||
}
|
||||
13
src/types/enums/Operation.ts
Normal file
13
src/types/enums/Operation.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export enum Operation {
|
||||
INSERT_BACK = "insert_back",
|
||||
INSERT_FRONT = "insert_front",
|
||||
INSERT_AFTER = "insert_after",
|
||||
INSERT_BEFORE = "insert_before",
|
||||
MOVE_BACK = "move_back",
|
||||
MOVE_FRONT = "move_front",
|
||||
MOVE_AFTER = "move_after",
|
||||
MOVE_BEFORE = "move_before",
|
||||
SWAP = "swap",
|
||||
REMOVE = "remove",
|
||||
REPLACE = "replace",
|
||||
}
|
||||
|
|
@ -3,22 +3,59 @@ import { AnimType } from "../../enums/AnimType.js"
|
|||
import { Easing } from "../../enums/Easing.js"
|
||||
import { Array2, Array3, Value } from "../value.js"
|
||||
|
||||
export interface Animation {
|
||||
anim_type?: Value<string | AnimType>
|
||||
export interface DurationAnimation {
|
||||
duration?: Value<number>
|
||||
next?: Value<string | AnimationKeyframe>
|
||||
}
|
||||
|
||||
export interface EasingAnimation extends DurationAnimation {
|
||||
easing?: Value<string | Easing>
|
||||
}
|
||||
|
||||
export interface NumberAnimation extends DurationAnimation, EasingAnimation {
|
||||
from: Value<number>
|
||||
to: Value<number>
|
||||
}
|
||||
|
||||
export interface Array2Animation extends DurationAnimation, EasingAnimation {
|
||||
from: Array2<number | string>
|
||||
to: Array2<number | string>
|
||||
}
|
||||
|
||||
export interface Array3Animation extends DurationAnimation, EasingAnimation {
|
||||
from: Array3<number | string>
|
||||
to: Array3<number | string>
|
||||
}
|
||||
|
||||
export interface AsepriteFlipBookAnimation {
|
||||
initial_uv?: Value<Array2<number>>
|
||||
}
|
||||
|
||||
export interface FlipbookAnimation extends AsepriteFlipBookAnimation {
|
||||
frame_count?: Value<number>
|
||||
frame_step?: Value<number>
|
||||
fps?: Value<number>
|
||||
easing?: Value<string | Easing>
|
||||
}
|
||||
|
||||
export interface AnimationValueType {
|
||||
[AnimType.OFFSET]: Array2Animation
|
||||
[AnimType.SIZE]: Array2Animation
|
||||
[AnimType.UV]: Array2Animation
|
||||
[AnimType.CLIP]: Array2Animation
|
||||
[AnimType.COLOR]: Array3Animation
|
||||
[AnimType.ALPHA]: NumberAnimation
|
||||
[AnimType.WAIT]: DurationAnimation
|
||||
[AnimType.FLIP_BOOK]: FlipbookAnimation
|
||||
[AnimType.ASEPRITE_FLIP_BOOK]: AsepriteFlipBookAnimation
|
||||
}
|
||||
|
||||
export interface AnimationPropertiesItem {
|
||||
destroy_at_end?: Value<string>
|
||||
play_event?: Value<string>
|
||||
end_event?: Value<string>
|
||||
start_event?: Value<string>
|
||||
reset_event?: Value<string>
|
||||
easing?: Value<string | Easing>
|
||||
from?: Value<number | string | Array3<number> | Array2<string>>
|
||||
to?: Value<number | string | Array3<number> | Array2<string>>
|
||||
initial_uv?: Value<Array2<number>>
|
||||
fps?: Value<number>
|
||||
frame_count?: Value<number>
|
||||
frame_step?: Value<number>
|
||||
|
||||
reversible?: Value<boolean>
|
||||
resettable?: Value<boolean>
|
||||
scale_from_starting_alpha?: Value<boolean>
|
||||
|
|
@ -26,3 +63,13 @@ export interface Animation {
|
|||
looping?: Value<boolean>
|
||||
wait_until_rendered_to_play?: Value<boolean>
|
||||
}
|
||||
|
||||
export interface KeyframeAnimationPropertiesItem extends AnimationPropertiesItem {
|
||||
next?: Value<string | AnimationKeyframe<AnimType>>
|
||||
}
|
||||
|
||||
export type KeyframeAnimationProperties<T extends keyof AnimationValueType> = Partial<AnimationValueType[T]> &
|
||||
KeyframeAnimationPropertiesItem
|
||||
|
||||
export type AnimationProperties<T extends keyof AnimationValueType> = Partial<AnimationValueType[T]> &
|
||||
AnimationPropertiesItem
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
export * as ElementProperties from "./element/index.js"
|
||||
export * as ComponentProperties from "./components.js"
|
||||
export * as Value from "./value.js"
|
||||
|
||||
export { ArrayName } from "../enums/ArrayName.js"
|
||||
export { Operation } from "../enums/Operation.js"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import { ButtonId } from "../enums/ButtonId.js"
|
|||
import { MappingType } from "../enums/MappingType.js"
|
||||
import { InputModeCondition } from "../enums/InputModeCondition.js"
|
||||
import { Scope } from "../enums/Scope.js"
|
||||
import { ArrayName } from "../enums/ArrayName.js"
|
||||
import { Operation } from "../enums/Operation.js"
|
||||
|
||||
export type Variable = `$${string}`
|
||||
export type Binding = `#${string}`
|
||||
|
|
@ -34,6 +36,15 @@ export type BindingItem = {
|
|||
resolve_ancestor_scope?: Value<boolean>
|
||||
}
|
||||
|
||||
export type VariableItem = {
|
||||
requires: string
|
||||
[key: Variable]: Value<any>
|
||||
}
|
||||
|
||||
export type Variables = {
|
||||
[key: Variable | `(${string})`]: Record<Variable, unknown>
|
||||
}
|
||||
|
||||
export type FocusContainerCustom = Array<{
|
||||
other_focus_container_name?: Value<string>
|
||||
focus_id_inside?: Value<string>
|
||||
|
|
@ -56,3 +67,13 @@ export type ButtonMapping = {
|
|||
export type PropertyBags = {
|
||||
[key: Binding]: Value<any>
|
||||
}
|
||||
|
||||
export type ModificationItem = {
|
||||
array_name?: ArrayName
|
||||
control_name?: string
|
||||
where?: BindingItem | object
|
||||
target?: object
|
||||
target_control?: string
|
||||
value?: object | (object | BindingItem)[]
|
||||
operation?: Operation
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
11
test/app.ts
11
test/app.ts
|
|
@ -1 +1,10 @@
|
|||
import { Modify, Panel, StackPanel, Toggle, GetItemByAuxID, ItemAuxID } from ".."
|
||||
import { Animation, AnimType } from ".."
|
||||
|
||||
const animation = new Animation(
|
||||
AnimType.OFFSET,
|
||||
{
|
||||
from: [0, 0],
|
||||
to: [100, 100],
|
||||
},
|
||||
123,
|
||||
).setLoop(false)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
"target": "esnext",
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"outDir": "dist",
|
||||
"outDir": "dist/js",
|
||||
"declarationDir": "dist/types",
|
||||
"declaration": true,
|
||||
"sourceMap": false,
|
||||
"strict": true,
|
||||
|
|
|
|||
Reference in a new issue