1
1
/* eslint-disable @typescript-eslint/no-unsafe-argument */
2
2
/* eslint-disable @typescript-eslint/no-unsafe-return */
3
3
/* eslint-disable @typescript-eslint/no-unsafe-call */
4
- import React from 'react' ;
4
+ import React , { useEffect } from 'react' ;
5
5
import logo from '../assets/logo.svg' ;
6
6
import '@utilitywarehouse/css-reset' ;
7
- // import '@utilitywarehouse/fontsource';
8
7
import '../styles/ui.css' ;
9
8
import { encodeContent , kebabCase } from '../utils' ;
10
- import { Heading } from '@utilitywarehouse/web-ui' ;
9
+ import {
10
+ Heading ,
11
+ Button ,
12
+ CheckboxGroup ,
13
+ Checkbox ,
14
+ Box ,
15
+ Flex ,
16
+ Alert ,
17
+ TextField ,
18
+ } from '@utilitywarehouse/web-ui' ;
11
19
12
20
const LoadingSpinner = ( ) => (
13
21
< div className = "spinner-container" >
@@ -28,7 +36,7 @@ function App() {
28
36
const repoName = 'design-systems' ;
29
37
const branchName = 'main' ;
30
38
const [ selectAll , setSelectAll ] = React . useState ( false ) ;
31
- const [ statusType , setStatusType ] = React . useState < 'success ' | 'error ' | '' > ( '' ) ;
39
+ const [ statusType , setStatusType ] = React . useState < 'green ' | 'red ' | 'cyan ' > ( 'cyan ' ) ;
32
40
33
41
React . useEffect ( ( ) => {
34
42
// Load saved GitHub token from clientStorage
@@ -38,6 +46,15 @@ function App() {
38
46
parent . postMessage ( { pluginMessage : { type : 'get-collections' } } , '*' ) ;
39
47
} , [ ] ) ;
40
48
49
+ useEffect ( ( ) => {
50
+ if ( statusMessage ) {
51
+ const timeout = setTimeout ( ( ) => {
52
+ setStatusMessage ( '' ) ;
53
+ } , 5000 ) ;
54
+ return ( ) => clearTimeout ( timeout ) ;
55
+ }
56
+ } , [ statusMessage ] ) ;
57
+
41
58
// Handle messages from the plugin code
42
59
window . onmessage = async event => {
43
60
const { pluginMessage } = event . data ;
@@ -46,11 +63,12 @@ function App() {
46
63
if ( pluginMessage ?. token ) {
47
64
setTokenLoaded ( true ) ;
48
65
setStatusMessage ( 'GitHub token loaded.' ) ;
66
+ setStatusType ( 'green' ) ;
49
67
}
50
68
} else if ( pluginMessage . type === 'variables-exported' ) {
51
69
const tokensData = pluginMessage . data ;
52
70
setStatusMessage ( 'Variables exported. Creating PRs...' ) ;
53
- setStatusType ( 'success ' ) ;
71
+ setStatusType ( 'cyan ' ) ;
54
72
await createPullRequests ( tokensData ) ;
55
73
setExporting ( false ) ;
56
74
}
@@ -68,8 +86,11 @@ function App() {
68
86
const saveToken = ( ) => {
69
87
parent . postMessage ( { pluginMessage : { type : 'save-token' , token : githubToken } } , '*' ) ;
70
88
setShowTokenInput ( false ) ;
89
+ if ( githubToken ) {
90
+ setTokenLoaded ( true ) ;
91
+ }
71
92
setStatusMessage ( 'GitHub token saved.' ) ;
72
- setStatusType ( 'success ' ) ;
93
+ setStatusType ( 'green ' ) ;
73
94
} ;
74
95
75
96
const handleSelectAll = ( ) => {
@@ -82,22 +103,10 @@ function App() {
82
103
setSelectAll ( ! selectAll ) ;
83
104
} ;
84
105
85
- const handleCollectionSelection = ( collectionKey : string ) => {
86
- setSelectedCollections ( prevSelected => {
87
- let updatedSelected ;
88
- if ( prevSelected . includes ( collectionKey ) ) {
89
- updatedSelected = prevSelected . filter ( key => key !== collectionKey ) ;
90
- } else {
91
- updatedSelected = [ ...prevSelected , collectionKey ] ;
92
- }
93
- setSelectAll ( updatedSelected . length === collections . length ) ;
94
- return updatedSelected ;
95
- } ) ;
96
- } ;
97
-
98
106
// Export variables and initiate PR creation
99
107
const exportVariables = ( ) => {
100
108
setExporting ( true ) ;
109
+ document . body . scrollTop = document . documentElement . scrollTop = 0 ;
101
110
console . log ( 'Exporting with selectedCollections:' , selectedCollections ) ;
102
111
parent . postMessage (
103
112
{
@@ -252,11 +261,11 @@ function App() {
252
261
if ( ! prResponse . ok ) throw new Error ( 'Failed to create pull request.' ) ;
253
262
254
263
setStatusMessage ( 'Pull request created successfully.' ) ;
255
- setStatusType ( 'success ' ) ;
264
+ setStatusType ( 'green ' ) ;
256
265
} catch ( error ) {
257
266
console . error ( error ) ;
258
267
setStatusMessage ( `Error: ${ error . message } ` ) ;
259
- setStatusType ( 'error ' ) ;
268
+ setStatusType ( 'red ' ) ;
260
269
} finally {
261
270
setExporting ( false ) ;
262
271
}
@@ -266,73 +275,111 @@ function App() {
266
275
setShowTokenInput ( true ) ;
267
276
} ;
268
277
278
+ function resizeWindow ( e ) {
279
+ const size = {
280
+ w : Math . max ( 50 , Math . floor ( e . clientX + 5 ) ) ,
281
+ h : Math . max ( 50 , Math . floor ( e . clientY + 5 ) ) ,
282
+ } ;
283
+ parent . postMessage ( { pluginMessage : { type : 'resize' , size : size } } , '*' ) ;
284
+ }
285
+
269
286
return (
270
287
< div >
271
288
{ tokenLoaded && ! showTokenInput && (
272
- < button onClick = { editToken } className = "edit-token-button" >
289
+ < Button
290
+ onClick = { editToken }
291
+ className = "edit-token-button"
292
+ size = "small"
293
+ sx = { { position : 'absolute' , top : 16 , right : 16 } }
294
+ >
273
295
Edit Token
274
- </ button >
296
+ </ Button >
275
297
) }
276
298
< img src = { logo } />
277
- < h2 > Export Figma Variables</ h2 >
299
+ < Heading variant = "h3" sx = { { my : 2 } } >
300
+ Export Figma Variables
301
+ </ Heading >
278
302
{ ! githubToken && (
279
- < p className = "token-message" > Enter your GitHub token to export variables and create PRs.</ p >
303
+ < Alert
304
+ colorScheme = "cyan"
305
+ text = "Enter your GitHub token to be able to export the variables and create a PR."
306
+ sx = { { mb : 3 } }
307
+ />
308
+ ) }
309
+ { loadingImport && (
310
+ < Alert colorScheme = "cyan" text = "Importing variables, please wait..." sx = { { mb : 3 } } />
280
311
) }
281
312
{ ( ( tokenLoaded && showTokenInput ) || ! tokenLoaded ) && (
282
- < div className = "token-wrap" >
283
- < label > GitHub Token:</ label >
284
- < input
313
+ < Box sx = { { padding : 3 , backgroundColor : '#fff' , borderRadius : '14px' , mb : 3 } } >
314
+ < TextField
285
315
type = "password"
316
+ label = "GitHub Token"
286
317
value = { githubToken }
287
318
onChange = { e => setGithubToken ( e . target . value ) }
288
319
/>
289
- < button onClick = { saveToken } > Save Token</ button >
290
- </ div >
320
+ < Button onClick = { saveToken } > Save Token</ Button >
321
+ </ Box >
291
322
) }
323
+ { statusMessage && < Alert colorScheme = { statusType } text = { statusMessage } sx = { { mb : 3 } } /> }
292
324
{ githubToken && (
293
- < div >
294
- < div >
295
- < div className = "top-content" >
296
- < h3 > Select Collections to Export:</ h3 >
297
- < div >
298
- < input
299
- type = "checkbox"
300
- checked = { selectAll }
301
- onChange = { handleSelectAll }
302
- id = "select-all"
303
- />
304
- < label htmlFor = "select-all" > Select All</ label >
305
- </ div >
306
- </ div >
307
- { collections . map ( collection => (
308
- < div key = { collection . key } className = "checkbox-group" >
309
- < input
310
- type = "checkbox"
325
+ < Box sx = { { padding : 3 , backgroundColor : '#fff' , borderRadius : '14px' } } >
326
+ < Box mb = { 2 } >
327
+ < CheckboxGroup direction = "column" label = "Select Collections to Export:" sx = { { mb : 3 } } >
328
+ < Checkbox
329
+ id = "select-all"
330
+ value = "select-all"
331
+ label = "Select All"
332
+ checked = { selectAll }
333
+ onCheckedChange = { handleSelectAll }
334
+ />
335
+ </ CheckboxGroup >
336
+ < CheckboxGroup
337
+ direction = "row"
338
+ wrap = "wrap"
339
+ value = { selectedCollections }
340
+ onValueChange = { val => setSelectedCollections ( val ) }
341
+ >
342
+ { collections . map ( collection => (
343
+ < Checkbox
344
+ key = { collection . key }
311
345
id = { `checkbox-${ collection . key } ` }
312
346
value = { collection . key }
313
- checked = { selectedCollections . includes ( collection . key ) }
314
- onChange = { ( ) => handleCollectionSelection ( collection . key ) }
347
+ label = { collection . name }
348
+ helperText = { collection . libraryName }
315
349
/>
316
- < label htmlFor = { `checkbox-${ collection . key } ` } >
317
- { collection . libraryName } - { collection . name }
318
- </ label >
319
- </ div >
320
- ) ) }
321
- </ div >
322
- { loadingImport && < p > Importing variables, please wait...</ p > }
323
- < button
324
- onClick = { exportVariables }
325
- disabled = { exporting || loadingImport }
326
- className = "export"
327
- >
328
- { exporting ? 'Exporting...' : 'Export Variables' }
329
- </ button >
330
- </ div >
350
+ ) ) }
351
+ </ CheckboxGroup >
352
+ </ Box >
353
+
354
+ < Flex direction = "column" align = { { mobile : 'stretch' , desktop : 'start' } } >
355
+ < Button onClick = { exportVariables } disabled = { exporting || loadingImport } fullWidth >
356
+ { exporting ? 'Exporting...' : 'Export Variables' }
357
+ </ Button >
358
+ </ Flex >
359
+ </ Box >
331
360
) }
332
361
{ ( exporting || loadingImport ) && < LoadingSpinner /> }
333
- < p className = { statusType ? `status-${ statusType } ` : '' + ' status-message' } >
334
- { statusMessage }
335
- </ p >
362
+
363
+ < svg
364
+ id = "corner"
365
+ width = "16"
366
+ height = "16"
367
+ viewBox = "0 0 16 16"
368
+ fill = "none"
369
+ xmlns = "http://www.w3.org/2000/svg"
370
+ onPointerDown = { e => {
371
+ e . currentTarget . onpointermove = resizeWindow ;
372
+ e . currentTarget . setPointerCapture ( e . pointerId ) ;
373
+ } }
374
+ onPointerUp = { e => {
375
+ e . currentTarget . onpointermove = null ;
376
+ e . currentTarget . releasePointerCapture ( e . pointerId ) ;
377
+ } }
378
+ >
379
+ < path d = "M16 0V16H0L16 0Z" fill = "white" />
380
+ < path d = "M6.22577 16H3L16 3V6.22576L6.22577 16Z" fill = "#8C8C8C" />
381
+ < path d = "M11.8602 16H8.63441L16 8.63441V11.8602L11.8602 16Z" fill = "#8C8C8C" />
382
+ </ svg >
336
383
</ div >
337
384
) ;
338
385
}
0 commit comments