@@ -64,6 +64,7 @@ export class SparqlEditor extends HTMLElement {
6464 container . innerHTML = `
6565 <div id="sparql-editor">
6666 <button id="sparql-add-prefixes-btn" class="btn" style="margin-bottom: 0.3em;">Add common prefixes</button>
67+ <button id="sparql-save-example-btn" class="btn" style="margin-bottom: 0.3em;">Save query as example</button>
6768 <div id="yasgui"></div>
6869 </div>
6970 <div>
@@ -172,6 +173,61 @@ export class SparqlEditor extends HTMLElement {
172173 } ) ;
173174 } ) ;
174175
176+ // Button to pop a dialog to save the query as an example in a turtle file
177+ const addExampleBtnEl = this . shadowRoot ?. getElementById ( "sparql-save-example-btn" ) ;
178+ const capitalize = ( str : any ) => str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 ) . toLowerCase ( ) ;
179+ addExampleBtnEl ?. addEventListener ( "click" , ( ) => {
180+ const dialog = document . createElement ( "dialog" ) ;
181+ dialog . style . width = "400px" ;
182+ dialog . style . padding = "1em" ;
183+ dialog . style . borderRadius = "8px" ;
184+ dialog . style . borderColor = "#cccccc" ;
185+ // <textarea id="description" name="description" rows="4" style="width: 100%;"></textarea><br><br>
186+ dialog . innerHTML = `
187+ <form id="example-form" method="dialog">
188+ <h3>Save query as example</h3>
189+ <p>Save the current query as an example in a turtle file that you can then submit to the repository where all examples are stored.</p>
190+ <label for="description">Description:</label><br>
191+ <input type="text" id="description" name="description" style="width: 100%;" maxlength="200"><br><br>
192+ <label for="keywords">Keywords (optional, comma separated):</label><br>
193+ <input type="text" id="keywords" name="keywords" style="width: 100%;"><br><br>
194+ <button type="submit" class="btn">Save</button>
195+ <button type="button" class="btn" onclick="this.closest('dialog').close()">Cancel</button>
196+ </form>
197+ ` ;
198+ this . shadowRoot ?. appendChild ( dialog ) ;
199+ dialog . showModal ( ) ;
200+ dialog . querySelector ( "#example-form" ) ?. addEventListener ( "submit" , ( e ) => {
201+ e . preventDefault ( ) ;
202+ const description = ( dialog . querySelector ( "#description" ) as HTMLTextAreaElement ) . value ;
203+ const keywordsStr = ( dialog . querySelector ( "#keywords" ) as HTMLInputElement ) . value . split ( "," ) . map ( ( kw : string ) => `"${ kw . trim ( ) } "` ) . join ( ', ' ) ;
204+ const queryType = capitalize ( this . yasgui ?. getTab ( ) ?. getYasqe ( ) . getQueryType ( ) )
205+ const endpointUrlWithSlash = this . endpointUrl . endsWith ( '/' ) ? this . endpointUrl : `${ this . endpointUrl } /` ;
206+ const exampleNumberForId = ( this . exampleQueries . length + 1 ) . toString ( ) . padStart ( 3 , '0' ) ;
207+ const keywordsBit = keywordsStr . length > 2 ? `schema:keyword ${ keywordsStr } ;\n ` : '' ;
208+
209+ const shaclStr = `@prefix ex: <${ endpointUrlWithSlash } .well-known/sparql-examples/> .
210+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
211+ @prefix schema: <https://schema.org/> .
212+ @prefix sh: <http://www.w3.org/ns/shacl#> .
213+
214+ ex:${ exampleNumberForId } a sh:SPARQLExecutable${ [ 'Select' , 'Construct' , "Ask" ] . includes ( queryType ) ? `,
215+ sh:SPARQL${ queryType } Executable` : '' } ;
216+ rdfs:comment "${ description } "@en ;
217+ sh:prefixes _:sparql_examples_prefixes ;
218+ sh:${ queryType . toLowerCase ( ) } """${ this . yasgui ?. getTab ( ) ?. getYasqe ( ) . getValue ( ) } """ ;
219+ ${ keywordsBit } schema:target <${ this . endpointUrl } > .`
220+
221+ const dataStr = `data:text/turtle;charset=utf-8,${ encodeURIComponent ( shaclStr ) } ` ;
222+ const downloadAnchor = document . createElement ( "a" ) ;
223+ downloadAnchor . setAttribute ( "href" , dataStr ) ;
224+ downloadAnchor . setAttribute ( "download" , `${ exampleNumberForId } .ttl` ) ;
225+ downloadAnchor . click ( ) ;
226+ dialog . close ( ) ;
227+ } ) ;
228+ } ) ;
229+
230+
175231 // Parse query params from URL and auto run query if provided in URL
176232 // NOTE: Yasqe already automatically load query param in the editor and run the query
177233 // But it does not trigger the .on("query") event, so it does not add limit
@@ -191,6 +247,11 @@ export class SparqlEditor extends HTMLElement {
191247 }
192248 }
193249
250+ // TODO: there is a SUGGESTIONS_LIMIT of 100 on the get. So doing filtering in postProcessHints is not ideal...
251+ // Best would be to have a way to filter the results in the get method directly
252+ // (it will also reduce the amount of SPARQL request done!)
253+ // It ctreates problem with UniProt query3 up:Natural_Variant_Annotation
254+
194255 // Original autocompleters: https://github.com/zazuko/Yasgui/blob/main/packages/yasqe/src/autocompleters/classes.ts#L8
195256 // Fork examples: https://github.com/zazuko/Yasgui/blob/main/webpack/pages/yasqe.html#L61
196257 prefixesCompleter = {
@@ -553,6 +614,9 @@ export class SparqlEditor extends HTMLElement {
553614 return curie ;
554615 }
555616 }
617+
618+
619+
556620}
557621
558622function extractAllSubjectsAndTypes ( query : string ) : Map < string , Set < string > > {
0 commit comments