@siliconst/metaui 1.4.8 → 1.4.10
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 +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/DynamicHost-dbg.js +13 -0
- package/src/nz/co/siliconst/ui5/metaui/controls/DynamicHost-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/DynamicHost.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/DynamicHost.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/DynamicHost.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/DynamicHost.ts +14 -0
- package/src/nz/co/siliconst/ui5/metaui/controls/host/delegates/ODataDelegate-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/host/delegates/ODataDelegate.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/host/delegates/ODataDelegate.ts +1 -1
- package/src/nz/co/siliconst/ui5/metaui/core/Engine-dbg.js +2 -0
- package/src/nz/co/siliconst/ui5/metaui/core/Engine-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/core/Engine.d.ts +2 -0
- package/src/nz/co/siliconst/ui5/metaui/core/Engine.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/core/Engine.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/core/Engine.ts +3 -0
- package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout-dbg.js +10 -0
- package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/layouts/TableLayout.ts +10 -0
- package/src/nz/co/siliconst/ui5/metaui/library-dbg.js +2 -2
- package/src/nz/co/siliconst/ui5/metaui/library-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/library-preload.js +10 -9
- package/src/nz/co/siliconst/ui5/metaui/library-preload.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/library.d.ts +13 -0
- package/src/nz/co/siliconst/ui5/metaui/library.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/library.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/library.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/library.ts +19 -1
- package/src/nz/co/siliconst/ui5/metaui/manifest.json +2 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ArrayPlugin-dbg.js +11 -3
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ArrayPlugin-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ArrayPlugin.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ArrayPlugin.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ArrayPlugin.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ArrayPlugin.ts +10 -3
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/BasePlugin-dbg.js +14 -3
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/BasePlugin-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/BasePlugin.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/BasePlugin.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/BasePlugin.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/BasePlugin.ts +14 -3
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/CodeEditorPlugin-dbg.js +5 -2
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/CodeEditorPlugin-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/CodeEditorPlugin.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/CodeEditorPlugin.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/CodeEditorPlugin.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/CodeEditorPlugin.ts +5 -2
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ObjectPlugin-dbg.js +11 -3
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ObjectPlugin-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ObjectPlugin.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ObjectPlugin.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ObjectPlugin.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/ObjectPlugin.ts +10 -3
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin-dbg.js +39 -3
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin.d.ts +4 -0
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin.ts +40 -2
- package/src/nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder-dbg.js +50 -0
- package/src/nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder-dbg.js.map +1 -0
- package/src/nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder.d.ts +19 -0
- package/src/nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder.d.ts.map +1 -0
- package/src/nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder.js +2 -0
- package/src/nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder.js.map +1 -0
- package/src/nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder.ts +43 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeEditorPlugin.js","names":["BasePlugin","___BasePlugin","CodeEditorPlugin","detectLanguage","value","trimmed","trim","startsWith","JSON","parse","e","test","adjustHeight","this","control","val","getValue","lines","split","length","newHeight","Math","max","setHeight","render","fieldMetadata","bindingPath","modelName","engineScopeId","onChange","metadata","fieldKey","replace","CodeEditor","id","generateStableId","type","ui","args","editable","isEditable","readOnly","height","width","change","result","validate","isValid","currentVal","detectedType","getType","setType","attachEventOnce","oBinding","getBinding","initLogic","attachChange","applyCommonDirectives","applyState","setEditable","__exports","__esModule"],"sources":["CodeEditorPlugin.ts"],"sourcesContent":["/**\n * @file CodeEditorPlugin.ts\n * @description Renders a sap.ui.codeeditor.CodeEditor for editing source code/JSON.\n */\n\nimport { BasePlugin } from \"./BasePlugin\";\nimport { IPropertyMetadata } from \"../../interfaces/ISchema\";\nimport Control from \"sap/ui/core/Control\";\nimport CodeEditor from \"sap/ui/codeeditor/CodeEditor\";\n\n/**\n * Handles rendering logic for code editing.\n * Requires the `sap.ui.codeeditor` library to be loaded.\n * \n * @namespace nz.co.siliconst.ui5.metaui.plugins.controls\n * @public\n */\nexport class CodeEditorPlugin extends BasePlugin {\n /**\n * Infers the language type from the content.\n * @param value The raw string.\n * @returns The language string.\n */\n private detectLanguage(value: string): string {\n if (!value || typeof value !== \"string\") return \"javascript\";\n \n const trimmed = value.trim();\n if (trimmed === \"\") return \"javascript\";\n\n // 1. JSON\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n JSON.parse(trimmed);\n return \"json\";\n } catch (e) {\n // Not valid JSON, continue testing\n }\n }\n\n // 2. XML / HTML\n if (/^\\s*<[\\s\\S]*>\\s*$/.test(trimmed)) {\n return \"xml\";\n }\n\n // 3. Fallback to JavaScript\n return \"javascript\";\n }\n\n /**\n * Dynamically adjusts the height of the editor based on content lines.\n */\n private adjustHeight(): void {\n if (!this.control) return;\n const val = (this.control as CodeEditor).getValue() || \"\";\n const lines = val.split(/\\r\\n|\\r|\\n/).length;\n // 18px per line + 20px padding, minimum 100px\n const newHeight = Math.max(100, (lines * 18) + 20);\n (this.control as CodeEditor).setHeight(newHeight + \"px\");\n }\n\n /**\n * Renders a `sap.ui.codeeditor.CodeEditor` component.\n * \n * @param fieldMetadata The specific JSON schema properties for this field.\n * @param bindingPath The JSON path bound to this control.\n * @param modelName The UI5 JSONModel name.\n * @param engineScopeId The deterministic scope ID.\n * @param onChange The callback fired on value change.\n * @returns {Control} The configured CodeEditor control.\n */\n public render(fieldMetadata: IPropertyMetadata, bindingPath: string, modelName: string = \"meta\", engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string) => void): Control {\n this.onChange = onChange;\n this.metadata = fieldMetadata;\n this.fieldKey = bindingPath.replace(\"/\", \"\"); // For EventBus\n \n \n \n this.control = new CodeEditor({\n id: this.generateStableId(engineScopeId, bindingPath),\n value: `{${modelName}>${bindingPath}}`,\n type: fieldMetadata.ui?.args || \"javascript\", // will be dynamically overridden if args is missing\n editable: !this.isEditable ? false : !fieldMetadata.ui?.readOnly,\n height: \"100px\", // Initial minimum height\n width: \"100%\",\n change: () => {\n const result = this.validate();\n if (this.onChange) {\n this.onChange(result.isValid, this.fieldKey);\n }\n \n // If language isn't explicitly defined, try to guess it dynamically\n if (!fieldMetadata.ui?.args) {\n const currentVal = (this.control as CodeEditor).getValue();\n const detectedType = this.detectLanguage(currentVal);\n if ((this.control as CodeEditor).getType() !== detectedType) {\n (this.control as CodeEditor).setType(detectedType);\n }\n }\n \n this.adjustHeight();\n }\n });\n\n // Clean architecture: Wait for the model context, then attach safely to the binding itself.\n // No timeouts, no DOM hacks, pure UI5 Eventing.\n (this.control as CodeEditor).attachEventOnce(\"modelContextChange\", () => {\n const oBinding = (this.control as CodeEditor).getBinding(\"value\");\n \n const initLogic = () => {\n if (this.control) {\n const currentVal = (this.control as CodeEditor).getValue();\n if (!fieldMetadata.ui?.args) {\n (this.control as CodeEditor).setType(this.detectLanguage(currentVal));\n }\n this.adjustHeight();\n }\n };\n\n if (oBinding) {\n // Attach for future model changes\n oBinding.attachChange(initLogic);\n }\n \n // Execute immediately in case the model is synchronous (JSONModel)\n initLogic();\n });\n\n this.applyCommonDirectives(this.control, fieldMetadata, modelName);\n\n return this.control as Control;\n }\n\n /**\n * Retrieves the current code string.\n * @returns {unknown} The code value.\n */\n protected getValue(): unknown {\n return this.control ? (this.control as CodeEditor).getValue() : null;\n }\n\n /**\n * Applies dynamic read-only state.\n */\n protected applyState(): void {\n if (this.control && this.metadata) {\n if (!this.isEditable)
|
|
1
|
+
{"version":3,"file":"CodeEditorPlugin.js","names":["BasePlugin","___BasePlugin","CodeEditorPlugin","detectLanguage","value","trimmed","trim","startsWith","JSON","parse","e","test","adjustHeight","this","control","val","getValue","lines","split","length","newHeight","Math","max","setHeight","render","fieldMetadata","bindingPath","modelName","engineScopeId","onChange","metadata","fieldKey","replace","CodeEditor","id","generateStableId","type","ui","args","editable","isEditable","readOnly","height","width","change","result","validate","isValid","currentVal","detectedType","getType","setType","attachEventOnce","oBinding","getBinding","initLogic","attachChange","applyCommonDirectives","applyState","setEditable","__exports","__esModule"],"sources":["CodeEditorPlugin.ts"],"sourcesContent":["/**\n * @file CodeEditorPlugin.ts\n * @description Renders a sap.ui.codeeditor.CodeEditor for editing source code/JSON.\n */\n\nimport { BasePlugin } from \"./BasePlugin\";\nimport { IPropertyMetadata } from \"../../interfaces/ISchema\";\nimport Control from \"sap/ui/core/Control\";\nimport CodeEditor from \"sap/ui/codeeditor/CodeEditor\";\n\n/**\n * Handles rendering logic for code editing.\n * Requires the `sap.ui.codeeditor` library to be loaded.\n * \n * @namespace nz.co.siliconst.ui5.metaui.plugins.controls\n * @public\n */\nexport class CodeEditorPlugin extends BasePlugin {\n /**\n * Infers the language type from the content.\n * @param value The raw string.\n * @returns The language string.\n */\n private detectLanguage(value: string): string {\n if (!value || typeof value !== \"string\") return \"javascript\";\n \n const trimmed = value.trim();\n if (trimmed === \"\") return \"javascript\";\n\n // 1. JSON\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n JSON.parse(trimmed);\n return \"json\";\n } catch (e) {\n // Not valid JSON, continue testing\n }\n }\n\n // 2. XML / HTML\n if (/^\\s*<[\\s\\S]*>\\s*$/.test(trimmed)) {\n return \"xml\";\n }\n\n // 3. Fallback to JavaScript\n return \"javascript\";\n }\n\n /**\n * Dynamically adjusts the height of the editor based on content lines.\n */\n private adjustHeight(): void {\n if (!this.control) return;\n const val = (this.control as CodeEditor).getValue() || \"\";\n const lines = val.split(/\\r\\n|\\r|\\n/).length;\n // 18px per line + 20px padding, minimum 100px\n const newHeight = Math.max(100, (lines * 18) + 20);\n (this.control as CodeEditor).setHeight(newHeight + \"px\");\n }\n\n /**\n * Renders a `sap.ui.codeeditor.CodeEditor` component.\n * \n * @param fieldMetadata The specific JSON schema properties for this field.\n * @param bindingPath The JSON path bound to this control.\n * @param modelName The UI5 JSONModel name.\n * @param engineScopeId The deterministic scope ID.\n * @param onChange The callback fired on value change.\n * @returns {Control} The configured CodeEditor control.\n */\n public render(fieldMetadata: IPropertyMetadata, bindingPath: string, modelName: string = \"meta\", engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string) => void): Control {\n this.onChange = onChange;\n this.metadata = fieldMetadata;\n this.fieldKey = bindingPath.replace(\"/\", \"\"); // For EventBus\n \n \n \n this.control = new CodeEditor({\n id: this.generateStableId(engineScopeId, bindingPath),\n value: `{${modelName}>${bindingPath}}`,\n type: fieldMetadata.ui?.args || \"javascript\", // will be dynamically overridden if args is missing\n editable: !this.isEditable ? false : !fieldMetadata.ui?.readOnly,\n height: \"100px\", // Initial minimum height\n width: \"100%\",\n change: () => {\n const result = this.validate();\n if (this.onChange) {\n this.onChange(result.isValid, this.fieldKey);\n }\n \n // If language isn't explicitly defined, try to guess it dynamically\n if (!fieldMetadata.ui?.args) {\n const currentVal = (this.control as CodeEditor).getValue();\n const detectedType = this.detectLanguage(currentVal);\n if ((this.control as CodeEditor).getType() !== detectedType) {\n (this.control as CodeEditor).setType(detectedType);\n }\n }\n \n this.adjustHeight();\n }\n });\n\n // Clean architecture: Wait for the model context, then attach safely to the binding itself.\n // No timeouts, no DOM hacks, pure UI5 Eventing.\n (this.control as CodeEditor).attachEventOnce(\"modelContextChange\", () => {\n const oBinding = (this.control as CodeEditor).getBinding(\"value\");\n \n const initLogic = () => {\n if (this.control) {\n const currentVal = (this.control as CodeEditor).getValue();\n if (!fieldMetadata.ui?.args) {\n (this.control as CodeEditor).setType(this.detectLanguage(currentVal));\n }\n this.adjustHeight();\n }\n };\n\n if (oBinding) {\n // Attach for future model changes\n oBinding.attachChange(initLogic);\n }\n \n // Execute immediately in case the model is synchronous (JSONModel)\n initLogic();\n });\n\n this.applyCommonDirectives(this.control, fieldMetadata, modelName);\n\n return this.control as Control;\n }\n\n /**\n * Retrieves the current code string.\n * @returns {unknown} The code value.\n */\n protected getValue(): unknown {\n return this.control ? (this.control as CodeEditor).getValue() : null;\n }\n\n /**\n * Applies dynamic read-only state.\n */\n protected applyState(): void {\n if (this.control && this.metadata) {\n if (!this.isEditable) {\n (this.control as CodeEditor).setEditable(false);\n } else {\n (this.control as CodeEditor).setEditable(!this.metadata.ui?.readOnly);\n }\n }\n }\n}\n"],"mappings":"yFAAA,MAKSA,EAAUC,EAAA,cAYZ,MAAMC,UAAyBF,EAM1BG,eAAeC,GACnB,IAAKA,UAAgBA,IAAU,SAAU,MAAO,aAEhD,MAAMC,EAAUD,EAAME,OACtB,GAAID,IAAY,GAAI,MAAO,aAG3B,GAAIA,EAAQE,WAAW,MAAQF,EAAQE,WAAW,KAAM,CACpD,IACIC,KAAKC,MAAMJ,GACX,MAAO,MACX,CAAE,MAAOK,GACL,CAER,CAGA,GAAI,oBAAoBC,KAAKN,GAAU,CACnC,MAAO,KACX,CAGA,MAAO,YACX,CAKQO,eACJ,IAAKC,KAAKC,QAAS,OACnB,MAAMC,EAAOF,KAAKC,QAAuBE,YAAc,GACvD,MAAMC,EAAQF,EAAIG,MAAM,cAAcC,OAEtC,MAAMC,EAAYC,KAAKC,IAAI,IAAML,EAAQ,GAAM,IAC9CJ,KAAKC,QAAuBS,UAAUH,EAAY,KACvD,CAYOI,OAAOC,EAAkCC,EAAqBC,EAAoB,OAAQC,EAAwBC,GACrHhB,KAAKgB,SAAWA,EAChBhB,KAAKiB,SAAWL,EAChBZ,KAAKkB,SAAWL,EAAYM,QAAQ,IAAK,IAIzCnB,KAAKC,QAAU,IAAImB,EAAW,CAC1BC,GAAIrB,KAAKsB,iBAAiBP,EAAeF,GACzCtB,MAAO,IAAIuB,KAAaD,KACxBU,KAAMX,EAAcY,IAAIC,MAAQ,aAChCC,UAAW1B,KAAK2B,WAAa,OAASf,EAAcY,IAAII,SACxDC,OAAQ,QACRC,MAAO,OACPC,OAAQA,KACJ,MAAMC,EAAShC,KAAKiC,WACpB,GAAIjC,KAAKgB,SAAU,CACfhB,KAAKgB,SAASgB,EAAOE,QAASlC,KAAKkB,SACvC,CAGA,IAAKN,EAAcY,IAAIC,KAAM,CACzB,MAAMU,EAAcnC,KAAKC,QAAuBE,WAChD,MAAMiC,EAAepC,KAAKV,eAAe6C,GACzC,GAAKnC,KAAKC,QAAuBoC,YAAcD,EAAc,CACxDpC,KAAKC,QAAuBqC,QAAQF,EACzC,CACJ,CAEApC,KAAKD,kBAMZC,KAAKC,QAAuBsC,gBAAgB,qBAAsB,KAC/D,MAAMC,EAAYxC,KAAKC,QAAuBwC,WAAW,SAEzD,MAAMC,EAAYA,KACd,GAAI1C,KAAKC,QAAS,CACd,MAAMkC,EAAcnC,KAAKC,QAAuBE,WAChD,IAAKS,EAAcY,IAAIC,KAAM,CACxBzB,KAAKC,QAAuBqC,QAAQtC,KAAKV,eAAe6C,GAC7D,CACAnC,KAAKD,cACT,GAGJ,GAAIyC,EAAU,CAEVA,EAASG,aAAaD,EAC1B,CAGAA,MAGJ1C,KAAK4C,sBAAsB5C,KAAKC,QAASW,EAAeE,GAExD,OAAOd,KAAKC,OAChB,CAMUE,WACN,OAAOH,KAAKC,QAAWD,KAAKC,QAAuBE,WAAa,IACpE,CAKU0C,aACN,GAAI7C,KAAKC,SAAWD,KAAKiB,SAAU,CAC/B,IAAKjB,KAAK2B,WAAY,CACjB3B,KAAKC,QAAuB6C,YAAY,MAC7C,KAAO,CACF9C,KAAKC,QAAuB6C,aAAa9C,KAAKiB,SAASO,IAAII,SAChE,CACJ,CACJ,EACH,IAAAmB,EAAA,CAAAC,WAAA,MAAAD,EAAA1D,mBAAA,OAAA0D,CAAA","ignoreList":[]}
|
|
@@ -143,8 +143,11 @@ export class CodeEditorPlugin extends BasePlugin {
|
|
|
143
143
|
*/
|
|
144
144
|
protected applyState(): void {
|
|
145
145
|
if (this.control && this.metadata) {
|
|
146
|
-
if (!this.isEditable)
|
|
147
|
-
|
|
146
|
+
if (!this.isEditable) {
|
|
147
|
+
(this.control as CodeEditor).setEditable(false);
|
|
148
|
+
} else {
|
|
149
|
+
(this.control as CodeEditor).setEditable(!this.metadata.ui?.readOnly);
|
|
150
|
+
}
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
}
|
|
@@ -43,9 +43,11 @@ sap.ui.define(["./BasePlugin", "sap/m/Button"], function (___BasePlugin, Button)
|
|
|
43
43
|
const ctxPath = bindingContext.getPath();
|
|
44
44
|
updatePath = ctxPath.endsWith("/") ? ctxPath + bindingPath : ctxPath === "/" ? "/" + bindingPath : ctxPath + "/" + bindingPath;
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
|
|
47
|
+
// Deep clone to prevent live-mutations on the parent model before 'Submit' is clicked
|
|
48
|
+
const nestedData = JSON.parse(JSON.stringify(parentModel.getProperty(updatePath) || {}));
|
|
49
|
+
sap.ui.require(["nz/co/siliconst/ui5/metaui/controls/DynamicHost"], DynamicHost => {
|
|
50
|
+
const host = new DynamicHost({
|
|
49
51
|
schemaDefinition: subSchema,
|
|
50
52
|
data: nestedData,
|
|
51
53
|
editable: this.isEditable
|
|
@@ -54,6 +56,12 @@ sap.ui.define(["./BasePlugin", "sap/m/Button"], function (___BasePlugin, Button)
|
|
|
54
56
|
host.attachSubmit(e => {
|
|
55
57
|
const payload = e.getParameter("payload");
|
|
56
58
|
parentModel.setProperty(updatePath, payload);
|
|
59
|
+
|
|
60
|
+
// CRITICAL: Notify the parent Engine that this field mutated
|
|
61
|
+
// so it can trigger LiveUpdate syncs and validate!
|
|
62
|
+
if (this.onChange) {
|
|
63
|
+
this.onChange(true, propKey);
|
|
64
|
+
}
|
|
57
65
|
});
|
|
58
66
|
}
|
|
59
67
|
const buttonText = field.ui?.dialogButtonText || (!this.isEditable ? "Close" : "OK");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectPlugin-dbg.js","names":["BasePlugin","___BasePlugin","ObjectPlugin","render","field","bindingPath","modelName","engineScopeId","onChange","metadata","propKey","startsWith","substring","subSchema","type","title","ui","label","properties","uiLayout","control","Button","id","generateStableId","text","isEditable","icon","press","oEvent","btn","getSource","parentModel","getModel","bindingContext","getBindingContext","updatePath","ctxPath","getPath","endsWith","nestedData","getProperty","sap","require","
|
|
1
|
+
{"version":3,"file":"ObjectPlugin-dbg.js","names":["BasePlugin","___BasePlugin","ObjectPlugin","render","field","bindingPath","modelName","engineScopeId","onChange","metadata","propKey","startsWith","substring","subSchema","type","title","ui","label","properties","uiLayout","control","Button","id","generateStableId","text","isEditable","icon","press","oEvent","btn","getSource","parentModel","getModel","bindingContext","getBindingContext","updatePath","ctxPath","getPath","endsWith","nestedData","JSON","parse","stringify","getProperty","sap","require","DynamicHost","host","schemaDefinition","data","editable","attachSubmit","e","payload","getParameter","setProperty","buttonText","dialogButtonText","openInDialog","validate","isValid","getValue","applyState","__exports","__esModule"],"sources":["ObjectPlugin.ts"],"sourcesContent":["/**\n * @file ObjectPlugin.ts\n * @description Renders a button that opens a dialog to edit a nested object structure.\n */\n\nimport { BasePlugin } from \"./BasePlugin\";\nimport { IPluginValidationResult } from \"../../interfaces/IPlugin\";\nimport { ISchema, IPropertyMetadata } from \"../../interfaces/ISchema\";\nimport Control from \"sap/ui/core/Control\";\nimport Event from \"sap/ui/base/Event\";\nimport Button from \"sap/m/Button\";\nimport JSONModel from \"sap/ui/model/json/JSONModel\";\n\nexport class ObjectPlugin extends BasePlugin {\n /**\n * Renders a `sap.m.Button` to open a nested Object form dialog.\n * \n * @param field The specific JSON schema properties for this field.\n * @param bindingPath The JSON path bound to this control.\n * @param modelName The UI5 JSONModel name.\n * @param engineScopeId The deterministic scope ID.\n * @param onChange The callback fired on value change.\n * @returns {Control} The configured Button control.\n */\n public render(field: IPropertyMetadata, bindingPath: string, modelName: string = \"meta\", engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string) => void): Control {\n this.onChange = onChange;\n this.metadata = field;\n const propKey = bindingPath.startsWith(\"/\") ? bindingPath.substring(1) : bindingPath;\n \n const subSchema: ISchema = {\n type: \"object\",\n title: field.ui?.label || propKey,\n properties: field.properties || {}\n };\n \n if (field.uiLayout) {\n subSchema.uiLayout = field.uiLayout;\n }\n\n this.control = new Button({\n id: this.generateStableId(engineScopeId, bindingPath),\n text: !this.isEditable ? \"View Details\" : \"Edit Details\",\n icon: !this.isEditable ? \"sap-icon://display\" : \"sap-icon://form\",\n press: (oEvent: Event) => {\n const btn = oEvent.getSource() as Button;\n const parentModel = btn.getModel(modelName) as JSONModel;\n if (!parentModel) return;\n \n const bindingContext = btn.getBindingContext(modelName);\n let updatePath = bindingPath;\n if (bindingContext && !bindingPath.startsWith(\"/\")) {\n const ctxPath = bindingContext.getPath();\n updatePath = ctxPath.endsWith(\"/\") ? ctxPath + bindingPath : (ctxPath === \"/\" ? \"/\" + bindingPath : ctxPath + \"/\" + bindingPath);\n }\n \n // Deep clone to prevent live-mutations on the parent model before 'Submit' is clicked\n const nestedData = JSON.parse(JSON.stringify(parentModel.getProperty(updatePath) || {}));\n\n sap.ui.require([\"nz/co/siliconst/ui5/metaui/controls/DynamicHost\"], (DynamicHost: typeof import(\"../../controls/DynamicHost\").default) => {\n const host = new DynamicHost({\n schemaDefinition: subSchema,\n data: nestedData,\n editable: this.isEditable\n });\n\n if (!!this.isEditable) {\n host.attachSubmit((e: Event) => {\n const payload = (e.getParameter(\"payload\") as unknown);\n parentModel.setProperty(updatePath, payload);\n \n // CRITICAL: Notify the parent Engine that this field mutated\n // so it can trigger LiveUpdate syncs and validate!\n if (this.onChange) {\n this.onChange(true, propKey);\n }\n });\n }\n\n const buttonText = field.ui?.dialogButtonText || (!this.isEditable ? \"Close\" : \"OK\");\n host.openInDialog(`Nested Details: ${field.ui?.label || propKey}`, buttonText);\n });\n }\n });\n\n return this.control as Control;\n }\n\n /**\n * Validates the object plugin (always true as validation is deferred to the dialog host).\n * @returns {IPluginValidationResult} The validation result.\n */\n public validate(): IPluginValidationResult {\n return { isValid: true };\n }\n\n /**\n * Read-only component for the engine, returns null.\n */\n protected getValue(): null {\n return null;\n }\n\n /**\n * Read-only component, no dynamic state handling needed for the button itself.\n */\n protected applyState(): void {\n // No dynamic state handling needed for the button\n }\n}\n"],"mappings":";;;EAAA;AACA;AACA;AACA;EAHA,MAKSA,UAAU,GAAAC,aAAA;EAQZ,MAAMC,YAAY,SAASF,UAAU,CAAC;IACzC;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACWG,MAAMA,CAACC,KAAwB,EAAGC,WAAmB,EAAGC,SAAiB,GAAG,MAAM,EAAEC,aAAsB,EAAEC,QAAwD,EAAW;MAClL,IAAI,CAACA,QAAQ,GAAGA,QAAQ;MACxB,IAAI,CAACC,QAAQ,GAAGL,KAAK;MACrB,MAAMM,OAAO,GAAGL,WAAW,CAACM,UAAU,CAAC,GAAG,CAAC,GAAGN,WAAW,CAACO,SAAS,CAAC,CAAC,CAAC,GAAGP,WAAW;MAEpF,MAAMQ,SAAkB,GAAG;QACvBC,IAAI,EAAE,QAAQ;QACdC,KAAK,EAAEX,KAAK,CAACY,EAAE,EAAEC,KAAK,IAAIP,OAAO;QACjCQ,UAAU,EAAEd,KAAK,CAACc,UAAU,IAAI,CAAC;MACrC,CAAC;MAED,IAAId,KAAK,CAACe,QAAQ,EAAE;QAChBN,SAAS,CAACM,QAAQ,GAAGf,KAAK,CAACe,QAAQ;MACvC;MAEA,IAAI,CAACC,OAAO,GAAG,IAAIC,MAAM,CAAC;QACtBC,EAAE,EAAE,IAAI,CAACC,gBAAgB,CAAChB,aAAa,EAAEF,WAAW,CAAC;QACrDmB,IAAI,EAAE,CAAC,IAAI,CAACC,UAAU,GAAG,cAAc,GAAG,cAAc;QACxDC,IAAI,EAAE,CAAC,IAAI,CAACD,UAAU,GAAG,oBAAoB,GAAG,iBAAiB;QACjEE,KAAK,EAAGC,MAAa,IAAK;UACtB,MAAMC,GAAG,GAAGD,MAAM,CAACE,SAAS,CAAC,CAAW;UACxC,MAAMC,WAAW,GAAGF,GAAG,CAACG,QAAQ,CAAC1B,SAAS,CAAc;UACxD,IAAI,CAACyB,WAAW,EAAE;UAElB,MAAME,cAAc,GAAGJ,GAAG,CAACK,iBAAiB,CAAC5B,SAAS,CAAC;UACvD,IAAI6B,UAAU,GAAG9B,WAAW;UAC5B,IAAI4B,cAAc,IAAI,CAAC5B,WAAW,CAACM,UAAU,CAAC,GAAG,CAAC,EAAE;YAChD,MAAMyB,OAAO,GAAGH,cAAc,CAACI,OAAO,CAAC,CAAC;YACxCF,UAAU,GAAGC,OAAO,CAACE,QAAQ,CAAC,GAAG,CAAC,GAAGF,OAAO,GAAG/B,WAAW,GAAI+B,OAAO,KAAK,GAAG,GAAG,GAAG,GAAG/B,WAAW,GAAG+B,OAAO,GAAG,GAAG,GAAG/B,WAAY;UACpI;;UAEA;UACA,MAAMkC,UAAU,GAAGC,IAAI,CAACC,KAAK,CAACD,IAAI,CAACE,SAAS,CAACX,WAAW,CAACY,WAAW,CAACR,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;UAExFS,GAAG,CAAC5B,EAAE,CAAC6B,OAAO,CAAC,CAAC,iDAAiD,CAAC,EAAGC,WAAgE,IAAK;YACtI,MAAMC,IAAI,GAAG,IAAID,WAAW,CAAC;cACzBE,gBAAgB,EAAEnC,SAAS;cAC3BoC,IAAI,EAAEV,UAAU;cAChBW,QAAQ,EAAE,IAAI,CAACzB;YACnB,CAAC,CAAC;YAEF,IAAI,CAAC,CAAC,IAAI,CAACA,UAAU,EAAE;cACnBsB,IAAI,CAACI,YAAY,CAAEC,CAAQ,IAAK;gBAC5B,MAAMC,OAAO,GAAID,CAAC,CAACE,YAAY,CAAC,SAAS,CAAa;gBACtDvB,WAAW,CAACwB,WAAW,CAACpB,UAAU,EAAEkB,OAAO,CAAC;;gBAE5C;gBACA;gBACA,IAAI,IAAI,CAAC7C,QAAQ,EAAE;kBACf,IAAI,CAACA,QAAQ,CAAC,IAAI,EAAEE,OAAO,CAAC;gBAChC;cACJ,CAAC,CAAC;YACN;YAEA,MAAM8C,UAAU,GAAGpD,KAAK,CAACY,EAAE,EAAEyC,gBAAgB,KAAK,CAAC,IAAI,CAAChC,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC;YACpFsB,IAAI,CAACW,YAAY,CAAC,mBAAmBtD,KAAK,CAACY,EAAE,EAAEC,KAAK,IAAIP,OAAO,EAAE,EAAE8C,UAAU,CAAC;UAClF,CAAC,CAAC;QACN;MACJ,CAAC,CAAC;MAEF,OAAO,IAAI,CAACpC,OAAO;IACvB;;IAEA;AACJ;AACA;AACA;IACWuC,QAAQA,CAAA,EAA4B;MACvC,OAAO;QAAEC,OAAO,EAAE;MAAK,CAAC;IAC5B;;IAEA;AACJ;AACA;IACcC,QAAQA,CAAA,EAAS;MACvB,OAAO,IAAI;IACf;;IAEA;AACJ;AACA;IACcC,UAAUA,CAAA,EAAS;MACzB;IAAA;EAER;EAAC,IAAAC,SAAA;IAAAC,UAAA;EAAA;EAAAD,SAAA,CAAA7D,YAAA,GAAAA,YAAA;EAAA,OAAA6D,SAAA;AAAA","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectPlugin.d.ts","sourceRoot":"../../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/plugins/controls/ObjectPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,0DAA0D,CAAC;IAC1E;;;OAGG;IAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wDAAwD,CAAC;IACpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;IACxF,OAAO,EAAW,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;IAC3F,OAAO,OAAO,MAAM,qBAAqB,CAAC;IAK1C,MAAa,YAAa,SAAQ,UAAU;QACxC;;;;;;;;;WASG;QACI,MAAM,CAAC,KAAK,EAAE,iBAAiB,EAAG,WAAW,EAAE,MAAM,EAAG,SAAS,GAAE,MAAe,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"ObjectPlugin.d.ts","sourceRoot":"../../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/plugins/controls/ObjectPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,0DAA0D,CAAC;IAC1E;;;OAGG;IAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wDAAwD,CAAC;IACpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;IACxF,OAAO,EAAW,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;IAC3F,OAAO,OAAO,MAAM,qBAAqB,CAAC;IAK1C,MAAa,YAAa,SAAQ,UAAU;QACxC;;;;;;;;;WASG;QACI,MAAM,CAAC,KAAK,EAAE,iBAAiB,EAAG,WAAW,EAAE,MAAM,EAAG,SAAS,GAAE,MAAe,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO;QA+DrL;;;WAGG;QACI,QAAQ,IAAI,uBAAuB;QAI1C;;WAEG;QACH,SAAS,CAAC,QAAQ,IAAI,IAAI;QAI1B;;WAEG;QACH,SAAS,CAAC,UAAU,IAAI,IAAI;KAG/B;CAEA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
sap.ui.define(["./BasePlugin","sap/m/Button"],function(t,e){"use strict";const i=t["BasePlugin"];class s extends i{render(t,i,s="meta",a,n){this.onChange=n;this.metadata=t;const o=i.startsWith("/")?i.substring(1):i;const r={type:"object",title:t.ui?.label||o,properties:t.properties||{}};if(t.uiLayout){r.uiLayout=t.uiLayout}this.control=new e({id:this.generateStableId(a,i),text:!this.isEditable?"View Details":"Edit Details",icon:!this.isEditable?"sap-icon://display":"sap-icon://form",press:e=>{const a=e.getSource();const n=a.getModel(s);if(!n)return;const l=a.getBindingContext(s);let u=i;if(l&&!i.startsWith("/")){const t=l.getPath();u=t.endsWith("/")?t+i:t==="/"?"/"+i:t+"/"+i}const c=n.getProperty(u)||{};sap.ui.require(["nz/co/siliconst/ui5/metaui/controls/
|
|
1
|
+
sap.ui.define(["./BasePlugin","sap/m/Button"],function(t,e){"use strict";const i=t["BasePlugin"];class s extends i{render(t,i,s="meta",a,n){this.onChange=n;this.metadata=t;const o=i.startsWith("/")?i.substring(1):i;const r={type:"object",title:t.ui?.label||o,properties:t.properties||{}};if(t.uiLayout){r.uiLayout=t.uiLayout}this.control=new e({id:this.generateStableId(a,i),text:!this.isEditable?"View Details":"Edit Details",icon:!this.isEditable?"sap-icon://display":"sap-icon://form",press:e=>{const a=e.getSource();const n=a.getModel(s);if(!n)return;const l=a.getBindingContext(s);let u=i;if(l&&!i.startsWith("/")){const t=l.getPath();u=t.endsWith("/")?t+i:t==="/"?"/"+i:t+"/"+i}const c=JSON.parse(JSON.stringify(n.getProperty(u)||{}));sap.ui.require(["nz/co/siliconst/ui5/metaui/controls/DynamicHost"],e=>{const i=new e({schemaDefinition:r,data:c,editable:this.isEditable});if(!!this.isEditable){i.attachSubmit(t=>{const e=t.getParameter("payload");n.setProperty(u,e);if(this.onChange){this.onChange(true,o)}})}const s=t.ui?.dialogButtonText||(!this.isEditable?"Close":"OK");i.openInDialog(`Nested Details: ${t.ui?.label||o}`,s)})}});return this.control}validate(){return{isValid:true}}getValue(){return null}applyState(){}}var a={__esModule:true};a.ObjectPlugin=s;return a});
|
|
2
2
|
//# sourceMappingURL=ObjectPlugin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectPlugin.js","names":["BasePlugin","___BasePlugin","ObjectPlugin","render","field","bindingPath","modelName","engineScopeId","onChange","this","metadata","propKey","startsWith","substring","subSchema","type","title","ui","label","properties","uiLayout","control","Button","id","generateStableId","text","isEditable","icon","press","oEvent","btn","getSource","parentModel","getModel","bindingContext","getBindingContext","updatePath","ctxPath","getPath","endsWith","nestedData","getProperty","sap","require","
|
|
1
|
+
{"version":3,"file":"ObjectPlugin.js","names":["BasePlugin","___BasePlugin","ObjectPlugin","render","field","bindingPath","modelName","engineScopeId","onChange","this","metadata","propKey","startsWith","substring","subSchema","type","title","ui","label","properties","uiLayout","control","Button","id","generateStableId","text","isEditable","icon","press","oEvent","btn","getSource","parentModel","getModel","bindingContext","getBindingContext","updatePath","ctxPath","getPath","endsWith","nestedData","JSON","parse","stringify","getProperty","sap","require","DynamicHost","host","schemaDefinition","data","editable","attachSubmit","e","payload","getParameter","setProperty","buttonText","dialogButtonText","openInDialog","validate","isValid","getValue","applyState","__exports","__esModule"],"sources":["ObjectPlugin.ts"],"sourcesContent":["/**\n * @file ObjectPlugin.ts\n * @description Renders a button that opens a dialog to edit a nested object structure.\n */\n\nimport { BasePlugin } from \"./BasePlugin\";\nimport { IPluginValidationResult } from \"../../interfaces/IPlugin\";\nimport { ISchema, IPropertyMetadata } from \"../../interfaces/ISchema\";\nimport Control from \"sap/ui/core/Control\";\nimport Event from \"sap/ui/base/Event\";\nimport Button from \"sap/m/Button\";\nimport JSONModel from \"sap/ui/model/json/JSONModel\";\n\nexport class ObjectPlugin extends BasePlugin {\n /**\n * Renders a `sap.m.Button` to open a nested Object form dialog.\n * \n * @param field The specific JSON schema properties for this field.\n * @param bindingPath The JSON path bound to this control.\n * @param modelName The UI5 JSONModel name.\n * @param engineScopeId The deterministic scope ID.\n * @param onChange The callback fired on value change.\n * @returns {Control} The configured Button control.\n */\n public render(field: IPropertyMetadata, bindingPath: string, modelName: string = \"meta\", engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string) => void): Control {\n this.onChange = onChange;\n this.metadata = field;\n const propKey = bindingPath.startsWith(\"/\") ? bindingPath.substring(1) : bindingPath;\n \n const subSchema: ISchema = {\n type: \"object\",\n title: field.ui?.label || propKey,\n properties: field.properties || {}\n };\n \n if (field.uiLayout) {\n subSchema.uiLayout = field.uiLayout;\n }\n\n this.control = new Button({\n id: this.generateStableId(engineScopeId, bindingPath),\n text: !this.isEditable ? \"View Details\" : \"Edit Details\",\n icon: !this.isEditable ? \"sap-icon://display\" : \"sap-icon://form\",\n press: (oEvent: Event) => {\n const btn = oEvent.getSource() as Button;\n const parentModel = btn.getModel(modelName) as JSONModel;\n if (!parentModel) return;\n \n const bindingContext = btn.getBindingContext(modelName);\n let updatePath = bindingPath;\n if (bindingContext && !bindingPath.startsWith(\"/\")) {\n const ctxPath = bindingContext.getPath();\n updatePath = ctxPath.endsWith(\"/\") ? ctxPath + bindingPath : (ctxPath === \"/\" ? \"/\" + bindingPath : ctxPath + \"/\" + bindingPath);\n }\n \n // Deep clone to prevent live-mutations on the parent model before 'Submit' is clicked\n const nestedData = JSON.parse(JSON.stringify(parentModel.getProperty(updatePath) || {}));\n\n sap.ui.require([\"nz/co/siliconst/ui5/metaui/controls/DynamicHost\"], (DynamicHost: typeof import(\"../../controls/DynamicHost\").default) => {\n const host = new DynamicHost({\n schemaDefinition: subSchema,\n data: nestedData,\n editable: this.isEditable\n });\n\n if (!!this.isEditable) {\n host.attachSubmit((e: Event) => {\n const payload = (e.getParameter(\"payload\") as unknown);\n parentModel.setProperty(updatePath, payload);\n \n // CRITICAL: Notify the parent Engine that this field mutated\n // so it can trigger LiveUpdate syncs and validate!\n if (this.onChange) {\n this.onChange(true, propKey);\n }\n });\n }\n\n const buttonText = field.ui?.dialogButtonText || (!this.isEditable ? \"Close\" : \"OK\");\n host.openInDialog(`Nested Details: ${field.ui?.label || propKey}`, buttonText);\n });\n }\n });\n\n return this.control as Control;\n }\n\n /**\n * Validates the object plugin (always true as validation is deferred to the dialog host).\n * @returns {IPluginValidationResult} The validation result.\n */\n public validate(): IPluginValidationResult {\n return { isValid: true };\n }\n\n /**\n * Read-only component for the engine, returns null.\n */\n protected getValue(): null {\n return null;\n }\n\n /**\n * Read-only component, no dynamic state handling needed for the button itself.\n */\n protected applyState(): void {\n // No dynamic state handling needed for the button\n }\n}\n"],"mappings":"yEAAA,MAKSA,EAAUC,EAAA,cAQZ,MAAMC,UAAqBF,EAWvBG,OAAOC,EAA2BC,EAAsBC,EAAoB,OAAQC,EAAwBC,GAC/GC,KAAKD,SAAWA,EAChBC,KAAKC,SAAWN,EAChB,MAAMO,EAAUN,EAAYO,WAAW,KAAOP,EAAYQ,UAAU,GAAKR,EAEzE,MAAMS,EAAqB,CACvBC,KAAM,SACNC,MAAOZ,EAAMa,IAAIC,OAASP,EAC1BQ,WAAYf,EAAMe,YAAc,CAAC,GAGrC,GAAIf,EAAMgB,SAAU,CAChBN,EAAUM,SAAWhB,EAAMgB,QAC/B,CAEAX,KAAKY,QAAU,IAAIC,EAAO,CACtBC,GAAId,KAAKe,iBAAiBjB,EAAeF,GACzCoB,MAAOhB,KAAKiB,WAAa,eAAiB,eAC1CC,MAAOlB,KAAKiB,WAAa,qBAAuB,kBAChDE,MAAQC,IACJ,MAAMC,EAAMD,EAAOE,YACnB,MAAMC,EAAcF,EAAIG,SAAS3B,GACjC,IAAK0B,EAAa,OAElB,MAAME,EAAiBJ,EAAIK,kBAAkB7B,GAC7C,IAAI8B,EAAa/B,EACjB,GAAI6B,IAAmB7B,EAAYO,WAAW,KAAM,CAChD,MAAMyB,EAAUH,EAAeI,UAC/BF,EAAaC,EAAQE,SAAS,KAAOF,EAAUhC,EAAegC,IAAY,IAAM,IAAMhC,EAAcgC,EAAU,IAAMhC,CACxH,CAGA,MAAMmC,EAAaC,KAAKC,MAAMD,KAAKE,UAAUX,EAAYY,YAAYR,IAAe,CAAC,IAErFS,IAAI5B,GAAG6B,QAAQ,CAAC,mDAAqDC,IACjE,MAAMC,EAAO,IAAID,EAAY,CACzBE,iBAAkBnC,EAClBoC,KAAMV,EACNW,SAAU1C,KAAKiB,aAGnB,KAAMjB,KAAKiB,WAAY,CACnBsB,EAAKI,aAAcC,IACf,MAAMC,EAAWD,EAAEE,aAAa,WAChCvB,EAAYwB,YAAYpB,EAAYkB,GAIpC,GAAI7C,KAAKD,SAAU,CACfC,KAAKD,SAAS,KAAMG,EACxB,GAER,CAEA,MAAM8C,EAAarD,EAAMa,IAAIyC,oBAAsBjD,KAAKiB,WAAa,QAAU,MAC/EsB,EAAKW,aAAa,mBAAmBvD,EAAMa,IAAIC,OAASP,IAAW8C,QAK/E,OAAOhD,KAAKY,OAChB,CAMOuC,WACH,MAAO,CAAEC,QAAS,KACtB,CAKUC,WACN,OAAO,IACX,CAKUC,aACN,EAEP,IAAAC,EAAA,CAAAC,WAAA,MAAAD,EAAA9D,eAAA,OAAA8D,CAAA","ignoreList":[]}
|
|
@@ -53,10 +53,11 @@ export class ObjectPlugin extends BasePlugin {
|
|
|
53
53
|
updatePath = ctxPath.endsWith("/") ? ctxPath + bindingPath : (ctxPath === "/" ? "/" + bindingPath : ctxPath + "/" + bindingPath);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
// Deep clone to prevent live-mutations on the parent model before 'Submit' is clicked
|
|
57
|
+
const nestedData = JSON.parse(JSON.stringify(parentModel.getProperty(updatePath) || {}));
|
|
57
58
|
|
|
58
|
-
sap.ui.require(["nz/co/siliconst/ui5/metaui/controls/
|
|
59
|
-
const host = new
|
|
59
|
+
sap.ui.require(["nz/co/siliconst/ui5/metaui/controls/DynamicHost"], (DynamicHost: typeof import("../../controls/DynamicHost").default) => {
|
|
60
|
+
const host = new DynamicHost({
|
|
60
61
|
schemaDefinition: subSchema,
|
|
61
62
|
data: nestedData,
|
|
62
63
|
editable: this.isEditable
|
|
@@ -66,6 +67,12 @@ export class ObjectPlugin extends BasePlugin {
|
|
|
66
67
|
host.attachSubmit((e: Event) => {
|
|
67
68
|
const payload = (e.getParameter("payload") as unknown);
|
|
68
69
|
parentModel.setProperty(updatePath, payload);
|
|
70
|
+
|
|
71
|
+
// CRITICAL: Notify the parent Engine that this field mutated
|
|
72
|
+
// so it can trigger LiveUpdate syncs and validate!
|
|
73
|
+
if (this.onChange) {
|
|
74
|
+
this.onChange(true, propKey);
|
|
75
|
+
}
|
|
69
76
|
});
|
|
70
77
|
}
|
|
71
78
|
|
|
@@ -38,20 +38,56 @@ sap.ui.define(["./BasePlugin", "sap/m/TextArea", "sap/m/Text"], function (___Bas
|
|
|
38
38
|
value: `{${modelName}>${bindingPath}}`,
|
|
39
39
|
maxLength: fieldMetadata.maxLength || 0,
|
|
40
40
|
required: !!fieldMetadata.required,
|
|
41
|
-
rows:
|
|
42
|
-
//
|
|
41
|
+
rows: 3,
|
|
42
|
+
// Start with at least 3 lines
|
|
43
|
+
growing: false,
|
|
44
|
+
// We manage height manually to maintain the +2 lines requirement
|
|
43
45
|
change: oEvent => {
|
|
44
|
-
|
|
46
|
+
this.updateHeight(oEvent.getSource());
|
|
45
47
|
const result = this.validate();
|
|
46
48
|
if (this.onChange) {
|
|
47
49
|
this.onChange(result.isValid, this.fieldKey);
|
|
48
50
|
}
|
|
51
|
+
},
|
|
52
|
+
liveChange: oEvent => {
|
|
53
|
+
this.updateHeight(oEvent.getSource());
|
|
49
54
|
}
|
|
50
55
|
});
|
|
56
|
+
|
|
57
|
+
// Clean architecture: Wait for the model context, then calculate initial height
|
|
58
|
+
this.control.attachEventOnce("modelContextChange", () => {
|
|
59
|
+
const oBinding = this.control.getBinding("value");
|
|
60
|
+
const initLogic = () => {
|
|
61
|
+
if (this.control) {
|
|
62
|
+
this.updateHeight(this.control);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
if (oBinding) {
|
|
66
|
+
oBinding.attachChange(initLogic);
|
|
67
|
+
}
|
|
68
|
+
// Execute immediately in case the model is synchronous (JSONModel)
|
|
69
|
+
initLogic();
|
|
70
|
+
});
|
|
51
71
|
this.applyCommonDirectives(this.control, fieldMetadata, modelName);
|
|
52
72
|
return this.control;
|
|
53
73
|
}
|
|
54
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Dynamically sets the rows to the content length + 2
|
|
77
|
+
*/
|
|
78
|
+
updateHeight(oTextArea) {
|
|
79
|
+
const val = oTextArea.getValue() || "";
|
|
80
|
+
// Count actual newlines in the string
|
|
81
|
+
const numLines = val.split(/\r\n|\r|\n/).length;
|
|
82
|
+
// Also estimate word wrapping if they type a really long single line without pressing enter
|
|
83
|
+
// A standard character width approximation, assuming ~80 chars per line for full width
|
|
84
|
+
const wrappedLines = Math.ceil(val.length / 80);
|
|
85
|
+
const totalLines = Math.max(numLines, wrappedLines);
|
|
86
|
+
|
|
87
|
+
// Ensure there is always 2 lines of extra breathing room
|
|
88
|
+
oTextArea.setRows(Math.max(3, totalLines + 2));
|
|
89
|
+
}
|
|
90
|
+
|
|
55
91
|
/**
|
|
56
92
|
* Retrieves the current text string.
|
|
57
93
|
* @returns {unknown} The text string.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextAreaPlugin-dbg.js","names":["BasePlugin","___BasePlugin","TextAreaPlugin","render","fieldMetadata","bindingPath","modelName","engineScopeId","onChange","metadata","fieldKey","replace","isEditable","control","TextControl","id","generateStableId","text","applyCommonDirectives","TextArea","value","maxLength","required","rows","
|
|
1
|
+
{"version":3,"file":"TextAreaPlugin-dbg.js","names":["BasePlugin","___BasePlugin","TextAreaPlugin","render","fieldMetadata","bindingPath","modelName","engineScopeId","onChange","metadata","fieldKey","replace","isEditable","control","TextControl","id","generateStableId","text","applyCommonDirectives","TextArea","value","maxLength","required","rows","growing","change","oEvent","updateHeight","getSource","result","validate","isValid","liveChange","attachEventOnce","oBinding","getBinding","initLogic","attachChange","oTextArea","val","getValue","numLines","split","length","wrappedLines","Math","ceil","totalLines","max","setRows","applyState","input","setEditable","ui","readOnly","setRequired","__exports","__esModule"],"sources":["TextAreaPlugin.ts"],"sourcesContent":["/**\n * @file TextAreaPlugin.ts\n * @description Renders a sap.m.TextArea for string data specifically marked with the textArea widget.\n */\n\nimport { BasePlugin } from \"./BasePlugin\";\nimport { IPropertyMetadata } from \"../../interfaces/ISchema\";\nimport TextArea from \"sap/m/TextArea\";\nimport Control from \"sap/ui/core/Control\";\nimport TextControl from \"sap/m/Text\";\n\n/**\n * Handles rendering and logic for multi-line text areas.\n */\nexport class TextAreaPlugin extends BasePlugin {\n /**\n * Renders a `sap.m.TextArea` component.\n * \n * @param fieldMetadata The specific JSON schema properties for this field.\n * @param bindingPath The JSON path bound to this control.\n * @param modelName The UI5 JSONModel name.\n * @param engineScopeId The deterministic scope ID.\n * @param onChange The callback fired on value change.\n * @returns {Control} The configured TextArea control.\n */\n public render(fieldMetadata: IPropertyMetadata, bindingPath: string, modelName: string = \"meta\", engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string) => void): Control {\n this.onChange = onChange;\n this.metadata = fieldMetadata;\n this.fieldKey = bindingPath.replace(\"/\", \"\"); // For EventBus\n \n if (!this.isEditable) {\n \n this.control = new TextControl({\n id: this.generateStableId(engineScopeId, bindingPath),\n text: `{${modelName}>${bindingPath}}`\n });\n this.applyCommonDirectives(this.control, fieldMetadata, modelName);\n return this.control as Control;\n }\n\n this.control = new TextArea({\n id: this.generateStableId(engineScopeId, bindingPath),\n value: `{${modelName}>${bindingPath}}`,\n maxLength: fieldMetadata.maxLength || 0,\n required: !!fieldMetadata.required,\n rows: 3, // Start with at least 3 lines\n growing: false, // We manage height manually to maintain the +2 lines requirement\n change: (oEvent: sap.ui.base.Event) => {\n this.updateHeight(oEvent.getSource() as TextArea);\n const result = this.validate();\n if (this.onChange) {\n this.onChange(result.isValid, this.fieldKey);\n }\n },\n liveChange: (oEvent: sap.ui.base.Event) => {\n this.updateHeight(oEvent.getSource() as TextArea);\n }\n });\n\n // Clean architecture: Wait for the model context, then calculate initial height\n (this.control as TextArea).attachEventOnce(\"modelContextChange\", () => {\n const oBinding = (this.control as TextArea).getBinding(\"value\");\n \n const initLogic = () => {\n if (this.control) {\n this.updateHeight(this.control as TextArea);\n }\n };\n\n if (oBinding) {\n oBinding.attachChange(initLogic);\n }\n // Execute immediately in case the model is synchronous (JSONModel)\n initLogic();\n });\n\n this.applyCommonDirectives(this.control, fieldMetadata, modelName);\n\n return this.control as Control;\n }\n\n /**\n * Dynamically sets the rows to the content length + 2\n */\n private updateHeight(oTextArea: TextArea): void {\n const val = oTextArea.getValue() || \"\";\n // Count actual newlines in the string\n const numLines = val.split(/\\r\\n|\\r|\\n/).length;\n // Also estimate word wrapping if they type a really long single line without pressing enter\n // A standard character width approximation, assuming ~80 chars per line for full width\n const wrappedLines = Math.ceil(val.length / 80);\n \n const totalLines = Math.max(numLines, wrappedLines);\n \n // Ensure there is always 2 lines of extra breathing room\n oTextArea.setRows(Math.max(3, totalLines + 2));\n }\n\n /**\n * Retrieves the current text string.\n * @returns {unknown} The text string.\n */\n protected getValue(): unknown {\n return this.control ? (this.control as TextArea).getValue() : null;\n }\n\n /**\n * Applies dynamic read-only state.\n */\n protected applyState(): void {\n if (this.control && this.metadata) {\n if (!this.isEditable) return;\n const input = this.control as TextArea;\n input.setEditable(!this.metadata.ui?.readOnly);\n input.setRequired(this.metadata.required);\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -21,6 +21,10 @@ declare module "nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin" {
|
|
|
21
21
|
* @returns {Control} The configured TextArea control.
|
|
22
22
|
*/
|
|
23
23
|
render(fieldMetadata: IPropertyMetadata, bindingPath: string, modelName?: string, engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string) => void): Control;
|
|
24
|
+
/**
|
|
25
|
+
* Dynamically sets the rows to the content length + 2
|
|
26
|
+
*/
|
|
27
|
+
private updateHeight;
|
|
24
28
|
/**
|
|
25
29
|
* Retrieves the current text string.
|
|
26
30
|
* @returns {unknown} The text string.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextAreaPlugin.d.ts","sourceRoot":"../../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,4DAA4D,CAAC;IAC5E;;;OAGG;IAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wDAAwD,CAAC;IACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;IAElF,OAAO,OAAO,MAAM,qBAAqB,CAAC;IAG1C;;OAEG;IACH,MAAa,cAAe,SAAQ,UAAU;QAC1C;;;;;;;;;WASG;QACI,MAAM,CAAC,aAAa,EAAE,iBAAiB,EAAG,WAAW,EAAE,MAAM,EAAG,SAAS,GAAE,MAAe,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"TextAreaPlugin.d.ts","sourceRoot":"../../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/plugins/controls/TextAreaPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,4DAA4D,CAAC;IAC5E;;;OAGG;IAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wDAAwD,CAAC;IACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;IAElF,OAAO,OAAO,MAAM,qBAAqB,CAAC;IAG1C;;OAEG;IACH,MAAa,cAAe,SAAQ,UAAU;QAC1C;;;;;;;;;WASG;QACI,MAAM,CAAC,aAAa,EAAE,iBAAiB,EAAG,WAAW,EAAE,MAAM,EAAG,SAAS,GAAE,MAAe,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO;QAwD7L;;WAEG;QACH,OAAO,CAAC,YAAY;QAcpB;;;WAGG;QACH,SAAS,CAAC,QAAQ,IAAI,OAAO;QAI7B;;WAEG;QACH,SAAS,CAAC,UAAU,IAAI,IAAI;KAQ/B;CAEA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
sap.ui.define(["./BasePlugin","sap/m/TextArea","sap/m/Text"],function(t,e,i){"use strict";const a=t["BasePlugin"];class
|
|
1
|
+
sap.ui.define(["./BasePlugin","sap/m/TextArea","sap/m/Text"],function(t,e,i){"use strict";const a=t["BasePlugin"];class n extends a{render(t,a,n="meta",s,h){this.onChange=h;this.metadata=t;this.fieldKey=a.replace("/","");if(!this.isEditable){this.control=new i({id:this.generateStableId(s,a),text:`{${n}>${a}}`});this.applyCommonDirectives(this.control,t,n);return this.control}this.control=new e({id:this.generateStableId(s,a),value:`{${n}>${a}}`,maxLength:t.maxLength||0,required:!!t.required,rows:3,growing:false,change:t=>{this.updateHeight(t.getSource());const e=this.validate();if(this.onChange){this.onChange(e.isValid,this.fieldKey)}},liveChange:t=>{this.updateHeight(t.getSource())}});this.control.attachEventOnce("modelContextChange",()=>{const t=this.control.getBinding("value");const e=()=>{if(this.control){this.updateHeight(this.control)}};if(t){t.attachChange(e)}e()});this.applyCommonDirectives(this.control,t,n);return this.control}updateHeight(t){const e=t.getValue()||"";const i=e.split(/\r\n|\r|\n/).length;const a=Math.ceil(e.length/80);const n=Math.max(i,a);t.setRows(Math.max(3,n+2))}getValue(){return this.control?this.control.getValue():null}applyState(){if(this.control&&this.metadata){if(!this.isEditable)return;const t=this.control;t.setEditable(!this.metadata.ui?.readOnly);t.setRequired(this.metadata.required)}}}var s={__esModule:true};s.TextAreaPlugin=n;return s});
|
|
2
2
|
//# sourceMappingURL=TextAreaPlugin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextAreaPlugin.js","names":["BasePlugin","___BasePlugin","TextAreaPlugin","render","fieldMetadata","bindingPath","modelName","engineScopeId","onChange","this","metadata","fieldKey","replace","isEditable","control","TextControl","id","generateStableId","text","applyCommonDirectives","TextArea","value","maxLength","required","rows","
|
|
1
|
+
{"version":3,"file":"TextAreaPlugin.js","names":["BasePlugin","___BasePlugin","TextAreaPlugin","render","fieldMetadata","bindingPath","modelName","engineScopeId","onChange","this","metadata","fieldKey","replace","isEditable","control","TextControl","id","generateStableId","text","applyCommonDirectives","TextArea","value","maxLength","required","rows","growing","change","oEvent","updateHeight","getSource","result","validate","isValid","liveChange","attachEventOnce","oBinding","getBinding","initLogic","attachChange","oTextArea","val","getValue","numLines","split","length","wrappedLines","Math","ceil","totalLines","max","setRows","applyState","input","setEditable","ui","readOnly","setRequired","__exports","__esModule"],"sources":["TextAreaPlugin.ts"],"sourcesContent":["/**\n * @file TextAreaPlugin.ts\n * @description Renders a sap.m.TextArea for string data specifically marked with the textArea widget.\n */\n\nimport { BasePlugin } from \"./BasePlugin\";\nimport { IPropertyMetadata } from \"../../interfaces/ISchema\";\nimport TextArea from \"sap/m/TextArea\";\nimport Control from \"sap/ui/core/Control\";\nimport TextControl from \"sap/m/Text\";\n\n/**\n * Handles rendering and logic for multi-line text areas.\n */\nexport class TextAreaPlugin extends BasePlugin {\n /**\n * Renders a `sap.m.TextArea` component.\n * \n * @param fieldMetadata The specific JSON schema properties for this field.\n * @param bindingPath The JSON path bound to this control.\n * @param modelName The UI5 JSONModel name.\n * @param engineScopeId The deterministic scope ID.\n * @param onChange The callback fired on value change.\n * @returns {Control} The configured TextArea control.\n */\n public render(fieldMetadata: IPropertyMetadata, bindingPath: string, modelName: string = \"meta\", engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string) => void): Control {\n this.onChange = onChange;\n this.metadata = fieldMetadata;\n this.fieldKey = bindingPath.replace(\"/\", \"\"); // For EventBus\n \n if (!this.isEditable) {\n \n this.control = new TextControl({\n id: this.generateStableId(engineScopeId, bindingPath),\n text: `{${modelName}>${bindingPath}}`\n });\n this.applyCommonDirectives(this.control, fieldMetadata, modelName);\n return this.control as Control;\n }\n\n this.control = new TextArea({\n id: this.generateStableId(engineScopeId, bindingPath),\n value: `{${modelName}>${bindingPath}}`,\n maxLength: fieldMetadata.maxLength || 0,\n required: !!fieldMetadata.required,\n rows: 3, // Start with at least 3 lines\n growing: false, // We manage height manually to maintain the +2 lines requirement\n change: (oEvent: sap.ui.base.Event) => {\n this.updateHeight(oEvent.getSource() as TextArea);\n const result = this.validate();\n if (this.onChange) {\n this.onChange(result.isValid, this.fieldKey);\n }\n },\n liveChange: (oEvent: sap.ui.base.Event) => {\n this.updateHeight(oEvent.getSource() as TextArea);\n }\n });\n\n // Clean architecture: Wait for the model context, then calculate initial height\n (this.control as TextArea).attachEventOnce(\"modelContextChange\", () => {\n const oBinding = (this.control as TextArea).getBinding(\"value\");\n \n const initLogic = () => {\n if (this.control) {\n this.updateHeight(this.control as TextArea);\n }\n };\n\n if (oBinding) {\n oBinding.attachChange(initLogic);\n }\n // Execute immediately in case the model is synchronous (JSONModel)\n initLogic();\n });\n\n this.applyCommonDirectives(this.control, fieldMetadata, modelName);\n\n return this.control as Control;\n }\n\n /**\n * Dynamically sets the rows to the content length + 2\n */\n private updateHeight(oTextArea: TextArea): void {\n const val = oTextArea.getValue() || \"\";\n // Count actual newlines in the string\n const numLines = val.split(/\\r\\n|\\r|\\n/).length;\n // Also estimate word wrapping if they type a really long single line without pressing enter\n // A standard character width approximation, assuming ~80 chars per line for full width\n const wrappedLines = Math.ceil(val.length / 80);\n \n const totalLines = Math.max(numLines, wrappedLines);\n \n // Ensure there is always 2 lines of extra breathing room\n oTextArea.setRows(Math.max(3, totalLines + 2));\n }\n\n /**\n * Retrieves the current text string.\n * @returns {unknown} The text string.\n */\n protected getValue(): unknown {\n return this.control ? (this.control as TextArea).getValue() : null;\n }\n\n /**\n * Applies dynamic read-only state.\n */\n protected applyState(): void {\n if (this.control && this.metadata) {\n if (!this.isEditable) return;\n const input = this.control as TextArea;\n input.setEditable(!this.metadata.ui?.readOnly);\n input.setRequired(this.metadata.required);\n }\n }\n}\n"],"mappings":"0FAAA,MAKSA,EAAUC,EAAA,cASZ,MAAMC,UAAuBF,EAWzBG,OAAOC,EAAmCC,EAAsBC,EAAoB,OAAQC,EAAwBC,GACvHC,KAAKD,SAAWA,EAChBC,KAAKC,SAAWN,EAChBK,KAAKE,SAAWN,EAAYO,QAAQ,IAAK,IAEzC,IAAKH,KAAKI,WAAY,CAElBJ,KAAKK,QAAU,IAAIC,EAAY,CAC3BC,GAAIP,KAAKQ,iBAAiBV,EAAeF,GACzCa,KAAM,IAAIZ,KAAaD,OAE3BI,KAAKU,sBAAsBV,KAAKK,QAASV,EAAeE,GACxD,OAAOG,KAAKK,OAChB,CAEAL,KAAKK,QAAU,IAAIM,EAAS,CACxBJ,GAAIP,KAAKQ,iBAAiBV,EAAeF,GACzCgB,MAAO,IAAIf,KAAaD,KACxBiB,UAAWlB,EAAckB,WAAa,EACtCC,WAAYnB,EAAcmB,SAC1BC,KAAM,EACNC,QAAS,MACTC,OAASC,IACLlB,KAAKmB,aAAaD,EAAOE,aACzB,MAAMC,EAASrB,KAAKsB,WACpB,GAAItB,KAAKD,SAAU,CACfC,KAAKD,SAASsB,EAAOE,QAASvB,KAAKE,SACvC,GAEJsB,WAAaN,IACTlB,KAAKmB,aAAaD,EAAOE,gBAKhCpB,KAAKK,QAAqBoB,gBAAgB,qBAAsB,KAC7D,MAAMC,EAAY1B,KAAKK,QAAqBsB,WAAW,SAEvD,MAAMC,EAAYA,KACd,GAAI5B,KAAKK,QAAS,CACdL,KAAKmB,aAAanB,KAAKK,QAC3B,GAGJ,GAAIqB,EAAU,CACVA,EAASG,aAAaD,EAC1B,CAEAA,MAGJ5B,KAAKU,sBAAsBV,KAAKK,QAASV,EAAeE,GAExD,OAAOG,KAAKK,OAChB,CAKQc,aAAaW,GACjB,MAAMC,EAAMD,EAAUE,YAAc,GAEpC,MAAMC,EAAWF,EAAIG,MAAM,cAAcC,OAGzC,MAAMC,EAAeC,KAAKC,KAAKP,EAAII,OAAS,IAE5C,MAAMI,EAAaF,KAAKG,IAAIP,EAAUG,GAGtCN,EAAUW,QAAQJ,KAAKG,IAAI,EAAGD,EAAa,GAC/C,CAMUP,WACN,OAAOhC,KAAKK,QAAWL,KAAKK,QAAqB2B,WAAa,IAClE,CAKUU,aACN,GAAI1C,KAAKK,SAAWL,KAAKC,SAAU,CAC/B,IAAKD,KAAKI,WAAY,OACtB,MAAMuC,EAAQ3C,KAAKK,QACnBsC,EAAMC,aAAa5C,KAAKC,SAAS4C,IAAIC,UACrCH,EAAMI,YAAY/C,KAAKC,SAASa,SACpC,CACJ,EACH,IAAAkC,EAAA,CAAAC,WAAA,MAAAD,EAAAvD,iBAAA,OAAAuD,CAAA","ignoreList":[]}
|
|
@@ -43,21 +43,59 @@ export class TextAreaPlugin extends BasePlugin {
|
|
|
43
43
|
value: `{${modelName}>${bindingPath}}`,
|
|
44
44
|
maxLength: fieldMetadata.maxLength || 0,
|
|
45
45
|
required: !!fieldMetadata.required,
|
|
46
|
-
rows:
|
|
46
|
+
rows: 3, // Start with at least 3 lines
|
|
47
|
+
growing: false, // We manage height manually to maintain the +2 lines requirement
|
|
47
48
|
change: (oEvent: sap.ui.base.Event) => {
|
|
48
|
-
|
|
49
|
+
this.updateHeight(oEvent.getSource() as TextArea);
|
|
49
50
|
const result = this.validate();
|
|
50
51
|
if (this.onChange) {
|
|
51
52
|
this.onChange(result.isValid, this.fieldKey);
|
|
52
53
|
}
|
|
54
|
+
},
|
|
55
|
+
liveChange: (oEvent: sap.ui.base.Event) => {
|
|
56
|
+
this.updateHeight(oEvent.getSource() as TextArea);
|
|
53
57
|
}
|
|
54
58
|
});
|
|
55
59
|
|
|
60
|
+
// Clean architecture: Wait for the model context, then calculate initial height
|
|
61
|
+
(this.control as TextArea).attachEventOnce("modelContextChange", () => {
|
|
62
|
+
const oBinding = (this.control as TextArea).getBinding("value");
|
|
63
|
+
|
|
64
|
+
const initLogic = () => {
|
|
65
|
+
if (this.control) {
|
|
66
|
+
this.updateHeight(this.control as TextArea);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (oBinding) {
|
|
71
|
+
oBinding.attachChange(initLogic);
|
|
72
|
+
}
|
|
73
|
+
// Execute immediately in case the model is synchronous (JSONModel)
|
|
74
|
+
initLogic();
|
|
75
|
+
});
|
|
76
|
+
|
|
56
77
|
this.applyCommonDirectives(this.control, fieldMetadata, modelName);
|
|
57
78
|
|
|
58
79
|
return this.control as Control;
|
|
59
80
|
}
|
|
60
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Dynamically sets the rows to the content length + 2
|
|
84
|
+
*/
|
|
85
|
+
private updateHeight(oTextArea: TextArea): void {
|
|
86
|
+
const val = oTextArea.getValue() || "";
|
|
87
|
+
// Count actual newlines in the string
|
|
88
|
+
const numLines = val.split(/\r\n|\r|\n/).length;
|
|
89
|
+
// Also estimate word wrapping if they type a really long single line without pressing enter
|
|
90
|
+
// A standard character width approximation, assuming ~80 chars per line for full width
|
|
91
|
+
const wrappedLines = Math.ceil(val.length / 80);
|
|
92
|
+
|
|
93
|
+
const totalLines = Math.max(numLines, wrappedLines);
|
|
94
|
+
|
|
95
|
+
// Ensure there is always 2 lines of extra breathing room
|
|
96
|
+
oTextArea.setRows(Math.max(3, totalLines + 2));
|
|
97
|
+
}
|
|
98
|
+
|
|
61
99
|
/**
|
|
62
100
|
* Retrieves the current text string.
|
|
63
101
|
* @returns {unknown} The text string.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
sap.ui.define([], function () {
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file ExpressionBuilder.ts
|
|
6
|
+
* @description Safely parses and builds UI5 expression bindings from JSON Schema condition rules.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
class ExpressionBuilder {
|
|
10
|
+
/**
|
|
11
|
+
* Safely converts a raw condition rule (e.g. "$root.Status === 'NEW'") into a valid UI5
|
|
12
|
+
* expression binding string, preserving relative vs absolute paths.
|
|
13
|
+
*
|
|
14
|
+
* @param rule The raw schema condition rule (e.g., "$root.Category === 'A' || SubCategory === 'B'").
|
|
15
|
+
* @param bindingPath The absolute binding path of the current field (e.g., "/Items/0/Category").
|
|
16
|
+
* @param modelName The UI5 JSONModel name (default "meta").
|
|
17
|
+
* @returns {string} The fully formed UI5 expression binding (e.g., "{= ${meta>/Status} === 'NEW' }").
|
|
18
|
+
*/
|
|
19
|
+
static build(rule, bindingPath, modelName = "meta") {
|
|
20
|
+
if (!rule) return "{= true }";
|
|
21
|
+
let parsedRule = rule;
|
|
22
|
+
|
|
23
|
+
// 1. Replace absolute $root paths
|
|
24
|
+
// $root.Status -> ${meta>/Status}
|
|
25
|
+
parsedRule = parsedRule.replace(/\$root\.([a-zA-Z0-9_.]+)/g, (match, path) => {
|
|
26
|
+
return `\${${modelName}>/${path.replace(/\./g, '/')}}`;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// 2. Replace relative paths
|
|
30
|
+
parsedRule = parsedRule.replace(/(?<!['"\${a-zA-Z0-9_.>/-])([a-zA-Z_][a-zA-Z0-9_.]+)(?!['"a-zA-Z0-9_.(])/g, (match, path) => {
|
|
31
|
+
const reserved = ["true", "false", "null", "undefined", "Math", "String", "Number", "Array", "Object"];
|
|
32
|
+
if (reserved.includes(path)) return path;
|
|
33
|
+
|
|
34
|
+
// Resolve relative to the current binding path context
|
|
35
|
+
let basePath = bindingPath.startsWith("/") ? bindingPath.substring(1) : bindingPath;
|
|
36
|
+
const parts = basePath.split('/');
|
|
37
|
+
parts.pop();
|
|
38
|
+
const contextPrefix = parts.length > 0 ? parts.join('/') + '/' : '';
|
|
39
|
+
return `\${${modelName}>/${contextPrefix}${path.replace(/\./g, '/')}}`;
|
|
40
|
+
});
|
|
41
|
+
return `{= ${parsedRule} }`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
var __exports = {
|
|
45
|
+
__esModule: true
|
|
46
|
+
};
|
|
47
|
+
__exports.ExpressionBuilder = ExpressionBuilder;
|
|
48
|
+
return __exports;
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=ExpressionBuilder-dbg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressionBuilder-dbg.js","names":["ExpressionBuilder","build","rule","bindingPath","modelName","parsedRule","replace","match","path","reserved","includes","basePath","startsWith","substring","parts","split","pop","contextPrefix","length","join","__exports","__esModule"],"sources":["ExpressionBuilder.ts"],"sourcesContent":["/**\n * @file ExpressionBuilder.ts\n * @description Safely parses and builds UI5 expression bindings from JSON Schema condition rules.\n */\n\nexport class ExpressionBuilder {\n /**\n * Safely converts a raw condition rule (e.g. \"$root.Status === 'NEW'\") into a valid UI5 \n * expression binding string, preserving relative vs absolute paths.\n * \n * @param rule The raw schema condition rule (e.g., \"$root.Category === 'A' || SubCategory === 'B'\").\n * @param bindingPath The absolute binding path of the current field (e.g., \"/Items/0/Category\").\n * @param modelName The UI5 JSONModel name (default \"meta\").\n * @returns {string} The fully formed UI5 expression binding (e.g., \"{= ${meta>/Status} === 'NEW' }\").\n */\n public static build(rule: string, bindingPath: string, modelName: string = \"meta\"): string {\n if (!rule) return \"{= true }\";\n\n let parsedRule = rule;\n\n // 1. Replace absolute $root paths\n // $root.Status -> ${meta>/Status}\n parsedRule = parsedRule.replace(/\\$root\\.([a-zA-Z0-9_.]+)/g, (match, path) => {\n return `\\${${modelName}>/${path.replace(/\\./g, '/')}}`;\n });\n\n // 2. Replace relative paths\n parsedRule = parsedRule.replace(/(?<!['\"\\${a-zA-Z0-9_.>/-])([a-zA-Z_][a-zA-Z0-9_.]+)(?!['\"a-zA-Z0-9_.(])/g, (match, path) => {\n const reserved = [\"true\", \"false\", \"null\", \"undefined\", \"Math\", \"String\", \"Number\", \"Array\", \"Object\"];\n if (reserved.includes(path)) return path;\n\n // Resolve relative to the current binding path context\n let basePath = bindingPath.startsWith(\"/\") ? bindingPath.substring(1) : bindingPath;\n const parts = basePath.split('/');\n parts.pop(); \n const contextPrefix = parts.length > 0 ? parts.join('/') + '/' : '';\n \n return `\\${${modelName}>/${contextPrefix}${path.replace(/\\./g, '/')}}`;\n });\n\n return `{= ${parsedRule} }`;\n }\n}\n"],"mappings":";;;EAAA;AACA;AACA;AACA;;EAEO,MAAMA,iBAAiB,CAAC;IAC3B;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,OAAcC,KAAKA,CAACC,IAAY,EAAEC,WAAmB,EAAEC,SAAiB,GAAG,MAAM,EAAU;MACvF,IAAI,CAACF,IAAI,EAAE,OAAO,WAAW;MAE7B,IAAIG,UAAU,GAAGH,IAAI;;MAErB;MACA;MACAG,UAAU,GAAGA,UAAU,CAACC,OAAO,CAAC,2BAA2B,EAAE,CAACC,KAAK,EAAEC,IAAI,KAAK;QAC1E,OAAO,MAAMJ,SAAS,KAAKI,IAAI,CAACF,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG;MAC1D,CAAC,CAAC;;MAEF;MACAD,UAAU,GAAGA,UAAU,CAACC,OAAO,CAAC,0EAA0E,EAAE,CAACC,KAAK,EAAEC,IAAI,KAAK;QACzH,MAAMC,QAAQ,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC;QACtG,IAAIA,QAAQ,CAACC,QAAQ,CAACF,IAAI,CAAC,EAAE,OAAOA,IAAI;;QAExC;QACA,IAAIG,QAAQ,GAAGR,WAAW,CAACS,UAAU,CAAC,GAAG,CAAC,GAAGT,WAAW,CAACU,SAAS,CAAC,CAAC,CAAC,GAAGV,WAAW;QACnF,MAAMW,KAAK,GAAGH,QAAQ,CAACI,KAAK,CAAC,GAAG,CAAC;QACjCD,KAAK,CAACE,GAAG,CAAC,CAAC;QACX,MAAMC,aAAa,GAAGH,KAAK,CAACI,MAAM,GAAG,CAAC,GAAGJ,KAAK,CAACK,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE;QAEnE,OAAO,MAAMf,SAAS,KAAKa,aAAa,GAAGT,IAAI,CAACF,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG;MAC1E,CAAC,CAAC;MAEF,OAAO,MAAMD,UAAU,IAAI;IAC/B;EACJ;EAAC,IAAAe,SAAA;IAAAC,UAAA;EAAA;EAAAD,SAAA,CAAApB,iBAAA,GAAAA,iBAAA;EAAA,OAAAoB,SAAA;AAAA","ignoreList":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare module "nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder" {
|
|
2
|
+
/**
|
|
3
|
+
* @file ExpressionBuilder.ts
|
|
4
|
+
* @description Safely parses and builds UI5 expression bindings from JSON Schema condition rules.
|
|
5
|
+
*/
|
|
6
|
+
export class ExpressionBuilder {
|
|
7
|
+
/**
|
|
8
|
+
* Safely converts a raw condition rule (e.g. "$root.Status === 'NEW'") into a valid UI5
|
|
9
|
+
* expression binding string, preserving relative vs absolute paths.
|
|
10
|
+
*
|
|
11
|
+
* @param rule The raw schema condition rule (e.g., "$root.Category === 'A' || SubCategory === 'B'").
|
|
12
|
+
* @param bindingPath The absolute binding path of the current field (e.g., "/Items/0/Category").
|
|
13
|
+
* @param modelName The UI5 JSONModel name (default "meta").
|
|
14
|
+
* @returns {string} The fully formed UI5 expression binding (e.g., "{= ${meta>/Status} === 'NEW' }").
|
|
15
|
+
*/
|
|
16
|
+
static build(rule: string, bindingPath: string, modelName?: string): string;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=ExpressionBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressionBuilder.d.ts","sourceRoot":"../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/utils/ExpressionBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,oDAAoD,CAAC;IACpE;;;OAGG;IAEH,MAAa,iBAAiB;QAC1B;;;;;;;;WAQG;eACW,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAE,MAAe,GAAG,MAAM;KA2B7F;CAEA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
sap.ui.define([],function(){"use strict";class e{static build(e,t,r="meta"){if(!e)return"{= true }";let n=e;n=n.replace(/\$root\.([a-zA-Z0-9_.]+)/g,(e,t)=>`\${${r}>/${t.replace(/\./g,"/")}}`);n=n.replace(/(?<!['"\${a-zA-Z0-9_.>/-])([a-zA-Z_][a-zA-Z0-9_.]+)(?!['"a-zA-Z0-9_.(])/g,(e,n)=>{const s=["true","false","null","undefined","Math","String","Number","Array","Object"];if(s.includes(n))return n;let u=t.startsWith("/")?t.substring(1):t;const a=u.split("/");a.pop();const i=a.length>0?a.join("/")+"/":"";return`\${${r}>/${i}${n.replace(/\./g,"/")}}`});return`{= ${n} }`}}var t={__esModule:true};t.ExpressionBuilder=e;return t});
|
|
2
|
+
//# sourceMappingURL=ExpressionBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressionBuilder.js","names":["ExpressionBuilder","build","rule","bindingPath","modelName","parsedRule","replace","match","path","reserved","includes","basePath","startsWith","substring","parts","split","pop","contextPrefix","length","join","__exports","__esModule"],"sources":["ExpressionBuilder.ts"],"sourcesContent":["/**\n * @file ExpressionBuilder.ts\n * @description Safely parses and builds UI5 expression bindings from JSON Schema condition rules.\n */\n\nexport class ExpressionBuilder {\n /**\n * Safely converts a raw condition rule (e.g. \"$root.Status === 'NEW'\") into a valid UI5 \n * expression binding string, preserving relative vs absolute paths.\n * \n * @param rule The raw schema condition rule (e.g., \"$root.Category === 'A' || SubCategory === 'B'\").\n * @param bindingPath The absolute binding path of the current field (e.g., \"/Items/0/Category\").\n * @param modelName The UI5 JSONModel name (default \"meta\").\n * @returns {string} The fully formed UI5 expression binding (e.g., \"{= ${meta>/Status} === 'NEW' }\").\n */\n public static build(rule: string, bindingPath: string, modelName: string = \"meta\"): string {\n if (!rule) return \"{= true }\";\n\n let parsedRule = rule;\n\n // 1. Replace absolute $root paths\n // $root.Status -> ${meta>/Status}\n parsedRule = parsedRule.replace(/\\$root\\.([a-zA-Z0-9_.]+)/g, (match, path) => {\n return `\\${${modelName}>/${path.replace(/\\./g, '/')}}`;\n });\n\n // 2. Replace relative paths\n parsedRule = parsedRule.replace(/(?<!['\"\\${a-zA-Z0-9_.>/-])([a-zA-Z_][a-zA-Z0-9_.]+)(?!['\"a-zA-Z0-9_.(])/g, (match, path) => {\n const reserved = [\"true\", \"false\", \"null\", \"undefined\", \"Math\", \"String\", \"Number\", \"Array\", \"Object\"];\n if (reserved.includes(path)) return path;\n\n // Resolve relative to the current binding path context\n let basePath = bindingPath.startsWith(\"/\") ? bindingPath.substring(1) : bindingPath;\n const parts = basePath.split('/');\n parts.pop(); \n const contextPrefix = parts.length > 0 ? parts.join('/') + '/' : '';\n \n return `\\${${modelName}>/${contextPrefix}${path.replace(/\\./g, '/')}}`;\n });\n\n return `{= ${parsedRule} }`;\n }\n}\n"],"mappings":"yCAKO,MAAMA,EAUT,YAAcC,CAAMC,EAAcC,EAAqBC,EAAoB,QACvE,IAAKF,EAAM,MAAO,YAElB,IAAIG,EAAaH,EAIjBG,EAAaA,EAAWC,QAAQ,4BAA6B,CAACC,EAAOC,IAC1D,MAAMJ,MAAcI,EAAKF,QAAQ,MAAO,SAInDD,EAAaA,EAAWC,QAAQ,2EAA4E,CAACC,EAAOC,KAChH,MAAMC,EAAW,CAAC,OAAQ,QAAS,OAAQ,YAAa,OAAQ,SAAU,SAAU,QAAS,UAC7F,GAAIA,EAASC,SAASF,GAAO,OAAOA,EAGpC,IAAIG,EAAWR,EAAYS,WAAW,KAAOT,EAAYU,UAAU,GAAKV,EACxE,MAAMW,EAAQH,EAASI,MAAM,KAC7BD,EAAME,MACN,MAAMC,EAAgBH,EAAMI,OAAS,EAAIJ,EAAMK,KAAK,KAAO,IAAM,GAEjE,MAAO,MAAMf,MAAca,IAAgBT,EAAKF,QAAQ,MAAO,UAGnE,MAAO,MAAMD,KACjB,EACH,IAAAe,EAAA,CAAAC,WAAA,MAAAD,EAAApB,oBAAA,OAAAoB,CAAA","ignoreList":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ExpressionBuilder.ts
|
|
3
|
+
* @description Safely parses and builds UI5 expression bindings from JSON Schema condition rules.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class ExpressionBuilder {
|
|
7
|
+
/**
|
|
8
|
+
* Safely converts a raw condition rule (e.g. "$root.Status === 'NEW'") into a valid UI5
|
|
9
|
+
* expression binding string, preserving relative vs absolute paths.
|
|
10
|
+
*
|
|
11
|
+
* @param rule The raw schema condition rule (e.g., "$root.Category === 'A' || SubCategory === 'B'").
|
|
12
|
+
* @param bindingPath The absolute binding path of the current field (e.g., "/Items/0/Category").
|
|
13
|
+
* @param modelName The UI5 JSONModel name (default "meta").
|
|
14
|
+
* @returns {string} The fully formed UI5 expression binding (e.g., "{= ${meta>/Status} === 'NEW' }").
|
|
15
|
+
*/
|
|
16
|
+
public static build(rule: string, bindingPath: string, modelName: string = "meta"): string {
|
|
17
|
+
if (!rule) return "{= true }";
|
|
18
|
+
|
|
19
|
+
let parsedRule = rule;
|
|
20
|
+
|
|
21
|
+
// 1. Replace absolute $root paths
|
|
22
|
+
// $root.Status -> ${meta>/Status}
|
|
23
|
+
parsedRule = parsedRule.replace(/\$root\.([a-zA-Z0-9_.]+)/g, (match, path) => {
|
|
24
|
+
return `\${${modelName}>/${path.replace(/\./g, '/')}}`;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 2. Replace relative paths
|
|
28
|
+
parsedRule = parsedRule.replace(/(?<!['"\${a-zA-Z0-9_.>/-])([a-zA-Z_][a-zA-Z0-9_.]+)(?!['"a-zA-Z0-9_.(])/g, (match, path) => {
|
|
29
|
+
const reserved = ["true", "false", "null", "undefined", "Math", "String", "Number", "Array", "Object"];
|
|
30
|
+
if (reserved.includes(path)) return path;
|
|
31
|
+
|
|
32
|
+
// Resolve relative to the current binding path context
|
|
33
|
+
let basePath = bindingPath.startsWith("/") ? bindingPath.substring(1) : bindingPath;
|
|
34
|
+
const parts = basePath.split('/');
|
|
35
|
+
parts.pop();
|
|
36
|
+
const contextPrefix = parts.length > 0 ? parts.join('/') + '/' : '';
|
|
37
|
+
|
|
38
|
+
return `\${${modelName}>/${contextPrefix}${path.replace(/\./g, '/')}}`;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return `{= ${parsedRule} }`;
|
|
42
|
+
}
|
|
43
|
+
}
|