feat(json-diff): add new tool to get the diff of two given JSONs
This commit is contained in:
		
							parent
							
								
									7d7cc99866
								
							
						
					
					
						commit
						80af4a3eea
					
				| @ -56,6 +56,7 @@ | ||||
|     "fuse.js": "^6.6.2", | ||||
|     "highlight.js": "^11.7.0", | ||||
|     "json5": "^2.2.3", | ||||
|     "jsondiffpatch-rc": "^0.4.2", | ||||
|     "jwt-decode": "^3.1.2", | ||||
|     "lodash": "^4.17.21", | ||||
|     "mathjs": "^10.6.4", | ||||
| @ -68,6 +69,7 @@ | ||||
|     "plausible-tracker": "^0.3.8", | ||||
|     "qrcode": "^1.5.1", | ||||
|     "randombytes": "^2.1.0", | ||||
|     "sanitize-html": "^2.10.0", | ||||
|     "sql-formatter": "^8.2.0", | ||||
|     "ts-pattern": "^4.2.2", | ||||
|     "ua-parser-js": "^1.0.35", | ||||
|  | ||||
							
								
								
									
										77
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										77
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -70,6 +70,9 @@ dependencies: | ||||
|   json5: | ||||
|     specifier: ^2.2.3 | ||||
|     version: 2.2.3 | ||||
|   jsondiffpatch-rc: | ||||
|     specifier: ^0.4.2 | ||||
|     version: 0.4.2 | ||||
|   jwt-decode: | ||||
|     specifier: ^3.1.2 | ||||
|     version: 3.1.2 | ||||
| @ -106,6 +109,9 @@ dependencies: | ||||
|   randombytes: | ||||
|     specifier: ^2.1.0 | ||||
|     version: 2.1.0 | ||||
|   sanitize-html: | ||||
|     specifier: ^2.10.0 | ||||
|     version: 2.10.0 | ||||
|   sql-formatter: | ||||
|     specifier: ^8.2.0 | ||||
|     version: 8.2.0 | ||||
| @ -4039,6 +4045,10 @@ packages: | ||||
|     engines: {node: '>=8'} | ||||
|     dev: true | ||||
| 
 | ||||
|   /diff-match-patch@1.0.5: | ||||
|     resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /dijkstrajs@1.0.2: | ||||
|     resolution: {integrity: sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==} | ||||
|     dev: false | ||||
| @ -4076,9 +4086,16 @@ packages: | ||||
|       entities: 2.2.0 | ||||
|     dev: true | ||||
| 
 | ||||
|   /dom-serializer@2.0.0: | ||||
|     resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} | ||||
|     dependencies: | ||||
|       domelementtype: 2.3.0 | ||||
|       domhandler: 5.0.3 | ||||
|       entities: 4.4.0 | ||||
|     dev: false | ||||
| 
 | ||||
|   /domelementtype@2.3.0: | ||||
|     resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /domexception@4.0.0: | ||||
|     resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} | ||||
| @ -4094,6 +4111,13 @@ packages: | ||||
|       domelementtype: 2.3.0 | ||||
|     dev: true | ||||
| 
 | ||||
|   /domhandler@5.0.3: | ||||
|     resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} | ||||
|     engines: {node: '>= 4'} | ||||
|     dependencies: | ||||
|       domelementtype: 2.3.0 | ||||
|     dev: false | ||||
| 
 | ||||
|   /domutils@2.8.0: | ||||
|     resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} | ||||
|     dependencies: | ||||
| @ -4102,6 +4126,14 @@ packages: | ||||
|       domhandler: 4.3.1 | ||||
|     dev: true | ||||
| 
 | ||||
|   /domutils@3.0.1: | ||||
|     resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} | ||||
|     dependencies: | ||||
|       dom-serializer: 2.0.0 | ||||
|       domelementtype: 2.3.0 | ||||
|       domhandler: 5.0.3 | ||||
|     dev: false | ||||
| 
 | ||||
|   /dot-case@3.0.4: | ||||
|     resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} | ||||
|     dependencies: | ||||
| @ -4184,6 +4216,11 @@ packages: | ||||
|     resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} | ||||
|     engines: {node: '>=0.12'} | ||||
| 
 | ||||
|   /entities@4.4.0: | ||||
|     resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} | ||||
|     engines: {node: '>=0.12'} | ||||
|     dev: false | ||||
| 
 | ||||
|   /errno@0.1.8: | ||||
|     resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} | ||||
|     hasBin: true | ||||
| @ -5349,6 +5386,15 @@ packages: | ||||
|       entities: 3.0.1 | ||||
|     dev: true | ||||
| 
 | ||||
|   /htmlparser2@8.0.2: | ||||
|     resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} | ||||
|     dependencies: | ||||
|       domelementtype: 2.3.0 | ||||
|       domhandler: 5.0.3 | ||||
|       domutils: 3.0.1 | ||||
|       entities: 4.4.0 | ||||
|     dev: false | ||||
| 
 | ||||
|   /http-proxy-agent@4.0.1: | ||||
|     resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} | ||||
|     engines: {node: '>= 6'} | ||||
| @ -5611,6 +5657,11 @@ packages: | ||||
|       isobject: 3.0.1 | ||||
|     dev: false | ||||
| 
 | ||||
|   /is-plain-object@5.0.0: | ||||
|     resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} | ||||
|     engines: {node: '>=0.10.0'} | ||||
|     dev: false | ||||
| 
 | ||||
|   /is-potential-custom-element-name@1.0.1: | ||||
|     resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} | ||||
|     dev: true | ||||
| @ -5924,6 +5975,15 @@ packages: | ||||
|     resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /jsondiffpatch-rc@0.4.2: | ||||
|     resolution: {integrity: sha512-Y1qBHcinsSX6E24KvYEAzybrmhnyy/eg2uhablTW76oKrdY0nYeWoXRlMCvTXG0Nv/zlzGwAfb3mxg1JzitLug==} | ||||
|     engines: {node: '>=8.17.0'} | ||||
|     hasBin: true | ||||
|     dependencies: | ||||
|       diff-match-patch: 1.0.5 | ||||
|     dev: false | ||||
|     bundledDependencies: [] | ||||
| 
 | ||||
|   /jsonfile@6.1.0: | ||||
|     resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} | ||||
|     dependencies: | ||||
| @ -6698,6 +6758,10 @@ packages: | ||||
|     engines: {node: '>= 0.10'} | ||||
|     dev: true | ||||
| 
 | ||||
|   /parse-srcset@1.0.2: | ||||
|     resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /parse5@6.0.1: | ||||
|     resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} | ||||
|     dev: true | ||||
| @ -7422,6 +7486,17 @@ packages: | ||||
|     resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /sanitize-html@2.10.0: | ||||
|     resolution: {integrity: sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==} | ||||
|     dependencies: | ||||
|       deepmerge: 4.3.1 | ||||
|       escape-string-regexp: 4.0.0 | ||||
|       htmlparser2: 8.0.2 | ||||
|       is-plain-object: 5.0.0 | ||||
|       parse-srcset: 1.0.2 | ||||
|       postcss: 8.4.21 | ||||
|     dev: false | ||||
| 
 | ||||
|   /sax@1.2.4: | ||||
|     resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} | ||||
|     dev: true | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import { tool as base64FileConverter } from './base64-file-converter'; | ||||
| import { tool as base64StringConverter } from './base64-string-converter'; | ||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||
| import { tool as jsonDiff } from './json-diff'; | ||||
| import { tool as yamlToJson } from './yaml-to-json-converter'; | ||||
| import { tool as jsonToYaml } from './json-to-yaml-converter'; | ||||
| import { tool as ipv6UlaGenerator } from './ipv6-ula-generator'; | ||||
| @ -102,6 +103,7 @@ export const toolsByCategory: ToolCategory[] = [ | ||||
|       crontabGenerator, | ||||
|       jsonViewer, | ||||
|       jsonMinify, | ||||
|       jsonDiff, | ||||
|       sqlPrettify, | ||||
|       chmodCalculator, | ||||
|       dockerRunToDockerComposeConverter, | ||||
|  | ||||
							
								
								
									
										12
									
								
								src/tools/json-diff/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/json-diff/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| import { ArrowsShuffle } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'JSON diff', | ||||
|   path: '/json-diff', | ||||
|   description: 'Compares two given JSONs and build a visual comparison of them', | ||||
|   keywords: ['json', 'diff', 'visual'], | ||||
|   component: () => import('./json-diff.vue'), | ||||
|   icon: ArrowsShuffle, | ||||
|   createdAt: new Date('2023-04-12'), | ||||
| }); | ||||
							
								
								
									
										70
									
								
								src/tools/json-diff/json-diff-result.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/tools/json-diff/json-diff-result.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| <template> | ||||
|   <div style="flex: 0 0 100%"> | ||||
|     <n-space style="margin: 0 auto; max-width: 600px" justify="center"> | ||||
|       <n-form-item label="Show unchanged :" label-placement="left" label-width="160"> | ||||
|         <n-switch v-model:value="showUnchanged" :disabled="!validInput" /> | ||||
|       </n-form-item> | ||||
|     </n-space> | ||||
|   </div> | ||||
| 
 | ||||
|   <div style="flex: 0 0 100%"> | ||||
|     <n-space style="margin: 0 auto; max-width: 600px" justify="center"> | ||||
|       <n-card title="Diff result" data-test-id="result"> | ||||
|         <div ref="result"> | ||||
|           <SanitizedHtml :html="diffResult" /> | ||||
|         </div> | ||||
|       </n-card> | ||||
|     </n-space> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { computed, ref, toRefs } from 'vue'; | ||||
| import { withDefaultOnError } from '@/utils/defaults'; | ||||
| import JSON5 from 'json5'; | ||||
| import { DiffPatcher, formatters } from 'jsondiffpatch-rc'; | ||||
| import SanitizedHtml from './sanitized-html.vue'; | ||||
| import './styles.css'; | ||||
| 
 | ||||
| const result = ref<HTMLDivElement | null>(null); | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   showHideUnchanged(); | ||||
| }); | ||||
| 
 | ||||
| const props = withDefaults(defineProps<{ left: string; right: string }>(), { left: '', right: '' }); | ||||
| const { left, right } = toRefs(props); | ||||
| const showUnchanged = ref(true); | ||||
| 
 | ||||
| const leftJson = computed(() => withDefaultOnError(() => JSON5.parse(left.value), '')); | ||||
| const rightJson = computed(() => withDefaultOnError(() => JSON5.parse(right.value), '')); | ||||
| const diffResult = computed(() => { | ||||
|   if (!validInput.value) { | ||||
|     return ''; | ||||
|   } | ||||
| 
 | ||||
|   const diffPatcher = new DiffPatcher({ | ||||
|     objectHash: function (obj, index) { | ||||
|       if (typeof obj._id !== 'undefined') { | ||||
|         return obj._id; | ||||
|       } | ||||
|       if (typeof obj.id !== 'undefined') { | ||||
|         return obj.id; | ||||
|       } | ||||
|       return '$$index:' + index; | ||||
|     }, | ||||
|     arrays: { detectMove: true, includeValueOnMove: true }, | ||||
|   }); | ||||
|   const delta = diffPatcher.diff(leftJson.value, rightJson.value); | ||||
|   return delta === undefined ? 'both JSONs are identical' : formatters.html.format(delta, leftJson.value); | ||||
| }); | ||||
| const validInput = computed(() => leftJson.value !== '' && rightJson.value !== ''); | ||||
| 
 | ||||
| watch([diffResult, showUnchanged], () => showHideUnchanged(), { immediate: true }); | ||||
| 
 | ||||
| function showHideUnchanged() { | ||||
|   if (result.value) { | ||||
|     formatters.html.showUnchanged(showUnchanged.value, result.value, 200); | ||||
|   } | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										28
									
								
								src/tools/json-diff/json-diff.e2e.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/tools/json-diff/json-diff.e2e.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| import { test, expect } from '@playwright/test'; | ||||
| 
 | ||||
| test.describe('Tool - Json diff', () => { | ||||
|   test.beforeEach(async ({ page }) => { | ||||
|     await page.goto('/json-diff'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Has correct title', async ({ page }) => { | ||||
|     await expect(page).toHaveTitle('JSON diff - IT Tools'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Compare two identical JSONs with corresponding result message', async ({ page }) => { | ||||
|     const json = '{"foo":"bar","list":["item",{"key":"value"}]}'; | ||||
|     await page.getByTestId('leftJson').fill(json); | ||||
|     await page.getByTestId('rightJson').fill(json); | ||||
| 
 | ||||
|     const generatedResult = await page.getByTestId('result').innerText(); | ||||
| 
 | ||||
|     expect(generatedResult.trim()).toContain('both JSONs are identical'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Compare two different JSONs with corresponding result message', async ({ page }) => { | ||||
|     await page.getByTestId('leftJson').fill('{"foo":"bar","list":["item","item2",{"key":"value"}]}'); | ||||
|     await page.getByTestId('rightJson').fill('{"foo":"bar","list":["item",{"key":"value"}]}'); | ||||
| 
 | ||||
|     await expect(page.getByTestId('result').getByRole('listitem')).toHaveCount(6); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										146
									
								
								src/tools/json-diff/json-diff.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/tools/json-diff/json-diff.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,146 @@ | ||||
| <template> | ||||
|   <n-form-item | ||||
|     label="Your first json" | ||||
|     :feedback="leftJsonValidation.message" | ||||
|     :validation-status="leftJsonValidation.status" | ||||
|   > | ||||
|     <n-input | ||||
|       v-model:value="rawLeftJson" | ||||
|       placeholder="Paste your first json here..." | ||||
|       type="textarea" | ||||
|       rows="20" | ||||
|       autocomplete="off" | ||||
|       autocorrect="off" | ||||
|       autocapitalize="off" | ||||
|       spellcheck="false" | ||||
|       :input-props="{ 'data-test-id': 'leftJson' }" | ||||
|     /> | ||||
|   </n-form-item> | ||||
|   <n-form-item | ||||
|     label="Your json to compare" | ||||
|     :feedback="rightJsonValidation.message" | ||||
|     :validation-status="rightJsonValidation.status" | ||||
|   > | ||||
|     <n-input | ||||
|       v-model:value="rawRightJson" | ||||
|       placeholder="Paste your json to compare here..." | ||||
|       type="textarea" | ||||
|       rows="20" | ||||
|       autocomplete="off" | ||||
|       autocorrect="off" | ||||
|       autocapitalize="off" | ||||
|       spellcheck="false" | ||||
|       :input-props="{ 'data-test-id': 'rightJson' }" | ||||
|     /> | ||||
|   </n-form-item> | ||||
| 
 | ||||
|   <JsonDiffResult :left="rawLeftJson" :right="rawRightJson" /> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { useStorage } from '@vueuse/core'; | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| import JSON5 from 'json5'; | ||||
| import JsonDiffResult from './json-diff-result.vue'; | ||||
| 
 | ||||
| const rawLeftJson = useStorage( | ||||
|   'json-compare:left-json', | ||||
|   `{ | ||||
|   "Actors": [ | ||||
|     { | ||||
|       "name": "Tom Cruise", | ||||
|       "age": 56, | ||||
|       "Born At": "Syracuse, NY", | ||||
|       "Birthdate": "July 3, 1962", | ||||
|       "photo": "https://jsonformatter.org/img/tom-cruise.jpg", | ||||
|       "wife": null, | ||||
|       "weight": 67.5, | ||||
|       "hasChildren": true, | ||||
|       "hasGreyHair": false, | ||||
|       "children": [ | ||||
|         "Suri", | ||||
|         "Isabella Jane", | ||||
|         "Connor" | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "name": "Robert Downey Jr.", | ||||
|       "age": 53, | ||||
|       "Born At": "New York City, NY", | ||||
|       "Birthdate": "April 4, 1965", | ||||
|       "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", | ||||
|       "wife": "Susan Downey", | ||||
|       "weight": 77.1, | ||||
|       "hasChildren": true, | ||||
|       "hasGreyHair": false, | ||||
|       "children": [ | ||||
|         "Indio Falconer", | ||||
|         "Avri Roel", | ||||
|         "Exton Elias" | ||||
|       ] | ||||
|     } | ||||
|   ] | ||||
| }`, | ||||
| ); | ||||
| const rawRightJson = useStorage( | ||||
|   'json-compare:right-json', | ||||
|   `{ | ||||
|   "Actors": [ | ||||
|     { | ||||
|       "name": "Tom Cruise", | ||||
|       "age": 56, | ||||
|       "Born At": "Syracuse, NY", | ||||
|       "Birthdate": "July 3, 1962", | ||||
|       "photo": "https://jsonformatter.org/img/tom-cruise.jpg", | ||||
|       "wife": null, | ||||
|       "weight": 57.7, | ||||
|       "hasChildren": true, | ||||
|       "hasGreyHair": false, | ||||
|       "children": [ | ||||
|         "Connor", | ||||
|         "Suri", | ||||
|         "Isabella Jane" | ||||
|       ], | ||||
|       "favoriteFood": "Spaghetti" | ||||
|     }, | ||||
|     { | ||||
|       "name": "Robert Downey Sr.", | ||||
|       "age": 53, | ||||
|       "Born At": "New York City, NY", | ||||
|       "Birthdate": "April 4, 1965", | ||||
|       "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", | ||||
|       "weight": 77.1, | ||||
|       "hasChildren": true, | ||||
|       "hasGreyHair": false, | ||||
|       "children": [ | ||||
|         "Indio Falconer", | ||||
|         "Avri Roel", | ||||
|         "Exton Elias" | ||||
|       ] | ||||
|     } | ||||
|   ] | ||||
| }`, | ||||
| ); | ||||
| 
 | ||||
| const leftJsonValidation = useValidation({ | ||||
|   source: rawLeftJson, | ||||
|   rules: [ | ||||
|     { | ||||
|       validator: (v) => v === '' || JSON5.parse(v), | ||||
|       message: 'Provided JSON is not valid.', | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| 
 | ||||
| const rightJsonValidation = useValidation({ | ||||
|   source: rawRightJson, | ||||
|   rules: [ | ||||
|     { | ||||
|       validator: (v) => v === '' || JSON5.parse(v), | ||||
|       message: 'Provided JSON is not valid.', | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped></style> | ||||
							
								
								
									
										39
									
								
								src/tools/json-diff/sanitized-html.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/tools/json-diff/sanitized-html.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| <template> | ||||
|   <span ref="block"></span> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import sanitizeHtml from 'sanitize-html'; | ||||
| import { Ref, ref, watch } from 'vue'; | ||||
| 
 | ||||
| const block = ref() as Ref<HTMLSpanElement>; | ||||
| 
 | ||||
| const props = withDefaults(defineProps<{ html: string }>(), { html: undefined }); | ||||
| 
 | ||||
| let options = { | ||||
|   allowedTags: ['div', 'ul', 'li', 'pre'], | ||||
|   allowedAttributes: { | ||||
|     div: ['class'], | ||||
|     ul: ['class'], | ||||
|     li: ['class', 'data-key'], | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| const onUpdateContent = () => { | ||||
|   if (block.value) { | ||||
|     block.value.innerHTML = sanitizeHtml(props.html, options); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   onUpdateContent(); | ||||
| }); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.html as string | undefined, | ||||
|   () => { | ||||
|     onUpdateContent(); | ||||
|   }, | ||||
|   { immediate: true }, | ||||
| ); | ||||
| </script> | ||||
							
								
								
									
										184
									
								
								src/tools/json-diff/styles.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								src/tools/json-diff/styles.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,184 @@ | ||||
| :root { | ||||
|     --jsdiff-color_fg: #888888ff; | ||||
|     --jsdiff-color_fg_location: #bbbbbbff; | ||||
|     --jsdiff-color_bg_added: #066f1988; | ||||
|     --jsdiff-color_bg_deleted: #ab060988; | ||||
|     --jsdiff-color_bg_moved: #ffffbbff; | ||||
|     --jsdiff-font_family: monospace; | ||||
| } | ||||
| .jsondiffpatch-delta { | ||||
|     font-family: var(--jsdiff-font_family); | ||||
|     margin: 0; | ||||
|     padding: 0 0 0 12px; | ||||
|     display: inline-block; | ||||
| } | ||||
| .jsondiffpatch-delta pre { | ||||
|     font-family: var(--jsdiff-font_family); | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
|     display: inline-block; | ||||
| } | ||||
| .jsondiffpatch-delta ul { | ||||
|     list-style-type: none; | ||||
|     padding: 0 0 0 20px; | ||||
|     margin: 0; | ||||
| } | ||||
| ul.jsondiffpatch-delta { | ||||
|     list-style-type: none; | ||||
|     padding: 0 0 0 20px; | ||||
|     margin: 0; | ||||
| } | ||||
| .jsondiffpatch-added .jsondiffpatch-property-name { | ||||
|     background: var(--jsdiff-color_bg_added); | ||||
| } | ||||
| .jsondiffpatch-added .jsondiffpatch-value pre { | ||||
|     background: var(--jsdiff-color_bg_added); | ||||
| } | ||||
| .jsondiffpatch-modified .jsondiffpatch-right-value { | ||||
|     margin-left: 5px; | ||||
| } | ||||
| .jsondiffpatch-modified .jsondiffpatch-right-value pre { | ||||
|     background: var(--jsdiff-color_bg_added); | ||||
| } | ||||
| .jsondiffpatch-modified .jsondiffpatch-left-value pre { | ||||
|     background: var(--jsdiff-color_bg_deleted); | ||||
|     text-decoration: line-through; | ||||
| } | ||||
| .jsondiffpatch-modified >.jsondiffpatch-left-value pre:after { | ||||
|     content: ''; | ||||
| } | ||||
| .jsondiffpatch-modified .jsondiffpatch-value { | ||||
|     display: inline-block; | ||||
| } | ||||
| .jsondiffpatch-textdiff-added { | ||||
|     background: var(--jsdiff-color_bg_added); | ||||
| } | ||||
| .jsondiffpatch-deleted .jsondiffpatch-property-name { | ||||
|     background: var(--jsdiff-color_bg_deleted); | ||||
|     text-decoration: line-through; | ||||
| } | ||||
| .jsondiffpatch-deleted pre { | ||||
|     background: var(--jsdiff-color_bg_deleted); | ||||
|     text-decoration: line-through; | ||||
| } | ||||
| .jsondiffpatch-textdiff-deleted { | ||||
|     background: var(--jsdiff-color_bg_deleted); | ||||
|     text-decoration: line-through; | ||||
| } | ||||
| .jsondiffpatch-unchanged { | ||||
|     color: var(--jsdiff-color_fg); | ||||
|     transition: all 0.5s; | ||||
|     -webkit-transition: all 0.5s; | ||||
|     overflow-y: hidden; | ||||
| } | ||||
| .jsondiffpatch-movedestination { | ||||
|     color: var(--jsdiff-color_fg); | ||||
| } | ||||
| .jsondiffpatch-movedestination >.jsondiffpatch-value { | ||||
|     transition: all 0.5s; | ||||
|     -webkit-transition: all 0.5s; | ||||
|     overflow-y: hidden; | ||||
| } | ||||
| .jsondiffpatch-unchanged-showing .jsondiffpatch-unchanged { | ||||
|     max-height: 100px; | ||||
| } | ||||
| .jsondiffpatch-unchanged-showing .jsondiffpatch-movedestination >.jsondiffpatch-value { | ||||
|     max-height: 100px; | ||||
| } | ||||
| .jsondiffpatch-unchanged-showing .jsondiffpatch-arrow { | ||||
|     display: none; | ||||
| } | ||||
| .jsondiffpatch-unchanged-hidden .jsondiffpatch-unchanged { | ||||
|     max-height: 0; | ||||
| } | ||||
| .jsondiffpatch-unchanged-hidden .jsondiffpatch-movedestination >.jsondiffpatch-value { | ||||
|     max-height: 0; | ||||
|     display: block; | ||||
| } | ||||
| .jsondiffpatch-unchanged-hiding .jsondiffpatch-movedestination >.jsondiffpatch-value { | ||||
|     display: block; | ||||
|     max-height: 0; | ||||
| } | ||||
| .jsondiffpatch-unchanged-hiding .jsondiffpatch-unchanged { | ||||
|     max-height: 0; | ||||
| } | ||||
| .jsondiffpatch-unchanged-hiding .jsondiffpatch-arrow { | ||||
|     display: none; | ||||
| } | ||||
| .jsondiffpatch-unchanged-visible .jsondiffpatch-unchanged { | ||||
|     max-height: 100px; | ||||
| } | ||||
| .jsondiffpatch-unchanged-visible .jsondiffpatch-movedestination >.jsondiffpatch-value { | ||||
|     max-height: 100px; | ||||
| } | ||||
| .jsondiffpatch-value { | ||||
|     display: inline-block; | ||||
| } | ||||
| .jsondiffpatch-value pre:after { | ||||
|     content: ','; | ||||
| } | ||||
| .jsondiffpatch-property-name { | ||||
|     display: inline-block; | ||||
|     padding-right: 5px; | ||||
|     vertical-align: top; | ||||
| } | ||||
| .jsondiffpatch-property-name:after { | ||||
|     content: ': '; | ||||
| } | ||||
| .jsondiffpatch-child-node-type-array >.jsondiffpatch-property-name:after { | ||||
|     content: ': ['; | ||||
| } | ||||
| .jsondiffpatch-child-node-type-array:after { | ||||
|     content: '],'; | ||||
| } | ||||
| div.jsondiffpatch-child-node-type-array:before { | ||||
|     content: '['; | ||||
| } | ||||
| div.jsondiffpatch-child-node-type-array:after { | ||||
|     content: ']'; | ||||
| } | ||||
| .jsondiffpatch-child-node-type-object >.jsondiffpatch-property-name:after { | ||||
|     content: ': {'; | ||||
| } | ||||
| .jsondiffpatch-child-node-type-object:after { | ||||
|     content: '},'; | ||||
| } | ||||
| div.jsondiffpatch-child-node-type-object:before { | ||||
|     content: '{'; | ||||
| } | ||||
| div.jsondiffpatch-child-node-type-object:after { | ||||
|     content: '}'; | ||||
| } | ||||
| li:last-child >.jsondiffpatch-value pre:after { | ||||
|     content: ''; | ||||
| } | ||||
| .jsondiffpatch-moved .jsondiffpatch-value { | ||||
|     display: none; | ||||
| } | ||||
| .jsondiffpatch-moved .jsondiffpatch-moved-destination { | ||||
|     display: inline-block; | ||||
|     background: var(--jsdiff-color_bg_moved); | ||||
|     color: var(--jsdiff-color_fg); | ||||
| } | ||||
| .jsondiffpatch-moved .jsondiffpatch-moved-destination:before { | ||||
|     content: ' => '; | ||||
| } | ||||
| ul.jsondiffpatch-textdiff { | ||||
|     padding: 0; | ||||
| } | ||||
| .jsondiffpatch-textdiff-location { | ||||
|     color: var(--jsdiff-color_fg_location); | ||||
|     display: inline-block; | ||||
|     min-width: 60px; | ||||
| } | ||||
| .jsondiffpatch-textdiff-line { | ||||
|     display: inline-block; | ||||
| } | ||||
| .jsondiffpatch-textdiff-line-number:after { | ||||
|     content: ','; | ||||
| } | ||||
| .jsondiffpatch-error { | ||||
|     background: red; | ||||
|     color: white; | ||||
|     font-weight: bold; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user