diff --git a/components.d.ts b/components.d.ts
index e31119b3..e34c50e1 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -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']
@@ -159,6 +175,7 @@ declare module '@vue/runtime-core' {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
+ SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
diff --git a/locales/en.yml b/locales/en.yml
index 50d48af9..c038dad9 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -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 white spaces.
diff --git a/locales/es.yml b/locales/es.yml
index b87502fc..15683602 100644
--- a/locales/es.yml
+++ b/locales/es.yml
@@ -68,4 +68,8 @@ tools:
math: Math
measurement: Measurement
text: Text
- data: Data
\ No newline at end of file
+ data: Data
+
+ json-sort-master:
+ title: Maestro de clasificación JSON
+ description: Ordena tu JSON por claves y valores.
\ No newline at end of file
diff --git a/locales/fr.yml b/locales/fr.yml
index 35c7df2e..7f25d371 100644
--- a/locales/fr.yml
+++ b/locales/fr.yml
@@ -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.
\ No newline at end of file
diff --git a/locales/pt.yml b/locales/pt.yml
index 96fdaed4..e953a3ea 100644
--- a/locales/pt.yml
+++ b/locales/pt.yml
@@ -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.'
diff --git a/locales/uk.yml b/locales/uk.yml
index 2d28f157..449eeb94 100644
--- a/locales/uk.yml
+++ b/locales/uk.yml
@@ -69,3 +69,7 @@ tools:
measurement: Вимірювання
text: Текст
data: Дані
+
+ json-sort-master:
+ title: JSON сортувальник
+ description: Сортуйте ваш JSON за ключами та значеннями.
diff --git a/locales/vi.yml b/locales/vi.yml
index 9eb16bf0..3131c1b7 100644
--- a/locales/vi.yml
+++ b/locales/vi.yml
@@ -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!
diff --git a/locales/zh.yml b/locales/zh.yml
index 160fe1fa..f1c06bde 100644
--- a/locales/zh.yml
+++ b/locales/zh.yml
@@ -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 文件!
diff --git a/src/tools/index.ts b/src/tools/index.ts
index aa861c93..e3896bd4 100644
--- a/src/tools/index.ts
+++ b/src/tools/index.ts
@@ -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,
diff --git a/src/tools/json-sort-master/index.ts b/src/tools/json-sort-master/index.ts
new file mode 100644
index 00000000..5bb84d1c
--- /dev/null
+++ b/src/tools/json-sort-master/index.ts
@@ -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'),
+});
diff --git a/src/tools/json-sort-master/json-sort-master.vue b/src/tools/json-sort-master/json-sort-master.vue
new file mode 100644
index 00000000..28d5f1c9
--- /dev/null
+++ b/src/tools/json-sort-master/json-sort-master.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tools/json-sort-master/json.models.ts b/src/tools/json-sort-master/json.models.ts
new file mode 100644
index 00000000..de487e49
--- /dev/null
+++ b/src/tools/json-sort-master/json.models.ts
@@ -0,0 +1,74 @@
+import { type MaybeRef, get } from '@vueuse/core';
+import JSON5 from 'json5';
+
+export { sortObjectKeys, sortObjectValues, formatJson };
+
+function sortObjectKeys(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)[key], sortMethod);
+ return sortedObj;
+ }, {} as Record) as T;
+}
+
+function sortObjectValues(obj: any, sortMethod: string, keyName: string): any {
+ 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)[key], sortMethod, keyName);
+ return sortedObj;
+ }, {} as Record) as T;
+ }
+
+ return obj;
+}
+
+function formatJson({
+ rawJson,
+ sortMethod = 'key_name',
+ keyName = '',
+ indentSize = 3,
+}: {
+ rawJson: MaybeRef
+ sortMethod: MaybeRef
+ keyName: MaybeRef
+ indentSize?: MaybeRef
+}) {
+ 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));
+}