11/********************************************************************************
22 * Copyright (c) 2018 Contributors to the Eclipse Foundation
33 *
4+ * See the NOTICE file(s) distributed with this work for additional
5+ * information regarding copyright ownership.
6+ *
7+ * This program and the accompanying materials are made available under the
8+ * terms of the Eclipse Public License v. 2.0 which is available at
9+ * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and
10+ *
411 * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
512 ********************************************************************************/
613import React , {
@@ -15,8 +22,10 @@ import ediTDorContext from "../../context/ediTDorContext";
1522import DialogTemplate from "./DialogTemplate" ;
1623import {
1724 processConversionTMtoTD ,
25+ extractPlaceholders ,
1826 isVersionValid ,
1927} from "../../services/operations" ;
28+ import TmInputForm from "../base/TmInputForm" ;
2029import TextField from "../base/TextField" ;
2130
2231export interface ConvertTmDialogRef {
@@ -25,28 +34,65 @@ export interface ConvertTmDialogRef {
2534}
2635
2736const ConvertTmDialog = forwardRef < ConvertTmDialogRef > ( ( props , ref ) => {
28- const context = useContext ( ediTDorContext ) ;
37+ const context : IEdiTDorContext = useContext ( ediTDorContext ) ;
38+ const td : string = context . offlineTD ;
39+ const [ htmlInputs , setHtmlInputs ] = useState < JSX . Element [ ] > ( [ ] ) ;
40+ const [ display , setDisplay ] = useState < boolean > ( ( ) => {
41+ return false ;
42+ } ) ;
43+ const [ affordanceElements , setAffordanceElements ] = useState < JSX . Element [ ] > (
44+ [ ]
45+ ) ;
46+ const [ placeholderValues , setPlaceholderValues ] = useState <
47+ Record < string , string >
48+ > ( { } ) ;
2949
30- const [ display , setDisplay ] = useState ( false ) ;
31- const [ validVersion , setValidVersion ] = useState ( false ) ;
32- const [ versionInput , setVersionInput ] = useState ( "" ) ;
50+ const [ validVersion , setValidVersion ] = useState < boolean > ( false ) ;
51+ const [ versionInput , setVersionInput ] = useState < string > ( "" ) ;
3352
3453 useEffect ( ( ) => {
3554 setValidVersion ( isVersionValid ( context . parsedTD ) ) ;
3655 } , [ context . parsedTD ] ) ;
3756
57+ useEffect ( ( ) => {
58+ setHtmlInputs ( createHtmlInputs ( context . offlineTD ) ) ;
59+ setAffordanceElements ( createAffordanceElements ( context . offlineTD ) ) ;
60+ } , [ context . offlineTD ] ) ;
61+
62+ useEffect ( ( ) => {
63+ if ( td ) {
64+ const placeholders = extractPlaceholders ( td ) ;
65+ const initialValues = placeholders . reduce < Record < string , string > > (
66+ ( acc , key ) => {
67+ acc [ key ] = "" ;
68+ return acc ;
69+ } ,
70+ { }
71+ ) ;
72+ setPlaceholderValues ( initialValues ) ;
73+ }
74+ } , [ td ] ) ;
75+
3876 useImperativeHandle ( ref , ( ) => ( {
3977 openModal : ( ) => setDisplay ( true ) ,
4078 close : ( ) => setDisplay ( false ) ,
4179 } ) ) ;
4280
81+ const handleFieldChange = ( placeholder : string , value : string ) => {
82+ setPlaceholderValues ( ( prev ) => ( {
83+ ...prev ,
84+ [ placeholder ] : value ,
85+ } ) ) ;
86+ } ;
4387 const handleGenerateTd = ( ) => {
88+ const selections = getSelectedAffordances ( affordanceElements ) ;
89+
4490 const newTD = processConversionTMtoTD (
4591 context . offlineTD ,
46- { } ,
47- [ ] ,
48- [ ] ,
49- [ ] ,
92+ placeholderValues ,
93+ selections . properties ,
94+ selections . actions ,
95+ selections . events ,
5096 versionInput
5197 ) ;
5298 const resultJson = JSON . stringify ( newTD , null , 2 ) ;
@@ -57,30 +103,190 @@ const ConvertTmDialog = forwardRef<ConvertTmDialogRef>((props, ref) => {
57103 ) ;
58104 } ;
59105
106+ const handleVersionInputChange = (
107+ e : React . ChangeEvent < HTMLInputElement >
108+ ) : void => {
109+ const value = e . target . value ;
110+ const trimmedValue = value . trim ( ) ;
111+ setVersionInput ( trimmedValue ) ;
112+ } ;
113+
60114 if ( ! display ) return null ;
61115
62- return ReactDOM . createPortal (
63- < DialogTemplate
64- onHandleEventLeftButton = { ( ) => setDisplay ( false ) }
65- onHandleEventRightButton = { handleGenerateTd }
66- rightButton = { "Generate TD" }
67- title = { "Generate TD From TM" }
68- description = { "Please provide values to switch the placeholders with." }
69- >
70- < >
71- { ! validVersion && (
72- < TextField
73- label = "TD instance version"
74- onChange = { ( e ) => setVersionInput ( e . target . value . trim ( ) ) }
75- value = { versionInput }
76- placeholder = "ex: 1.0.0"
116+ if ( display ) {
117+ return ReactDOM . createPortal (
118+ < DialogTemplate
119+ onHandleEventLeftButton = { ( ) => setDisplay ( false ) }
120+ onHandleEventRightButton = { handleGenerateTd }
121+ rightButton = { "Generate TD" }
122+ title = { "Generate TD From TM" }
123+ description = { "Please provide values to switch the placeholders with." }
124+ >
125+ < >
126+ < TmInputForm
127+ inputValues = { placeholderValues }
128+ onValueChange = { handleFieldChange }
77129 />
78- ) }
79- </ >
80- </ DialogTemplate > ,
81- document . getElementById ( "modal-root" ) as HTMLElement
82- ) ;
130+
131+ { ! validVersion && (
132+ < TextField
133+ label = "TD instance version"
134+ id = "instance"
135+ autoFocus = { true }
136+ onChange = { handleVersionInputChange }
137+ placeholder = "ex: 1.0.0"
138+ value = { versionInput }
139+ helperText = "The Thing Model contains a version without instance key and corresponding value. If you leave this field empty it will automatic generate a instance value."
140+ > </ TextField >
141+ ) }
142+ < h2 className = "pb-2 pt-4 text-gray-400" >
143+ Select/unselect the interaction affordances you would like to see in
144+ the new TD.
145+ </ h2 >
146+
147+ < div className = "affordances-container" > { affordanceElements } </ div >
148+ </ >
149+ </ DialogTemplate > ,
150+ document . getElementById ( "modal-root" ) as HTMLElement
151+ ) ;
152+ }
153+
154+ return null ;
83155} ) ;
84156
157+ function getSelectedAffordances ( elements : JSX . Element [ ] ) {
158+ const result = {
159+ properties : [ ] as string [ ] ,
160+ actions : [ ] as string [ ] ,
161+ events : [ ] as string [ ] ,
162+ } ;
163+
164+ elements . forEach ( ( element ) => {
165+ if ( element . props . className . includes ( "form-checkbox" ) ) {
166+ const checkbox = document . getElementById (
167+ element . props . children [ 0 ] . props . id
168+ ) as HTMLInputElement | null ;
169+ if ( checkbox ?. checked ) {
170+ const [ type , name ] = element . key ?. toString ( ) . split ( "/" ) ?? [ ] ;
171+
172+ if ( type === "properties" ) result . properties . push ( name ) ;
173+ else if ( type === "actions" ) result . actions . push ( name ) ;
174+ else if ( type === "events" ) result . events . push ( name ) ;
175+ }
176+ }
177+ } ) ;
178+
179+ return result ;
180+ }
181+
182+ // Create affordance element remains similar to your original implementation
183+ function createAffordanceElements ( tmContent : string ) : JSX . Element [ ] {
184+ try {
185+ if ( ! tmContent ) return [ ] ;
186+ const parsed = JSON . parse ( tmContent ) ;
187+ const { properties, actions, events, requiredFields } =
188+ extractAffordances ( parsed ) ;
189+
190+ const propertyElements = createAffordanceHtml (
191+ "properties" ,
192+ properties ,
193+ requiredFields
194+ ) ;
195+ const actionElements = createAffordanceHtml (
196+ "actions" ,
197+ actions ,
198+ requiredFields
199+ ) ;
200+ const eventElements = createAffordanceHtml (
201+ "events" ,
202+ events ,
203+ requiredFields
204+ ) ;
205+
206+ return [ ...propertyElements , ...actionElements , ...eventElements ] ;
207+ } catch ( e ) {
208+ console . error ( "Error creating affordance elements:" , e ) ;
209+ return [ ] ;
210+ }
211+ }
212+
213+ const createHtmlInputs = ( td : string ) : JSX . Element [ ] => {
214+ try {
215+ let htmlProperties : JSX . Element [ ] = [ ] ;
216+ let htmlActions : JSX . Element [ ] = [ ] ;
217+ let htmlEvents : JSX . Element [ ] = [ ] ;
218+
219+ try {
220+ const parsed = JSON . parse ( td ) ;
221+
222+ const { properties, actions, events, requiredFields } =
223+ extractAffordances ( parsed ) ;
224+
225+ htmlProperties = createAffordanceHtml (
226+ "properties" ,
227+ properties ,
228+ requiredFields
229+ ) ;
230+ htmlActions = createAffordanceHtml ( "actions" , actions , requiredFields ) ;
231+ htmlEvents = createAffordanceHtml ( "events" , events , requiredFields ) ;
232+ } catch ( ignored ) { }
233+
234+ return [ ...htmlProperties , ...htmlActions , ...htmlEvents ] ;
235+ } catch ( e ) {
236+ console . error ( "Error creating HTML inputs:" , e ) ;
237+ return [ ] ;
238+ }
239+ } ;
240+
241+ function createAffordanceHtml (
242+ affName : "properties" | "actions" | "events" ,
243+ affContainer : string [ ] ,
244+ requiredFields : { [ k : string ] : string [ ] }
245+ ) : JSX . Element [ ] {
246+ return affContainer . map ( ( aff ) => {
247+ const required = requiredFields [ affName ] . includes ( aff ) ;
248+ return (
249+ < div key = { `${ affName } /${ aff } ` } className = "form-checkbox py-1 pl-2" >
250+ < input
251+ id = { `${ affName } /${ aff } ` }
252+ className = "form-checkbox-input"
253+ type = "checkbox"
254+ value = { `#${ affName } /${ aff } ` }
255+ disabled = { required }
256+ defaultChecked = { true }
257+ title = { required ? "This field is required by the TM." : "" }
258+ data-interaction = { affName }
259+ />
260+ < label
261+ className = "form-checkbox-label pl-2"
262+ htmlFor = { `${ affName } /${ aff } ` }
263+ > { `#${ affName } /${ aff } ` } </ label >
264+ </ div >
265+ ) ;
266+ } ) ;
267+ }
268+
269+ function extractAffordances ( parsed : any ) {
270+ const properties = Object . keys ( parsed [ "properties" ] || { } ) ;
271+ const actions = Object . keys ( parsed [ "actions" ] || { } ) ;
272+ const events = Object . keys ( parsed [ "events" ] || { } ) ;
273+ const requiredFields = { properties : [ ] , actions : [ ] , events : [ ] } ;
274+
275+ if ( parsed [ "tm:required" ] ) {
276+ for ( const field of parsed [ "tm:required" ] ) {
277+ if ( field . startsWith ( "#properties/" ) )
278+ // @ts -ignore
279+ requiredFields [ "properties" ] . push ( field . split ( "/" ) [ 1 ] ) ;
280+ else if ( field . startsWith ( "#actions/" ) )
281+ // @ts -ignore
282+ requiredFields [ "actions" ] . push ( field . split ( "/" ) [ 1 ] ) ;
283+ else if ( field . startsWith ( "#events/" ) )
284+ // @ts -ignore
285+ requiredFields [ "events" ] . push ( field . split ( "/" ) [ 1 ] ) ;
286+ }
287+ }
288+ return { properties, actions, events, requiredFields } ;
289+ }
290+
85291ConvertTmDialog . displayName = "ConvertTmDialog" ;
86292export default ConvertTmDialog ;
0 commit comments