Skip to content

Commit

Permalink
Add options to disable primitive logic, add OnClickReturnType, suppor…
Browse files Browse the repository at this point in the history
…t promise return in onClick, fix getRawValue type, allow promise in undo action function.
  • Loading branch information
repalash committed Jan 12, 2025
1 parent becd4e4 commit 64dcfa0
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 18 deletions.
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,26 @@

uiconfig.js is a UI renderer framework to generate UIs from a JSON/ts decorators/zod schema etc

## Upgrade to v0.1.0
## v0.1.3
Minor update
- Add options to disable primitive logic (for textures, objects etc)
- Add `OnClickReturnType`, Support promise return in onClick
- Fix `getRawValue` type to allow `any`
- Allow promise return in undo action function.

## v0.1.2
Minor update
- Fix dynamic `generateUiFolder`
- Fix types of `PrimitiveValObject`

## v0.1.1
Minor update
- Fix initial setting of `undoManager`

## v0.1.0
Major updates

### Migration Guide
- `UiConfigRendererBase` split into 2 classes.
- Use class `UiConfigRenderer` in place of `UiConfigRendererBase<T>`
- Removed `protected _root: T`. It can be defined in the subclass if used.
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "uiconfig.js",
"version": "0.1.2",
"version": "0.1.3",
"description": "A framework for building user interface layouts with JSON configuration.",
"main": "dist/index.js",
"module": "dist/index.js",
Expand Down Expand Up @@ -28,7 +28,8 @@
},
"exports": {
".": {
"import": "./dist/index.js"
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./dist/": {
"import": "./dist/",
Expand Down Expand Up @@ -89,7 +90,7 @@
"rollup-plugin-delete": "^2.1.0",
"rollup-plugin-license": "^3.5.3",
"rollup-plugin-multi-input": "^1.5.0",
"ts-browser-helpers": "^0.16.0",
"ts-browser-helpers": "^0.16.1",
"tslib": "^2.6.3",
"typedoc": "^0.27.5",
"typescript": "^5.7.2"
Expand Down
13 changes: 10 additions & 3 deletions src/UiConfigMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,27 @@ export class UiConfigMethods {
return prop
}

getRawValue<T extends PrimitiveVal>(config: UiObjectConfig<T>): T | undefined {
getRawValue<T extends any /*PrimitiveVal*/>(config: UiObjectConfig<T>): T | undefined {
const [tar, key] = this.getBinding(config)
if (!tar) return undefined
const res = tar[key]
return res as T | undefined
}

/**
* Get the value from config
* @param config
* @param val - existing value, new value can be copied to this if not equal.
* @param copyOnEqual - whether the value should be copied to val if equal. Default is true.
* @returns The value from the binding, cloned or copied if possible. If the value is equal and copyOnEqual is false, then undefined is returned. this can be used to check if the value is changed
*/
getValue<T extends PrimitiveVal>(config: UiObjectConfig<T>, val: T | undefined, copyOnEqual = true): T | undefined {
const [tar, key] = this.getBinding(config)
if (!tar) return undefined
const res = tar[key]
// console.log('get', config, res)
if (val !== undefined && res !== undefined) {
if (equalsPrimitive(val, res) && !copyOnEqual) return undefined
if (equalsPrimitive(val, res) && !copyOnEqual) return undefined // returns undefined if equal
return copyPrimitive(val, res)
}
return clonePrimitive(res)
Expand Down Expand Up @@ -201,7 +208,7 @@ export class UiConfigMethods {
const resAction = typeof res !== 'function' ? res?.action?.bind(res) : null
const redo = typeof res === 'function' ? action : res?.redo?.bind(res) ?? resAction
if (typeof resAction === 'function') {
res = resAction() // execute the action now.
res = await resAction() // execute the action now. adding await just in case
}
if (typeof undo === 'function') {
this.recordUndo({
Expand Down
17 changes: 13 additions & 4 deletions src/primitive_value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,30 @@ export interface PrimitiveValObject {
clone(): this
equals(other: this|any): boolean
copy(other: this|any): void|this|any

// these can be set if the object does not want to use the default implementation (like Texture, Object3D)
_ui_isPrimitive?: boolean
// disable clone.
_ui_primitiveClone?: false
// disable copy
_ui_primitiveCopy?: false
// disable equals
_ui_primitiveEquals?: false
}
// note that arbitrary objects are not allowed
export type PrimitiveVal = string | number | boolean | null | PrimitiveValObject | PrimitiveVal[]

export function clonePrimitive<T extends PrimitiveVal>(a: T): T {
if (a === null || typeof a !== 'object') return a
if (Array.isArray(a)) return a.map(clonePrimitive) as T
if (typeof a.clone === 'function') return a.clone() as T
if (!a._ui_isPrimitive && typeof a.clone === 'function' && a._ui_primitiveClone !== false) return a.clone() as T
return a
}

export function equalsPrimitive<T extends PrimitiveVal>(a: T, b: T): boolean {
if (a === null || typeof a !== 'object') return a === b
if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i)=>equalsPrimitive(v, b[i]))
if (typeof a.equals === 'function') return !!a.equals(b)
if (!a._ui_isPrimitive && typeof a.equals === 'function' && a._ui_primitiveEquals !== false) return !!a.equals(b)
// direct equality check in case of objects
return a === b
}
Expand All @@ -36,9 +45,9 @@ export function copyPrimitive<T extends PrimitiveVal>(a: T, b: T) {
}
return a
} else return clonePrimitive(b)
} else if (typeof b.copy === 'function') {
} else if (!b._ui_isPrimitive && typeof b.copy === 'function' && b._ui_primitiveCopy !== false) {
// const a = target[key]
if (a && typeof a === 'object' && !Array.isArray(a) && typeof a.copy === 'function') {
if (a && typeof a === 'object' && !Array.isArray(a) && !a._ui_isPrimitive && typeof a.copy === 'function' && a._ui_primitiveCopy !== false) {
a.copy(b)
return a
} else return clonePrimitive(b)
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface ChangeEvent {
lastValue?: any, // the old value
}
export type ChangeArgs = [ChangeEvent, ...any[]]
export type OnClickReturnType = (void|(()=>any)|{action?: (()=>any), undo?: (()=>any), redo?: (()=>any)})

export interface UiObjectConfig<T = any, TType extends UiObjectType = UiObjectType, TTarget = any> {
/**
Expand Down Expand Up @@ -114,7 +115,7 @@ export interface UiObjectConfig<T = any, TType extends UiObjectType = UiObjectTy
* Return a function for undo, or (undo, redo) or (action, undo) for undo/redo support. action will be exec immediately and on undo
* @param args
*/
onClick?: (...args: any[]) => (void|(()=>any)|{action?: (()=>any), undo?: (()=>any), redo?: (()=>any)}); // for button-like types
onClick?: ((...args: any[]) => OnClickReturnType) | ((...args: any[]) => Promise<OnClickReturnType>); // for button-like types

/**
* bounds for the value of the object. This is used for numeric inputs like number and sliders.
Expand Down

0 comments on commit 64dcfa0

Please sign in to comment.