diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json index 90487551..e52fba3c 100644 --- a/.eslintrc-auto-import.json +++ b/.eslintrc-auto-import.json @@ -293,6 +293,7 @@ "ExtractDefaultPropTypes": true, "ExtractPropTypes": true, "ExtractPublicPropTypes": true, - "WritableComputedRef": true + "WritableComputedRef": true, + "useClipboardItems": true } } diff --git a/components.d.ts b/components.d.ts index 52c1dbf2..1c8e1232 100644 --- a/components.d.ts +++ b/components.d.ts @@ -71,6 +71,7 @@ declare module '@vue/runtime-core' { CTooltip: typeof import('./src/ui/c-tooltip/c-tooltip.vue')['default'] 'CTooltip.demo': typeof import('./src/ui/c-tooltip/c-tooltip.demo.vue')['default'] CurlConverter: typeof import('./src/tools/curl-converter/curl-converter.vue')['default'] + CurrencyConverter: typeof import('./src/tools/currency-converter/currency-converter.vue')['default'] DateTimeConverter: typeof import('./src/tools/date-time-converter/date-time-converter.vue')['default'] 'DemoHome.page': typeof import('./src/ui/demo/demo-home.page.vue')['default'] DemoWrapper: typeof import('./src/ui/demo/demo-wrapper.vue')['default'] @@ -148,6 +149,7 @@ declare module '@vue/runtime-core' { JsUnobfuscator: typeof import('./src/tools/js-unobfuscator/js-unobfuscator.vue')['default'] JwtParser: typeof import('./src/tools/jwt-parser/jwt-parser.vue')['default'] KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default'] + ListComparer: typeof import('./src/tools/list-comparer/list-comparer.vue')['default'] ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default'] LiveCode: typeof import('./src/tools/live-code/live-code.vue')['default'] LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default'] diff --git a/package.json b/package.json index feaf03e1..795d03c7 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@tiptap/pm": "2.1.6", "@tiptap/starter-kit": "2.1.6", "@tiptap/vue-3": "2.0.3", + "@types/arr-diff": "^4.0.3", "@types/figlet": "^1.5.8", "@types/markdown-it": "^13.0.7", "@types/memorystream": "^0.3.4", @@ -54,6 +55,7 @@ "@vueuse/core": "^10.11.1", "@vueuse/head": "^1.0.0", "@vueuse/router": "^10.0.0", + "arr-diff": "^4.0.0", "bcryptjs": "^2.4.3", "change-case": "^4.1.2", "cidr-tools": "^7.0.4", @@ -65,6 +67,8 @@ "cronstrue": "^2.26.0", "crypto-js": "^4.1.1", "curlconverter": "^4.10.1", + "currency-codes-ts": "^3.0.0", + "currency-exchanger-js": "^1.0.4", "date-fns": "^2.29.3", "decomposerize": "^1.4.1", "dns-query": "^0.11.2", @@ -74,6 +78,7 @@ "exif-be-gone": "^1.5.1", "exifreader": "^4.20.0", "fflate": "^0.8.2", + "fast_array_intersect": "^1.1.0", "figlet": "^1.7.0", "figue": "^1.2.0", "fuse.js": "^6.6.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90477ad0..6e58cf29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: '@tiptap/vue-3': specifier: 2.0.3 version: 2.0.3(@tiptap/core@2.1.12(@tiptap/pm@2.1.6))(@tiptap/pm@2.1.6)(vue@3.3.4) + '@types/arr-diff': + specifier: ^4.0.3 + version: 4.0.3 '@types/figlet': specifier: ^1.5.8 version: 1.5.8 @@ -62,6 +65,9 @@ importers: '@vueuse/router': specifier: ^10.0.0 version: 10.0.0(vue-router@4.1.6(vue@3.3.4))(vue@3.3.4) + arr-diff: + specifier: ^4.0.0 + version: 4.0.0 bcryptjs: specifier: ^2.4.3 version: 2.4.3 @@ -95,6 +101,12 @@ importers: curlconverter: specifier: ^4.10.1 version: 4.11.0 + currency-codes-ts: + specifier: ^3.0.0 + version: 3.0.0 + currency-exchanger-js: + specifier: ^1.0.4 + version: 1.0.4 date-fns: specifier: ^2.29.3 version: 2.29.3 @@ -119,6 +131,9 @@ importers: exifreader: specifier: ^4.20.0 version: 4.25.0 + fast_array_intersect: + specifier: ^1.1.0 + version: 1.1.0 fflate: specifier: ^0.8.2 version: 0.8.2 @@ -2127,6 +2142,9 @@ packages: '@tsconfig/node18@18.2.0': resolution: {integrity: sha512-yhxwIlFVSVcMym3O31HoMnRXpoenmpIxcj4Yoes2DUpe+xCJnA7ECQP1Vw889V0jTt/2nzvpLQ/UuMYCd3JPIg==} + '@types/arr-diff@4.0.3': + resolution: {integrity: sha512-oIBe7qtc48Q1JPNuqSIUYCNMQxYzTkQAEw07b0NEZpwKbUeOswzex3qfvXCLrZDIQ9t3ucIxH6JqFmjRTPbDEg==} + '@types/bcryptjs@2.4.2': resolution: {integrity: sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==} @@ -2764,6 +2782,10 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} + arr-diff@4.0.0: + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} + engines: {node: '>=0.10.0'} + array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -3220,6 +3242,9 @@ packages: resolution: {integrity: sha512-M1VdV3hpBAsd1Zzvqcvf63wgDpcwCuS4WiNEVFpJ0s33MGO2sVDTfswYq0EPypCmESrCzmgL8h68DTzJuSDbVA==} hasBin: true + cross-fetch@3.1.8: + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -3282,6 +3307,13 @@ packages: resolution: {integrity: sha512-jBSvfDN10L6rGWVlkAYgtkIG8lYprDvtBgos7mafxtv15keYeQWsxUgnzns3JmqEcGJMeaGlDNdRUszURPCUaw==} hasBin: true + currency-codes-ts@3.0.0: + resolution: {integrity: sha512-ZJeCpq5uY2t8dDl4xdF15shkp5o8jrHcD4lHftK/O8j8xTHlXg0E5YhpZbRJvnLRaKe+JQh1/q1AI9Wc2Dl3Nw==} + engines: {node: '>=14'} + + currency-exchanger-js@1.0.4: + resolution: {integrity: sha512-znd30Ueuf3IFTwNEuSy45uBEZZXJIUG9Z4Cpym5VxSxafx/CazPCRx5NMrqOFE2ZYPXezo7FfJLa8SuES3PnYw==} + dash-get@1.0.2: resolution: {integrity: sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==} @@ -3831,6 +3863,9 @@ packages: fast-uri@3.0.3: resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==} + fast_array_intersect@1.1.0: + resolution: {integrity: sha512-/DCilZlUdz2XyNDF+ASs0PwY+RKG9Y4Silp/gbS72Cvbg4oibc778xcecg+pnNyiNHYgh/TApsiDTjpdniyShw==} + fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} @@ -8844,6 +8879,8 @@ snapshots: '@tsconfig/node18@18.2.0': {} + '@types/arr-diff@4.0.3': {} + '@types/bcryptjs@2.4.2': {} '@types/chai-subset@1.3.3': @@ -9719,6 +9756,8 @@ snapshots: aria-query@5.3.2: {} + arr-diff@4.0.0: {} + array-buffer-byte-length@1.0.0: dependencies: call-bind: 1.0.7 @@ -10273,6 +10312,12 @@ snapshots: cronstrue@2.26.0: {} + cross-fetch@3.1.8: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -10357,6 +10402,16 @@ snapshots: transitivePeerDependencies: - tree_sitter + currency-codes-ts@3.0.0: + dependencies: + lodash-es: 4.17.21 + + currency-exchanger-js@1.0.4: + dependencies: + cross-fetch: 3.1.8 + transitivePeerDependencies: + - encoding + dash-get@1.0.2: {} data-uri-to-buffer@4.0.1: {} @@ -11017,6 +11072,8 @@ snapshots: fast-uri@3.0.3: {} + fast_array_intersect@1.1.0: {} + fastq@1.15.0: dependencies: reusify: 1.0.4 diff --git a/src/tools/currency-converter/currency-converter.vue b/src/tools/currency-converter/currency-converter.vue new file mode 100644 index 00000000..eee3696f --- /dev/null +++ b/src/tools/currency-converter/currency-converter.vue @@ -0,0 +1,119 @@ + + + diff --git a/src/tools/currency-converter/currency-exchanger-js.d.ts b/src/tools/currency-converter/currency-exchanger-js.d.ts new file mode 100644 index 00000000..8e94badc --- /dev/null +++ b/src/tools/currency-converter/currency-exchanger-js.d.ts @@ -0,0 +1,4 @@ +declare module 'currency-exchanger-js'{ + export function convertOnDate(value: number,fromCurrency: string,toCurrency: string,inputDate: Date): number; + export function convert(value: number,fromCurrency: string,toCurrency: string): number; +} diff --git a/src/tools/currency-converter/index.ts b/src/tools/currency-converter/index.ts new file mode 100644 index 00000000..92401531 --- /dev/null +++ b/src/tools/currency-converter/index.ts @@ -0,0 +1,12 @@ +import { Currency } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'Currency Converter', + path: '/currency-converter', + description: 'Convert currency values using ExchangeRate-API', + keywords: ['currency', 'converter'], + component: () => import('./currency-converter.vue'), + icon: Currency, + createdAt: new Date('2024-08-15'), +}); diff --git a/src/tools/currency-converter/moneys.json b/src/tools/currency-converter/moneys.json new file mode 100644 index 00000000..d8e52071 --- /dev/null +++ b/src/tools/currency-converter/moneys.json @@ -0,0 +1 @@ +{ "1000sats": "", "1inch": "1inch", "aave": "Aave", "ada": "Cardano", "aed": "Emirati Dirham", "afn": "Afghan Afghani", "agix": "SingularityNET", "akt": "Akash Network", "algo": "Algorand", "all": "Albanian Lek", "amd": "Armenian Dram", "amp": "Amp", "ang": "Dutch Guilder", "aoa": "Angolan Kwanza", "ape": "ApeCoin", "apt": "Aptos", "ar": "Arweave", "arb": "Arbitrum", "ars": "Argentine Peso", "atom": "Cosmos", "ats": "Austrian Schilling", "aud": "Australian Dollar", "avax": "Avalanche", "awg": "Aruban or Dutch Guilder", "axs": "Axie Infinity", "azm": "Azerbaijani Manat", "azn": "Azerbaijan Manat", "bake": "BakeryToken", "bam": "Bosnian Convertible Mark", "bat": "Basic Attention Token", "bbd": "Barbadian or Bajan Dollar", "bch": "Bitcoin Cash", "bdt": "Bangladeshi Taka", "bef": "Belgian Franc", "bgn": "Bulgarian Lev", "bhd": "Bahraini Dinar", "bif": "Burundian Franc", "bmd": "Bermudian Dollar", "bnb": "Binance Coin", "bnd": "Bruneian Dollar", "bob": "Bolivian Bolíviano", "brl": "Brazilian Real", "bsd": "Bahamian Dollar", "bsv": "Bitcoin SV", "bsw": "Biswap", "btc": "Bitcoin", "btcb": "Bitcoin BEP2", "btg": "Bitcoin Gold", "btn": "Bhutanese Ngultrum", "btt": "BitTorrent", "busd": "Binance USD", "bwp": "Botswana Pula", "byn": "Belarusian Ruble", "byr": "Belarusian Ruble", "bzd": "Belizean Dollar", "cad": "Canadian Dollar", "cake": "PancakeSwap", "cdf": "Congolese Franc", "celo": "Celo", "cfx": "Conflux", "chf": "Swiss Franc", "chz": "Chiliz", "clp": "Chilean Peso", "cnh": "Chinese Yuan Renminbi Offshore", "cny": "Chinese Yuan Renminbi", "comp": "Compound", "cop": "Colombian Peso", "crc": "Costa Rican Colon", "cro": "Crypto.com Chain", "crv": "Curve DAO Token", "cspr": "Casper", "cuc": "Cuban Convertible Peso", "cup": "Cuban Peso", "cve": "Cape Verdean Escudo", "cvx": "Convex Finance", "cyp": "Cypriot Pound", "czk": "Czech Koruna", "dai": "DAI", "dash": "Digital Cash", "dcr": "Decred", "dem": "German Deutsche Mark", "dfi": "DfiStarter", "djf": "Djiboutian Franc", "dkk": "Danish Krone", "doge": "Dogecoin", "dop": "Dominican Peso", "dot": "Polkadot", "dydx": "dYdX", "dzd": "Algerian Dinar", "eek": "Estonian Kroon", "egld": "Elrond", "egp": "Egyptian Pound", "enj": "Enjin Coin", "eos": "EOS", "ern": "Eritrean Nakfa", "esp": "Spanish Peseta", "etb": "Ethiopian Birr", "etc": "Ethereum Classic", "eth": "Ethereum", "eur": "Euro", "fei": "Fei USD", "fil": "Filecoin", "fim": "Finnish Markka", "fjd": "Fijian Dollar", "fkp": "Falkland Island Pound", "flow": "Flow", "flr": "FLARE", "frax": "Frax", "frf": "French Franc", "ftm": "Fantom", "ftt": "FarmaTrust", "fxs": "Frax Share", "gala": "Gala", "gbp": "British Pound", "gel": "Georgian Lari", "ggp": "Guernsey Pound", "ghc": "Ghanaian Cedi", "ghs": "Ghanaian Cedi", "gip": "Gibraltar Pound", "gmd": "Gambian Dalasi", "gmx": "Goldmaxcoin", "gnf": "Guinean Franc", "gno": "Gnosis", "grd": "Greek Drachma", "grt": "The Graph", "gt": "GateToken", "gtq": "Guatemalan Quetzal", "gusd": "Gemini US Dollar", "gyd": "Guyanese Dollar", "hbar": "Hedera", "hkd": "Hong Kong Dollar", "hnl": "Honduran Lempira", "hnt": "Helium", "hot": "Hydro Protocol", "hrk": "Croatian Kuna", "ht": "Huobi Token", "htg": "Haitian Gourde", "huf": "Hungarian Forint", "icp": "Internet Computer", "idr": "Indonesian Rupiah", "iep": "Irish Pound", "ils": "Israeli Shekel", "imp": "Isle of Man Pound", "imx": "Immutable X", "inj": "Injective", "inr": "Indian Rupee", "iqd": "Iraqi Dinar", "irr": "Iranian Rial", "isk": "Icelandic Krona", "itl": "Italian Lira", "jep": "Jersey Pound", "jmd": "Jamaican Dollar", "jod": "Jordanian Dinar", "jpy": "Japanese Yen", "kas": "", "kava": "Kava", "kcs": "Kucoin", "kda": "Kadena", "kes": "Kenyan Shilling", "kgs": "Kyrgyzstani Som", "khr": "Cambodian Riel", "klay": "Klaytn", "kmf": "Comorian Franc", "knc": "Kyber Network Crystals", "kpw": "North Korean Won", "krw": "South Korean Won", "ksm": "Kusama", "kwd": "Kuwaiti Dinar", "kyd": "Caymanian Dollar", "kzt": "Kazakhstani Tenge", "lak": "Lao Kip", "lbp": "Lebanese Pound", "ldo": "Lido DAO Token", "leo": "LEOcoin", "link": "Chainlink", "lkr": "Sri Lankan Rupee", "lrc": "Loopring", "lrd": "Liberian Dollar", "lsl": "Basotho Loti", "ltc": "Litecoin", "ltl": "Lithuanian Litas", "luf": "Luxembourg Franc", "luna": "Terra", "lunc": "", "lvl": "Latvian Lat", "lyd": "Libyan Dinar", "mad": "Moroccan Dirham", "mana": "Mana Coin Decentraland", "matic": "Polygon", "mbx": "MobieCoin", "mdl": "Moldovan Leu", "mga": "Malagasy Ariary", "mgf": "Malagasy Franc", "mina": "Mina", "mkd": "Macedonian Denar", "mkr": "Maker", "mmk": "Burmese Kyat", "mnt": "Mongolian Tughrik", "mop": "Macau Pataca", "mro": "Mauritanian Ouguiya", "mru": "Mauritanian Ouguiya", "mtl": "Maltese Lira", "mur": "Mauritian Rupee", "mvr": "Maldivian Rufiyaa", "mwk": "Malawian Kwacha", "mxn": "Mexican Peso", "mxv": "", "myr": "Malaysian Ringgit", "mzm": "Mozambican Metical", "mzn": "Mozambican Metical", "nad": "Namibian Dollar", "near": "NEAR Protocol", "neo": "NEO", "nexo": "NEXO", "nft": "NFT", "ngn": "Nigerian Naira", "nio": "Nicaraguan Cordoba", "nlg": "Dutch Guilder", "nok": "Norwegian Krone", "npr": "Nepalese Rupee", "nzd": "New Zealand Dollar", "okb": "Okex", "omr": "Omani Rial", "one": "Menlo One", "op": "Optimism", "ordi": "", "pab": "Panamanian Balboa", "paxg": "PAX Gold", "pen": "Peruvian Sol", "pepe": "", "pgk": "Papua New Guinean Kina", "php": "Philippine Peso", "pkr": "Pakistani Rupee", "pln": "Polish Zloty", "pte": "Portuguese Escudo", "pyg": "Paraguayan Guarani", "qar": "Qatari Riyal", "qnt": "Quant", "qtum": "QTUM", "rol": "Romanian Leu", "ron": "Romanian Leu", "rpl": "Rocket Pool", "rsd": "Serbian Dinar", "rub": "Russian Ruble", "rune": "THORChain (ERC20)", "rvn": "Ravencoin", "rwf": "Rwandan Franc", "sand": "The Sandbox", "sar": "Saudi Arabian Riyal", "sbd": "Solomon Islander Dollar", "scr": "Seychellois Rupee", "sdd": "Sudanese Dinar", "sdg": "Sudanese Pound", "sek": "Swedish Krona", "sgd": "Singapore Dollar", "shib": "Shiba Inu", "shp": "Saint Helenian Pound", "sit": "Slovenian Tolar", "skk": "Slovak Koruna", "sle": "Sierra Leonean Leone", "sll": "Sierra Leonean Leone", "snx": "Synthetix Network", "sol": "Solana", "sos": "Somali Shilling", "spl": "Seborgan Luigino", "srd": "Surinamese Dollar", "srg": "Surinamese Guilder", "std": "Sao Tomean Dobra", "stn": "Sao Tomean Dobra", "stx": "Stacks", "sui": "Sui", "svc": "Salvadoran Colon", "syp": "Syrian Pound", "szl": "Swazi Lilangeni", "thb": "Thai Baht", "theta": "Theta", "tjs": "Tajikistani Somoni", "tmm": "Turkmenistani Manat", "tmt": "Turkmenistani Manat", "tnd": "Tunisian Dinar", "ton": "Tokamak Network", "top": "Tongan Pa'anga", "trl": "Turkish Lira", "trx": "TRON", "try": "Turkish Lira", "ttd": "Trinidadian Dollar", "tusd": "True USD", "tvd": "Tuvaluan Dollar", "twd": "Taiwan New Dollar", "twt": "Trust Wallet Token", "tzs": "Tanzanian Shilling", "uah": "Ukrainian Hryvnia", "ugx": "Ugandan Shilling", "uni": "Uniswap", "usd": "US Dollar", "usdc": "USDC", "usdd": "", "usdp": "USDP Stablecoin", "usdt": "Tether", "uyu": "Uruguayan Peso", "uzs": "Uzbekistani Som", "val": "Vatican City Lira", "veb": "Venezuelan Bolívar", "ved": "", "vef": "Venezuelan Bolívar", "ves": "Venezuelan Bolívar", "vet": "Vechain", "vnd": "Vietnamese Dong", "vuv": "Ni-Vanuatu Vatu", "waves": "Waves", "wemix": "WEMIX", "woo": "WOO Network", "wst": "Samoan Tala", "xaf": "Central African CFA Franc BEAC", "xag": "Silver Ounce", "xau": "Gold Ounce", "xaut": "Tether Gold", "xbt": "", "xcd": "East Caribbean Dollar", "xch": "Chia", "xdc": "XDC Network", "xdr": "IMF Special Drawing Rights", "xec": "Eternal Coin", "xem": "NEM", "xlm": "Stellar Lumen", "xmr": "Monero", "xof": "CFA Franc", "xpd": "Palladium Ounce", "xpf": "CFP Franc", "xpt": "Platinum Ounce", "xrp": "Ripple", "xtz": "Tezos", "yer": "Yemeni Rial", "zar": "South African Rand", "zec": "ZCash", "zil": "Zilliqa", "zmk": "Zambian Kwacha", "zmw": "Zambian Kwacha", "zwd": "Zimbabwean Dollar", "zwg": "", "zwl": "Zimbabwean Dollar" } diff --git a/src/tools/index.ts b/src/tools/index.ts index ea2c581c..8d283ebd 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -16,6 +16,7 @@ import { tool as jsUnobfuscator } from './js-unobfuscator'; import { tool as extractTextFromHtml } from './extract-text-from-html'; import { tool as colorWheel } from './color-wheel'; import { tool as codeHighlighter } from './code-highlighter'; +import { tool as currencyConverter } from './currency-converter'; import { tool as asciiTextDrawer } from './ascii-text-drawer'; import { tool as textToUnicode } from './text-to-unicode'; @@ -52,6 +53,7 @@ import { tool as tomlToJson } from './toml-to-json'; import { tool as jsonToCsv } from './json-to-csv'; import { tool as cameraRecorder } from './camera-recorder'; import { tool as listConverter } from './list-converter'; +import { tool as listComparer } from './list-comparer'; import { tool as phoneParserAndFormatter } from './phone-parser-and-formatter'; import { tool as jsonDiff } from './json-diff'; import { tool as ipv4RangeExpander } from './ipv4-range-expander'; @@ -151,10 +153,12 @@ export const toolsByCategory: ToolCategory[] = [ jsonToYaml, jsonToToml, listConverter, + listComparer, tomlToJson, tomlToYaml, xmlToJson, jsonToXml, + currencyConverter, markdownToHtml, gzipConverter, uuidConverter, diff --git a/src/tools/list-comparer/index.ts b/src/tools/list-comparer/index.ts new file mode 100644 index 00000000..88262c14 --- /dev/null +++ b/src/tools/list-comparer/index.ts @@ -0,0 +1,12 @@ +import { List } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'Lists Comparer', + path: '/list-comparer', + description: 'Compare two list items', + keywords: ['list', 'comparer'], + component: () => import('./list-comparer.vue'), + icon: List, + createdAt: new Date('2024-08-15'), +}); diff --git a/src/tools/list-comparer/list-comparer.service.test.ts b/src/tools/list-comparer/list-comparer.service.test.ts new file mode 100644 index 00000000..98affcf7 --- /dev/null +++ b/src/tools/list-comparer/list-comparer.service.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, it } from 'vitest'; +import { compareLists } from './list-comparer.service'; + +describe('list-comparer', () => { + describe('compareLists', () => { + it('return correct comparaison', () => { + expect(compareLists({ + list1: '1\n 2\n3\n4\n5\n4\n7\nA', + list2: '1\n2\n3\n4\n6\n4\n7\na', + trimItems: true, + ignoreCase: true, + })).to.deep.eq({ + list1Not2: [ + '5', + ], + list2Not1: [ + '6', + ], + same: [ + '1', + '2', + '3', + '4', + '7', + 'a', + ], + }); + + expect(compareLists({ + list1: '1\n 2\n3\n4\n5\n4\n7\nA', + list2: '1\n2\n3\n4\n6\n4\n7\na', + trimItems: false, + ignoreCase: false, + })).to.deep.eq({ + list1Not2: [ + ' 2', + '5', + 'A', + ], + list2Not1: [ + '2', + '6', + 'a', + ], + same: [ + '1', + '3', + '4', + '7', + ], + }); + + expect(compareLists({ + list1: '1, 2,3,4,5\n4,7,A,A', + list2: '1\n2\n3\n4\n6\n4\n7\na', + trimItems: false, + ignoreCase: false, + separator: ',', + })).to.deep.eq({ + list1Not2: [ + ' 2', + '5', + 'A', + ], + list2Not1: [ + '2', + '6', + 'a', + ], + same: [ + '1', + '3', + '4', + '7', + ], + }); + + expect(compareLists({ + list1: '10\n20\n20\n30', + list2: '30\n20\n40', + trimItems: false, + ignoreCase: false, + })).to.deep.eq({ + list1Not2: [ + '10', + ], + list2Not1: [ + '40', + ], + same: [ + '30', + '20', + ], + }); + }); + }); +}); diff --git a/src/tools/list-comparer/list-comparer.service.ts b/src/tools/list-comparer/list-comparer.service.ts new file mode 100644 index 00000000..c0ca4a45 --- /dev/null +++ b/src/tools/list-comparer/list-comparer.service.ts @@ -0,0 +1,36 @@ +import _ from 'lodash'; +import intersect from 'fast_array_intersect'; +import diff from 'arr-diff'; + +export function compareLists({ + list1, + list2, + ignoreCase = false, + trimItems = true, + separator = '', +}: { + list1: string + list2: string + separator?: string + ignoreCase?: boolean + trimItems?: boolean +}) { + const splitSep = separator ? `${separator}|` : ''; + const splitRegExp = new RegExp(`(?:${splitSep}\\n)`, 'g'); + + const prepareList = (list: string) => + _.chain(list ?? '') + .thru(text => ignoreCase ? text.toLowerCase() : text) + .split(splitRegExp) + .map(text => trimItems ? text.trim() : text) + .value(); + + const list1Arr = prepareList(list1); + const list2Arr = prepareList(list2); + + return { + same: [...new Set(intersect([list1Arr, list2Arr]))], + list2Not1: [...new Set(diff(list2Arr, list1Arr))], + list1Not2: [...new Set(diff(list1Arr, list2Arr))], + }; +} diff --git a/src/tools/list-comparer/list-comparer.vue b/src/tools/list-comparer/list-comparer.vue new file mode 100644 index 00000000..94caa5a4 --- /dev/null +++ b/src/tools/list-comparer/list-comparer.vue @@ -0,0 +1,82 @@ + + +