diff --git a/components.d.ts b/components.d.ts index f2c3146f..a0eeeecb 100644 --- a/components.d.ts +++ b/components.d.ts @@ -13,6 +13,8 @@ declare module '@vue/runtime-core' { About: typeof import('./src/pages/About.vue')['default'] App: typeof import('./src/App.vue')['default'] AsciiTextDrawer: typeof import('./src/tools/ascii-text-drawer/ascii-text-drawer.vue')['default'] + BarcodeGenerator: typeof import('./src/tools/barcode-generator/barcode-generator.vue')['default'] + BarcodeReader: typeof import('./src/tools/barcode-reader/barcode-reader.vue')['default'] 'Base.layout': typeof import('./src/layouts/base.layout.vue')['default'] Base64FileConverter: typeof import('./src/tools/base64-file-converter/base64-file-converter.vue')['default'] Base64StringConverter: typeof import('./src/tools/base64-string-converter/base64-string-converter.vue')['default'] @@ -127,11 +129,14 @@ declare module '@vue/runtime-core' { MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] 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'] 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'] @@ -139,12 +144,10 @@ declare module '@vue/runtime-core' { NH3: typeof import('naive-ui')['NH3'] NIcon: typeof import('naive-ui')['NIcon'] 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'] - NSpin: typeof import('naive-ui')['NSpin'] 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'] diff --git a/package.json b/package.json index 9f39ff1d..5a3651b9 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "release": "node ./scripts/release.mjs" }, "dependencies": { + "@chenfengyuan/vue-barcode": "^2.0.2", "@it-tools/bip39": "^0.0.4", "@it-tools/oggen": "^1.3.0", "@sindresorhus/slugify": "^2.2.1", @@ -47,6 +48,7 @@ "@vueuse/core": "^10.3.0", "@vueuse/head": "^1.0.0", "@vueuse/router": "^10.0.0", + "@zxing/library": "^0.21.0", "bcryptjs": "^2.4.3", "change-case": "^4.1.2", "colord": "^2.9.3", @@ -64,6 +66,7 @@ "highlight.js": "^11.7.0", "iarna-toml-esm": "^3.0.5", "ibantools": "^4.3.3", + "jsbarcode": "^3.11.6", "json5": "^2.2.3", "jwt-decode": "^3.1.2", "libphonenumber-js": "^1.10.28", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd6c38c9..4e890ba2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@chenfengyuan/vue-barcode': + specifier: ^2.0.2 + version: 2.0.2(jsbarcode@3.11.6)(vue@3.3.4) '@it-tools/bip39': specifier: ^0.0.4 version: 0.0.4 @@ -41,6 +44,9 @@ dependencies: '@vueuse/router': specifier: ^10.0.0 version: 10.0.0(vue-router@4.1.6)(vue@3.3.4) + '@zxing/library': + specifier: ^0.21.0 + version: 0.21.0 bcryptjs: specifier: ^2.4.3 version: 2.4.3 @@ -92,6 +98,9 @@ dependencies: ibantools: specifier: ^4.3.3 version: 4.3.3 + jsbarcode: + specifier: ^3.11.6 + version: 3.11.6 json5: specifier: ^2.2.3 version: 2.2.3 @@ -1895,6 +1904,16 @@ packages: '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 + /@chenfengyuan/vue-barcode@2.0.2(jsbarcode@3.11.6)(vue@3.3.4): + resolution: {integrity: sha512-lHQhYfcqWeeA/UYoe4O+O6c8WD+t9WO8SiWohKOVVIiwPhcvC58YxhvlFvJme0/CPL7+Sh1XCpMaC9Mo2Dt5jQ==} + peerDependencies: + jsbarcode: ^3.11.0 + vue: ^3.0.0 + dependencies: + jsbarcode: 3.11.6 + vue: 3.3.4 + dev: false + /@css-render/plugin-bem@0.15.12(css-render@0.15.12): resolution: {integrity: sha512-Lq2jSOZn+wYQtsyaFj6QRz2EzAnd3iW5fZeHO1WSXQdVYwvwGX0ZiH3X2JQgtgYLT1yeGtrwrqJdNdMEUD2xTw==} peerDependencies: @@ -3351,7 +3370,7 @@ packages: dependencies: '@unhead/dom': 0.5.1 '@unhead/schema': 0.5.1 - '@vueuse/shared': 10.7.2(vue@3.3.4) + '@vueuse/shared': 10.9.0(vue@3.3.4) unhead: 0.5.1 vue: 3.3.4 transitivePeerDependencies: @@ -3993,10 +4012,10 @@ packages: - vue dev: false - /@vueuse/shared@10.7.2(vue@3.3.4): - resolution: {integrity: sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==} + /@vueuse/shared@10.9.0(vue@3.3.4): + resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==} dependencies: - vue-demi: 0.14.6(vue@3.3.4) + vue-demi: 0.14.7(vue@3.3.4) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -4006,6 +4025,21 @@ packages: resolution: {integrity: sha512-P1A1vRGFBhITco8Iw4/hvnDYoE/SoVrd71dW1pBFdXJb3vP+pBtoOuhbEKy0ROJGOyzQuqvFibcwzyLlWMqNiQ==} dev: false + /@zxing/library@0.21.0: + resolution: {integrity: sha512-2+DFYM6NLl+ZXXUbDOkmVn4TAAOgTniEx5MJD2kStujcxSl6j9CfAgIT8DcsBYIlT5DW7oeiShEzWZjpL0hNOw==} + engines: {node: '>= 10.4.0'} + dependencies: + ts-custom-error: 3.3.1 + optionalDependencies: + '@zxing/text-encoding': 0.9.0 + dev: false + + /@zxing/text-encoding@0.9.0: + resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} + requiresBuild: true + dev: false + optional: true + /abab@2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true @@ -6504,6 +6538,10 @@ packages: argparse: 2.0.1 dev: true + /jsbarcode@3.11.6: + resolution: {integrity: sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA==} + dev: false + /jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} dev: false @@ -8494,6 +8532,11 @@ packages: typescript: 5.2.2 dev: true + /ts-custom-error@3.3.1: + resolution: {integrity: sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==} + engines: {node: '>=14.0.0'} + dev: false + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true @@ -9151,8 +9194,8 @@ packages: vue: 3.3.4 dev: false - /vue-demi@0.14.6(vue@3.3.4): - resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} + /vue-demi@0.14.7(vue@3.3.4): + resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} hasBin: true requiresBuild: true @@ -9442,6 +9485,7 @@ packages: /workbox-google-analytics@7.0.0: resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained dependencies: workbox-background-sync: 7.0.0 workbox-core: 7.0.0 diff --git a/src/composable/queryParams.ts b/src/composable/queryParams.ts index 9699abbc..9e184610 100644 --- a/src/composable/queryParams.ts +++ b/src/composable/queryParams.ts @@ -1,7 +1,8 @@ import { useRouteQuery } from '@vueuse/router'; import { computed } from 'vue'; +import { useStorage } from '@vueuse/core'; -export { useQueryParam }; +export { useQueryParam, useQueryParamOrStorage }; const transformers = { number: { @@ -33,3 +34,31 @@ function useQueryParam({ name, defaultValue }: { name: string; defaultValue: }, }); } + +function useQueryParamOrStorage({ name, storageName, defaultValue }: { name: string; storageName: string; defaultValue?: T }) { + const type = typeof defaultValue; + const transformer = transformers[type as keyof typeof transformers] ?? transformers.string; + + const storageRef = useStorage(storageName, defaultValue); + const storageDefaultValue = storageRef.value ?? defaultValue; + + const proxy = useRouteQuery(name, transformer.toQuery(storageDefaultValue as never)); + + const ref = computed({ + get() { + return transformer.fromQuery(proxy.value) as unknown as T; + }, + set(value) { + proxy.value = transformer.toQuery(value as never); + }, + }); + + watch( + ref, + (newValue) => { + storageRef.value = newValue; + }, + ); + + return ref; +} diff --git a/src/tools/barcode-generator/barcode-generator.vue b/src/tools/barcode-generator/barcode-generator.vue new file mode 100644 index 00000000..d0f60341 --- /dev/null +++ b/src/tools/barcode-generator/barcode-generator.vue @@ -0,0 +1,109 @@ + + + diff --git a/src/tools/barcode-generator/index.ts b/src/tools/barcode-generator/index.ts new file mode 100644 index 00000000..e07c3850 --- /dev/null +++ b/src/tools/barcode-generator/index.ts @@ -0,0 +1,12 @@ +import { Barcode } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'Barcode Generator', + path: '/barcode-generator', + description: 'Barcode generator', + keywords: ['barcode', 'generator'], + component: () => import('./barcode-generator.vue'), + icon: Barcode, + createdAt: new Date('2024-04-20'), +}); diff --git a/src/tools/barcode-reader/barcode-reader.vue b/src/tools/barcode-reader/barcode-reader.vue new file mode 100644 index 00000000..17882e23 --- /dev/null +++ b/src/tools/barcode-reader/barcode-reader.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/src/tools/barcode-reader/index.ts b/src/tools/barcode-reader/index.ts new file mode 100644 index 00000000..d2d29037 --- /dev/null +++ b/src/tools/barcode-reader/index.ts @@ -0,0 +1,12 @@ +import { Barcode } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'Barcode Reader', + path: '/barcode-reader', + description: 'Barcode reader', + keywords: ['barcode', 'reader'], + component: () => import('./barcode-reader.vue'), + icon: Barcode, + createdAt: new Date('2024-04-20'), +}); diff --git a/src/tools/index.ts b/src/tools/index.ts index aa861c93..f2747897 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -81,6 +81,8 @@ import { tool as uuidGenerator } from './uuid-generator'; import { tool as macAddressLookup } from './mac-address-lookup'; import { tool as xmlFormatter } from './xml-formatter'; import { tool as yamlViewer } from './yaml-viewer'; +import { tool as barcodeReader } from './barcode-reader'; +import { tool as barcodeGenerator } from './barcode-generator'; export const toolsByCategory: ToolCategory[] = [ { @@ -132,7 +134,14 @@ export const toolsByCategory: ToolCategory[] = [ }, { name: 'Images and videos', - components: [qrCodeGenerator, wifiQrCodeGenerator, svgPlaceholderGenerator, cameraRecorder], + components: [ + qrCodeGenerator, + wifiQrCodeGenerator, + svgPlaceholderGenerator, + cameraRecorder, + barcodeReader, + barcodeGenerator, + ], }, { name: 'Development',