This commit is contained in:
utf26 2024-06-09 01:58:17 -07:00 committed by GitHub
commit ac51305309
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 214 additions and 1 deletions

16
components.d.ts vendored
View File

@ -89,7 +89,9 @@ declare module '@vue/runtime-core' {
HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default']
'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
IconMdiArrowDown: typeof import('~icons/mdi/arrow-down')['default']
IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
IconMdiClose: typeof import('~icons/mdi/close')['default']
@ -108,6 +110,7 @@ declare module '@vue/runtime-core' {
Ipv6UlaGenerator: typeof import('./src/tools/ipv6-ula-generator/ipv6-ula-generator.vue')['default']
JsonDiff: typeof import('./src/tools/json-diff/json-diff.vue')['default']
JsonMinify: typeof import('./src/tools/json-minify/json-minify.vue')['default']
JsonSortMaster: typeof import('./src/tools/json-sort-master/json-sort-master.vue')['default']
JsonToCsv: typeof import('./src/tools/json-to-csv/json-to-csv.vue')['default']
JsonToToml: typeof import('./src/tools/json-to-toml/json-to-toml.vue')['default']
JsonToYaml: typeof import('./src/tools/json-to-yaml-converter/json-to-yaml.vue')['default']
@ -126,25 +129,38 @@ declare module '@vue/runtime-core' {
MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCode: typeof import('naive-ui')['NCode']
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
NColorPicker: typeof import('naive-ui')['NColorPicker']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDatePicker: typeof import('naive-ui')['NDatePicker']
NDivider: typeof import('naive-ui')['NDivider']
NEllipsis: typeof import('naive-ui')['NEllipsis']
NForm: typeof import('naive-ui')['NForm']
NFormItem: typeof import('naive-ui')['NFormItem']
NGi: typeof import('naive-ui')['NGi']
NGrid: typeof import('naive-ui')['NGrid']
NH1: typeof import('naive-ui')['NH1']
NH3: typeof import('naive-ui')['NH3']
NIcon: typeof import('naive-ui')['NIcon']
NImage: typeof import('naive-ui')['NImage']
NInputGroup: typeof import('naive-ui')['NInputGroup']
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
NInputNumber: typeof import('naive-ui')['NInputNumber']
NLabel: typeof import('naive-ui')['NLabel']
NLayout: typeof import('naive-ui')['NLayout']
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
NMenu: typeof import('naive-ui')['NMenu']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSlider: typeof import('naive-ui')['NSlider']
NSpin: typeof import('naive-ui')['NSpin']
NStatistic: typeof import('naive-ui')['NStatistic']
NSwitch: typeof import('naive-ui')['NSwitch']
NTable: typeof import('naive-ui')['NTable']
NTag: typeof import('naive-ui')['NTag']
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']

View File

@ -328,6 +328,10 @@ tools:
title: PDF signature checker
description: Verify the signatures of a PDF file. A signed PDF file contains one or more signatures that may be used to determine whether the contents of the file have been altered since the file was signed.
json-sort-master:
title: JSON sort master
description: Sort your JSON by keys and values.
json-minify:
title: JSON minify
description: Minify and compress your JSON by removing unnecessary whitespace.

View File

@ -68,4 +68,8 @@ tools:
math: Math
measurement: Measurement
text: Text
data: Data
data: Data
json-sort-master:
title: Maestro de clasificación JSON
description: Ordena tu JSON por claves y valores.

View File

@ -79,3 +79,7 @@ tools:
copied: Le token a été copié
length: Longueur
tokenPlaceholder: Le token...
json-sort-master:
title: Maître de tri JSON
description: Triez votre JSON par clés et valeurs.

View File

@ -69,3 +69,7 @@ tools:
measurement: 'Medidas'
text: 'Texto'
data: 'Dados'
json-sort-master:
title: 'Mestre de classificação JSON'
description: 'Ordene seu JSON por chaves e valores.'

View File

@ -69,3 +69,7 @@ tools:
measurement: Вимірювання
text: Текст
data: Дані
json-sort-master:
title: JSON сортувальник
description: Сортуйте ваш JSON за ключами та значеннями.

View File

@ -221,6 +221,10 @@ tools:
title: Định dạng và làm đẹp JSON
description: Định dạng chuỗi JSON của bạn thành một định dạng dễ đọc và thân thiện với con người.
json-sort-master:
title: Sắp xếp JSON
description: Sắp xếp JSON của bạn theo các khóa và giá trị.
docker-run-to-docker-compose-converter:
title: Chuyển đổi lệnh docker run thành tệp docker-compose
description: Chuyển đổi các lệnh docker run thành tệp docker-compose!

View File

@ -225,6 +225,10 @@ tools:
title: JSON美化和格式化
description: 将JSON字符串修饰为友好的可读格式。
json-sort-master:
title: JSON排序大师
description: 按键和值对您的JSON进行排序。
docker-run-to-docker-compose-converter:
title: Docker Run 到 docker-compose 转换器
description: 将 docker run 命令行转换为 docker-compose 文件!

View File

@ -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 jsonSortMaster } from './json-sort-master';
import { tool as asciiTextDrawer } from './ascii-text-drawer';
@ -104,6 +105,7 @@ export const toolsByCategory: ToolCategory[] = [
yamlToToml,
jsonToYaml,
jsonToToml,
jsonSortMaster,
listConverter,
tomlToJson,
tomlToYaml,

View File

@ -0,0 +1,13 @@
import { ArrowsSort } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: translate('tools.json-sort-master.title'),
path: '/json-sort-master',
description: translate('tools.json-sort-master.description'),
keywords: ['json', 'sort'],
component: () => import('./json-sort-master.vue'),
icon: ArrowsSort,
createdAt: new Date('2024-03-27'),
});

View File

@ -0,0 +1,80 @@
<script setup lang="ts">
import JSON5 from 'json5';
import { useStorage } from '@vueuse/core';
import { formatJson } from './json.models';
import { withDefaultOnError } from '@/utils/defaults';
import { useValidation } from '@/composable/validation';
import TextareaCopyable from '@/components/TextareaCopyable.vue';
const inputElement = ref<HTMLElement>();
const rawJson = useStorage('json-prettify:raw-json', '{"hello": "world", "foo": "bar"}');
const sortMethod = useStorage('json-prettify:sort-method', 'key_name');
const indentSize = useStorage('json-prettify:indent-size', 3);
const keyName = ref('');
const cleanJson = computed(() => withDefaultOnError(() => formatJson({ rawJson, sortMethod, keyName, indentSize }), ''));
const rawJsonValidation = useValidation({
source: rawJson,
rules: [
{
validator: v => v === '' || JSON5.parse(v),
message: 'Provided JSON is not valid.',
},
],
});
</script>
<template>
<div style="flex: 0 0 100%">
<div style="margin: 0 auto; max-width: 400px" flex justify-center gap-3>
<c-select
v-model:value="sortMethod" mb-4 style="width: 200px" label="Sort Method" :options="[
{
label: 'Key Name',
value: 'key_name',
},
{
label: 'Key Value',
value: 'key_val',
},
{
label: 'Key Name (Descending)',
value: 'key_name_desc',
},
{
label: 'Key Value (Descending)',
value: 'key_val_desc',
},
]"
/>
<c-input-text v-if="!['key_name', 'key_name_desc'].includes(sortMethod)" v-model:value="keyName" label="Key Name:" style="width: 200px" clearable raw-text />
</div>
</div>
<n-form-item
label="Your raw JSON" :feedback="rawJsonValidation.message"
:validation-status="rawJsonValidation.status"
>
<c-input-text
ref="inputElement" v-model:value="rawJson" placeholder="Paste your raw JSON here..." rows="20"
multiline autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" monospace
/>
</n-form-item>
<n-form-item label="Sorted version of your JSON">
<TextareaCopyable :value="cleanJson" language="json" :follow-height-of="inputElement" />
</n-form-item>
</template>
<style lang="less" scoped>
.result-card {
position: relative;
.copy-button {
position: absolute;
top: 10px;
right: 10px;
}
}
</style>

View File

@ -0,0 +1,74 @@
import { type MaybeRef, get } from '@vueuse/core';
import JSON5 from 'json5';
export { sortObjectKeys, sortObjectValues, formatJson };
function sortObjectKeys<T>(obj: T, sortMethod: string): T {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(value => sortObjectKeys(value, sortMethod)) as unknown as T;
}
return Object.keys(obj)
.sort((a, b) => sortMethod === 'key_name' ? a.localeCompare(b) : b.localeCompare(a))
.reduce((sortedObj, key) => {
sortedObj[key] = sortObjectKeys((obj as Record<string, unknown>)[key], sortMethod);
return sortedObj;
}, {} as Record<string, unknown>) as T;
}
function sortObjectValues<T>(obj: T, sortMethod: string, keyName: string): T {
if (Array.isArray(obj)) {
return obj.sort((a, b) => {
const valueA = a[keyName];
const valueB = b[keyName];
if (typeof valueA === 'number' && typeof valueB === 'number') {
return sortMethod === 'key_val' ? valueA - valueB : valueB - valueA;
}
if (typeof valueA === 'string' && typeof valueB === 'string') {
return sortMethod === 'key_val' ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA);
}
return 0;
});
}
if (typeof obj === 'object' && obj !== null) {
const sortedKeys = Object.keys(obj).sort((a, b) => {
if (sortMethod === 'key_val') {
return a.localeCompare(b);
}
return b.localeCompare(a);
});
return sortedKeys.reduce((sortedObj, key) => {
sortedObj[key] = sortObjectValues((obj as Record<string, unknown>)[key], sortMethod, keyName);
return sortedObj;
}, {} as Record<string, unknown>) as T;
}
return obj;
}
function formatJson({
rawJson,
sortMethod = 'key_name',
keyName = '',
indentSize = 3,
}: {
rawJson: MaybeRef<string>
sortMethod: MaybeRef<string>
keyName: MaybeRef<string>
indentSize?: MaybeRef<number>
}) {
const parsedObject = JSON5.parse(get(rawJson));
if (['key_name', 'key_name_desc'].includes(get(sortMethod))) {
return JSON.stringify(sortObjectKeys(parsedObject, get(sortMethod)), null, get(indentSize));
}
return JSON.stringify(sortObjectValues(parsedObject, get(sortMethod), get(keyName)), null, get(indentSize));
}