mirror of
				https://github.com/crypto-pro-web/crypto-pro-js.git
				synced 2025-10-31 06:13:22 +03:00 
			
		
		
		
	Актуализировал пример использования с React
This commit is contained in:
		
							parent
							
								
									3d71049078
								
							
						
					
					
						commit
						864c05f81c
					
				| @ -1,38 +0,0 @@ | ||||
| .App { | ||||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
| .App-logo { | ||||
|   height: 40vmin; | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| @media (prefers-reduced-motion: no-preference) { | ||||
|   .App-logo { | ||||
|     animation: App-logo-spin infinite 20s linear; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .App-header { | ||||
|   background-color: #282c34; | ||||
|   min-height: 100vh; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   font-size: calc(10px + 2vmin); | ||||
|   color: white; | ||||
| } | ||||
| 
 | ||||
| .App-link { | ||||
|   color: #61dafb; | ||||
| } | ||||
| 
 | ||||
| @keyframes App-logo-spin { | ||||
|   from { | ||||
|     transform: rotate(0deg); | ||||
|   } | ||||
|   to { | ||||
|     transform: rotate(360deg); | ||||
|   } | ||||
| } | ||||
| @ -1,21 +1,118 @@ | ||||
| import React from 'react'; | ||||
| import './App.css'; | ||||
| import CryptoPro from './components/CryptoPro'; | ||||
| import React, { useState } from 'react'; | ||||
| import { createAttachedSignature, createDetachedSignature, createHash } from 'crypto-pro'; | ||||
| import Message from './components/Message'; | ||||
| import Certificate from './components/Certificate'; | ||||
| import SignatureType from './components/SignatureType'; | ||||
| import Hash from './components/Hash'; | ||||
| import Signature from './components/Signature'; | ||||
| import SystemInfo from './components/SystemInfo'; | ||||
| 
 | ||||
| function App() { | ||||
|   const [message, setMessage] = useState(''); | ||||
|   const [certificate, setCertificate] = useState(null); | ||||
|   const [detachedSignature, setSignatureType] = useState(null); | ||||
|   const [hash, setHash] = useState(''); | ||||
|   const [hashStatus, setHashStatus] = useState('Не вычислен'); | ||||
|   const [hashError, setHashError] = useState(null); | ||||
|   const [signature, setSignature] = useState(''); | ||||
|   const [signatureStatus, setSignatureStatus] = useState('Не создана'); | ||||
|   const [signatureError, setSignatureError] = useState(null); | ||||
| 
 | ||||
|   async function createSignature(event) { | ||||
|     let hash; | ||||
| 
 | ||||
|     event.preventDefault(); | ||||
| 
 | ||||
|     setSignature(''); | ||||
|     setSignatureError(null); | ||||
| 
 | ||||
|     setHash(''); | ||||
|     setHashError(null); | ||||
|     setHashStatus('Вычисляется...'); | ||||
| 
 | ||||
|     try { | ||||
|       hash = await createHash(message); | ||||
| 
 | ||||
|       setHash(hash); | ||||
|     } catch (error) { | ||||
|       setHashError(error.message); | ||||
| 
 | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     setHashStatus('Не вычислен'); | ||||
|     setSignatureStatus('Создается...'); | ||||
| 
 | ||||
|     if (detachedSignature) { | ||||
|       try { | ||||
|         setSignature(await createDetachedSignature(certificate.thumbprint, hash)); | ||||
|       } catch (error) { | ||||
|         setSignatureError(error.message); | ||||
|       } | ||||
| 
 | ||||
|       setSignatureStatus('Не создана'); | ||||
| 
 | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       setSignature(await createAttachedSignature(certificate.thumbprint, message)); | ||||
|     } catch (error) { | ||||
|       setSignatureError(error.message); | ||||
|     } | ||||
| 
 | ||||
|     setSignatureStatus('Не создана'); | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <CryptoPro>{({certificate, setCertificate, signature, setSignature, error, setError}) => | ||||
|       <> | ||||
|         <CryptoPro.CertList onChange={setCertificate} onError={setError}/> | ||||
|         <CryptoPro.Sign certificate={certificate} onSign={setSignature} onError={setError}> | ||||
|           Создать подпись | ||||
|         </CryptoPro.Sign> | ||||
|         <CryptoPro.CertInfo certificate={certificate} onError={setError}/> | ||||
|         <textarea value={signature} readOnly cols="100" rows="30"/> | ||||
|         <pre>{error}</pre> | ||||
|         <CryptoPro.SystemInfo onError={setError}/> | ||||
|       </> | ||||
|     }</CryptoPro> | ||||
|     <> | ||||
|       <form onSubmit={createSignature} noValidate> | ||||
|         <fieldset> | ||||
|           <Message onChange={setMessage}/> | ||||
| 
 | ||||
|           <br/><br/> | ||||
| 
 | ||||
|           <Certificate onChange={setCertificate}/> | ||||
| 
 | ||||
|           <SignatureType onChange={setSignatureType}/> | ||||
| 
 | ||||
|           <br/><br/> | ||||
|           <hr/> | ||||
| 
 | ||||
|           <button | ||||
|             type="submit" | ||||
|             disabled={!certificate || !message}> | ||||
|             Создать подпись | ||||
|           </button> | ||||
|         </fieldset> | ||||
|       </form> | ||||
| 
 | ||||
|       <fieldset> | ||||
|         <Hash | ||||
|           hash={hash} | ||||
|           hashStatus={hashStatus} | ||||
|           hashError={hashError}/> | ||||
| 
 | ||||
|         <Signature | ||||
|           signature={signature} | ||||
|           signatureStatus={signatureStatus} | ||||
|           signatureError={signatureError}/> | ||||
| 
 | ||||
|         <p> | ||||
|           Для <a href="https://www.gosuslugi.ru/pgu/eds/" | ||||
|                  target="_blank" | ||||
|                  rel="nofollow noopener noreferrer" | ||||
|                  title="Перейти к проверке подписи">проверки</a> нужно | ||||
|           создать файл со сгенерированной подписью в кодировке UTF-8 с расширением *.sgn | ||||
|           <br/> | ||||
|           для отделенной подписи (или *.sig для совмещенной). | ||||
|         </p> | ||||
|       </fieldset> | ||||
| 
 | ||||
|       <fieldset> | ||||
|         <SystemInfo/> | ||||
|       </fieldset> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										101
									
								
								examples/react/src/components/Certificate.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								examples/react/src/components/Certificate.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,101 @@ | ||||
| import React, { useState, useEffect } from 'react'; | ||||
| import { getCertificate, getUserCertificates } from 'crypto-pro'; | ||||
| 
 | ||||
| function Certificate({onChange}) { | ||||
|   const [certificates, setCertificates] = useState([]); | ||||
|   const [certificatesError, setCertificatesError] = useState([]); | ||||
|   const [certificate, setCertificate] = useState(null); | ||||
|   const [certificateDetails, setCertificateDetails] = useState(null); | ||||
|   const [detailsError, setDetailsError] = useState(null); | ||||
| 
 | ||||
|   function selectCertificate(event) { | ||||
|     const certificate = certificates.find(({thumbprint}) => thumbprint === event.target.value); | ||||
| 
 | ||||
|     setCertificate(certificate); | ||||
|     onChange(certificate); | ||||
|   } | ||||
| 
 | ||||
|   async function loadCertificateDetails(thumbprint) { | ||||
|     try { | ||||
|       const certificate = await getCertificate(thumbprint); | ||||
| 
 | ||||
|       setCertificateDetails({ | ||||
|         name: certificate.name, | ||||
|         issuerName: certificate.issuerName, | ||||
|         subjectName: certificate.subjectName, | ||||
|         thumbprint: certificate.thumbprint, | ||||
|         validFrom: certificate.validFrom, | ||||
|         validTo: certificate.validTo, | ||||
|         isValid: await certificate.isValid(), | ||||
|         version: await certificate.getCadesProp('Version'), | ||||
|         base64: await certificate.exportBase64(), | ||||
|         algorithm: await certificate.getAlgorithm(), | ||||
|         extendedKeyUsage: await certificate.getExtendedKeyUsage(), | ||||
|         ownerInfo: await certificate.getOwnerInfo(), | ||||
|         issuerInfo: await certificate.getIssuerInfo(), | ||||
|         decodedExtendedKeyUsage: await certificate.getDecodedExtendedKeyUsage(), | ||||
|         '1.3.6.1.4.1.311.80.1': await certificate.hasExtendedKeyUsage('1.3.6.1.4.1.311.80.1'), | ||||
|         '[\'1.3.6.1.5.5.7.3.2\', \'1.3.6.1.4.1.311.10.3.12\']': await certificate.hasExtendedKeyUsage([ | ||||
|           '1.3.6.1.5.5.7.3.2', | ||||
|           '1.3.6.1.4.1.311.10.3.12' | ||||
|         ]), | ||||
|         '1.3.6.1.4.1.311.80.2': await certificate.hasExtendedKeyUsage('1.3.6.1.4.1.311.80.2'), | ||||
|         '\'1.3.6.1.5.5.7.3.3\', \'1.3.6.1.4.1.311.10.3.12\'': await certificate.hasExtendedKeyUsage([ | ||||
|           '1.3.6.1.5.5.7.3.3', | ||||
|           '1.3.6.1.4.1.311.10.3.12' | ||||
|         ]), | ||||
|       }); | ||||
|     } catch (error) { | ||||
|       setDetailsError(error); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     (async () => { | ||||
|       try { | ||||
|         setCertificates(await getUserCertificates()); | ||||
|       } catch (error) { | ||||
|         setCertificatesError(error.message); | ||||
|       } | ||||
|     })(); | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       <label htmlFor="certificate">Сертификат: *</label> | ||||
| 
 | ||||
|       <br/> | ||||
| 
 | ||||
|       <select id="certificate" onChange={selectCertificate}> | ||||
|         <option defaultValue={null}>Не выбран</option> | ||||
| 
 | ||||
|         {certificates.map(({name, thumbprint, validTo}) => | ||||
|           <option key={thumbprint} value={thumbprint}> | ||||
|             {name + ' (действителен до: ' + validTo + ')'} | ||||
|           </option> | ||||
|         )} | ||||
|       </select> | ||||
| 
 | ||||
|       <pre>{certificatesError || null}</pre> | ||||
| 
 | ||||
|       {certificate ? ( | ||||
|         <> | ||||
|           <details | ||||
|             onClick={loadCertificateDetails.bind(this, certificate.thumbprint)}> | ||||
|             <summary>Информация о сертификате</summary> | ||||
| 
 | ||||
|             <pre> | ||||
|               {certificateDetails ? ( | ||||
|                 JSON.stringify(certificateDetails, null, '  ') | ||||
|               ) : 'Запрашивается...'} | ||||
|             </pre> | ||||
|           </details> | ||||
| 
 | ||||
|           <pre>{detailsError || null}</pre> | ||||
|         </> | ||||
|       ) : null} | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default Certificate; | ||||
| @ -1,55 +0,0 @@ | ||||
| import React, { useState } from 'react'; | ||||
| 
 | ||||
| function CertInfo({certificate, onError}) { | ||||
|   const [certInfo, setCertInfo] = useState(null); | ||||
| 
 | ||||
|   async function showCertInfo() { | ||||
|     try { | ||||
|       setCertInfo({ | ||||
|         name: certificate.name, | ||||
|         issuerName: certificate.issuerName, | ||||
|         subjectName: certificate.subjectName, | ||||
|         thumbprint: certificate.thumbprint, | ||||
|         validFrom: certificate.validFrom, | ||||
|         validTo: certificate.validTo, | ||||
|         isValid: await certificate.isValid(), | ||||
|         version: await certificate.getCadesProp('Version'), | ||||
|         base64: await certificate.exportBase64(), | ||||
|         algorithm: await certificate.getAlgorithm(), | ||||
|         extendedKeyUsage: await certificate.getExtendedKeyUsage(), | ||||
|         ownerInfo: await certificate.getOwnerInfo(), | ||||
|         issuerInfo: await certificate.getIssuerInfo(), | ||||
|         decodedExtendedKeyUsage: await certificate.getDecodedExtendedKeyUsage(), | ||||
|         '1.3.6.1.4.1.311.80.1': await certificate.hasExtendedKeyUsage('1.3.6.1.4.1.311.80.1'), | ||||
|         "['1.3.6.1.5.5.7.3.2', '1.3.6.1.4.1.311.10.3.12']": await certificate.hasExtendedKeyUsage([ | ||||
|           '1.3.6.1.5.5.7.3.2', | ||||
|           '1.3.6.1.4.1.311.10.3.12' | ||||
|         ]), | ||||
|         '1.3.6.1.4.1.311.80.2': await certificate.hasExtendedKeyUsage('1.3.6.1.4.1.311.80.2'), | ||||
|         "'1.3.6.1.5.5.7.3.3', '1.3.6.1.4.1.311.10.3.12'": await certificate.hasExtendedKeyUsage([ | ||||
|           '1.3.6.1.5.5.7.3.3', | ||||
|           '1.3.6.1.4.1.311.10.3.12' | ||||
|         ]), | ||||
|       }); | ||||
|     } catch (error) { | ||||
|       onError(error.message); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       <button | ||||
|         type="button" | ||||
|         onClick={showCertInfo} | ||||
|         disabled={!certificate}> | ||||
|         Информация о сертификате | ||||
|       </button> | ||||
|       <br/> | ||||
|       {certInfo ? ( | ||||
|         <pre>{JSON.stringify(certInfo, null, '  ')}</pre> | ||||
|       ) : null} | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default CertInfo; | ||||
| @ -1,31 +0,0 @@ | ||||
| import React, { useState, useEffect } from 'react'; | ||||
| import { getUserCertificates } from 'crypto-pro'; | ||||
| 
 | ||||
| function CertList({onChange, onError}) { | ||||
|   const [certificates, setCertificates] = useState([]); | ||||
| 
 | ||||
|   function selectCertificate(event) { | ||||
|     onChange(certificates.find(({thumbprint}) => thumbprint === event.target.value)); | ||||
|   } | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     (async () => { | ||||
|       try { | ||||
|         setCertificates(await getUserCertificates()); | ||||
|       } catch (error) { | ||||
|         onError(error.message); | ||||
|       } | ||||
|     })(); | ||||
|   }, [onError]); | ||||
| 
 | ||||
|   return ( | ||||
|     <select onChange={selectCertificate}> | ||||
|       <option defaultValue={null}>Не выбран</option> | ||||
|       {certificates.map(({name, thumbprint}) => | ||||
|         <option key={thumbprint} value={thumbprint}>{name}</option> | ||||
|       )} | ||||
|     </select> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default CertList; | ||||
| @ -1,27 +0,0 @@ | ||||
| import React from 'react'; | ||||
| import { createSignature } from 'crypto-pro'; | ||||
| 
 | ||||
| function Sign({certificate, onSign, onError, children}) { | ||||
|   async function sign() { | ||||
|     // Вычислинный hash по ГОСТ Р 34.11-94 для строки: "abc"
 | ||||
|     const hash = 'b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c'; | ||||
|     const hashBase64 = window.btoa(hash); | ||||
| 
 | ||||
|     try { | ||||
|       onSign(await createSignature(certificate.thumbprint, hashBase64)); | ||||
|     } catch (error) { | ||||
|       onError(error.message); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <button | ||||
|       type="button" | ||||
|       disabled={!certificate} | ||||
|       onClick={sign}> | ||||
|       {children} | ||||
|     </button> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default Sign; | ||||
| @ -1,27 +0,0 @@ | ||||
| import { useState } from 'react'; | ||||
| import CertList from './CertList'; | ||||
| import CertInfo from './CertInfo'; | ||||
| import Sign from './Sign'; | ||||
| import SystemInfo from './SystemInfo'; | ||||
| 
 | ||||
| const CryptoPro = ({children}) => { | ||||
|   const [certificate, setCertificate] = useState(null); | ||||
|   const [signature, setSignature] = useState(''); | ||||
|   const [error, setError] = useState(''); | ||||
| 
 | ||||
|   return children({ | ||||
|     certificate, | ||||
|     setCertificate, | ||||
|     signature, | ||||
|     setSignature, | ||||
|     error, | ||||
|     setError | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| CryptoPro.CertList = CertList; | ||||
| CryptoPro.Sign = Sign; | ||||
| CryptoPro.CertInfo = CertInfo; | ||||
| CryptoPro.SystemInfo = SystemInfo; | ||||
| 
 | ||||
| export default CryptoPro; | ||||
							
								
								
									
										25
									
								
								examples/react/src/components/Hash.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								examples/react/src/components/Hash.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| import React from 'react'; | ||||
| 
 | ||||
| function Hash({hash, hashStatus, hashError}) { | ||||
|   return ( | ||||
|     <> | ||||
|       <legend>Результат</legend> | ||||
| 
 | ||||
|       <label htmlFor="hash">Хэш (ГОСТ Р 34.11-2012 256 бит):</label> | ||||
| 
 | ||||
|       <br/> | ||||
| 
 | ||||
|       <textarea | ||||
|         id="hash" | ||||
|         cols="80" | ||||
|         rows="5" | ||||
|         value={hash} | ||||
|         placeholder={hashStatus} | ||||
|         readOnly/> | ||||
| 
 | ||||
|       <pre>{hashError || null}</pre> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| export default Hash; | ||||
							
								
								
									
										35
									
								
								examples/react/src/components/Message.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								examples/react/src/components/Message.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| import React, { useEffect, useState } from 'react'; | ||||
| 
 | ||||
| function Message({onChange}) { | ||||
|   const [message, setMessage] = useState('Привет мир!'); | ||||
| 
 | ||||
|   function onMessageChange(event) { | ||||
|     setMessage(event.target.value); | ||||
|     onChange(event.target.value); | ||||
|   } | ||||
| 
 | ||||
|   useEffect(() => onChange(message)); | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       <legend>Создание подписи</legend> | ||||
| 
 | ||||
|       <label htmlFor="message">Подписываемое сообщение: *</label> | ||||
| 
 | ||||
|       <br/> | ||||
| 
 | ||||
|       <textarea | ||||
|         id="message" | ||||
|         name="message" | ||||
|         cols="80" | ||||
|         rows="5" | ||||
|         placeholder="Введите сообщение" | ||||
|         value={message} | ||||
|         onChange={onMessageChange} | ||||
|         autoFocus | ||||
|         required/> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default Message; | ||||
							
								
								
									
										23
									
								
								examples/react/src/components/Signature.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								examples/react/src/components/Signature.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| import React from 'react'; | ||||
| 
 | ||||
| function Signature({signature, signatureStatus, signatureError}) { | ||||
|   return ( | ||||
|     <> | ||||
|       <label htmlFor="signature">Подпись (PKCS7):</label> | ||||
| 
 | ||||
|       <br/> | ||||
| 
 | ||||
|       <textarea | ||||
|         id="signature" | ||||
|         cols="80" | ||||
|         rows="30" | ||||
|         value={signature} | ||||
|         placeholder={signatureStatus} | ||||
|         readOnly/> | ||||
| 
 | ||||
|       <pre>{signatureError || null}</pre> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| export default Signature; | ||||
							
								
								
									
										36
									
								
								examples/react/src/components/SignatureType.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								examples/react/src/components/SignatureType.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| import React, { useEffect, useState } from 'react'; | ||||
| 
 | ||||
| function SignatureType({onChange}) { | ||||
|   const [type, setType] = useState(true); | ||||
| 
 | ||||
|   function onTypeToggle() { | ||||
|     setType(!type); | ||||
|     onChange(!type); | ||||
|   } | ||||
| 
 | ||||
|   useEffect(() => onChange(type)); | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       <label>Тип подписи: *</label> | ||||
| 
 | ||||
|       <br/> | ||||
| 
 | ||||
|       <label> | ||||
|         <input | ||||
|           type="radio" | ||||
|           checked={!type} | ||||
|           onChange={onTypeToggle}/>Совмещенная</label> | ||||
| 
 | ||||
|       <br/> | ||||
| 
 | ||||
|       <label> | ||||
|         <input | ||||
|           type="radio" | ||||
|           checked={type} | ||||
|           onChange={onTypeToggle}/>Отделенная</label> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| export default SignatureType; | ||||
| @ -1,8 +1,9 @@ | ||||
| import React, { useState, useEffect } from 'react'; | ||||
| import { getSystemInfo, isValidSystemSetup } from 'crypto-pro'; | ||||
| 
 | ||||
| function SystemInfo({onError}) { | ||||
| function SystemInfo() { | ||||
|   const [systemInfo, setSystemInfo] = useState(null); | ||||
|   const [systemInfoError, setSystemInfoError] = useState(null); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     (async () => { | ||||
| @ -12,17 +13,27 @@ function SystemInfo({onError}) { | ||||
|           isValidSystemSetup: await isValidSystemSetup() | ||||
|         }); | ||||
|       } catch (error) { | ||||
|         onError(error.message); | ||||
|         setSystemInfoError(error.message); | ||||
|       } | ||||
|     })(); | ||||
|   }, [onError]); | ||||
|   }); | ||||
| 
 | ||||
|   if (!systemInfo) { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <pre>{JSON.stringify(systemInfo, null, '  ')}</pre> | ||||
|     <> | ||||
|       <legend>Информация о системе</legend> | ||||
| 
 | ||||
|       <pre> | ||||
|         {systemInfo ? ( | ||||
|           JSON.stringify(systemInfo, null, '  ') | ||||
|         ) : ( | ||||
|           systemInfoError || null | ||||
|         )} | ||||
|       </pre> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| @ -1,13 +0,0 @@ | ||||
| body { | ||||
|   margin: 0; | ||||
|   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', | ||||
|     'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', | ||||
|     sans-serif; | ||||
|   -webkit-font-smoothing: antialiased; | ||||
|   -moz-osx-font-smoothing: grayscale; | ||||
| } | ||||
| 
 | ||||
| code { | ||||
|   font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', | ||||
|     monospace; | ||||
| } | ||||
| @ -3,7 +3,6 @@ import 'react-app-polyfill/stable'; | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import ReactDOM from 'react-dom'; | ||||
| import './index.css'; | ||||
| import App from './App'; | ||||
| import * as serviceWorker from './serviceWorker'; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 vgoma
						vgoma