@@ -2,6 +2,8 @@ import { LightningElement, api } from 'lwc';
22import { getFunctionsAndOperators } from 'c/functions' ;
33import monaco from '@salesforce/resourceUrl/monaco' ;
44import getFunctions from "@salesforce/apex/PlaygroundController.getFunctionNames" ;
5+ import validate from '@salesforce/apex/PlaygroundController.validate' ;
6+ import { ShowToastEvent } from 'lightning/platformShowToastEvent' ;
57
68export default class MiniEditor extends LightningElement {
79 @api
@@ -27,11 +29,26 @@ export default class MiniEditor extends LightningElement {
2729 @api
2830 defaultExpression = '' ;
2931
32+ @api
33+ enableValidation = false ;
34+
35+ @api
36+ recordId = '' ;
37+
3038 iframeUrl = `${ monaco } /main.html` ;
3139
3240 categories = [ ] ;
3341 expression = '' ;
3442 lastHoveredFunction = null ;
43+ result = { } ;
44+ diagnostics = {
45+ cpuTime : "Unavailable" ,
46+ dmlStatements : "Unavailable" ,
47+ queries : "Unavailable" ,
48+ queryRows : "Unavailable" ,
49+ } ;
50+ ast = "" ;
51+ showResults = false ;
3552
3653 async connectedCallback ( ) {
3754 this . categories = await getFunctionsAndOperators ( ) ;
@@ -50,6 +67,14 @@ export default class MiniEditor extends LightningElement {
5067 return this . variant === 'editor' ;
5168 }
5269
70+ get showValidationButton ( ) {
71+ return this . enableValidation && ( this . variant === 'editor' || this . variant === 'textarea' ) ;
72+ }
73+
74+ get resultColor ( ) {
75+ return this . result . type === 'error' ? 'slds-text-color_error' : 'slds-text-color_default' ;
76+ }
77+
5378 async iframeLoaded ( ) {
5479 const functionKeywords = await getFunctions ( ) ;
5580 this . iframeWindow . postMessage ( {
@@ -132,4 +157,143 @@ export default class MiniEditor extends LightningElement {
132157 }
133158 }
134159 }
160+
161+ handleFunctionClick ( event ) {
162+ event . preventDefault ( ) ;
163+ const functionName = event . target . dataset . name ;
164+ if ( ! functionName ) {
165+ return ;
166+ }
167+
168+ // Find the function to insert
169+ for ( const category of this . categories ) {
170+ const foundValue = category . values . find ( ( value ) => value . name === functionName ) ;
171+ if ( foundValue ) {
172+ const functionToInsert = foundValue . autoCompleteValue || foundValue . name ;
173+
174+ if ( this . variant === 'editor' ) {
175+ // For Monaco editor, send message to iframe
176+ this . iframeWindow . postMessage ( {
177+ name : 'append' ,
178+ payload : functionToInsert
179+ } ) ;
180+ } else if ( this . variant === 'textarea' || this . variant === 'input' ) {
181+ // For textarea and input, insert at cursor position
182+ const inputElement = this . template . querySelector ( `${ this . variant } [name="expression"]` ) ;
183+ if ( inputElement ) {
184+ const start = inputElement . selectionStart ;
185+ const end = inputElement . selectionEnd ;
186+ const text = inputElement . value ;
187+ const before = text . substring ( 0 , start ) ;
188+ const after = text . substring ( end , text . length ) ;
189+
190+ this . expression = before + functionToInsert + after ;
191+ inputElement . value = this . expression ;
192+
193+ // Set cursor position after inserted text
194+ setTimeout ( ( ) => {
195+ const newPosition = start + functionToInsert . length ;
196+ inputElement . setSelectionRange ( newPosition , newPosition ) ;
197+ inputElement . focus ( ) ;
198+ } , 0 ) ;
199+ }
200+ }
201+ break ;
202+ }
203+ }
204+ }
205+
206+ handleRecordIdChange ( event ) {
207+ this . recordId = event . target . value ;
208+ }
209+
210+ async handleValidate ( ) {
211+ if ( ! this . expression ) {
212+ return ;
213+ }
214+
215+ if ( this . variant === 'editor' ) {
216+ this . iframeWindow . postMessage ( {
217+ name : 'clear_markers'
218+ } ) ;
219+ }
220+
221+ try {
222+ const result = await validate ( { expr : this . expression , recordId : this . recordId } ) ;
223+ if ( result . error ) {
224+ this . result = {
225+ type : "error" ,
226+ payload : [ { type : 'error' , message : result . error . message } ]
227+ } ;
228+
229+ if ( this . variant === 'editor' ) {
230+ this . iframeWindow . postMessage ( {
231+ name : 'evaluation_error' ,
232+ payload : result . error
233+ } ) ;
234+ }
235+ } else {
236+ const payload = result . result ?? null ;
237+ const toPrint = result . toPrint . map ( ( item ) => item ?? null ) ;
238+ const allResults = [ ...toPrint , payload ] ;
239+ this . result = {
240+ type : "success" ,
241+ payload : allResults . map ( ( current , i ) => ( {
242+ type : i === allResults . length - 1 ? "result" : "printed" ,
243+ message : this . _syntaxHighlight ( JSON . stringify ( current , null , 4 ) )
244+ } ) )
245+ } ;
246+ }
247+
248+ this . _setDiagnostics ( result . diagnostics ?? { } ) ;
249+ this . ast = result . ast ?
250+ this . _syntaxHighlight ( JSON . stringify ( result . ast , null , 4 ) ) :
251+ "" ;
252+ this . showResults = true ;
253+ } catch ( e ) {
254+ const event = new ShowToastEvent ( {
255+ title : 'Unknown error' ,
256+ variant : 'error' ,
257+ message : e . body ?. message || 'An unknown error occurred' ,
258+ } ) ;
259+ this . dispatchEvent ( event ) ;
260+
261+ this . result = {
262+ type : "error" ,
263+ payload : [ { type : 'error' , message : 'An unknown error occurred while evaluating the expression.' } ]
264+ } ;
265+ this . showResults = true ;
266+ }
267+ }
268+
269+ _setDiagnostics ( diagnostics ) {
270+ this . diagnostics = Object . keys ( diagnostics ) . reduce ( ( acc , key ) => {
271+ acc [ key ] = diagnostics [ key ] ?? "Unavailable" ;
272+ return acc ;
273+ } , {
274+ cpuTime : "Unavailable" ,
275+ dmlStatements : "Unavailable" ,
276+ queries : "Unavailable" ,
277+ queryRows : "Unavailable" ,
278+ } ) ;
279+ }
280+
281+ _syntaxHighlight ( json ) {
282+ json = json . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) ;
283+ return json . replace ( / ( " ( \\ u [ a - z A - Z 0 - 9 ] { 4 } | \\ [ ^ u ] | [ ^ \\ " ] ) * " ( \s * : ) ? | \b ( t r u e | f a l s e | n u l l ) \b | - ? \d + (?: \. \d * ) ? (?: [ e E ] [ + \- ] ? \d + ) ? ) / g, function ( match ) {
284+ let style = 'color: #c2410c;' ;
285+ if ( / ^ " / . test ( match ) ) {
286+ if ( / : $ / . test ( match ) ) {
287+ style = 'color: #b91c1c;' ;
288+ } else {
289+ style = 'color: #0f766e;' ;
290+ }
291+ } else if ( / t r u e | f a l s e / . test ( match ) ) {
292+ style = 'color: #4338ca;' ;
293+ } else if ( / n u l l / . test ( match ) ) {
294+ style = 'color: #0e7490;' ;
295+ }
296+ return '<span style="' + style + '">' + match + '</span>' ;
297+ } ) ;
298+ }
135299}
0 commit comments