uicore-ts 1.1.115 → 1.1.118

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uicore-ts",
3
- "version": "1.1.115",
3
+ "version": "1.1.118",
4
4
  "description": "UICore is a library to build native-like user interfaces using pure Typescript. No HTML is needed at all. Components are described as TS classes and all user interactions are handled explicitly. This library is strongly inspired by the UIKit framework that is used in IOS. In addition, UICore has tools to handle URL based routing, array sorting and filtering and adds a number of other utilities for convenience.",
5
5
  "main": "compiledScripts/index.js",
6
6
  "types": "compiledScripts/index.d.ts",
@@ -311,7 +311,7 @@ export class UIButton extends UIBaseButton {
311
311
 
312
312
  let bounds = this.bounds
313
313
 
314
- this.hoverText = this.titleLabel?.text ?? ""
314
+ this.hoverText = this.hoverText ?? this.titleLabel?.text ?? ""
315
315
 
316
316
  // Image only if text is not present
317
317
  if (IS_NOT(this.imageView.hidden) && !IS(this.titleLabel?.text)) {
package/scripts/UICore.ts CHANGED
@@ -10,7 +10,7 @@ export class UICore extends UIObject {
10
10
 
11
11
  rootViewController: UIViewController
12
12
 
13
- paddingLength = 20
13
+
14
14
 
15
15
  static RootViewControllerClass: typeof UIViewController
16
16
  static main: UICore
@@ -24,7 +24,7 @@ export class UICore extends UIObject {
24
24
 
25
25
  }
26
26
 
27
- constructor(rootDivElementID: string, rootViewControllerClass: typeof UIViewController) {
27
+ constructor(rootDivElementID: string, rootViewControllerClass: typeof UIViewController, public paddingLength = 20) {
28
28
 
29
29
  super()
30
30
 
@@ -118,9 +118,11 @@ export class UIDialogView<ViewType extends UIView = UIView> extends UIView {
118
118
 
119
119
 
120
120
  containerView.addSubview(this)
121
+ this.view.setNeedsLayoutUpToRootView()
121
122
 
122
123
  if (animated) {
123
124
 
125
+ UIView.layoutViewsIfNeeded()
124
126
  this.layoutSubviews()
125
127
 
126
128
  UIView.animateViewOrViewsWithDurationDelayAndFunction(
@@ -250,6 +252,8 @@ export class UIDialogView<ViewType extends UIView = UIView> extends UIView {
250
252
 
251
253
  this.view.style.zIndex = "" + this.zIndex
252
254
 
255
+ this.view.setNeedsLayout()
256
+
253
257
  // this.view.style.maxHeight = "" + (bounds.height - margin * 2).integerValue + "px";
254
258
  // this.view.style.maxWidth = "" + (bounds.width - margin * 2).integerValue + "px";
255
259
 
@@ -1016,13 +1016,12 @@ export class UIRectangle extends UIObject {
1016
1016
  // 1. Methods available when holding a UIRectangle
1017
1017
  type RectangleChainMethods<TResult> = {
1018
1018
  [K in keyof UIRectangle as (
1019
- // Exclude IF to prevent circular reference TS2615
1020
- // Exclude Control Flow keys as they are handled in SharedChainMethods
1021
1019
  K extends 'IF' | 'ELSE' | 'ELSE_IF' | 'ENDIF' ? never : K
1022
1020
  )]:
1023
1021
  UIRectangle[K] extends (...args: infer Args) => infer R
1024
- ? R extends UIRectangle | UIRectangle[] // Filter: Must return Rect or Rect[]
1025
- ? (...args: Args) => UIRectangleConditionalChain<R, TResult | R>
1022
+ ? R extends UIRectangle | UIRectangle[]
1023
+ // CHANGE: We do NOT add 'R' to 'TResult' here. We only update the current state (R).
1024
+ ? (...args: Args) => UIRectangleConditionalChain<R, TResult>
1026
1025
  : never
1027
1026
  : never
1028
1027
  };
@@ -1030,68 +1029,67 @@ type RectangleChainMethods<TResult> = {
1030
1029
  // 2. Methods available when holding a UIRectangle[]
1031
1030
  type ArrayChainMethods<TResult> = {
1032
1031
  [K in keyof UIRectangle[]]:
1033
- // Case 1: It is a property (getter) that returns a UIRectangle
1034
1032
  UIRectangle[][K] extends UIRectangle
1035
- ? UIRectangleConditionalChain<UIRectangle, TResult | UIRectangle>
1036
- // Case 2: It is a function that returns a UIRectangle
1033
+ ? UIRectangleConditionalChain<UIRectangle, TResult> // No accumulation for properties
1037
1034
  : UIRectangle[][K] extends (...args: infer Args) => infer R
1038
- ? R extends UIRectangle
1039
- ? (...args: Args) => UIRectangleConditionalChain<R, TResult | R>
1035
+ ? R extends UIRectangle | UIRectangle[]
1036
+ // CHANGE: We do NOT add 'R' to 'TResult' here either.
1037
+ ? (...args: Args) => UIRectangleConditionalChain<R, TResult>
1040
1038
  : never
1041
1039
  : never
1042
1040
  };
1043
1041
 
1044
-
1045
-
1046
1042
  // 3. Methods available in both states (Control Flow + Transform)
1047
1043
  type SharedChainMethods<TCurrent, TResult> = {
1048
- // TRANSFORM allows manual conversion from TCurrent to a UIRectangle
1049
- TRANSFORM<R extends UIRectangle>(fn: (current: TCurrent) => R): UIRectangleConditionalChain<R, TResult | R>;
1044
+ // TRANSFORM acts as a standard method, it should not leak intermediate types into TResult
1045
+ TRANSFORM<R extends UIRectangle>(fn: (current: TCurrent) => R): UIRectangleConditionalChain<R, TResult>;
1046
+
1047
+ // ELSE_IF marks the end of a branch. We MUST capture the current state (TCurrent) and add it to TResult.
1048
+ // The new chain starts with the original UIRectangle state for the next branch.
1049
+ ELSE_IF(condition: boolean): UIRectangleConditionalChain<UIRectangle, TResult | TCurrent>;
1050
1050
 
1051
- // ELSE_IF and ELSE reset the state to the base UIRectangle
1052
- ELSE_IF<U>(condition: boolean): UIRectangleConditionalChain<UIRectangle, TResult | U>;
1053
- ELSE<U>(): UIRectangleConditionalChain<UIRectangle, TResult | U>;
1051
+ // ELSE marks the end of a branch. Same logic as ELSE_IF.
1052
+ ELSE(): UIRectangleConditionalChain<UIRectangle, TResult | TCurrent>;
1054
1053
 
1055
- // ENDIF returns the final result
1056
- ENDIF(): TResult;
1057
- ENDIF<R>(performFunction: (result: TResult) => R): R;
1054
+ // ENDIF marks the end of the block. Same logic: capture TCurrent into TResult.
1055
+ ENDIF(): TResult | TCurrent;
1056
+ ENDIF<R>(performFunction: (result: TResult | TCurrent) => R): R;
1058
1057
  };
1059
1058
 
1060
- // 4. The Main Type
1061
- // Conditionally combines the specific methods based on TCurrent, plus the shared methods
1059
+ // 4. The Main Type (No changes needed here, just re-stating for context)
1062
1060
  type UIRectangleConditionalChain<TCurrent, TResult = TCurrent> =
1063
- // Use {} instead of never to avoid collapsing the intersection to 'never'
1064
1061
  (TCurrent extends UIRectangle ? RectangleChainMethods<TResult> : {}) &
1065
1062
  (TCurrent extends UIRectangle[] ? ArrayChainMethods<TResult> : {}) &
1066
1063
  SharedChainMethods<TCurrent, TResult>;
1067
1064
 
1068
1065
 
1069
1066
  class UIRectangleConditionalBlock {
1070
- private parentRectangle: UIRectangle
1071
- private conditionMet: boolean
1072
- private branchHasExecuted: boolean = false
1067
+ // The value we are currently chaining on (can be UIRectangle or UIRectangle[])
1073
1068
  private currentResult: any
1069
+ // The value where the IF block started (needed to reset for ELSE branches)
1070
+ private originalResult: any
1071
+ private conditionMet: boolean
1074
1072
 
1075
- constructor(rectangle: UIRectangle, condition: boolean, initialResult?: any) {
1076
- this.parentRectangle = rectangle
1073
+ constructor(initialResult: UIRectangle, condition: boolean) {
1074
+ this.originalResult = initialResult
1075
+ this.currentResult = initialResult
1077
1076
  this.conditionMet = condition
1078
- this.currentResult = initialResult ?? rectangle
1079
1077
  }
1080
1078
 
1081
1079
  private createProxy(): UIRectangleConditionalChain<any, any> {
1082
1080
  const self = this
1083
- return new Proxy(this.parentRectangle, {
1084
- get(target, prop) {
1081
+
1082
+ // The target is irrelevant; we delegate everything to self.currentResult
1083
+ return new Proxy({}, {
1084
+ get(_, prop) {
1085
+
1086
+ // 1. Control Flow Methods
1085
1087
 
1086
- // 1. Handle Control Flow Methods
1087
1088
  if (prop === 'TRANSFORM') {
1088
1089
  return <R extends UIRectangle>(fn: (current: any) => R) => {
1089
1090
  if (self.conditionMet) {
1090
1091
  const result = fn(self.currentResult)
1091
1092
  self.currentResult = result
1092
- if (result instanceof UIRectangle) {
1093
- self.parentRectangle = result
1094
- }
1095
1093
  }
1096
1094
  return self.createProxy()
1097
1095
  }
@@ -1099,31 +1097,24 @@ class UIRectangleConditionalBlock {
1099
1097
 
1100
1098
  if (prop === 'ELSE_IF') {
1101
1099
  return <U>(condition: boolean) => {
1102
- if (!self.branchHasExecuted && !self.conditionMet) {
1100
+ // Only enter this branch if no previous branch has run
1101
+ if (!self.conditionMet) {
1103
1102
  self.conditionMet = condition
1103
+ // Reset the state to the original starting point for this new branch
1104
+ self.currentResult = self.originalResult
1104
1105
  }
1105
- const newBlock = new UIRectangleConditionalBlock(
1106
- self.parentRectangle,
1107
- self.conditionMet,
1108
- self.currentResult
1109
- )
1110
- newBlock.branchHasExecuted = self.branchHasExecuted
1111
- return newBlock.createProxy()
1106
+ return self.createProxy()
1112
1107
  }
1113
1108
  }
1114
1109
 
1115
1110
  if (prop === 'ELSE') {
1116
1111
  return <U>() => {
1117
- if (!self.branchHasExecuted && !self.conditionMet) {
1112
+ if (!self.conditionMet) {
1118
1113
  self.conditionMet = true
1114
+ // Reset the state to the original starting point
1115
+ self.currentResult = self.originalResult
1119
1116
  }
1120
- const newBlock = new UIRectangleConditionalBlock(
1121
- self.parentRectangle,
1122
- self.conditionMet,
1123
- self.currentResult
1124
- )
1125
- newBlock.branchHasExecuted = self.branchHasExecuted
1126
- return newBlock.createProxy()
1117
+ return self.createProxy()
1127
1118
  }
1128
1119
  }
1129
1120
 
@@ -1131,77 +1122,34 @@ class UIRectangleConditionalBlock {
1131
1122
  function endif(): any
1132
1123
  function endif<R>(performFunction: (result: any) => R): R
1133
1124
  function endif<R>(performFunction?: (result: any) => R): R | any {
1134
- if (performFunction) {
1135
- return performFunction(self.currentResult)
1136
- }
1137
- return self.currentResult
1125
+ return performFunction ? performFunction(self.currentResult) : self.currentResult
1138
1126
  }
1139
1127
  return endif
1140
1128
  }
1141
1129
 
1142
- // 2. Handle Array State
1143
- const isArrayState = Array.isArray(self.currentResult)
1130
+ // 2. Forwarding to currentResult (Rectangle or Array)
1131
+
1132
+ const value = self.currentResult[prop]
1144
1133
 
1145
- if (isArrayState) {
1146
- const array = self.currentResult
1147
- const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, prop)
1148
-
1149
- // Case A: It's a method on the Array prototype
1150
- if (descriptor && descriptor.value instanceof Function) {
1151
- return (...args: any[]) => {
1152
- if (self.conditionMet) {
1153
- if (!self.branchHasExecuted) self.branchHasExecuted = true
1154
- const result = descriptor.value.apply(array, args)
1155
- self.currentResult = result
1156
- if (result instanceof UIRectangle) {
1157
- self.parentRectangle = result
1158
- }
1159
- }
1160
- return self.createProxy()
1134
+ // Case A: It's a function (method call)
1135
+ if (typeof value === 'function') {
1136
+ return (...args: any[]) => {
1137
+ if (self.conditionMet) {
1138
+ // Call the method on the CURRENT object, and update the state
1139
+ const result = value.apply(self.currentResult, args)
1140
+ self.currentResult = result
1161
1141
  }
1142
+ return self.createProxy()
1162
1143
  }
1163
-
1164
- // Case B: It's a getter/property on the Array prototype (e.g., lastElement)
1165
- // We access the value on the array instance
1166
- const value = array[prop]
1167
-
1168
- if (value instanceof UIRectangle) {
1169
- // Special handling: If accessing a property that is a UIRectangle,
1170
- // we treat it as a transition to keep the chain alive.
1171
- return (() => {
1172
- if (self.conditionMet) {
1173
- if (!self.branchHasExecuted) self.branchHasExecuted = true
1174
- self.currentResult = value
1175
- self.parentRectangle = value
1176
- }
1177
- return self.createProxy()
1178
- })()
1179
- }
1180
-
1181
- // Case C: Other properties (like 'length', 'map', etc) -> Return raw value
1182
- // This breaks the chain, which is expected behavior for non-Rectangle properties
1183
- return value
1184
1144
  }
1185
1145
 
1186
- // 3. Handle Rectangle State
1187
- else {
1188
- const value = target[prop as keyof UIRectangle]
1189
- if (value instanceof Function) {
1190
- return (...args: any[]) => {
1191
- if (self.conditionMet) {
1192
- if (!self.branchHasExecuted) self.branchHasExecuted = true
1193
- const result = (value as Function).apply(target, args)
1194
- self.currentResult = result
1195
- if (result instanceof UIRectangle) {
1196
- self.parentRectangle = result
1197
- }
1198
- }
1199
- return self.createProxy()
1200
- }
1201
- }
1146
+ // Case B: It's a property (getter)
1147
+ // Accessing a property acts as a transition (e.g. accessing .lastElement)
1148
+ if (self.conditionMet) {
1149
+ self.currentResult = value
1202
1150
  }
1203
1151
 
1204
- return (target as any)[prop]
1152
+ return self.createProxy()
1205
1153
  }
1206
1154
  }) as any
1207
1155
  }
@@ -1209,6 +1157,7 @@ class UIRectangleConditionalBlock {
1209
1157
  getProxy(): UIRectangleConditionalChain<any, any> {
1210
1158
  return this.createProxy()
1211
1159
  }
1160
+
1212
1161
  }
1213
1162
 
1214
1163
 
package/scripts/UIView.ts CHANGED
@@ -5,7 +5,21 @@ import { UICore } from "./UICore"
5
5
  import "./UICoreExtensions"
6
6
  import type { UIDialogView } from "./UIDialogView"
7
7
  import { UILocalizedTextObject } from "./UIInterfaces"
8
- import { FIRST, FIRST_OR_NIL, IF, IS, IS_DEFINED, IS_NIL, IS_NOT, nil, NO, RETURNER, UIObject, YES } from "./UIObject"
8
+ import {
9
+ FIRST,
10
+ FIRST_OR_NIL,
11
+ IF,
12
+ IS,
13
+ IS_DEFINED,
14
+ IS_NIL,
15
+ IS_NOT,
16
+ IS_NOT_LIKE_NULL,
17
+ nil,
18
+ NO,
19
+ RETURNER,
20
+ UIObject,
21
+ YES
22
+ } from "./UIObject"
9
23
  import { UIPoint } from "./UIPoint"
10
24
  import { UIRectangle } from "./UIRectangle"
11
25
  import { UIViewController } from "./UIViewController"
@@ -642,13 +656,17 @@ export class UIView extends UIObject {
642
656
  }
643
657
 
644
658
 
645
- set hoverText(hoverText: string) {
646
- this.viewHTMLElement.setAttribute("title", hoverText)
659
+ set hoverText(hoverText: string | undefined | null) {
660
+ if (IS_NOT_LIKE_NULL(hoverText)) {
661
+ this.viewHTMLElement.setAttribute("title", hoverText)
662
+ }
663
+ else {
664
+ this.viewHTMLElement.removeAttribute("title")
665
+ }
647
666
  }
648
667
 
649
-
650
668
  get hoverText() {
651
- return this.viewHTMLElement.getAttribute("title") ?? ""
669
+ return this.viewHTMLElement.getAttribute("title")
652
670
  }
653
671
 
654
672