@siliconst/metaui 1.4.44 → 1.4.51
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 +24 -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 +24 -0
- 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 +15 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/host/GeneratorHost-dbg.js +34 -0
- package/src/nz/co/siliconst/ui5/metaui/controls/host/GeneratorHost-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/host/GeneratorHost.d.ts +34 -0
- package/src/nz/co/siliconst/ui5/metaui/controls/host/GeneratorHost.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/host/GeneratorHost.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/host/GeneratorHost.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/controls/host/GeneratorHost.ts +25 -1
- package/src/nz/co/siliconst/ui5/metaui/core/Engine-dbg.js +31 -1
- 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 +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 +38 -0
- package/src/nz/co/siliconst/ui5/metaui/core/StateManager-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/core/StateManager.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/core/StateManager.ts +1 -1
- package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.d.ts +1 -0
- package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.ts +1 -0
- package/src/nz/co/siliconst/ui5/metaui/layouts/WizardLayout-dbg.js +327 -67
- package/src/nz/co/siliconst/ui5/metaui/layouts/WizardLayout-dbg.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/layouts/WizardLayout.d.ts +57 -15
- package/src/nz/co/siliconst/ui5/metaui/layouts/WizardLayout.d.ts.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/layouts/WizardLayout.js +1 -1
- package/src/nz/co/siliconst/ui5/metaui/layouts/WizardLayout.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/layouts/WizardLayout.ts +375 -65
- package/src/nz/co/siliconst/ui5/metaui/library-preload.js +5 -5
- package/src/nz/co/siliconst/ui5/metaui/library-preload.js.map +1 -1
- package/src/nz/co/siliconst/ui5/metaui/plugins/controls/BasePlugin-dbg.js +10 -15
- 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 +4 -0
- 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 +10 -15
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
sap.ui.define(["./PluginRegistry","sap/m/Text","../utils/Logger","./DefaultLayoutGenerator","./PipelineManager"],function(e,t,i,r,n){"use strict";const
|
|
1
|
+
sap.ui.define(["./PluginRegistry","sap/m/Text","../utils/Logger","./DefaultLayoutGenerator","sap/ui/core/Messaging","sap/ui/core/message/Message","sap/ui/core/library","./PipelineManager"],function(e,t,i,s,r,a,n,o){"use strict";const l=e["PluginRegistry"];const g=i["Logger"];const u=s["DefaultLayoutGenerator"];const c=o["GlobalPipeline"];class d{activePlugins=[];templatePlugins=[];validationLedger=new Map;activeMessages=new Map;isEditable=true;hasPartialRenderingErrors=false;useMessageManager=false;constructor(e=true,t=false,i){this.isEditable=e;this.useMessageManager=t;this.pluginRegistry=i||l.getInstance()}build(e,i,s="meta",r,a,n){this.activePlugins=[];this.activeModel=i;this.engineScopeId=a;this.onChange=(e,t,i,s)=>{if(t){this.reportSchemaError(t,e?null:i||"Invalid input")}if(n){n(e,t,i,s)}};try{const t=e.layoutStrategy||(e.type==="array"?"table":"form");const i=this.pluginRegistry.getLayout(t);return i.render(e,s,this,r)}catch(e){this.hasPartialRenderingErrors=true;const i=e.message;g.error("[MetaUI Engine] Critical error during layout generation",i);g.showErrorPopup(`Engine failed to generate the layout.\n\nDetails: ${i}`);return new t({text:"Critical Layout Error"})}}generateField(e,i,s,r=false){try{const t=this.pluginRegistry.getPlugin(e.type||"string",e.ui?.widget);if(typeof t.setEditable==="function"){t.setEditable(this.isEditable)}if(typeof t.setUseMessageManager==="function"){t.setUseMessageManager(this.useMessageManager)}if(!r){this.activePlugins.push({plugin:t,path:i})}else{this.templatePlugins.push({plugin:t,path:i})}const a=r?undefined:this.engineScopeId;const n=t.render(e,i,s,a,this.onChange,this.activeModel);return n}catch(e){this.hasPartialRenderingErrors=true;const s=e.message;g.error(`[MetaUI Engine] Failed to generate field at ${i}`,s);g.showErrorPopup(`Failed to generate field '${i}'.\n\nDetails: ${s}`);return new t({text:`[Error generating field ${i}]`})}}generateLayout(e,i,s){try{if(u.ensureLayout(e)){g.warn(`[MetaUI Engine] Missing 'uiLayout' array in sub-schema for ${s}. Synthesized a default layout mapping.`)}const t=e.layoutStrategy||(e.type==="array"?"table":"form");const r=this.pluginRegistry.getLayout(t);return r.render(e,i,this,undefined,s)}catch(e){this.hasPartialRenderingErrors=true;const i=e.message;g.error(`[MetaUI Engine] Critical error generating sub-layout for ${s}`,i);g.showErrorPopup(`Engine failed to generate a sub-layout.\n\nDetails: ${i}`);return new t({text:"Critical Sub-Layout Error"})}}validateAll(e=false){const t=[];for(const i of this.activePlugins){try{const s=i.plugin.validate();if(e){this.reportSchemaError(i.path,s.isValid?null:s.errorMessage||"Invalid input")}if(!s.isValid){s.fieldKey=s.fieldKey||i.path.replace(/^\//,"");t.push(s);g.error("[MetaUI Validation] Plugin failed validation silently:",i.path||i.plugin.constructor.name)}}catch(e){g.error(`[MetaUI Engine] Error validating plugin at ${i.path}`,e.message)}}if(this.host&&typeof this.host.getStateManager==="function"){const i=this.host.getStateManager();if(i){const s=i.extractPayload();const r=i.getSchema?i.getSchema():this.host.getParsedSchema?.();if(s&&r){const i=this.validatePayloadArrays(r,s,"");if(e){i.forEach(e=>{if(e.fieldKey){this.reportSchemaError(e.fieldKey,e.isValid?null:e.errorMessage||"Invalid input")}})}t.push(...i.filter(e=>!e.isValid))}}}return t}onArrayMutated(e){if(this.host&&typeof this.host.getStateManager==="function"){const t=this.host.getStateManager();if(t&&typeof t.clearArrayMessages==="function"){t.clearArrayMessages(e)}}}validatePayloadArrays(e,t,i){const s=[];if(!e||!t||typeof t!=="object")return s;if(e.type==="object"&&e.properties){for(const r of Object.keys(e.properties)){const a=e.properties[r];if(t[r]!==undefined){const e=i?`${i}/${r}`:r;s.push(...this.validatePayloadArrays(a,t[r],e))}}}else if(e.type==="array"&&e.items&&Array.isArray(t)){t.forEach((t,r)=>{const a=i?`${i}/${r}`:`${r}`;if(e.items.type==="object"&&e.items.properties){for(const i of Object.keys(e.items.properties)){const r=e.items.properties[i];const n=`${a}/${i}`;const o=t?t[i]:undefined;const l=[];const g={};if(r.required)l.push("required");if(r.maxLength!==undefined){l.push("maxLength");g["maxLength"]=r.maxLength}if(r.minLength!==undefined){l.push("minLength");g["minLength"]=r.minLength}if(r.pattern!==undefined){l.push("pattern");g["pattern"]={regex:r.pattern}}if(r.minimum!==undefined||r.maximum!==undefined){l.push("range");g["range"]={min:r.minimum,max:r.maximum}}const u=r.ui?.format;if(u==="email"||u==="url"||u==="iban"){l.push(u)}if(l.length>0){const e=c.executeValidation(o,l,g);if(!e.isValid){s.push({isValid:false,errorMessage:e.errorMessage,fieldKey:n,fieldLabel:r.ui?.label||i})}else{s.push({isValid:true,fieldKey:n})}}}}})}return s}getPluginByPath(e){const t=e.replace(/^\//,"");const i=this.activePlugins.find(e=>e.path.replace(/^\//,"")===t);return i?i.plugin:undefined}reportSchemaError(e,t){const i=e.replace(/^\//,"");if(!this.validationLedger.has(i)){this.validationLedger.set(i,{schemaError:null,policyError:null})}this.validationLedger.get(i).schemaError=t;this.resolveVisualState(i)}reportPolicyError(e,t){const i=e.replace(/^\//,"");if(!this.validationLedger.has(i)){this.validationLedger.set(i,{schemaError:null,policyError:null})}this.validationLedger.get(i).policyError=t;this.resolveVisualState(i)}resolveVisualState(e){const t=this.getPluginByPath(e);const i=this.validationLedger.get(e);if(!i)return;const s=i.policyError!==null||i.schemaError!==null;const o=i.policyError||i.schemaError;if(t&&typeof t.setVisualValidationState==="function"){t.setVisualValidationState(!s,s?o||undefined:undefined)}else{this.setNativeControlError(e,s?o||undefined:undefined)}if(this.useMessageManager){const i=this.activeMessages.get(e);if(i){r.removeMessages(i);this.activeMessages.delete(e)}if(s){const i=`/${e}`;const s=this.host&&typeof this.host.getStateManager==="function"?this.host.getStateManager()?.getModel():undefined;const l=typeof t?.getFieldLabel==="function"?t.getFieldLabel():undefined;const g=l||e;const u=`Field '${g}': ${o||"Validation Error"}`;const c=new a({message:u,additionalText:g!==i?g:undefined,type:n.MessageType.Error,target:i,processor:s});r.addMessages(c);this.activeMessages.set(e,c)}}}setNativeControlError(e,t){if(this.host&&this.host.generatedContent){const i=this.host.generatedContent;this.traverseAndSetError(i,e,t)}}traverseAndSetError(e,t,i){if(!e)return;const s=[e.getBindingInfo("value"),e.getBindingInfo("selectedKey"),e.getBindingInfo("dateValue"),e.getBindingInfo("selected")];for(const r of s){if(r&&r.parts&&r.parts.length>0){const s=r.parts[0].path;if(s===t||t.endsWith(s)){const r=e.getBindingContext(this.host.activeModelName);if(r){const a=r.getPath()+"/"+s;const n=a.replace(/^\//,"");if(n===t){if(typeof e.setValueState==="function"){e.setValueState(i?"Error":"None");if(typeof e.setValueStateText==="function"){e.setValueStateText(i||"")}}}}}}}if(typeof e.getAggregation==="function"&&typeof e.getMetadata==="function"){const s=e.getMetadata().getAggregations();for(const r in s){const s=e.getAggregation(r);if(Array.isArray(s)){s.forEach(e=>this.traverseAndSetError(e,t,i))}else if(s){this.traverseAndSetError(s,t,i)}}}}destroy(){for(const e of this.activePlugins){if(typeof e.plugin.destroy==="function"){e.plugin.destroy()}}for(const e of this.templatePlugins){if(typeof e.plugin.destroy==="function"){e.plugin.destroy()}}if(this.useMessageManager&&this.activeMessages.size>0){r.removeMessages(Array.from(this.activeMessages.values()))}this.validationLedger.clear();this.activeMessages.clear();this.activePlugins=[];this.templatePlugins=[]}}var h={__esModule:true};h.Engine=d;return h});
|
|
2
2
|
//# sourceMappingURL=Engine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.js","names":["PluginRegistry","___PluginRegistry","Logger","___utils_Logger","DefaultLayoutGenerator","___DefaultLayoutGenerator","GlobalPipeline","___PipelineManager","Engine","activePlugins","templatePlugins","validationLedger","Map","isEditable","hasPartialRenderingErrors","useMessageManager","constructor","editable","pluginRegistry","this","getInstance","build","schema","model","modelName","onSubmit","engineScopeId","onChange","activeModel","isValid","fieldKey","errorMessage","controlId","reportSchemaError","layoutStrategy","type","layoutManager","getLayout","render","error","msg","message","showErrorPopup","Text","text","generateField","fieldMeta","bindingPath","isTemplate","plugin","getPlugin","ui","widget","setEditable","setUseMessageManager","push","path","scopeId","undefined","control","generateLayout","ensureLayout","warn","validateAll","applyVisualState","errors","item","result","validate","replace","name","host","getStateManager","stateManager","payload","extractPayload","getSchema","getParsedSchema","arrayErrors","validatePayloadArrays","forEach","err","filter","e","onArrayMutated","arrayPath","clearArrayMessages","currentPath","properties","key","Object","keys","propSchema","childPath","items","Array","isArray","index","itemPath","propMeta","propPath","val","validatorsToRun","argsMap","required","maxLength","minLength","pattern","regex","minimum","maximum","min","max","format","length","res","executeValidation","fieldLabel","label","getPluginByPath","cleanPath","match","find","p","fieldPath","has","set","schemaError","policyError","get","resolveVisualState","reportPolicyError","ledgerEntry","isError","setVisualValidationState","setNativeControlError","targetPath","generatedContent","rootControl","traverseAndSetError","bindingInfos","getBindingInfo","info","parts","boundPath","endsWith","context","getBindingContext","activeModelName","fullPath","getPath","cleanedFullPath","setValueState","setValueStateText","getAggregation","getMetadata","aggs","getAggregations","aggName","child","c","destroy","clear","__exports","__esModule"],"sources":["Engine.ts"],"sourcesContent":["/**\n * @file Engine.ts\n * @description The central orchestrator that routes data between the Layout Factory and Plugin Registry (v2).\n */\n\nimport { ISchema, IPropertyMetadata } from \"../interfaces/ISchema\";\nimport { ILayoutManager } from \"../interfaces/ILayoutManager\";\nimport { IPlugin, IPluginValidationResult } from \"../interfaces/IPlugin\";\n\nimport { PluginRegistry } from \"./PluginRegistry\";\nimport Control from \"sap/ui/core/Control\";\nimport VBox from \"sap/m/VBox\";\nimport Text from \"sap/m/Text\";\nimport { Logger } from \"../utils/Logger\";\nimport { DefaultLayoutGenerator } from \"./DefaultLayoutGenerator\";\nimport Core from \"sap/ui/core/Core\";\nimport Messaging from \"sap/ui/core/Messaging\";\nimport { GlobalPipeline } from \"./PipelineManager\";\n\n/**\n * The Engine is responsible for translating a normalized schema into a physical UI5 layout.\n * It delegates layout generation to the configured `LayoutManager` and field generation to the `PluginRegistry`.\n * It strictly routes execution flow and tracks active plugins for validation.\n * It embodies the \"Plugin-First Philosophy\" by routing all complex field and layout rendering\n * through discrete, decoupled plugins rather than handling native overrides.\n * \n * @namespace nz.co.siliconst.ui5.metaui.core\n * @public\n */\nexport class Engine {\n\n\n /** Array of all instantiated plugins generated during the current build. */\n private activePlugins: { plugin: IPlugin, path: string }[] = [];\n\n /** Array of template plugins generated for table rows to prevent memory leaks during destruction. */\n private templatePlugins: { plugin: IPlugin, path: string }[] = [];\n\n /** Deterministic scope ID injected by the host control to prevent clone collisions. */\n public engineScopeId?: string;\n\n private activeModel?: unknown;\n\n /** Ledger tracking local vs cross-field errors to prevent visual race conditions. */\n private validationLedger: Map<string, { schemaError: string | null, policyError: string | null }> = new Map();\n\n /** Reference to the GeneratorHost that instantiated this engine, for retrieving global definitions. */\n public host?: unknown;\n\n /** Callback to notify the host that an internal field changed. */\n public onChange?: (isValid: boolean, fieldKey?: string, errorMessage?: string, controlId?: string) => void;\n\n /** Tracks whether the entire engine is running in Editable mode. */\n public readonly isEditable: boolean = true;\n\n /** Tracks if any fields or sub-layouts failed to render, requiring a warning strip. */\n public hasPartialRenderingErrors: boolean = false;\n\n /** Tracks whether the engine should delegate visual state to the global MessageManager. */\n public readonly useMessageManager: boolean = false;\n\n /** The injected PluginRegistry instance */\n public readonly pluginRegistry: PluginRegistry;\n\n /**\n * Initializes a new Engine instance.\n * @param editable Whether the engine should render form fields as editable or read-only.\n * @param useMessageManager Whether to delegate visual state to UI5 MessageManager.\n * @param pluginRegistry Optional injected plugin registry. Defaults to singleton.\n */\n constructor(editable: boolean = true, useMessageManager: boolean = false, pluginRegistry?: PluginRegistry) {\n this.isEditable = editable;\n this.useMessageManager = useMessageManager;\n this.pluginRegistry = pluginRegistry || PluginRegistry.getInstance();\n }\n\n /**\n * Bootstraps the layout generation process by resolving the correct layout strategy.\n * \n * @param schema The normalized JSON schema representing the view.\n * @param model The active StateManager JSONModel instance.\n * @param modelName The UI5 model alias used for isolation.\n * @param onSubmit Callback fired when form is submitted.\n * @param engineScopeId Deterministic scope ID for this engine instance.\n * @param onChange Callback fired when field values change.\n * @returns The generated root UI5 Control container.\n */\n public build(schema: ISchema, model: unknown, modelName: string = \"meta\", onSubmit?: () => void, engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string, errorMessage?: string, controlId?: string) => void): Control {\n\n this.activePlugins = [];\n this.activeModel = model;\n this.engineScopeId = engineScopeId;\n this.onChange = (isValid: boolean, fieldKey?: string, errorMessage?: string, controlId?: string) => {\n if (fieldKey) {\n this.reportSchemaError(fieldKey, isValid ? null : errorMessage || \"Invalid input\");\n }\n\n if (onChange) {\n onChange(isValid, fieldKey, errorMessage, controlId);\n }\n };\n\n try {\n const layoutStrategy = schema.layoutStrategy || (schema.type === \"array\" ? \"table\" : \"form\");\n const layoutManager = this.pluginRegistry.getLayout(layoutStrategy);\n\n return layoutManager.render(schema, modelName, this, onSubmit);\n } catch (error) {\n this.hasPartialRenderingErrors = true;\n const msg = (error as Error).message;\n Logger.error(\"[MetaUI Engine] Critical error during layout generation\", msg);\n Logger.showErrorPopup(`Engine failed to generate the layout.\\n\\nDetails: ${msg}`);\n return new Text({ text: \"Critical Layout Error\" });\n }\n }\n\n /**\n * Instantiates the correct plugin for a specific field based on type and widget hints,\n * and tracks the plugin for global validation.\n * \n * @param fieldMeta The metadata schema specific to the field.\n * @param bindingPath The JSON path representing the field within the payload.\n * @param modelName The UI5 JSONModel name used for data binding.\n * @returns The generated UI5 Control for the field.\n */\n public generateField(fieldMeta: IPropertyMetadata, bindingPath: string, modelName: string, isTemplate: boolean = false): Control {\n try {\n const plugin = this.pluginRegistry.getPlugin(fieldMeta.type || \"string\", fieldMeta.ui?.widget);\n if (typeof plugin.setEditable === \"function\") {\n plugin.setEditable(this.isEditable);\n }\n\n if (typeof plugin.setUseMessageManager === \"function\") {\n plugin.setUseMessageManager(this.useMessageManager);\n }\n\n if (!isTemplate) {\n this.activePlugins.push({ plugin, path: bindingPath });\n } else {\n this.templatePlugins.push({ plugin, path: bindingPath });\n }\n const scopeId = isTemplate ? undefined : this.engineScopeId;\n const control = plugin.render(fieldMeta, bindingPath, modelName, scopeId, this.onChange, this.activeModel);\n\n\n\n return control;\n } catch (error) {\n this.hasPartialRenderingErrors = true;\n const msg = (error as Error).message;\n Logger.error(`[MetaUI Engine] Failed to generate field at ${bindingPath}`, msg);\n Logger.showErrorPopup(`Failed to generate field '${bindingPath}'.\\n\\nDetails: ${msg}`);\n return new Text({ text: `[Error generating field ${bindingPath}]` });\n }\n }\n\n /**\n * Generates a sub-layout (like an embedded table or nested form) without \n * destroying the current Engine state or Condition tracking.\n * \n * @param schema The normalized JSON schema representing the view.\n * @param modelName The UI5 JSONModel name used for data binding (defaults to \"meta\").\n * @param bindingPath Optional path to bind the sub-layout to a specific array/object property.\n * @returns The generated root UI5 Control container.\n */\n public generateLayout(schema: ISchema, modelName: string, bindingPath?: string): Control {\n try {\n if (DefaultLayoutGenerator.ensureLayout(schema)) {\n Logger.warn(`[MetaUI Engine] Missing 'uiLayout' array in sub-schema for ${bindingPath}. Synthesized a default layout mapping.`);\n }\n\n const layoutStrategy = schema.layoutStrategy || (schema.type === \"array\" ? \"table\" : \"form\");\n const layoutManager = this.pluginRegistry.getLayout(layoutStrategy);\n return layoutManager.render(schema, modelName, this, undefined, bindingPath);\n } catch (error) {\n this.hasPartialRenderingErrors = true;\n const msg = (error as Error).message;\n Logger.error(`[MetaUI Engine] Critical error generating sub-layout for ${bindingPath}`, msg);\n Logger.showErrorPopup(`Engine failed to generate a sub-layout.\\n\\nDetails: ${msg}`);\n return new Text({ text: \"Critical Sub-Layout Error\" });\n }\n }\n\n /**\n * Iterates through all active plugins and triggers their internal validation pipelines.\n * \n * @param applyVisualState If true, explicitly instructs the plugin to natively paint its own ValueState.\n * @returns {IPluginValidationResult[]} Array of validation error objects. Empty array if valid.\n */\n public validateAll(applyVisualState: boolean = false): IPluginValidationResult[] {\n const errors: IPluginValidationResult[] = [];\n for (const item of this.activePlugins) {\n try {\n const result = item.plugin.validate();\n if (applyVisualState) {\n this.reportSchemaError(item.path, result.isValid ? null : result.errorMessage || \"Invalid input\");\n }\n\n if (!result.isValid) {\n // Ensure the path is bound to the error so GeneratorHost knows where to target it\n result.fieldKey = result.fieldKey || item.path.replace(/^\\//, \"\");\n errors.push(result);\n // Add console log to find which plugin fails silently\n Logger.error(\"[MetaUI Validation] Plugin failed validation silently:\", item.path || item.plugin.constructor.name);\n }\n } catch (error) {\n Logger.error(`[MetaUI Engine] Error validating plugin at ${item.path}`, (error as Error).message);\n }\n }\n\n // 2. Recursively validate arrays via StateManager payload traversing for Table layout rows\n if (this.host && typeof (this.host as any).getStateManager === \"function\") {\n const stateManager = (this.host as any).getStateManager();\n if (stateManager) {\n const payload = stateManager.extractPayload();\n const schema = stateManager.getSchema ? stateManager.getSchema() : (this.host as any).getParsedSchema?.();\n if (payload && schema) {\n const arrayErrors = this.validatePayloadArrays(schema, payload, \"\");\n if (applyVisualState) {\n arrayErrors.forEach(err => {\n if (err.fieldKey) {\n this.reportSchemaError(err.fieldKey, err.isValid ? null : err.errorMessage || \"Invalid input\");\n }\n });\n }\n errors.push(...arrayErrors.filter(e => !e.isValid));\n }\n }\n }\n\n return errors;\n }\n\n public onArrayMutated(arrayPath: string): void {\n if (this.host && typeof (this.host as any).getStateManager === \"function\") {\n const stateManager = (this.host as any).getStateManager();\n if (stateManager && typeof stateManager.clearArrayMessages === \"function\") {\n stateManager.clearArrayMessages(arrayPath);\n }\n }\n }\n\n private validatePayloadArrays(schema: any, payload: any, currentPath: string): IPluginValidationResult[] {\n const errors: IPluginValidationResult[] = [];\n if (!schema || !payload || typeof payload !== \"object\") return errors;\n\n if (schema.type === \"object\" && schema.properties) {\n for (const key of Object.keys(schema.properties)) {\n const propSchema = schema.properties[key];\n if (payload[key] !== undefined) {\n const childPath = currentPath ? `${currentPath}/${key}` : key;\n errors.push(...this.validatePayloadArrays(propSchema, payload[key], childPath));\n }\n }\n } else if (schema.type === \"array\" && schema.items && Array.isArray(payload)) {\n payload.forEach((item, index) => {\n const itemPath = currentPath ? `${currentPath}/${index}` : `${index}`;\n if (schema.items.type === \"object\" && schema.items.properties) {\n for (const key of Object.keys(schema.items.properties)) {\n const propMeta = schema.items.properties[key];\n const propPath = `${itemPath}/${key}`;\n const val = item ? item[key] : undefined;\n \n const validatorsToRun: string[] = [];\n const argsMap: Record<string, unknown> = {};\n\n if (propMeta.required) validatorsToRun.push(\"required\");\n if (propMeta.maxLength !== undefined) {\n validatorsToRun.push(\"maxLength\");\n argsMap[\"maxLength\"] = propMeta.maxLength;\n }\n if (propMeta.minLength !== undefined) {\n validatorsToRun.push(\"minLength\");\n argsMap[\"minLength\"] = propMeta.minLength;\n }\n if (propMeta.pattern !== undefined) {\n validatorsToRun.push(\"pattern\");\n argsMap[\"pattern\"] = { regex: propMeta.pattern };\n }\n if (propMeta.minimum !== undefined || propMeta.maximum !== undefined) {\n validatorsToRun.push(\"range\");\n argsMap[\"range\"] = { min: propMeta.minimum, max: propMeta.maximum };\n }\n const format = propMeta.ui?.format;\n if (format === \"email\" || format === \"url\" || format === \"iban\") {\n validatorsToRun.push(format);\n }\n \n if (validatorsToRun.length > 0) {\n const res = GlobalPipeline.executeValidation(val, validatorsToRun, argsMap);\n if (!res.isValid) {\n errors.push({\n isValid: false,\n errorMessage: res.errorMessage,\n fieldKey: propPath,\n fieldLabel: propMeta.ui?.label || key\n });\n } else {\n errors.push({ isValid: true, fieldKey: propPath }); // Push successful state to clear old errors\n }\n }\n }\n }\n });\n }\n return errors;\n }\n\n /**\n * Retrieves an active plugin instance by its absolute binding path.\n * Useful for programmatic field manipulation (e.g., custom error injection).\n * \n * @param path The binding path (e.g. \"/General/CustomerName\").\n * @returns {IPlugin | undefined} The plugin instance, or undefined if not found.\n */\n public getPluginByPath(path: string): import(\"../interfaces/IPlugin\").IPlugin | undefined {\n const cleanPath = path.replace(/^\\//, \"\");\n const match = this.activePlugins.find(p => p.path.replace(/^\\//, \"\") === cleanPath);\n return match ? match.plugin : undefined;\n }\n\n /**\n * Reports a localized schema validation error to the coordinator ledger.\n */\n public reportSchemaError(fieldPath: string, errorMessage: string | null): void {\n const cleanPath = fieldPath.replace(/^\\//, \"\");\n if (!this.validationLedger.has(cleanPath)) {\n this.validationLedger.set(cleanPath, { schemaError: null, policyError: null });\n }\n this.validationLedger.get(cleanPath)!.schemaError = errorMessage;\n this.resolveVisualState(cleanPath);\n }\n\n /**\n * Reports a cross-field policy error to the coordinator ledger.\n */\n public reportPolicyError(fieldPath: string, errorMessage: string | null): void {\n const cleanPath = fieldPath.replace(/^\\//, \"\");\n if (!this.validationLedger.has(cleanPath)) {\n this.validationLedger.set(cleanPath, { schemaError: null, policyError: null });\n }\n this.validationLedger.get(cleanPath)!.policyError = errorMessage;\n this.resolveVisualState(cleanPath);\n }\n\n /**\n * Computes the final visual state by checking both engines and applies it to the plugin.\n */\n private resolveVisualState(cleanPath: string): void {\n const plugin = this.getPluginByPath(cleanPath);\n const ledgerEntry = this.validationLedger.get(cleanPath);\n if (!ledgerEntry) return;\n\n const isError = ledgerEntry.policyError !== null || ledgerEntry.schemaError !== null;\n const errorMessage = ledgerEntry.policyError || ledgerEntry.schemaError;\n\n if (plugin && typeof plugin.setVisualValidationState === \"function\") {\n plugin.setVisualValidationState(!isError, isError ? errorMessage || undefined : undefined);\n } else {\n // Un-tracked template clones (Table Layout rows)\n this.setNativeControlError(cleanPath, isError ? errorMessage || undefined : undefined);\n }\n }\n\n private setNativeControlError(targetPath: string, errorMessage?: string): void {\n if (this.host && (this.host as any).generatedContent) {\n const rootControl = (this.host as any).generatedContent;\n this.traverseAndSetError(rootControl, targetPath, errorMessage);\n }\n }\n\n private traverseAndSetError(control: any, targetPath: string, errorMessage?: string): void {\n if (!control) return;\n \n const bindingInfos = [control.getBindingInfo(\"value\"), control.getBindingInfo(\"selectedKey\"), control.getBindingInfo(\"dateValue\"), control.getBindingInfo(\"selected\")];\n for (const info of bindingInfos) {\n if (info && info.parts && info.parts.length > 0) {\n const boundPath = info.parts[0].path;\n // UI5 relative bindings drop the leading slash in the context, but retain it in the path\n if (boundPath === targetPath || targetPath.endsWith(boundPath)) {\n // Ensure we match the exact row context path\n const context = control.getBindingContext((this.host as any).activeModelName);\n if (context) {\n const fullPath = context.getPath() + \"/\" + boundPath;\n const cleanedFullPath = fullPath.replace(/^\\//, \"\");\n if (cleanedFullPath === targetPath) {\n if (typeof control.setValueState === \"function\") {\n control.setValueState(errorMessage ? \"Error\" : \"None\");\n if (typeof control.setValueStateText === \"function\") {\n control.setValueStateText(errorMessage || \"\");\n }\n }\n }\n }\n }\n }\n }\n \n if (typeof control.getAggregation === \"function\" && typeof control.getMetadata === \"function\") {\n const aggs = control.getMetadata().getAggregations();\n for (const aggName in aggs) {\n const child = control.getAggregation(aggName);\n if (Array.isArray(child)) {\n child.forEach(c => this.traverseAndSetError(c, targetPath, errorMessage));\n } else if (child) {\n this.traverseAndSetError(child, targetPath, errorMessage);\n }\n }\n }\n }\n\n /**\n * Cleans up internal state and destroys the condition engine to prevent memory leaks.\n */\n public destroy(): void {\n\n\n for (const item of this.activePlugins) {\n if (typeof item.plugin.destroy === \"function\") {\n item.plugin.destroy();\n }\n }\n\n for (const item of this.templatePlugins) {\n if (typeof item.plugin.destroy === \"function\") {\n item.plugin.destroy();\n }\n }\n\n this.validationLedger.clear();\n this.activePlugins = [];\n this.templatePlugins = [];\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"Engine.js","names":["PluginRegistry","___PluginRegistry","Logger","___utils_Logger","DefaultLayoutGenerator","___DefaultLayoutGenerator","GlobalPipeline","___PipelineManager","Engine","activePlugins","templatePlugins","validationLedger","Map","activeMessages","isEditable","hasPartialRenderingErrors","useMessageManager","constructor","editable","pluginRegistry","this","getInstance","build","schema","model","modelName","onSubmit","engineScopeId","onChange","activeModel","isValid","fieldKey","errorMessage","controlId","reportSchemaError","layoutStrategy","type","layoutManager","getLayout","render","error","msg","message","showErrorPopup","Text","text","generateField","fieldMeta","bindingPath","isTemplate","plugin","getPlugin","ui","widget","setEditable","setUseMessageManager","push","path","scopeId","undefined","control","generateLayout","ensureLayout","warn","validateAll","applyVisualState","errors","item","result","validate","replace","name","host","getStateManager","stateManager","payload","extractPayload","getSchema","getParsedSchema","arrayErrors","validatePayloadArrays","forEach","err","filter","e","onArrayMutated","arrayPath","clearArrayMessages","currentPath","properties","key","Object","keys","propSchema","childPath","items","Array","isArray","index","itemPath","propMeta","propPath","val","validatorsToRun","argsMap","required","maxLength","minLength","pattern","regex","minimum","maximum","min","max","format","length","res","executeValidation","fieldLabel","label","getPluginByPath","cleanPath","match","find","p","fieldPath","has","set","schemaError","policyError","get","resolveVisualState","reportPolicyError","ledgerEntry","isError","setVisualValidationState","setNativeControlError","currentMsg","Messaging","removeMessages","delete","targetPath","processorModel","getModel","getFieldLabel","displayLabel","displayText","newMsg","Message","additionalText","coreLibrary","MessageType","Error","target","processor","addMessages","generatedContent","rootControl","traverseAndSetError","bindingInfos","getBindingInfo","info","parts","boundPath","endsWith","context","getBindingContext","activeModelName","fullPath","getPath","cleanedFullPath","setValueState","setValueStateText","getAggregation","getMetadata","aggs","getAggregations","aggName","child","c","destroy","size","from","values","clear","__exports","__esModule"],"sources":["Engine.ts"],"sourcesContent":["/**\n * @file Engine.ts\n * @description The central orchestrator that routes data between the Layout Factory and Plugin Registry (v2).\n */\n\nimport { ISchema, IPropertyMetadata } from \"../interfaces/ISchema\";\nimport { ILayoutManager } from \"../interfaces/ILayoutManager\";\nimport { IPlugin, IPluginValidationResult } from \"../interfaces/IPlugin\";\n\nimport { PluginRegistry } from \"./PluginRegistry\";\nimport Control from \"sap/ui/core/Control\";\nimport VBox from \"sap/m/VBox\";\nimport Text from \"sap/m/Text\";\nimport { Logger } from \"../utils/Logger\";\nimport { DefaultLayoutGenerator } from \"./DefaultLayoutGenerator\";\nimport Core from \"sap/ui/core/Core\";\nimport Messaging from \"sap/ui/core/Messaging\";\nimport Message from \"sap/ui/core/message/Message\";\nimport coreLibrary from \"sap/ui/core/library\";\nimport { GlobalPipeline } from \"./PipelineManager\";\n\n/**\n * The Engine is responsible for translating a normalized schema into a physical UI5 layout.\n * It delegates layout generation to the configured `LayoutManager` and field generation to the `PluginRegistry`.\n * It strictly routes execution flow and tracks active plugins for validation.\n * It embodies the \"Plugin-First Philosophy\" by routing all complex field and layout rendering\n * through discrete, decoupled plugins rather than handling native overrides.\n * \n * @namespace nz.co.siliconst.ui5.metaui.core\n * @public\n */\nexport class Engine {\n\n\n /** Array of all instantiated plugins generated during the current build. */\n private activePlugins: { plugin: IPlugin, path: string }[] = [];\n\n /** Array of template plugins generated for table rows to prevent memory leaks during destruction. */\n private templatePlugins: { plugin: IPlugin, path: string }[] = [];\n\n /** Deterministic scope ID injected by the host control to prevent clone collisions. */\n public engineScopeId?: string;\n\n private activeModel?: unknown;\n\n /** Ledger tracking local vs cross-field errors to prevent visual race conditions. */\n private validationLedger: Map<string, { schemaError: string | null, policyError: string | null }> = new Map();\n\n /** Tracks active MessageManager messages created by this engine instance. */\n private activeMessages: Map<string, Message> = new Map();\n\n /** Reference to the GeneratorHost that instantiated this engine, for retrieving global definitions. */\n public host?: unknown;\n\n /** Callback to notify the host that an internal field changed. */\n public onChange?: (isValid: boolean, fieldKey?: string, errorMessage?: string, controlId?: string) => void;\n\n /** Tracks whether the entire engine is running in Editable mode. */\n public readonly isEditable: boolean = true;\n\n /** Tracks if any fields or sub-layouts failed to render, requiring a warning strip. */\n public hasPartialRenderingErrors: boolean = false;\n\n /** Tracks whether the engine should delegate visual state to the global MessageManager. */\n public readonly useMessageManager: boolean = false;\n\n /** The injected PluginRegistry instance */\n public readonly pluginRegistry: PluginRegistry;\n\n /**\n * Initializes a new Engine instance.\n * @param editable Whether the engine should render form fields as editable or read-only.\n * @param useMessageManager Whether to delegate visual state to UI5 MessageManager.\n * @param pluginRegistry Optional injected plugin registry. Defaults to singleton.\n */\n constructor(editable: boolean = true, useMessageManager: boolean = false, pluginRegistry?: PluginRegistry) {\n this.isEditable = editable;\n this.useMessageManager = useMessageManager;\n this.pluginRegistry = pluginRegistry || PluginRegistry.getInstance();\n }\n\n /**\n * Bootstraps the layout generation process by resolving the correct layout strategy.\n * \n * @param schema The normalized JSON schema representing the view.\n * @param model The active StateManager JSONModel instance.\n * @param modelName The UI5 model alias used for isolation.\n * @param onSubmit Callback fired when form is submitted.\n * @param engineScopeId Deterministic scope ID for this engine instance.\n * @param onChange Callback fired when field values change.\n * @returns The generated root UI5 Control container.\n */\n public build(schema: ISchema, model: unknown, modelName: string = \"meta\", onSubmit?: () => void, engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string, errorMessage?: string, controlId?: string) => void): Control {\n\n this.activePlugins = [];\n this.activeModel = model;\n this.engineScopeId = engineScopeId;\n this.onChange = (isValid: boolean, fieldKey?: string, errorMessage?: string, controlId?: string) => {\n if (fieldKey) {\n this.reportSchemaError(fieldKey, isValid ? null : errorMessage || \"Invalid input\");\n }\n\n if (onChange) {\n onChange(isValid, fieldKey, errorMessage, controlId);\n }\n };\n\n try {\n const layoutStrategy = schema.layoutStrategy || (schema.type === \"array\" ? \"table\" : \"form\");\n const layoutManager = this.pluginRegistry.getLayout(layoutStrategy);\n\n return layoutManager.render(schema, modelName, this, onSubmit);\n } catch (error) {\n this.hasPartialRenderingErrors = true;\n const msg = (error as Error).message;\n Logger.error(\"[MetaUI Engine] Critical error during layout generation\", msg);\n Logger.showErrorPopup(`Engine failed to generate the layout.\\n\\nDetails: ${msg}`);\n return new Text({ text: \"Critical Layout Error\" });\n }\n }\n\n /**\n * Instantiates the correct plugin for a specific field based on type and widget hints,\n * and tracks the plugin for global validation.\n * \n * @param fieldMeta The metadata schema specific to the field.\n * @param bindingPath The JSON path representing the field within the payload.\n * @param modelName The UI5 JSONModel name used for data binding.\n * @returns The generated UI5 Control for the field.\n */\n public generateField(fieldMeta: IPropertyMetadata, bindingPath: string, modelName: string, isTemplate: boolean = false): Control {\n try {\n const plugin = this.pluginRegistry.getPlugin(fieldMeta.type || \"string\", fieldMeta.ui?.widget);\n if (typeof plugin.setEditable === \"function\") {\n plugin.setEditable(this.isEditable);\n }\n\n if (typeof plugin.setUseMessageManager === \"function\") {\n plugin.setUseMessageManager(this.useMessageManager);\n }\n\n if (!isTemplate) {\n this.activePlugins.push({ plugin, path: bindingPath });\n } else {\n this.templatePlugins.push({ plugin, path: bindingPath });\n }\n const scopeId = isTemplate ? undefined : this.engineScopeId;\n const control = plugin.render(fieldMeta, bindingPath, modelName, scopeId, this.onChange, this.activeModel);\n\n\n\n return control;\n } catch (error) {\n this.hasPartialRenderingErrors = true;\n const msg = (error as Error).message;\n Logger.error(`[MetaUI Engine] Failed to generate field at ${bindingPath}`, msg);\n Logger.showErrorPopup(`Failed to generate field '${bindingPath}'.\\n\\nDetails: ${msg}`);\n return new Text({ text: `[Error generating field ${bindingPath}]` });\n }\n }\n\n /**\n * Generates a sub-layout (like an embedded table or nested form) without \n * destroying the current Engine state or Condition tracking.\n * \n * @param schema The normalized JSON schema representing the view.\n * @param modelName The UI5 JSONModel name used for data binding (defaults to \"meta\").\n * @param bindingPath Optional path to bind the sub-layout to a specific array/object property.\n * @returns The generated root UI5 Control container.\n */\n public generateLayout(schema: ISchema, modelName: string, bindingPath?: string): Control {\n try {\n if (DefaultLayoutGenerator.ensureLayout(schema)) {\n Logger.warn(`[MetaUI Engine] Missing 'uiLayout' array in sub-schema for ${bindingPath}. Synthesized a default layout mapping.`);\n }\n\n const layoutStrategy = schema.layoutStrategy || (schema.type === \"array\" ? \"table\" : \"form\");\n const layoutManager = this.pluginRegistry.getLayout(layoutStrategy);\n return layoutManager.render(schema, modelName, this, undefined, bindingPath);\n } catch (error) {\n this.hasPartialRenderingErrors = true;\n const msg = (error as Error).message;\n Logger.error(`[MetaUI Engine] Critical error generating sub-layout for ${bindingPath}`, msg);\n Logger.showErrorPopup(`Engine failed to generate a sub-layout.\\n\\nDetails: ${msg}`);\n return new Text({ text: \"Critical Sub-Layout Error\" });\n }\n }\n\n /**\n * Iterates through all active plugins and triggers their internal validation pipelines.\n * \n * @param applyVisualState If true, explicitly instructs the plugin to natively paint its own ValueState.\n * @returns {IPluginValidationResult[]} Array of validation error objects. Empty array if valid.\n */\n public validateAll(applyVisualState: boolean = false): IPluginValidationResult[] {\n const errors: IPluginValidationResult[] = [];\n for (const item of this.activePlugins) {\n try {\n const result = item.plugin.validate();\n if (applyVisualState) {\n this.reportSchemaError(item.path, result.isValid ? null : result.errorMessage || \"Invalid input\");\n }\n\n if (!result.isValid) {\n // Ensure the path is bound to the error so GeneratorHost knows where to target it\n result.fieldKey = result.fieldKey || item.path.replace(/^\\//, \"\");\n errors.push(result);\n // Add console log to find which plugin fails silently\n Logger.error(\"[MetaUI Validation] Plugin failed validation silently:\", item.path || item.plugin.constructor.name);\n }\n } catch (error) {\n Logger.error(`[MetaUI Engine] Error validating plugin at ${item.path}`, (error as Error).message);\n }\n }\n\n // 2. Recursively validate arrays via StateManager payload traversing for Table layout rows\n if (this.host && typeof (this.host as any).getStateManager === \"function\") {\n const stateManager = (this.host as any).getStateManager();\n if (stateManager) {\n const payload = stateManager.extractPayload();\n const schema = stateManager.getSchema ? stateManager.getSchema() : (this.host as any).getParsedSchema?.();\n if (payload && schema) {\n const arrayErrors = this.validatePayloadArrays(schema, payload, \"\");\n if (applyVisualState) {\n arrayErrors.forEach(err => {\n if (err.fieldKey) {\n this.reportSchemaError(err.fieldKey, err.isValid ? null : err.errorMessage || \"Invalid input\");\n }\n });\n }\n errors.push(...arrayErrors.filter(e => !e.isValid));\n }\n }\n }\n\n return errors;\n }\n\n public onArrayMutated(arrayPath: string): void {\n if (this.host && typeof (this.host as any).getStateManager === \"function\") {\n const stateManager = (this.host as any).getStateManager();\n if (stateManager && typeof stateManager.clearArrayMessages === \"function\") {\n stateManager.clearArrayMessages(arrayPath);\n }\n }\n }\n\n private validatePayloadArrays(schema: any, payload: any, currentPath: string): IPluginValidationResult[] {\n const errors: IPluginValidationResult[] = [];\n if (!schema || !payload || typeof payload !== \"object\") return errors;\n\n if (schema.type === \"object\" && schema.properties) {\n for (const key of Object.keys(schema.properties)) {\n const propSchema = schema.properties[key];\n if (payload[key] !== undefined) {\n const childPath = currentPath ? `${currentPath}/${key}` : key;\n errors.push(...this.validatePayloadArrays(propSchema, payload[key], childPath));\n }\n }\n } else if (schema.type === \"array\" && schema.items && Array.isArray(payload)) {\n payload.forEach((item, index) => {\n const itemPath = currentPath ? `${currentPath}/${index}` : `${index}`;\n if (schema.items.type === \"object\" && schema.items.properties) {\n for (const key of Object.keys(schema.items.properties)) {\n const propMeta = schema.items.properties[key];\n const propPath = `${itemPath}/${key}`;\n const val = item ? item[key] : undefined;\n \n const validatorsToRun: string[] = [];\n const argsMap: Record<string, unknown> = {};\n\n if (propMeta.required) validatorsToRun.push(\"required\");\n if (propMeta.maxLength !== undefined) {\n validatorsToRun.push(\"maxLength\");\n argsMap[\"maxLength\"] = propMeta.maxLength;\n }\n if (propMeta.minLength !== undefined) {\n validatorsToRun.push(\"minLength\");\n argsMap[\"minLength\"] = propMeta.minLength;\n }\n if (propMeta.pattern !== undefined) {\n validatorsToRun.push(\"pattern\");\n argsMap[\"pattern\"] = { regex: propMeta.pattern };\n }\n if (propMeta.minimum !== undefined || propMeta.maximum !== undefined) {\n validatorsToRun.push(\"range\");\n argsMap[\"range\"] = { min: propMeta.minimum, max: propMeta.maximum };\n }\n const format = propMeta.ui?.format;\n if (format === \"email\" || format === \"url\" || format === \"iban\") {\n validatorsToRun.push(format);\n }\n \n if (validatorsToRun.length > 0) {\n const res = GlobalPipeline.executeValidation(val, validatorsToRun, argsMap);\n if (!res.isValid) {\n errors.push({\n isValid: false,\n errorMessage: res.errorMessage,\n fieldKey: propPath,\n fieldLabel: propMeta.ui?.label || key\n });\n } else {\n errors.push({ isValid: true, fieldKey: propPath }); // Push successful state to clear old errors\n }\n }\n }\n }\n });\n }\n return errors;\n }\n\n /**\n * Retrieves an active plugin instance by its absolute binding path.\n * Useful for programmatic field manipulation (e.g., custom error injection).\n * \n * @param path The binding path (e.g. \"/General/CustomerName\").\n * @returns {IPlugin | undefined} The plugin instance, or undefined if not found.\n */\n public getPluginByPath(path: string): import(\"../interfaces/IPlugin\").IPlugin | undefined {\n const cleanPath = path.replace(/^\\//, \"\");\n const match = this.activePlugins.find(p => p.path.replace(/^\\//, \"\") === cleanPath);\n return match ? match.plugin : undefined;\n }\n\n /**\n * Reports a localized schema validation error to the coordinator ledger.\n */\n public reportSchemaError(fieldPath: string, errorMessage: string | null): void {\n const cleanPath = fieldPath.replace(/^\\//, \"\");\n if (!this.validationLedger.has(cleanPath)) {\n this.validationLedger.set(cleanPath, { schemaError: null, policyError: null });\n }\n this.validationLedger.get(cleanPath)!.schemaError = errorMessage;\n this.resolveVisualState(cleanPath);\n }\n\n /**\n * Reports a cross-field policy error to the coordinator ledger.\n */\n public reportPolicyError(fieldPath: string, errorMessage: string | null): void {\n const cleanPath = fieldPath.replace(/^\\//, \"\");\n if (!this.validationLedger.has(cleanPath)) {\n this.validationLedger.set(cleanPath, { schemaError: null, policyError: null });\n }\n this.validationLedger.get(cleanPath)!.policyError = errorMessage;\n this.resolveVisualState(cleanPath);\n }\n\n /**\n * Computes the final visual state by checking both engines and applies it to the plugin.\n */\n private resolveVisualState(cleanPath: string): void {\n const plugin = this.getPluginByPath(cleanPath);\n const ledgerEntry = this.validationLedger.get(cleanPath);\n if (!ledgerEntry) return;\n\n const isError = ledgerEntry.policyError !== null || ledgerEntry.schemaError !== null;\n const errorMessage = ledgerEntry.policyError || ledgerEntry.schemaError;\n\n if (plugin && typeof plugin.setVisualValidationState === \"function\") {\n plugin.setVisualValidationState(!isError, isError ? errorMessage || undefined : undefined);\n } else {\n // Un-tracked template clones (Table Layout rows)\n this.setNativeControlError(cleanPath, isError ? errorMessage || undefined : undefined);\n }\n\n if (this.useMessageManager) {\n const currentMsg = this.activeMessages.get(cleanPath);\n if (currentMsg) {\n Messaging.removeMessages(currentMsg);\n this.activeMessages.delete(cleanPath);\n }\n if (isError) {\n const targetPath = `/${cleanPath}`;\n const processorModel = this.host && typeof (this.host as any).getStateManager === \"function\" \n ? (this.host as any).getStateManager()?.getModel() \n : undefined;\n \n const fieldLabel = typeof plugin?.getFieldLabel === \"function\" ? plugin.getFieldLabel() : undefined;\n const displayLabel = fieldLabel || cleanPath;\n const displayText = `Field '${displayLabel}': ${errorMessage || \"Validation Error\"}`;\n \n const newMsg = new Message({\n message: displayText,\n additionalText: displayLabel !== targetPath ? displayLabel : undefined,\n type: coreLibrary.MessageType.Error,\n target: targetPath,\n processor: processorModel\n });\n Messaging.addMessages(newMsg);\n this.activeMessages.set(cleanPath, newMsg);\n }\n }\n }\n\n private setNativeControlError(targetPath: string, errorMessage?: string): void {\n if (this.host && (this.host as any).generatedContent) {\n const rootControl = (this.host as any).generatedContent;\n this.traverseAndSetError(rootControl, targetPath, errorMessage);\n }\n }\n\n private traverseAndSetError(control: any, targetPath: string, errorMessage?: string): void {\n if (!control) return;\n \n const bindingInfos = [control.getBindingInfo(\"value\"), control.getBindingInfo(\"selectedKey\"), control.getBindingInfo(\"dateValue\"), control.getBindingInfo(\"selected\")];\n for (const info of bindingInfos) {\n if (info && info.parts && info.parts.length > 0) {\n const boundPath = info.parts[0].path;\n // UI5 relative bindings drop the leading slash in the context, but retain it in the path\n if (boundPath === targetPath || targetPath.endsWith(boundPath)) {\n // Ensure we match the exact row context path\n const context = control.getBindingContext((this.host as any).activeModelName);\n if (context) {\n const fullPath = context.getPath() + \"/\" + boundPath;\n const cleanedFullPath = fullPath.replace(/^\\//, \"\");\n if (cleanedFullPath === targetPath) {\n if (typeof control.setValueState === \"function\") {\n control.setValueState(errorMessage ? \"Error\" : \"None\");\n if (typeof control.setValueStateText === \"function\") {\n control.setValueStateText(errorMessage || \"\");\n }\n }\n }\n }\n }\n }\n }\n \n if (typeof control.getAggregation === \"function\" && typeof control.getMetadata === \"function\") {\n const aggs = control.getMetadata().getAggregations();\n for (const aggName in aggs) {\n const child = control.getAggregation(aggName);\n if (Array.isArray(child)) {\n child.forEach(c => this.traverseAndSetError(c, targetPath, errorMessage));\n } else if (child) {\n this.traverseAndSetError(child, targetPath, errorMessage);\n }\n }\n }\n }\n\n /**\n * Cleans up internal state and destroys the condition engine to prevent memory leaks.\n */\n public destroy(): void {\n\n\n for (const item of this.activePlugins) {\n if (typeof item.plugin.destroy === \"function\") {\n item.plugin.destroy();\n }\n }\n\n for (const item of this.templatePlugins) {\n if (typeof item.plugin.destroy === \"function\") {\n item.plugin.destroy();\n }\n }\n\n if (this.useMessageManager && this.activeMessages.size > 0) {\n Messaging.removeMessages(Array.from(this.activeMessages.values()));\n }\n\n this.validationLedger.clear();\n this.activeMessages.clear();\n this.activePlugins = [];\n this.templatePlugins = [];\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -15,6 +15,8 @@ import { Logger } from "../utils/Logger";
|
|
|
15
15
|
import { DefaultLayoutGenerator } from "./DefaultLayoutGenerator";
|
|
16
16
|
import Core from "sap/ui/core/Core";
|
|
17
17
|
import Messaging from "sap/ui/core/Messaging";
|
|
18
|
+
import Message from "sap/ui/core/message/Message";
|
|
19
|
+
import coreLibrary from "sap/ui/core/library";
|
|
18
20
|
import { GlobalPipeline } from "./PipelineManager";
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -44,6 +46,9 @@ export class Engine {
|
|
|
44
46
|
/** Ledger tracking local vs cross-field errors to prevent visual race conditions. */
|
|
45
47
|
private validationLedger: Map<string, { schemaError: string | null, policyError: string | null }> = new Map();
|
|
46
48
|
|
|
49
|
+
/** Tracks active MessageManager messages created by this engine instance. */
|
|
50
|
+
private activeMessages: Map<string, Message> = new Map();
|
|
51
|
+
|
|
47
52
|
/** Reference to the GeneratorHost that instantiated this engine, for retrieving global definitions. */
|
|
48
53
|
public host?: unknown;
|
|
49
54
|
|
|
@@ -360,6 +365,34 @@ export class Engine {
|
|
|
360
365
|
// Un-tracked template clones (Table Layout rows)
|
|
361
366
|
this.setNativeControlError(cleanPath, isError ? errorMessage || undefined : undefined);
|
|
362
367
|
}
|
|
368
|
+
|
|
369
|
+
if (this.useMessageManager) {
|
|
370
|
+
const currentMsg = this.activeMessages.get(cleanPath);
|
|
371
|
+
if (currentMsg) {
|
|
372
|
+
Messaging.removeMessages(currentMsg);
|
|
373
|
+
this.activeMessages.delete(cleanPath);
|
|
374
|
+
}
|
|
375
|
+
if (isError) {
|
|
376
|
+
const targetPath = `/${cleanPath}`;
|
|
377
|
+
const processorModel = this.host && typeof (this.host as any).getStateManager === "function"
|
|
378
|
+
? (this.host as any).getStateManager()?.getModel()
|
|
379
|
+
: undefined;
|
|
380
|
+
|
|
381
|
+
const fieldLabel = typeof plugin?.getFieldLabel === "function" ? plugin.getFieldLabel() : undefined;
|
|
382
|
+
const displayLabel = fieldLabel || cleanPath;
|
|
383
|
+
const displayText = `Field '${displayLabel}': ${errorMessage || "Validation Error"}`;
|
|
384
|
+
|
|
385
|
+
const newMsg = new Message({
|
|
386
|
+
message: displayText,
|
|
387
|
+
additionalText: displayLabel !== targetPath ? displayLabel : undefined,
|
|
388
|
+
type: coreLibrary.MessageType.Error,
|
|
389
|
+
target: targetPath,
|
|
390
|
+
processor: processorModel
|
|
391
|
+
});
|
|
392
|
+
Messaging.addMessages(newMsg);
|
|
393
|
+
this.activeMessages.set(cleanPath, newMsg);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
363
396
|
}
|
|
364
397
|
|
|
365
398
|
private setNativeControlError(targetPath: string, errorMessage?: string): void {
|
|
@@ -427,7 +460,12 @@ export class Engine {
|
|
|
427
460
|
}
|
|
428
461
|
}
|
|
429
462
|
|
|
463
|
+
if (this.useMessageManager && this.activeMessages.size > 0) {
|
|
464
|
+
Messaging.removeMessages(Array.from(this.activeMessages.values()));
|
|
465
|
+
}
|
|
466
|
+
|
|
430
467
|
this.validationLedger.clear();
|
|
468
|
+
this.activeMessages.clear();
|
|
431
469
|
this.activePlugins = [];
|
|
432
470
|
this.templatePlugins = [];
|
|
433
471
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateManager-dbg.js","names":["SCHEMA_TYPE","___constants_MetaUIConstants","GlobalPipeline","___PipelineManager","SchemaNormalizer","___SchemaNormalizer","StateManager","schema","_useMessageManager","constructor","initialData","modelName","activeModelName","safeData","JSON","parse","stringify","model","JSONModel","setDefaultBindingMode","originalSetProperty","setProperty","bind","sPath","oValue","oContext","bAsyncUpdate","result","validatePath","setUseMessageManager","use","value","fieldKey","replace","metadata","findMetadataForPath","messageManager","Messaging","targetPath","startsWith","existingMessages","getMessageModel","getData","messagesToRemove","filter","msg","isMatch","getTarget","getMessageProcessor","getId","length","removeMessages","validatorsToRun","argsMap","required","push","type","STRING","maxLength","minLength","pattern","minimum","undefined","maximum","min","max","format","ui","validators","v","name","args","validationResult","executeValidation","isValid","newMsg","Message","message","errorMessage","coreLibrary","MessageType","Error","target","processor","addMessages","clearArrayMessages","arrayPath","currentMessages","pathPrefix","getProcessor","path","properties","absoluteScope","meta","resolveScope","getModel","bindControl","control","property","bindingPath","bindProperty","extractPayload","data","deepClone","obj","Date","getTime","Array","isArray","arrCopy","item","objCopy","key","Object","prototype","hasOwnProperty","call","absolutePath","destroy","__exports","__esModule"],"sources":["StateManager.ts"],"sourcesContent":["/**\n * @file StateManager.ts\n * @description Isolated internal state management. Decouples the MetaUI engine from external host models.\n */\n\nimport JSONModel from \"sap/ui/model/json/JSONModel\";\nimport ManagedObject from \"sap/ui/base/ManagedObject\";\nimport { ISchema, IPropertyMetadata } from \"../interfaces/ISchema\";\nimport { SCHEMA_TYPE } from \"../constants/MetaUIConstants\";\nimport { GlobalPipeline } from \"./PipelineManager\";\nimport { SchemaNormalizer } from \"./SchemaNormalizer\";\nimport Message from \"sap/ui/core/message/Message\";\nimport coreLibrary from \"sap/ui/core/library\";\nimport Core from \"sap/ui/core/Core\";\nimport Messaging from \"sap/ui/core/Messaging\";\n\n\n/**\n * Manages the isolated internal data payload for the MetaUI engine.\n */\nexport class StateManager {\n private model: JSONModel;\n private activeModelName: string;\n private schema: ISchema | null = null;\n private _useMessageManager: boolean = false;\n\n /**\n * Initializes the state manager with an authentic SAPUI5 JSONModel.\n * @param initialData The initial JSON payload to populate the fields.\n * @param schema The normalized schema for live validation.\n * @param modelName The UI5 model alias used for isolation.\n */\n constructor(initialData: Record<string, unknown> = {}, schema: ISchema, modelName: string = \"meta\") {\n this.activeModelName = modelName;\n this.schema = schema;\n \n // Deep copy to ensure no reference leakage from the host application\n const safeData = JSON.parse(JSON.stringify(initialData));\n this.model = new JSONModel(safeData);\n this.model.setDefaultBindingMode(\"TwoWay\");\n\n // Centralized Model-Level Validation Interceptor\n const originalSetProperty = this.model.setProperty.bind(this.model);\n this.model.setProperty = (sPath: string, oValue: unknown, oContext?: unknown, bAsyncUpdate?: boolean) => {\n const result = originalSetProperty(sPath, oValue, oContext, bAsyncUpdate);\n this.validatePath(sPath, oValue);\n return result;\n };\n }\n\n /**\n * Updates the flag indicating whether this manager should push validation errors directly to the global MessageManager.\n */\n public setUseMessageManager(use: boolean): void {\n this._useMessageManager = use;\n }\n\n /**\n * Validates a specific path against the schema and updates the MessageManager.\n */\n private validatePath(sPath: string, value: unknown): void {\n const fieldKey = sPath.replace(/^\\//, \"\");\n const metadata = this.findMetadataForPath(fieldKey);\n \n const messageManager = Messaging;\n const targetPath = sPath.startsWith(\"/\") ? sPath : `/${sPath}`; // Pure UI5: target MUST be absolute path\n \n // 1. Remove existing messages (strictly for THIS model instance) unconditionally\n const existingMessages = messageManager.getMessageModel().getData();\n const messagesToRemove = existingMessages.filter((msg: Message) => {\n const isMatch = msg.getTarget() === targetPath && msg.getMessageProcessor() && msg.getMessageProcessor().getId() === this.model.getId();\n return isMatch;\n });\n \n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n\n if (!metadata) return;\n\n // 2. Run GlobalPipeline\n const validatorsToRun: string[] = [];\n const argsMap: Record<string, unknown> = {};\n\n if (metadata.required) {\n validatorsToRun.push(\"required\");\n }\n if (metadata.type === SCHEMA_TYPE.STRING && metadata.maxLength) {\n validatorsToRun.push(\"maxLength\");\n argsMap[\"maxLength\"] = metadata.maxLength;\n }\n if (metadata.minLength) {\n validatorsToRun.push(\"minLength\");\n argsMap[\"minLength\"] = metadata.minLength;\n }\n if (metadata.pattern) {\n validatorsToRun.push(\"pattern\");\n argsMap[\"pattern\"] = metadata.pattern;\n }\n if (metadata.minimum !== undefined || metadata.maximum !== undefined) {\n validatorsToRun.push(\"range\");\n argsMap[\"range\"] = { min: metadata.minimum, max: metadata.maximum };\n }\n \n const format = metadata.ui?.format;\n if (format === \"email\" || format === \"url\" || format === \"iban\") {\n validatorsToRun.push(format);\n }\n if (metadata.ui?.validators) {\n for (const v of metadata.ui.validators) {\n if (typeof v === SCHEMA_TYPE.STRING) {\n validatorsToRun.push(v);\n } else if (v && v.name) {\n validatorsToRun.push(v.name);\n if (v.args !== undefined) {\n argsMap[v.name] = v.args;\n }\n }\n }\n }\n\n if (validatorsToRun.length > 0) {\n const validationResult = GlobalPipeline.executeValidation(value, validatorsToRun, argsMap);\n if (!validationResult.isValid && this._useMessageManager) {\n const newMsg = new Message({\n message: validationResult.errorMessage || \"Invalid value.\",\n type: coreLibrary.MessageType.Error,\n target: targetPath,\n processor: this.model\n });\n messageManager.addMessages(newMsg);\n }\n }\n }\n\n /**\n * Purges all MessageManager errors that start with a specific array path.\n * Used to clean up \"ghost errors\" when array structural shifts (deletion) occur.\n * @param arrayPath The root array path (e.g. /Items)\n */\n public clearArrayMessages(arrayPath: string): void {\n const messageManager = Messaging;\n const currentMessages = messageManager.getMessageModel().getData() as Message[];\n \n const pathPrefix = arrayPath.startsWith(\"/\") ? arrayPath : `/${arrayPath}`;\n \n const messagesToRemove = currentMessages.filter(msg => {\n return msg.getProcessor() === this.model && typeof msg.getTarget() === \"string\" && msg.getTarget().startsWith(pathPrefix);\n });\n\n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n }\n\n /**\n * Resolves the schema metadata for a given internal binding path.\n * @param path The flat binding path representing the field.\n * @returns The associated IPropertyMetadata, or undefined if not mapped.\n */\n private findMetadataForPath(path: string): IPropertyMetadata | undefined {\n if (!this.schema || !this.schema.properties) return undefined;\n \n // Deeply resolve the metadata using the SchemaNormalizer\n const absoluteScope = \"#/properties/\" + path.replace(/\\//g, \"/properties/\");\n const { meta } = SchemaNormalizer.resolveScope(this.schema, absoluteScope);\n return meta;\n }\n\n\n\n /**\n * Retrieves the isolated JSONModel instance to attach to the root GeneratorHost control.\n * @returns The internal JSONModel.\n */\n public getModel(): JSONModel {\n return this.model;\n }\n\n /**\n * Binds a generated UI control property to a path in the internal model.\n * @param control The target UI5 Control.\n * @param property The UI5 property name (e.g., 'value').\n * @param bindingPath The exact path in the JSON payload.\n */\n public bindControl(control: ManagedObject, property: string, bindingPath: string): void {\n control.bindProperty(property, {\n path: bindingPath,\n model: \"meta\" // Assumes the model is bound to the root container with the name 'meta'\n });\n }\n\n /**\n * Extracts the flat JavaScript object representing the current state of the UI.\n * @returns A serialized record of the data payload, free of UI5 wrappers.\n */\n public extractPayload(): Record<string, unknown> {\n // Create a deep copy to prevent accidental mutations by the host\n // Uses a robust deep clone to avoid destructively coercing native objects (like Dates) to strings\n const data = this.model.getData();\n return this.deepClone(data) as Record<string, unknown>;\n }\n\n /**\n * Recursively clones an object while preserving native Date instances and arrays.\n */\n private deepClone(obj: any): any {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime());\n }\n\n if (Array.isArray(obj)) {\n const arrCopy = [];\n for (const item of obj) {\n arrCopy.push(this.deepClone(item));\n }\n return arrCopy;\n }\n\n const objCopy: any = {};\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n objCopy[key] = this.deepClone(obj[key]);\n }\n }\n return objCopy;\n }\n\n /**\n * Programmatically mutates the internal JSONModel property (e.g. for PBRE state pruning).\n * @param path The path to the property.\n * @param value The value to set.\n */\n public setProperty(path: string, value: unknown): void {\n const absolutePath = path.startsWith(\"/\") ? path : `/${path}`;\n this.model.setProperty(absolutePath, value);\n }\n\n /**\n * Cleans up internal state and natively destroys the UI5 JSONModel to prevent memory leaks.\n */\n public destroy(): void {\n if (this.model) {\n // Fix Leaky Abstractions: Clean up any ghost errors in the global MessageManager bound to this model\n const messageManager = Messaging;\n const existingMessages = messageManager.getMessageModel().getData();\n const messagesToRemove = existingMessages.filter((msg: Message) => \n msg.getMessageProcessor() && msg.getMessageProcessor().getId() === this.model.getId()\n );\n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n\n this.model.destroy();\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"StateManager-dbg.js","names":["SCHEMA_TYPE","___constants_MetaUIConstants","GlobalPipeline","___PipelineManager","SchemaNormalizer","___SchemaNormalizer","StateManager","schema","_useMessageManager","constructor","initialData","modelName","activeModelName","safeData","JSON","parse","stringify","model","JSONModel","setDefaultBindingMode","originalSetProperty","setProperty","bind","sPath","oValue","oContext","bAsyncUpdate","result","validatePath","setUseMessageManager","use","value","fieldKey","replace","metadata","findMetadataForPath","messageManager","Messaging","targetPath","startsWith","existingMessages","getMessageModel","getData","messagesToRemove","filter","msg","isMatch","getTarget","getMessageProcessor","getId","length","removeMessages","validatorsToRun","argsMap","required","push","type","STRING","maxLength","minLength","pattern","minimum","undefined","maximum","min","max","format","ui","validators","v","name","args","validationResult","executeValidation","isValid","newMsg","Message","message","errorMessage","coreLibrary","MessageType","Error","target","processor","addMessages","clearArrayMessages","arrayPath","currentMessages","pathPrefix","getProcessor","path","properties","absoluteScope","meta","resolveScope","getModel","bindControl","control","property","bindingPath","bindProperty","extractPayload","data","deepClone","obj","Date","getTime","Array","isArray","arrCopy","item","objCopy","key","Object","prototype","hasOwnProperty","call","absolutePath","destroy","__exports","__esModule"],"sources":["StateManager.ts"],"sourcesContent":["/**\n * @file StateManager.ts\n * @description Isolated internal state management. Decouples the MetaUI engine from external host models.\n */\n\nimport JSONModel from \"sap/ui/model/json/JSONModel\";\nimport ManagedObject from \"sap/ui/base/ManagedObject\";\nimport { ISchema, IPropertyMetadata } from \"../interfaces/ISchema\";\nimport { SCHEMA_TYPE } from \"../constants/MetaUIConstants\";\nimport { GlobalPipeline } from \"./PipelineManager\";\nimport { SchemaNormalizer } from \"./SchemaNormalizer\";\nimport Message from \"sap/ui/core/message/Message\";\nimport coreLibrary from \"sap/ui/core/library\";\nimport Core from \"sap/ui/core/Core\";\nimport Messaging from \"sap/ui/core/Messaging\";\n\n\n/**\n * Manages the isolated internal data payload for the MetaUI engine.\n */\nexport class StateManager {\n private model: JSONModel;\n private activeModelName: string;\n private schema: ISchema | null = null;\n private _useMessageManager: boolean = false;\n\n /**\n * Initializes the state manager with an authentic SAPUI5 JSONModel.\n * @param initialData The initial JSON payload to populate the fields.\n * @param schema The normalized schema for live validation.\n * @param modelName The UI5 model alias used for isolation.\n */\n constructor(initialData: Record<string, unknown> = {}, schema: ISchema, modelName: string = \"meta\") {\n this.activeModelName = modelName;\n this.schema = schema;\n \n // Deep copy to ensure no reference leakage from the host application\n const safeData = JSON.parse(JSON.stringify(initialData));\n this.model = new JSONModel(safeData);\n this.model.setDefaultBindingMode(\"TwoWay\");\n\n // Centralized Model-Level Validation Interceptor\n const originalSetProperty = this.model.setProperty.bind(this.model);\n this.model.setProperty = (sPath: string, oValue: unknown, oContext?: unknown, bAsyncUpdate?: boolean) => {\n const result = originalSetProperty(sPath, oValue, oContext, bAsyncUpdate);\n this.validatePath(sPath, oValue);\n return result;\n };\n }\n\n /**\n * Updates the flag indicating whether this manager should push validation errors directly to the global MessageManager.\n */\n public setUseMessageManager(use: boolean): void {\n this._useMessageManager = use;\n }\n\n /**\n * Validates a specific path against the schema and updates the MessageManager.\n */\n private validatePath(sPath: string, value: unknown): void {\n const fieldKey = sPath.replace(/^\\//, \"\");\n const metadata = this.findMetadataForPath(fieldKey);\n \n const messageManager = Messaging;\n const targetPath = sPath.startsWith(\"/\") ? sPath : `/${sPath}`; // Pure UI5: target MUST be absolute path\n \n // 1. Remove existing messages (strictly for THIS model instance) unconditionally\n const existingMessages = messageManager.getMessageModel().getData();\n const messagesToRemove = existingMessages.filter((msg: Message) => {\n const isMatch = msg.getTarget() === targetPath && msg.getMessageProcessor() && msg.getMessageProcessor().getId() === this.model.getId();\n return isMatch;\n });\n \n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n\n if (!metadata) return;\n\n // 2. Run GlobalPipeline\n const validatorsToRun: string[] = [];\n const argsMap: Record<string, unknown> = {};\n\n if (metadata.required) {\n validatorsToRun.push(\"required\");\n }\n if (metadata.type === SCHEMA_TYPE.STRING && metadata.maxLength) {\n validatorsToRun.push(\"maxLength\");\n argsMap[\"maxLength\"] = metadata.maxLength;\n }\n if (metadata.minLength) {\n validatorsToRun.push(\"minLength\");\n argsMap[\"minLength\"] = metadata.minLength;\n }\n if (metadata.pattern) {\n validatorsToRun.push(\"pattern\");\n argsMap[\"pattern\"] = metadata.pattern;\n }\n if (metadata.minimum !== undefined || metadata.maximum !== undefined) {\n validatorsToRun.push(\"range\");\n argsMap[\"range\"] = { min: metadata.minimum, max: metadata.maximum };\n }\n \n const format = metadata.ui?.format;\n if (format === \"email\" || format === \"url\" || format === \"iban\") {\n validatorsToRun.push(format);\n }\n if (metadata.ui?.validators) {\n for (const v of metadata.ui.validators) {\n if (typeof v === SCHEMA_TYPE.STRING) {\n validatorsToRun.push(v);\n } else if (v && v.name) {\n validatorsToRun.push(v.name);\n if (v.args !== undefined) {\n argsMap[v.name] = v.args;\n }\n }\n }\n }\n\n if (validatorsToRun.length > 0) {\n const validationResult = GlobalPipeline.executeValidation(value, validatorsToRun, argsMap);\n if (!validationResult.isValid && this._useMessageManager) {\n const newMsg = new Message({\n message: validationResult.errorMessage || \"Invalid value.\",\n type: coreLibrary.MessageType.Error,\n target: targetPath,\n processor: this.model\n });\n messageManager.addMessages(newMsg);\n }\n }\n }\n\n /**\n * Purges all MessageManager errors that start with a specific array path.\n * Used to clean up \"ghost errors\" when array structural shifts (deletion) occur.\n * @param arrayPath The root array path (e.g. /Items)\n */\n public clearArrayMessages(arrayPath: string): void {\n const messageManager = Messaging;\n const currentMessages = messageManager.getMessageModel().getData() as Message[];\n \n const pathPrefix = arrayPath.startsWith(\"/\") ? arrayPath : `/${arrayPath}`;\n \n const messagesToRemove = currentMessages.filter(msg => {\n return (msg as any).getProcessor() === this.model && typeof (msg as any).getTarget() === \"string\" && (msg as any).getTarget().startsWith(pathPrefix);\n });\n\n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n }\n\n /**\n * Resolves the schema metadata for a given internal binding path.\n * @param path The flat binding path representing the field.\n * @returns The associated IPropertyMetadata, or undefined if not mapped.\n */\n private findMetadataForPath(path: string): IPropertyMetadata | undefined {\n if (!this.schema || !this.schema.properties) return undefined;\n \n // Deeply resolve the metadata using the SchemaNormalizer\n const absoluteScope = \"#/properties/\" + path.replace(/\\//g, \"/properties/\");\n const { meta } = SchemaNormalizer.resolveScope(this.schema, absoluteScope);\n return meta;\n }\n\n\n\n /**\n * Retrieves the isolated JSONModel instance to attach to the root GeneratorHost control.\n * @returns The internal JSONModel.\n */\n public getModel(): JSONModel {\n return this.model;\n }\n\n /**\n * Binds a generated UI control property to a path in the internal model.\n * @param control The target UI5 Control.\n * @param property The UI5 property name (e.g., 'value').\n * @param bindingPath The exact path in the JSON payload.\n */\n public bindControl(control: ManagedObject, property: string, bindingPath: string): void {\n control.bindProperty(property, {\n path: bindingPath,\n model: \"meta\" // Assumes the model is bound to the root container with the name 'meta'\n });\n }\n\n /**\n * Extracts the flat JavaScript object representing the current state of the UI.\n * @returns A serialized record of the data payload, free of UI5 wrappers.\n */\n public extractPayload(): Record<string, unknown> {\n // Create a deep copy to prevent accidental mutations by the host\n // Uses a robust deep clone to avoid destructively coercing native objects (like Dates) to strings\n const data = this.model.getData();\n return this.deepClone(data) as Record<string, unknown>;\n }\n\n /**\n * Recursively clones an object while preserving native Date instances and arrays.\n */\n private deepClone(obj: any): any {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime());\n }\n\n if (Array.isArray(obj)) {\n const arrCopy = [];\n for (const item of obj) {\n arrCopy.push(this.deepClone(item));\n }\n return arrCopy;\n }\n\n const objCopy: any = {};\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n objCopy[key] = this.deepClone(obj[key]);\n }\n }\n return objCopy;\n }\n\n /**\n * Programmatically mutates the internal JSONModel property (e.g. for PBRE state pruning).\n * @param path The path to the property.\n * @param value The value to set.\n */\n public setProperty(path: string, value: unknown): void {\n const absolutePath = path.startsWith(\"/\") ? path : `/${path}`;\n this.model.setProperty(absolutePath, value);\n }\n\n /**\n * Cleans up internal state and natively destroys the UI5 JSONModel to prevent memory leaks.\n */\n public destroy(): void {\n if (this.model) {\n // Fix Leaky Abstractions: Clean up any ghost errors in the global MessageManager bound to this model\n const messageManager = Messaging;\n const existingMessages = messageManager.getMessageModel().getData();\n const messagesToRemove = existingMessages.filter((msg: Message) => \n msg.getMessageProcessor() && msg.getMessageProcessor().getId() === this.model.getId()\n );\n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n\n this.model.destroy();\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateManager.js","names":["SCHEMA_TYPE","___constants_MetaUIConstants","GlobalPipeline","___PipelineManager","SchemaNormalizer","___SchemaNormalizer","StateManager","schema","_useMessageManager","constructor","initialData","modelName","this","activeModelName","safeData","JSON","parse","stringify","model","JSONModel","setDefaultBindingMode","originalSetProperty","setProperty","bind","sPath","oValue","oContext","bAsyncUpdate","result","validatePath","setUseMessageManager","use","value","fieldKey","replace","metadata","findMetadataForPath","messageManager","Messaging","targetPath","startsWith","existingMessages","getMessageModel","getData","messagesToRemove","filter","msg","isMatch","getTarget","getMessageProcessor","getId","length","removeMessages","validatorsToRun","argsMap","required","push","type","STRING","maxLength","minLength","pattern","minimum","undefined","maximum","min","max","format","ui","validators","v","name","args","validationResult","executeValidation","isValid","newMsg","Message","message","errorMessage","coreLibrary","MessageType","Error","target","processor","addMessages","clearArrayMessages","arrayPath","currentMessages","pathPrefix","getProcessor","path","properties","absoluteScope","meta","resolveScope","getModel","bindControl","control","property","bindingPath","bindProperty","extractPayload","data","deepClone","obj","Date","getTime","Array","isArray","arrCopy","item","objCopy","key","Object","prototype","hasOwnProperty","call","absolutePath","destroy","__exports","__esModule"],"sources":["StateManager.ts"],"sourcesContent":["/**\n * @file StateManager.ts\n * @description Isolated internal state management. Decouples the MetaUI engine from external host models.\n */\n\nimport JSONModel from \"sap/ui/model/json/JSONModel\";\nimport ManagedObject from \"sap/ui/base/ManagedObject\";\nimport { ISchema, IPropertyMetadata } from \"../interfaces/ISchema\";\nimport { SCHEMA_TYPE } from \"../constants/MetaUIConstants\";\nimport { GlobalPipeline } from \"./PipelineManager\";\nimport { SchemaNormalizer } from \"./SchemaNormalizer\";\nimport Message from \"sap/ui/core/message/Message\";\nimport coreLibrary from \"sap/ui/core/library\";\nimport Core from \"sap/ui/core/Core\";\nimport Messaging from \"sap/ui/core/Messaging\";\n\n\n/**\n * Manages the isolated internal data payload for the MetaUI engine.\n */\nexport class StateManager {\n private model: JSONModel;\n private activeModelName: string;\n private schema: ISchema | null = null;\n private _useMessageManager: boolean = false;\n\n /**\n * Initializes the state manager with an authentic SAPUI5 JSONModel.\n * @param initialData The initial JSON payload to populate the fields.\n * @param schema The normalized schema for live validation.\n * @param modelName The UI5 model alias used for isolation.\n */\n constructor(initialData: Record<string, unknown> = {}, schema: ISchema, modelName: string = \"meta\") {\n this.activeModelName = modelName;\n this.schema = schema;\n \n // Deep copy to ensure no reference leakage from the host application\n const safeData = JSON.parse(JSON.stringify(initialData));\n this.model = new JSONModel(safeData);\n this.model.setDefaultBindingMode(\"TwoWay\");\n\n // Centralized Model-Level Validation Interceptor\n const originalSetProperty = this.model.setProperty.bind(this.model);\n this.model.setProperty = (sPath: string, oValue: unknown, oContext?: unknown, bAsyncUpdate?: boolean) => {\n const result = originalSetProperty(sPath, oValue, oContext, bAsyncUpdate);\n this.validatePath(sPath, oValue);\n return result;\n };\n }\n\n /**\n * Updates the flag indicating whether this manager should push validation errors directly to the global MessageManager.\n */\n public setUseMessageManager(use: boolean): void {\n this._useMessageManager = use;\n }\n\n /**\n * Validates a specific path against the schema and updates the MessageManager.\n */\n private validatePath(sPath: string, value: unknown): void {\n const fieldKey = sPath.replace(/^\\//, \"\");\n const metadata = this.findMetadataForPath(fieldKey);\n \n const messageManager = Messaging;\n const targetPath = sPath.startsWith(\"/\") ? sPath : `/${sPath}`; // Pure UI5: target MUST be absolute path\n \n // 1. Remove existing messages (strictly for THIS model instance) unconditionally\n const existingMessages = messageManager.getMessageModel().getData();\n const messagesToRemove = existingMessages.filter((msg: Message) => {\n const isMatch = msg.getTarget() === targetPath && msg.getMessageProcessor() && msg.getMessageProcessor().getId() === this.model.getId();\n return isMatch;\n });\n \n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n\n if (!metadata) return;\n\n // 2. Run GlobalPipeline\n const validatorsToRun: string[] = [];\n const argsMap: Record<string, unknown> = {};\n\n if (metadata.required) {\n validatorsToRun.push(\"required\");\n }\n if (metadata.type === SCHEMA_TYPE.STRING && metadata.maxLength) {\n validatorsToRun.push(\"maxLength\");\n argsMap[\"maxLength\"] = metadata.maxLength;\n }\n if (metadata.minLength) {\n validatorsToRun.push(\"minLength\");\n argsMap[\"minLength\"] = metadata.minLength;\n }\n if (metadata.pattern) {\n validatorsToRun.push(\"pattern\");\n argsMap[\"pattern\"] = metadata.pattern;\n }\n if (metadata.minimum !== undefined || metadata.maximum !== undefined) {\n validatorsToRun.push(\"range\");\n argsMap[\"range\"] = { min: metadata.minimum, max: metadata.maximum };\n }\n \n const format = metadata.ui?.format;\n if (format === \"email\" || format === \"url\" || format === \"iban\") {\n validatorsToRun.push(format);\n }\n if (metadata.ui?.validators) {\n for (const v of metadata.ui.validators) {\n if (typeof v === SCHEMA_TYPE.STRING) {\n validatorsToRun.push(v);\n } else if (v && v.name) {\n validatorsToRun.push(v.name);\n if (v.args !== undefined) {\n argsMap[v.name] = v.args;\n }\n }\n }\n }\n\n if (validatorsToRun.length > 0) {\n const validationResult = GlobalPipeline.executeValidation(value, validatorsToRun, argsMap);\n if (!validationResult.isValid && this._useMessageManager) {\n const newMsg = new Message({\n message: validationResult.errorMessage || \"Invalid value.\",\n type: coreLibrary.MessageType.Error,\n target: targetPath,\n processor: this.model\n });\n messageManager.addMessages(newMsg);\n }\n }\n }\n\n /**\n * Purges all MessageManager errors that start with a specific array path.\n * Used to clean up \"ghost errors\" when array structural shifts (deletion) occur.\n * @param arrayPath The root array path (e.g. /Items)\n */\n public clearArrayMessages(arrayPath: string): void {\n const messageManager = Messaging;\n const currentMessages = messageManager.getMessageModel().getData() as Message[];\n \n const pathPrefix = arrayPath.startsWith(\"/\") ? arrayPath : `/${arrayPath}`;\n \n const messagesToRemove = currentMessages.filter(msg => {\n return msg.getProcessor() === this.model && typeof msg.getTarget() === \"string\" && msg.getTarget().startsWith(pathPrefix);\n });\n\n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n }\n\n /**\n * Resolves the schema metadata for a given internal binding path.\n * @param path The flat binding path representing the field.\n * @returns The associated IPropertyMetadata, or undefined if not mapped.\n */\n private findMetadataForPath(path: string): IPropertyMetadata | undefined {\n if (!this.schema || !this.schema.properties) return undefined;\n \n // Deeply resolve the metadata using the SchemaNormalizer\n const absoluteScope = \"#/properties/\" + path.replace(/\\//g, \"/properties/\");\n const { meta } = SchemaNormalizer.resolveScope(this.schema, absoluteScope);\n return meta;\n }\n\n\n\n /**\n * Retrieves the isolated JSONModel instance to attach to the root GeneratorHost control.\n * @returns The internal JSONModel.\n */\n public getModel(): JSONModel {\n return this.model;\n }\n\n /**\n * Binds a generated UI control property to a path in the internal model.\n * @param control The target UI5 Control.\n * @param property The UI5 property name (e.g., 'value').\n * @param bindingPath The exact path in the JSON payload.\n */\n public bindControl(control: ManagedObject, property: string, bindingPath: string): void {\n control.bindProperty(property, {\n path: bindingPath,\n model: \"meta\" // Assumes the model is bound to the root container with the name 'meta'\n });\n }\n\n /**\n * Extracts the flat JavaScript object representing the current state of the UI.\n * @returns A serialized record of the data payload, free of UI5 wrappers.\n */\n public extractPayload(): Record<string, unknown> {\n // Create a deep copy to prevent accidental mutations by the host\n // Uses a robust deep clone to avoid destructively coercing native objects (like Dates) to strings\n const data = this.model.getData();\n return this.deepClone(data) as Record<string, unknown>;\n }\n\n /**\n * Recursively clones an object while preserving native Date instances and arrays.\n */\n private deepClone(obj: any): any {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime());\n }\n\n if (Array.isArray(obj)) {\n const arrCopy = [];\n for (const item of obj) {\n arrCopy.push(this.deepClone(item));\n }\n return arrCopy;\n }\n\n const objCopy: any = {};\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n objCopy[key] = this.deepClone(obj[key]);\n }\n }\n return objCopy;\n }\n\n /**\n * Programmatically mutates the internal JSONModel property (e.g. for PBRE state pruning).\n * @param path The path to the property.\n * @param value The value to set.\n */\n public setProperty(path: string, value: unknown): void {\n const absolutePath = path.startsWith(\"/\") ? path : `/${path}`;\n this.model.setProperty(absolutePath, value);\n }\n\n /**\n * Cleans up internal state and natively destroys the UI5 JSONModel to prevent memory leaks.\n */\n public destroy(): void {\n if (this.model) {\n // Fix Leaky Abstractions: Clean up any ghost errors in the global MessageManager bound to this model\n const messageManager = Messaging;\n const existingMessages = messageManager.getMessageModel().getData();\n const messagesToRemove = existingMessages.filter((msg: Message) => \n msg.getMessageProcessor() && msg.getMessageProcessor().getId() === this.model.getId()\n );\n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n\n this.model.destroy();\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"StateManager.js","names":["SCHEMA_TYPE","___constants_MetaUIConstants","GlobalPipeline","___PipelineManager","SchemaNormalizer","___SchemaNormalizer","StateManager","schema","_useMessageManager","constructor","initialData","modelName","this","activeModelName","safeData","JSON","parse","stringify","model","JSONModel","setDefaultBindingMode","originalSetProperty","setProperty","bind","sPath","oValue","oContext","bAsyncUpdate","result","validatePath","setUseMessageManager","use","value","fieldKey","replace","metadata","findMetadataForPath","messageManager","Messaging","targetPath","startsWith","existingMessages","getMessageModel","getData","messagesToRemove","filter","msg","isMatch","getTarget","getMessageProcessor","getId","length","removeMessages","validatorsToRun","argsMap","required","push","type","STRING","maxLength","minLength","pattern","minimum","undefined","maximum","min","max","format","ui","validators","v","name","args","validationResult","executeValidation","isValid","newMsg","Message","message","errorMessage","coreLibrary","MessageType","Error","target","processor","addMessages","clearArrayMessages","arrayPath","currentMessages","pathPrefix","getProcessor","path","properties","absoluteScope","meta","resolveScope","getModel","bindControl","control","property","bindingPath","bindProperty","extractPayload","data","deepClone","obj","Date","getTime","Array","isArray","arrCopy","item","objCopy","key","Object","prototype","hasOwnProperty","call","absolutePath","destroy","__exports","__esModule"],"sources":["StateManager.ts"],"sourcesContent":["/**\n * @file StateManager.ts\n * @description Isolated internal state management. Decouples the MetaUI engine from external host models.\n */\n\nimport JSONModel from \"sap/ui/model/json/JSONModel\";\nimport ManagedObject from \"sap/ui/base/ManagedObject\";\nimport { ISchema, IPropertyMetadata } from \"../interfaces/ISchema\";\nimport { SCHEMA_TYPE } from \"../constants/MetaUIConstants\";\nimport { GlobalPipeline } from \"./PipelineManager\";\nimport { SchemaNormalizer } from \"./SchemaNormalizer\";\nimport Message from \"sap/ui/core/message/Message\";\nimport coreLibrary from \"sap/ui/core/library\";\nimport Core from \"sap/ui/core/Core\";\nimport Messaging from \"sap/ui/core/Messaging\";\n\n\n/**\n * Manages the isolated internal data payload for the MetaUI engine.\n */\nexport class StateManager {\n private model: JSONModel;\n private activeModelName: string;\n private schema: ISchema | null = null;\n private _useMessageManager: boolean = false;\n\n /**\n * Initializes the state manager with an authentic SAPUI5 JSONModel.\n * @param initialData The initial JSON payload to populate the fields.\n * @param schema The normalized schema for live validation.\n * @param modelName The UI5 model alias used for isolation.\n */\n constructor(initialData: Record<string, unknown> = {}, schema: ISchema, modelName: string = \"meta\") {\n this.activeModelName = modelName;\n this.schema = schema;\n \n // Deep copy to ensure no reference leakage from the host application\n const safeData = JSON.parse(JSON.stringify(initialData));\n this.model = new JSONModel(safeData);\n this.model.setDefaultBindingMode(\"TwoWay\");\n\n // Centralized Model-Level Validation Interceptor\n const originalSetProperty = this.model.setProperty.bind(this.model);\n this.model.setProperty = (sPath: string, oValue: unknown, oContext?: unknown, bAsyncUpdate?: boolean) => {\n const result = originalSetProperty(sPath, oValue, oContext, bAsyncUpdate);\n this.validatePath(sPath, oValue);\n return result;\n };\n }\n\n /**\n * Updates the flag indicating whether this manager should push validation errors directly to the global MessageManager.\n */\n public setUseMessageManager(use: boolean): void {\n this._useMessageManager = use;\n }\n\n /**\n * Validates a specific path against the schema and updates the MessageManager.\n */\n private validatePath(sPath: string, value: unknown): void {\n const fieldKey = sPath.replace(/^\\//, \"\");\n const metadata = this.findMetadataForPath(fieldKey);\n \n const messageManager = Messaging;\n const targetPath = sPath.startsWith(\"/\") ? sPath : `/${sPath}`; // Pure UI5: target MUST be absolute path\n \n // 1. Remove existing messages (strictly for THIS model instance) unconditionally\n const existingMessages = messageManager.getMessageModel().getData();\n const messagesToRemove = existingMessages.filter((msg: Message) => {\n const isMatch = msg.getTarget() === targetPath && msg.getMessageProcessor() && msg.getMessageProcessor().getId() === this.model.getId();\n return isMatch;\n });\n \n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n\n if (!metadata) return;\n\n // 2. Run GlobalPipeline\n const validatorsToRun: string[] = [];\n const argsMap: Record<string, unknown> = {};\n\n if (metadata.required) {\n validatorsToRun.push(\"required\");\n }\n if (metadata.type === SCHEMA_TYPE.STRING && metadata.maxLength) {\n validatorsToRun.push(\"maxLength\");\n argsMap[\"maxLength\"] = metadata.maxLength;\n }\n if (metadata.minLength) {\n validatorsToRun.push(\"minLength\");\n argsMap[\"minLength\"] = metadata.minLength;\n }\n if (metadata.pattern) {\n validatorsToRun.push(\"pattern\");\n argsMap[\"pattern\"] = metadata.pattern;\n }\n if (metadata.minimum !== undefined || metadata.maximum !== undefined) {\n validatorsToRun.push(\"range\");\n argsMap[\"range\"] = { min: metadata.minimum, max: metadata.maximum };\n }\n \n const format = metadata.ui?.format;\n if (format === \"email\" || format === \"url\" || format === \"iban\") {\n validatorsToRun.push(format);\n }\n if (metadata.ui?.validators) {\n for (const v of metadata.ui.validators) {\n if (typeof v === SCHEMA_TYPE.STRING) {\n validatorsToRun.push(v);\n } else if (v && v.name) {\n validatorsToRun.push(v.name);\n if (v.args !== undefined) {\n argsMap[v.name] = v.args;\n }\n }\n }\n }\n\n if (validatorsToRun.length > 0) {\n const validationResult = GlobalPipeline.executeValidation(value, validatorsToRun, argsMap);\n if (!validationResult.isValid && this._useMessageManager) {\n const newMsg = new Message({\n message: validationResult.errorMessage || \"Invalid value.\",\n type: coreLibrary.MessageType.Error,\n target: targetPath,\n processor: this.model\n });\n messageManager.addMessages(newMsg);\n }\n }\n }\n\n /**\n * Purges all MessageManager errors that start with a specific array path.\n * Used to clean up \"ghost errors\" when array structural shifts (deletion) occur.\n * @param arrayPath The root array path (e.g. /Items)\n */\n public clearArrayMessages(arrayPath: string): void {\n const messageManager = Messaging;\n const currentMessages = messageManager.getMessageModel().getData() as Message[];\n \n const pathPrefix = arrayPath.startsWith(\"/\") ? arrayPath : `/${arrayPath}`;\n \n const messagesToRemove = currentMessages.filter(msg => {\n return (msg as any).getProcessor() === this.model && typeof (msg as any).getTarget() === \"string\" && (msg as any).getTarget().startsWith(pathPrefix);\n });\n\n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n }\n\n /**\n * Resolves the schema metadata for a given internal binding path.\n * @param path The flat binding path representing the field.\n * @returns The associated IPropertyMetadata, or undefined if not mapped.\n */\n private findMetadataForPath(path: string): IPropertyMetadata | undefined {\n if (!this.schema || !this.schema.properties) return undefined;\n \n // Deeply resolve the metadata using the SchemaNormalizer\n const absoluteScope = \"#/properties/\" + path.replace(/\\//g, \"/properties/\");\n const { meta } = SchemaNormalizer.resolveScope(this.schema, absoluteScope);\n return meta;\n }\n\n\n\n /**\n * Retrieves the isolated JSONModel instance to attach to the root GeneratorHost control.\n * @returns The internal JSONModel.\n */\n public getModel(): JSONModel {\n return this.model;\n }\n\n /**\n * Binds a generated UI control property to a path in the internal model.\n * @param control The target UI5 Control.\n * @param property The UI5 property name (e.g., 'value').\n * @param bindingPath The exact path in the JSON payload.\n */\n public bindControl(control: ManagedObject, property: string, bindingPath: string): void {\n control.bindProperty(property, {\n path: bindingPath,\n model: \"meta\" // Assumes the model is bound to the root container with the name 'meta'\n });\n }\n\n /**\n * Extracts the flat JavaScript object representing the current state of the UI.\n * @returns A serialized record of the data payload, free of UI5 wrappers.\n */\n public extractPayload(): Record<string, unknown> {\n // Create a deep copy to prevent accidental mutations by the host\n // Uses a robust deep clone to avoid destructively coercing native objects (like Dates) to strings\n const data = this.model.getData();\n return this.deepClone(data) as Record<string, unknown>;\n }\n\n /**\n * Recursively clones an object while preserving native Date instances and arrays.\n */\n private deepClone(obj: any): any {\n if (obj === null || typeof obj !== \"object\") {\n return obj;\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime());\n }\n\n if (Array.isArray(obj)) {\n const arrCopy = [];\n for (const item of obj) {\n arrCopy.push(this.deepClone(item));\n }\n return arrCopy;\n }\n\n const objCopy: any = {};\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n objCopy[key] = this.deepClone(obj[key]);\n }\n }\n return objCopy;\n }\n\n /**\n * Programmatically mutates the internal JSONModel property (e.g. for PBRE state pruning).\n * @param path The path to the property.\n * @param value The value to set.\n */\n public setProperty(path: string, value: unknown): void {\n const absolutePath = path.startsWith(\"/\") ? path : `/${path}`;\n this.model.setProperty(absolutePath, value);\n }\n\n /**\n * Cleans up internal state and natively destroys the UI5 JSONModel to prevent memory leaks.\n */\n public destroy(): void {\n if (this.model) {\n // Fix Leaky Abstractions: Clean up any ghost errors in the global MessageManager bound to this model\n const messageManager = Messaging;\n const existingMessages = messageManager.getMessageModel().getData();\n const messagesToRemove = existingMessages.filter((msg: Message) => \n msg.getMessageProcessor() && msg.getMessageProcessor().getId() === this.model.getId()\n );\n if (messagesToRemove.length > 0) {\n messageManager.removeMessages(messagesToRemove);\n }\n\n this.model.destroy();\n }\n }\n}\n"],"mappings":"6OAQSA,EAAWC,EAAA,qBACXC,EAAcC,EAAA,wBACdC,EAAgBC,EAAA,oBAUlB,MAAMC,EAGDC,OAAyB,KACzBC,mBAA8B,MAQtCC,YAAYC,EAAuC,CAAC,EAAGH,EAAiBI,EAAoB,QACxFC,KAAKC,gBAAkBF,EACvBC,KAAKL,OAASA,EAGd,MAAMO,EAAWC,KAAKC,MAAMD,KAAKE,UAAUP,IAC3CE,KAAKM,MAAQ,IAAIC,EAAUL,GAC3BF,KAAKM,MAAME,sBAAsB,UAGjC,MAAMC,EAAsBT,KAAKM,MAAMI,YAAYC,KAAKX,KAAKM,OAC7DN,KAAKM,MAAMI,YAAc,CAACE,EAAeC,EAAiBC,EAAoBC,KAC1E,MAAMC,EAASP,EAAoBG,EAAOC,EAAQC,EAAUC,GAC5Df,KAAKiB,aAAaL,EAAOC,GACzB,OAAOG,EAEf,CAKOE,qBAAqBC,GACxBnB,KAAKJ,mBAAqBuB,CAC9B,CAKQF,aAAaL,EAAeQ,GAChC,MAAMC,EAAWT,EAAMU,QAAQ,MAAO,IACtC,MAAMC,EAAWvB,KAAKwB,oBAAoBH,GAE1C,MAAMI,EAAiBC,EACvB,MAAMC,EAAaf,EAAMgB,WAAW,KAAOhB,EAAQ,IAAIA,IAGvD,MAAMiB,EAAmBJ,EAAeK,kBAAkBC,UAC1D,MAAMC,EAAmBH,EAAiBI,OAAQC,IAC9C,MAAMC,EAAUD,EAAIE,cAAgBT,GAAcO,EAAIG,uBAAyBH,EAAIG,sBAAsBC,UAAYtC,KAAKM,MAAMgC,QAChI,OAAOH,IAGX,GAAIH,EAAiBO,OAAS,EAAG,CAC7Bd,EAAee,eAAeR,EAClC,CAEA,IAAKT,EAAU,OAGf,MAAMkB,EAA4B,GAClC,MAAMC,EAAmC,CAAC,EAE1C,GAAInB,EAASoB,SAAU,CACnBF,EAAgBG,KAAK,WACzB,CACA,GAAIrB,EAASsB,OAASzD,EAAY0D,QAAUvB,EAASwB,UAAW,CAC5DN,EAAgBG,KAAK,aACrBF,EAAQ,aAAenB,EAASwB,SACpC,CACA,GAAIxB,EAASyB,UAAW,CACpBP,EAAgBG,KAAK,aACrBF,EAAQ,aAAenB,EAASyB,SACpC,CACA,GAAIzB,EAAS0B,QAAS,CAClBR,EAAgBG,KAAK,WACrBF,EAAQ,WAAanB,EAAS0B,OAClC,CACA,GAAI1B,EAAS2B,UAAYC,WAAa5B,EAAS6B,UAAYD,UAAW,CAClEV,EAAgBG,KAAK,SACrBF,EAAQ,SAAW,CAAEW,IAAK9B,EAAS2B,QAASI,IAAK/B,EAAS6B,QAC9D,CAEA,MAAMG,EAAShC,EAASiC,IAAID,OAC5B,GAAIA,IAAW,SAAWA,IAAW,OAASA,IAAW,OAAQ,CAC7Dd,EAAgBG,KAAKW,EACzB,CACA,GAAIhC,EAASiC,IAAIC,WAAY,CACzB,IAAK,MAAMC,KAAKnC,EAASiC,GAAGC,WAAY,CACpC,UAAWC,IAAMtE,EAAY0D,OAAQ,CACjCL,EAAgBG,KAAKc,EACzB,MAAO,GAAIA,GAAKA,EAAEC,KAAM,CACpBlB,EAAgBG,KAAKc,EAAEC,MACvB,GAAID,EAAEE,OAAST,UAAW,CACtBT,EAAQgB,EAAEC,MAAQD,EAAEE,IACxB,CACJ,CACJ,CACJ,CAEA,GAAInB,EAAgBF,OAAS,EAAG,CAC5B,MAAMsB,EAAmBvE,EAAewE,kBAAkB1C,EAAOqB,EAAiBC,GAClF,IAAKmB,EAAiBE,SAAW/D,KAAKJ,mBAAoB,CACtD,MAAMoE,EAAS,IAAIC,EAAQ,CACvBC,QAASL,EAAiBM,cAAgB,iBAC1CtB,KAAMuB,EAAYC,YAAYC,MAC9BC,OAAQ5C,EACR6C,UAAWxE,KAAKM,QAEpBmB,EAAegD,YAAYT,EAC/B,CACJ,CACJ,CAOOU,mBAAmBC,GACtB,MAAMlD,EAAiBC,EACvB,MAAMkD,EAAkBnD,EAAeK,kBAAkBC,UAEzD,MAAM8C,EAAaF,EAAU/C,WAAW,KAAO+C,EAAY,IAAIA,IAE/D,MAAM3C,EAAmB4C,EAAgB3C,OAAOC,GACpCA,EAAY4C,iBAAmB9E,KAAKM,cAAiB4B,EAAYE,cAAgB,UAAaF,EAAYE,YAAYR,WAAWiD,IAG7I,GAAI7C,EAAiBO,OAAS,EAAG,CAC7Bd,EAAee,eAAeR,EAClC,CACJ,CAOQR,oBAAoBuD,GACxB,IAAK/E,KAAKL,SAAWK,KAAKL,OAAOqF,WAAY,OAAO7B,UAGpD,MAAM8B,EAAgB,gBAAkBF,EAAKzD,QAAQ,MAAO,gBAC5D,MAAM4D,KAAEA,GAAS1F,EAAiB2F,aAAanF,KAAKL,OAAQsF,GAC5D,OAAOC,CACX,CAQOE,WACH,OAAOpF,KAAKM,KAChB,CAQO+E,YAAYC,EAAwBC,EAAkBC,GACzDF,EAAQG,aAAaF,EAAU,CAC3BR,KAAMS,EACNlF,MAAO,QAEf,CAMOoF,iBAGH,MAAMC,EAAO3F,KAAKM,MAAMyB,UACxB,OAAO/B,KAAK4F,UAAUD,EAC1B,CAKQC,UAAUC,GACd,GAAIA,IAAQ,aAAeA,IAAQ,SAAU,CACzC,OAAOA,CACX,CAEA,GAAIA,aAAeC,KAAM,CACrB,OAAO,IAAIA,KAAKD,EAAIE,UACxB,CAEA,GAAIC,MAAMC,QAAQJ,GAAM,CACpB,MAAMK,EAAU,GAChB,IAAK,MAAMC,KAAQN,EAAK,CACpBK,EAAQtD,KAAK5C,KAAK4F,UAAUO,GAChC,CACA,OAAOD,CACX,CAEA,MAAME,EAAe,CAAC,EACtB,IAAK,MAAMC,KAAOR,EAAK,CACnB,GAAIS,OAAOC,UAAUC,eAAeC,KAAKZ,EAAKQ,GAAM,CAChDD,EAAQC,GAAOrG,KAAK4F,UAAUC,EAAIQ,GACtC,CACJ,CACA,OAAOD,CACX,CAOO1F,YAAYqE,EAAc3D,GAC7B,MAAMsF,EAAe3B,EAAKnD,WAAW,KAAOmD,EAAO,IAAIA,IACvD/E,KAAKM,MAAMI,YAAYgG,EAActF,EACzC,CAKOuF,UACH,GAAI3G,KAAKM,MAAO,CAEZ,MAAMmB,EAAiBC,EACvB,MAAMG,EAAmBJ,EAAeK,kBAAkBC,UAC1D,MAAMC,EAAmBH,EAAiBI,OAAQC,GAC9CA,EAAIG,uBAAyBH,EAAIG,sBAAsBC,UAAYtC,KAAKM,MAAMgC,SAElF,GAAIN,EAAiBO,OAAS,EAAG,CAC7Bd,EAAee,eAAeR,EAClC,CAEAhC,KAAKM,MAAMqG,SACf,CACJ,EACH,IAAAC,EAAA,CAAAC,WAAA,MAAAD,EAAAlH,eAAA,OAAAkH,CAAA","ignoreList":[]}
|
|
@@ -145,7 +145,7 @@ export class StateManager {
|
|
|
145
145
|
const pathPrefix = arrayPath.startsWith("/") ? arrayPath : `/${arrayPath}`;
|
|
146
146
|
|
|
147
147
|
const messagesToRemove = currentMessages.filter(msg => {
|
|
148
|
-
return msg.getProcessor() === this.model && typeof msg.getTarget() === "string" && msg.getTarget().startsWith(pathPrefix);
|
|
148
|
+
return (msg as any).getProcessor() === this.model && typeof (msg as any).getTarget() === "string" && (msg as any).getTarget().startsWith(pathPrefix);
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
if (messagesToRemove.length > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IPlugin-dbg.js","names":[],"sources":["IPlugin.ts"],"sourcesContent":["/**\n * @file IPlugin.ts\n * @description Contract for all discrete UI control generation plugins.\n * Enforces a strict lifecycle for rendering, validation, and dynamic state updates.\n */\n\nimport Control from \"sap/ui/core/Control\";\nimport { IPropertyMetadata } from \"./ISchema\";\n\nexport interface IPluginValidationResult {\n isValid: boolean;\n errorMessage?: string;\n fieldKey?: string;\n fieldLabel?: string;\n}\n\n/**\n * Standardized hooks for the Policy-Based Rule Engine (PBRE) to dynamically mutate visual state.\n */\nexport interface IPluginStateReceiver {\n applyError?(errorMessage: string): void;\n clearError?(): void;\n applyVisibility?(isVisible: boolean): void;\n applyRequired?(isRequired: boolean): void;\n applyEditable?(isEditable: boolean): void;\n}\n\nexport interface IPlugin extends IPluginStateReceiver {\n /**\n * Instructs the plugin whether to delegate visual error states to the global MessageManager.\n * @param useMessageManager True if the global MessageManager is active.\n */\n setUseMessageManager?(useMessageManager: boolean): void;\n\n /**\n * Instantiates the raw UI5 control and binds it to the specified data model.\n * \n * @param fieldMetadata The JSON Schema defining the field's constraints and UI hints.\n * @param bindingPath The specific JSON path representing the field within the payload.\n * @param modelName The UI5 JSONModel alias used for data binding (defaults to 'meta').\n * @param engineScopeId The deterministic scope ID provided by the Engine.\n * @param onChange The callback fired natively when a field value blur/change occurs.\n * @returns {Control} The generated UI5 control ready to be mounted.\n */\n render(fieldMetadata: IPropertyMetadata, bindingPath: string, modelName?: string, engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string, errorMessage?: string, controlId?: string) => void, model?: unknown): Control;\n\n /**\n * Triggers the internal validation pipeline for this specific plugin instance.\n * \n * @returns {IPluginValidationResult} The structural validity state and error messages.\n */\n validate(): IPluginValidationResult;\n\n /**\n * Executes when the ConditionEngine pushes dynamic metadata mutations to the field.\n * \n * @param newMetadata The mutated schema metadata.\n */\n onStateChange(newMetadata: IPropertyMetadata): void;\n\n /**\n * Optionally allows the plugin to natively manage its own UI control's visual ValueState (e.g., red borders).\n * Used when the global MessageManager is disabled.\n * \n * @param isValid Whether the field passed validation.\n * @param errorMessage The error message to display if invalid.\n */\n setVisualValidationState?(isValid: boolean, errorMessage?: string): void;\n\n /**\n * Optional lifecycle hook executed when the parent layout is destroyed.\n * Plugins must use this to destroy isolated JSONModels, custom EventBus listeners, \n * or embedded Dialogs to prevent memory leaks in the SAP Fiori Launchpad.\n */\n destroy?(): void;\n\n /**\n * Injects the global editable mode context into the plugin before rendering.\n * @param editable True if the plugin should render in an editable mode.\n */\n setEditable?(editable: boolean): void;\n}\n"],"mappings":"","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"IPlugin-dbg.js","names":[],"sources":["IPlugin.ts"],"sourcesContent":["/**\n * @file IPlugin.ts\n * @description Contract for all discrete UI control generation plugins.\n * Enforces a strict lifecycle for rendering, validation, and dynamic state updates.\n */\n\nimport Control from \"sap/ui/core/Control\";\nimport { IPropertyMetadata } from \"./ISchema\";\n\nexport interface IPluginValidationResult {\n isValid: boolean;\n errorMessage?: string;\n fieldKey?: string;\n fieldLabel?: string;\n}\n\n/**\n * Standardized hooks for the Policy-Based Rule Engine (PBRE) to dynamically mutate visual state.\n */\nexport interface IPluginStateReceiver {\n applyError?(errorMessage: string): void;\n clearError?(): void;\n applyVisibility?(isVisible: boolean): void;\n applyRequired?(isRequired: boolean): void;\n applyEditable?(isEditable: boolean): void;\n getFieldLabel?(): string | undefined;\n}\n\nexport interface IPlugin extends IPluginStateReceiver {\n /**\n * Instructs the plugin whether to delegate visual error states to the global MessageManager.\n * @param useMessageManager True if the global MessageManager is active.\n */\n setUseMessageManager?(useMessageManager: boolean): void;\n\n /**\n * Instantiates the raw UI5 control and binds it to the specified data model.\n * \n * @param fieldMetadata The JSON Schema defining the field's constraints and UI hints.\n * @param bindingPath The specific JSON path representing the field within the payload.\n * @param modelName The UI5 JSONModel alias used for data binding (defaults to 'meta').\n * @param engineScopeId The deterministic scope ID provided by the Engine.\n * @param onChange The callback fired natively when a field value blur/change occurs.\n * @returns {Control} The generated UI5 control ready to be mounted.\n */\n render(fieldMetadata: IPropertyMetadata, bindingPath: string, modelName?: string, engineScopeId?: string, onChange?: (isValid: boolean, fieldKey?: string, errorMessage?: string, controlId?: string) => void, model?: unknown): Control;\n\n /**\n * Triggers the internal validation pipeline for this specific plugin instance.\n * \n * @returns {IPluginValidationResult} The structural validity state and error messages.\n */\n validate(): IPluginValidationResult;\n\n /**\n * Executes when the ConditionEngine pushes dynamic metadata mutations to the field.\n * \n * @param newMetadata The mutated schema metadata.\n */\n onStateChange(newMetadata: IPropertyMetadata): void;\n\n /**\n * Optionally allows the plugin to natively manage its own UI control's visual ValueState (e.g., red borders).\n * Used when the global MessageManager is disabled.\n * \n * @param isValid Whether the field passed validation.\n * @param errorMessage The error message to display if invalid.\n */\n setVisualValidationState?(isValid: boolean, errorMessage?: string): void;\n\n /**\n * Optional lifecycle hook executed when the parent layout is destroyed.\n * Plugins must use this to destroy isolated JSONModels, custom EventBus listeners, \n * or embedded Dialogs to prevent memory leaks in the SAP Fiori Launchpad.\n */\n destroy?(): void;\n\n /**\n * Injects the global editable mode context into the plugin before rendering.\n * @param editable True if the plugin should render in an editable mode.\n */\n setEditable?(editable: boolean): void;\n}\n"],"mappings":"","ignoreList":[]}
|
|
@@ -21,6 +21,7 @@ declare module "nz/co/siliconst/ui5/metaui/interfaces/IPlugin" {
|
|
|
21
21
|
applyVisibility?(isVisible: boolean): void;
|
|
22
22
|
applyRequired?(isRequired: boolean): void;
|
|
23
23
|
applyEditable?(isEditable: boolean): void;
|
|
24
|
+
getFieldLabel?(): string | undefined;
|
|
24
25
|
}
|
|
25
26
|
export interface IPlugin extends IPluginStateReceiver {
|
|
26
27
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IPlugin.d.ts","sourceRoot":"../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,+CAA+C,CAAC;IAC/D;;;;OAIG;IAEH,OAAO,OAAO,MAAM,qBAAqB,CAAC;IAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;IAElF,UAAiB,uBAAuB;QACpC,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;KACvB;IAED;;OAEG;IACH,UAAiB,oBAAoB;QACjC,UAAU,CAAC,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QACxC,UAAU,CAAC,IAAI,IAAI,CAAC;QACpB,eAAe,CAAC,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;QAC3C,aAAa,CAAC,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;QAC1C,aAAa,CAAC,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"IPlugin.d.ts","sourceRoot":"../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/interfaces/IPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,+CAA+C,CAAC;IAC/D;;;;OAIG;IAEH,OAAO,OAAO,MAAM,qBAAqB,CAAC;IAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;IAElF,UAAiB,uBAAuB;QACpC,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;KACvB;IAED;;OAEG;IACH,UAAiB,oBAAoB;QACjC,UAAU,CAAC,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QACxC,UAAU,CAAC,IAAI,IAAI,CAAC;QACpB,eAAe,CAAC,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;QAC3C,aAAa,CAAC,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;QAC1C,aAAa,CAAC,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;QAC1C,aAAa,CAAC,IAAI,MAAM,GAAG,SAAS,CAAC;KACxC;IAED,UAAiB,OAAQ,SAAQ,oBAAoB;QACjD;;;WAGG;QACH,oBAAoB,CAAC,CAAC,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;QAExD;;;;;;;;;WASG;QACH,MAAM,CAAC,aAAa,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAEzO;;;;WAIG;QACH,QAAQ,IAAI,uBAAuB,CAAC;QAEpC;;;;WAIG;QACH,aAAa,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;QAEpD;;;;;;WAMG;QACH,wBAAwB,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAEzE;;;;WAIG;QACH,OAAO,CAAC,IAAI,IAAI,CAAC;QAEjB;;;WAGG;QACH,WAAW,CAAC,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;KACzC;CAEA"}
|
|
@@ -23,6 +23,7 @@ export interface IPluginStateReceiver {
|
|
|
23
23
|
applyVisibility?(isVisible: boolean): void;
|
|
24
24
|
applyRequired?(isRequired: boolean): void;
|
|
25
25
|
applyEditable?(isEditable: boolean): void;
|
|
26
|
+
getFieldLabel?(): string | undefined;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export interface IPlugin extends IPluginStateReceiver {
|