feat: animation system

This commit is contained in:
Asaki Yuki 2026-01-26 13:27:54 +07:00
parent 406e71575f
commit 7534a613cb
22 changed files with 19733 additions and 19122 deletions

1
README.md Normal file
View file

@ -0,0 +1 @@
![image](/resources/logo.png)

View file

@ -17,16 +17,18 @@
"license": "MIT", "license": "MIT",
"author": "Asaki Yuki", "author": "Asaki Yuki",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/js/index.js",
"types": "dist/index.d.ts", "types": "dist/types/index.d.ts",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"dev": "tsc --watch", "dev": "tsc --watch",
"test": "bun test/app.ts",
"prefetch": "bun scripts/prefetch", "prefetch": "bun scripts/prefetch",
"vanilla:defs": "bun scripts/vanilladefs", "vanilla:defs": "bun scripts/vanilladefs",
"gen:enums": "bun scripts/enum", "gen:enums": "bun scripts/enum",
"gen:items": "bun scripts/items", "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": { "devDependencies": {
"@types/node": "^25.0.3", "@types/node": "^25.0.3",

BIN
resources/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -10,7 +10,12 @@ const intelliSense: string[] = [
'import { Type as T } from "../enums/Type.js"\n', 'import { Type as T } from "../enums/Type.js"\n',
"export type Namespace = keyof IntelliSense;", "export type Namespace = keyof IntelliSense;",
"export type Element<T extends Namespace> = Extract<keyof IntelliSense[T], string>", "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 = {", "export type IntelliSense = {",
] ]
@ -27,9 +32,12 @@ for (const [namespace, element] of Object.entries(data)) {
ePaths: string[] = [` "${namespace}": {`] ePaths: string[] = [` "${namespace}": {`]
for (const [ePath, info] of Object.entries(<any>element)) { 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}"`) eType.push(`"${ePath}"`)
eType3.push(` "${ePath}": T.${type.toUpperCase()},`) eType3.push(` "${ePath}": { type: T.${type.toUpperCase()}, children: ${childType} },`)
ePaths.push(` "${ePath}": "${file}",`) ePaths.push(` "${ePath}": "${file}",`)
} }

View file

@ -6,6 +6,8 @@ const vanilla: NamespaceMap = new Map()
function readControls(namespace: string, file: string, elements: ElementMap, data: any[], prefix: string) { function readControls(namespace: string, file: string, elements: ElementMap, data: any[], prefix: string) {
prefix += "/" prefix += "/"
const childs: string[] = []
for (const element of data) { for (const element of data) {
const [fullname, properties] = Object.entries(element)[0] 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("@") const [name, $2] = fullname.split("@")
if (name.startsWith("$")) continue if (name.startsWith("$")) continue
childs.push(name)
if ($2 && !$2.startsWith("$")) { if ($2 && !$2.startsWith("$")) {
const [$3, $4] = $2.split(".") const [$3, $4] = $2.split(".")
@ -37,9 +40,12 @@ function readControls(namespace: string, file: string, elements: ElementMap, dat
const controls = (<any>properties).controls const controls = (<any>properties).controls
if (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) { 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 const controls = (<any>properties).controls
if (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 namespace: string
} }
anim_type?: string anim_type?: string
children?: string[]
type: string type: string
file: string file: string
} }

View file

@ -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" import { Binding } from "../types/properties/value.js"
export function FormatProperties(properties: any) { export function FormatProperties(properties: any) {
@ -27,3 +30,11 @@ export function FormatProperties(properties: any) {
return properties return properties
} }
export function FormatAnimationProperties(properties: KeyframeAnimationProperties<AnimType>) {
if (properties.next instanceof AnimationKeyframe) {
properties.next = `${properties.next}`
}
return properties
}

View file

@ -1,9 +1,11 @@
import { AnimationKeyframe } from "../components/AnimationKeyframe.js"
import { Class } from "../components/Class.js" import { Class } from "../components/Class.js"
import { UI } from "../components/UI.js" import { UI } from "../components/UI.js"
import { AnimType } from "../types/enums/AnimType.js"
import { Renderer } from "../types/enums/Renderer.js" import { Renderer } from "../types/enums/Renderer.js"
import { Type } from "../types/enums/Type.js" import { Type } from "../types/enums/Type.js"
type Element = UI<Type, Renderer | null> type Element = UI<Type, Renderer | null> | AnimationKeyframe<AnimType>
interface FileInterface { interface FileInterface {
namespace: string namespace: string
elements: Element[] elements: Element[]
@ -13,7 +15,7 @@ type Files = Map<string, FileInterface>
export class Memory extends Class { export class Memory extends Class {
protected static files: Files = new Map<string, FileInterface>() 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) let file = Memory.files.get(element.path)
if (!file) { if (!file) {

View file

@ -1,5 +1,9 @@
import { Memory } from "./Memory.js" import { Memory } from "./Memory.js"
const isBuildMode = process.argv.includes("--build")
if (isBuildMode) {
process.on("beforeExit", () => { process.on("beforeExit", () => {
// console.log(Memory.build()) console.log(JSON.stringify(Memory.build(), null, 2))
}) })
}

View file

@ -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 { 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
}
}

View file

@ -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 { 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`
}
}

View 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
}
}

View file

@ -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 { FormatProperties } from "../compilers/FormatProperties.js"
import { Memory } from "../compilers/Memory.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 { Renderer } from "../types/enums/Renderer.js"
import { Type } from "../types/enums/Type.js" import { Type } from "../types/enums/Type.js"
import { Properties } from "../types/properties/components.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 { Class } from "./Class.js"
import { ExtendsOf, RandomString } from "./Utils.js" import { ExtendsOf, RandomString, ResolveBinding } from "./Utils.js"
import util from "node:util" import util from "node:util"
export class UI<T extends Type, K extends Renderer | null = null> extends Class { export class UI<T extends Type, K extends Renderer | null = null> extends Class {
readonly path: string readonly path: string
readonly name: string readonly name: string
readonly namespace: string readonly namespace: string
readonly extend?: UI<Type, Renderer | null>
readonly extend?: UI<Type, Renderer | null>
readonly extendable: boolean readonly extendable: boolean
readonly controls = new Map<string, [UI<Type, Renderer | null>, Properties<Type, Renderer | null>]>() protected readonly controls = new Map<string, [UI<Type, Renderer | null>, Properties<Type, Renderer | null>]>()
readonly bindings: BindingItem[] = [] protected readonly bindings: BindingItem[] = []
readonly extendType?: Type protected readonly variables: VariableItem[] = []
properties: Properties<T, K> = <any>{} protected readonly buttonMappings: ButtonMapping[] = []
protected readonly extendType?: Type
protected properties: Properties<T, K> = <any>{}
constructor( constructor(
public type?: T, public type?: T,
@ -55,60 +56,72 @@ export class UI<T extends Type, K extends Renderer | null = null> extends Class
Memory.add(this) Memory.add(this)
} }
protected UI_JSON() { /**
const obj: any = { * Set properties for this element
...FormatProperties(this.properties), * @param properties
} * @returns
*/
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
}
setProperties(properties: Properties<T, K>) { setProperties(properties: Properties<T, K>) {
this.properties = { ...this.properties, ...properties } this.properties = { ...this.properties, ...properties }
return this return this
} }
/**
* Bind data (coming from the code) to this UI element to use.
* @param bindings
* @returns
*/
addBindings(...bindings: BindingItem[]) { addBindings(...bindings: BindingItem[]) {
for (const binding of bindings) { this.bindings.push(...ResolveBinding(...bindings))
if (binding.source_property_name) { return this
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
} }
binding.binding_type = BindingType.VIEW /**
* Changes variables values if conditions are met.
if (!binding.target_property_name) throw new Error("Binding must have a target property name") * @param variables
} * @returns
this.bindings.push(binding) */
} addVariables(variables: Variables) {
Object.entries(variables).forEach(([key, value]) => {
this.variables.push({
requires: key,
...value,
})
})
return this return this
} }
addChild<T extends Type, K extends Renderer | null>(child: UI<T, K>, properties?: Properties<T, K>, name?: string) { /**
if (this === <any>child) { * Add button mappings for this element
throw new Error("Cannot add a child to itself") * @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 || {}]) this.controls.set(name || RandomString(16), [child, properties || {}])
return this return this
} }
/**
* Return a extend of this element
* @param properties
* @param name
* @param namespace
* @returns
*/
clone(properties?: Properties<T, K>, name?: string, namespace?: string) { clone(properties?: Properties<T, K>, name?: string, namespace?: string) {
return ExtendsOf(this, properties, name, namespace) 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}` return `@${this.namespace}.${this.name}`
} }
protected toJSON() { protected toJsonUI() {
return this.UI_JSON()
}
protected [util.inspect.custom]($: any, opts: any) {
const obj: any = { const obj: any = {
...FormatProperties(this.properties), ...FormatProperties(this.properties),
} }
if (this.bindings.length) { if (this.type) obj.type = this.type
obj.bindings = this.bindings
} 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) { if (this.controls.size) {
obj.controls = [] obj.controls = []
this.controls.forEach((e, key) => obj.controls.push({ [key + e[0]]: e[1] })) 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 obj
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)
} }
protected toJSON() { protected toJSON() {
const obj = this.UI_JSON() 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 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`
}
} }

View file

@ -1,5 +1,5 @@
import { Type } from "../types/enums/Type.js" 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 { ModifyUI, UI } from "./UI.js"
import { Renderer } from "../types/enums/Renderer.js" import { Renderer } from "../types/enums/Renderer.js"
@ -27,8 +27,14 @@ import {
SliderBox, SliderBox,
} from "../types/properties/components.js" } from "../types/properties/components.js"
import { ItemAuxID } from "../types/enums/Items.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 { 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" const CHARS = "0123456789abcdefghijklmnopqrstuvwxyz"
type CompileBinding = `[${string}]` 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) { export function RandomString(length: number, base: number = 32) {
const chars = CHARS.slice(0, base) const chars = CHARS.slice(0, base)
const out = new Array<string>(length) const out = new Array<string>(length)
@ -112,7 +139,7 @@ export function b(input: string): CompileBinding {
// Quick Elements // Quick Elements
export function Modify<T extends Namespace, K extends Element<T>>(namespace: T, name: K) { export function Modify<T extends Namespace, K extends Element<T>>(namespace: T, name: K) {
// @ts-ignore -- TS cannot prove this, but runtime guarantees it // @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) { 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 ui.extendType = element.type || element.extendType
return ui as typeof element 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)
}

View file

@ -1,8 +1,9 @@
import "./compilers/PreCompile.js" import "./compilers/PreCompile.js"
import "./compilers/RunEnd.js" import "./compilers/RunEnd.js"
export * from "./components/Animation.js" export { Animation } from "./components/Animation.js"
export * from "./components/UI.js" export { AnimationKeyframe } from "./components/AnimationKeyframe.js"
export { ModifyUI, UI } from "./components/UI.js"
export * from "./components/Utils.js" export * from "./components/Utils.js"
export * from "./types/enums/index.js" export * from "./types/enums/index.js"

View file

@ -0,0 +1,5 @@
export enum ArrayName {
CONTROLS = "controls",
BINDINGS = "bindings",
BUTTON_MAPPINGS = "button_mappings",
}

View 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",
}

View file

@ -3,22 +3,59 @@ import { AnimType } from "../../enums/AnimType.js"
import { Easing } from "../../enums/Easing.js" import { Easing } from "../../enums/Easing.js"
import { Array2, Array3, Value } from "../value.js" import { Array2, Array3, Value } from "../value.js"
export interface Animation { export interface DurationAnimation {
anim_type?: Value<string | AnimType>
duration?: Value<number> 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> destroy_at_end?: Value<string>
play_event?: Value<string> play_event?: Value<string>
end_event?: Value<string> end_event?: Value<string>
start_event?: Value<string> start_event?: Value<string>
reset_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> reversible?: Value<boolean>
resettable?: Value<boolean> resettable?: Value<boolean>
scale_from_starting_alpha?: Value<boolean> scale_from_starting_alpha?: Value<boolean>
@ -26,3 +63,13 @@ export interface Animation {
looping?: Value<boolean> looping?: Value<boolean>
wait_until_rendered_to_play?: 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

View file

@ -1,3 +1,6 @@
export * as ElementProperties from "./element/index.js" export * as ElementProperties from "./element/index.js"
export * as ComponentProperties from "./components.js" export * as ComponentProperties from "./components.js"
export * as Value from "./value.js" export * as Value from "./value.js"
export { ArrayName } from "../enums/ArrayName.js"
export { Operation } from "../enums/Operation.js"

View file

@ -7,6 +7,8 @@ import { ButtonId } from "../enums/ButtonId.js"
import { MappingType } from "../enums/MappingType.js" import { MappingType } from "../enums/MappingType.js"
import { InputModeCondition } from "../enums/InputModeCondition.js" import { InputModeCondition } from "../enums/InputModeCondition.js"
import { Scope } from "../enums/Scope.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 Variable = `$${string}`
export type Binding = `#${string}` export type Binding = `#${string}`
@ -34,6 +36,15 @@ export type BindingItem = {
resolve_ancestor_scope?: Value<boolean> 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<{ export type FocusContainerCustom = Array<{
other_focus_container_name?: Value<string> other_focus_container_name?: Value<string>
focus_id_inside?: Value<string> focus_id_inside?: Value<string>
@ -56,3 +67,13 @@ export type ButtonMapping = {
export type PropertyBags = { export type PropertyBags = {
[key: Binding]: Value<any> [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

View file

@ -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)

View file

@ -3,7 +3,8 @@
"target": "esnext", "target": "esnext",
"module": "nodenext", "module": "nodenext",
"moduleResolution": "nodenext", "moduleResolution": "nodenext",
"outDir": "dist", "outDir": "dist/js",
"declarationDir": "dist/types",
"declaration": true, "declaration": true,
"sourceMap": false, "sourceMap": false,
"strict": true, "strict": true,