refactor(ui): replaced some n-input with c-input-text
This commit is contained in:
		
							parent
							
								
									a64ebd43d5
								
							
						
					
					
						commit
						006b566f0c
					
				
							
								
								
									
										72
									
								
								auto-imports.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										72
									
								
								auto-imports.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -19,7 +19,9 @@ declare global { | ||||
|   const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] | ||||
|   const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] | ||||
|   const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] | ||||
|   const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate'] | ||||
|   const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] | ||||
|   const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise'] | ||||
|   const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] | ||||
|   const customRef: typeof import('vue')['customRef'] | ||||
|   const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] | ||||
| @ -39,9 +41,6 @@ declare global { | ||||
|   const isReactive: typeof import('vue')['isReactive'] | ||||
|   const isReadonly: typeof import('vue')['isReadonly'] | ||||
|   const isRef: typeof import('vue')['isRef'] | ||||
|   const logicAnd: typeof import('@vueuse/core')['logicAnd'] | ||||
|   const logicNot: typeof import('@vueuse/core')['logicNot'] | ||||
|   const logicOr: typeof import('@vueuse/core')['logicOr'] | ||||
|   const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] | ||||
|   const markRaw: typeof import('vue')['markRaw'] | ||||
|   const nextTick: typeof import('vue')['nextTick'] | ||||
| @ -92,8 +91,9 @@ declare global { | ||||
|   const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] | ||||
|   const toRaw: typeof import('vue')['toRaw'] | ||||
|   const toReactive: typeof import('@vueuse/core')['toReactive'] | ||||
|   const toRef: typeof import('vue')['toRef'] | ||||
|   const toRef: typeof import('@vueuse/core')['toRef'] | ||||
|   const toRefs: typeof import('vue')['toRefs'] | ||||
|   const toValue: typeof import('@vueuse/core')['toValue'] | ||||
|   const triggerRef: typeof import('vue')['triggerRef'] | ||||
|   const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] | ||||
|   const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] | ||||
| @ -104,6 +104,19 @@ declare global { | ||||
|   const unrefElement: typeof import('@vueuse/core')['unrefElement'] | ||||
|   const until: typeof import('@vueuse/core')['until'] | ||||
|   const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] | ||||
|   const useAnimate: typeof import('@vueuse/core')['useAnimate'] | ||||
|   const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference'] | ||||
|   const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery'] | ||||
|   const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] | ||||
|   const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] | ||||
|   const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] | ||||
|   const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] | ||||
|   const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes'] | ||||
|   const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] | ||||
|   const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] | ||||
|   const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] | ||||
|   const useArraySome: typeof import('@vueuse/core')['useArraySome'] | ||||
|   const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] | ||||
|   const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] | ||||
|   const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] | ||||
|   const useAttrs: typeof import('vue')['useAttrs'] | ||||
| @ -114,8 +127,8 @@ declare global { | ||||
|   const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] | ||||
|   const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] | ||||
|   const useCached: typeof import('@vueuse/core')['useCached'] | ||||
|   const useClamp: typeof import('@vueuse/core')['useClamp'] | ||||
|   const useClipboard: typeof import('@vueuse/core')['useClipboard'] | ||||
|   const useCloned: typeof import('@vueuse/core')['useCloned'] | ||||
|   const useColorMode: typeof import('@vueuse/core')['useColorMode'] | ||||
|   const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] | ||||
|   const useCounter: typeof import('@vueuse/core')['useCounter'] | ||||
| @ -189,12 +202,18 @@ declare global { | ||||
|   const useOnline: typeof import('@vueuse/core')['useOnline'] | ||||
|   const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] | ||||
|   const useParallax: typeof import('@vueuse/core')['useParallax'] | ||||
|   const useParentElement: typeof import('@vueuse/core')['useParentElement'] | ||||
|   const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver'] | ||||
|   const usePermission: typeof import('@vueuse/core')['usePermission'] | ||||
|   const usePointer: typeof import('@vueuse/core')['usePointer'] | ||||
|   const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] | ||||
|   const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] | ||||
|   const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] | ||||
|   const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] | ||||
|   const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] | ||||
|   const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] | ||||
|   const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] | ||||
|   const usePrevious: typeof import('@vueuse/core')['usePrevious'] | ||||
|   const useRafFn: typeof import('@vueuse/core')['useRafFn'] | ||||
|   const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] | ||||
|   const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] | ||||
| @ -208,14 +227,17 @@ declare global { | ||||
|   const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] | ||||
|   const useShare: typeof import('@vueuse/core')['useShare'] | ||||
|   const useSlots: typeof import('vue')['useSlots'] | ||||
|   const useSorted: typeof import('@vueuse/core')['useSorted'] | ||||
|   const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] | ||||
|   const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] | ||||
|   const useStepper: typeof import('@vueuse/core')['useStepper'] | ||||
|   const useStorage: typeof import('@vueuse/core')['useStorage'] | ||||
|   const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] | ||||
|   const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] | ||||
|   const useSupported: typeof import('@vueuse/core')['useSupported'] | ||||
|   const useSwipe: typeof import('@vueuse/core')['useSwipe'] | ||||
|   const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] | ||||
|   const useTextDirection: typeof import('@vueuse/core')['useTextDirection'] | ||||
|   const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] | ||||
|   const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize'] | ||||
|   const useThrottle: typeof import('@vueuse/core')['useThrottle'] | ||||
| @ -227,6 +249,8 @@ declare global { | ||||
|   const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] | ||||
|   const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] | ||||
|   const useTitle: typeof import('@vueuse/core')['useTitle'] | ||||
|   const useToNumber: typeof import('@vueuse/core')['useToNumber'] | ||||
|   const useToString: typeof import('@vueuse/core')['useToString'] | ||||
|   const useToggle: typeof import('@vueuse/core')['useToggle'] | ||||
|   const useTransition: typeof import('@vueuse/core')['useTransition'] | ||||
|   const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] | ||||
| @ -247,8 +271,10 @@ declare global { | ||||
|   const watchArray: typeof import('@vueuse/core')['watchArray'] | ||||
|   const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] | ||||
|   const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] | ||||
|   const watchDeep: typeof import('@vueuse/core')['watchDeep'] | ||||
|   const watchEffect: typeof import('vue')['watchEffect'] | ||||
|   const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] | ||||
|   const watchImmediate: typeof import('@vueuse/core')['watchImmediate'] | ||||
|   const watchOnce: typeof import('@vueuse/core')['watchOnce'] | ||||
|   const watchPausable: typeof import('@vueuse/core')['watchPausable'] | ||||
|   const watchPostEffect: typeof import('vue')['watchPostEffect'] | ||||
| @ -282,7 +308,9 @@ declare module 'vue' { | ||||
|     readonly createGlobalState: UnwrapRef<typeof import('@vueuse/core')['createGlobalState']> | ||||
|     readonly createInjectionState: UnwrapRef<typeof import('@vueuse/core')['createInjectionState']> | ||||
|     readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']> | ||||
|     readonly createReusableTemplate: UnwrapRef<typeof import('@vueuse/core')['createReusableTemplate']> | ||||
|     readonly createSharedComposable: UnwrapRef<typeof import('@vueuse/core')['createSharedComposable']> | ||||
|     readonly createTemplatePromise: UnwrapRef<typeof import('@vueuse/core')['createTemplatePromise']> | ||||
|     readonly createUnrefFn: UnwrapRef<typeof import('@vueuse/core')['createUnrefFn']> | ||||
|     readonly customRef: UnwrapRef<typeof import('vue')['customRef']> | ||||
|     readonly debouncedRef: UnwrapRef<typeof import('@vueuse/core')['debouncedRef']> | ||||
| @ -302,9 +330,6 @@ declare module 'vue' { | ||||
|     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']> | ||||
|     readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']> | ||||
|     readonly isRef: UnwrapRef<typeof import('vue')['isRef']> | ||||
|     readonly logicAnd: UnwrapRef<typeof import('@vueuse/core')['logicAnd']> | ||||
|     readonly logicNot: UnwrapRef<typeof import('@vueuse/core')['logicNot']> | ||||
|     readonly logicOr: UnwrapRef<typeof import('@vueuse/core')['logicOr']> | ||||
|     readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']> | ||||
|     readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']> | ||||
|     readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']> | ||||
| @ -355,8 +380,9 @@ declare module 'vue' { | ||||
|     readonly throttledWatch: UnwrapRef<typeof import('@vueuse/core')['throttledWatch']> | ||||
|     readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']> | ||||
|     readonly toReactive: UnwrapRef<typeof import('@vueuse/core')['toReactive']> | ||||
|     readonly toRef: UnwrapRef<typeof import('vue')['toRef']> | ||||
|     readonly toRef: UnwrapRef<typeof import('@vueuse/core')['toRef']> | ||||
|     readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']> | ||||
|     readonly toValue: UnwrapRef<typeof import('@vueuse/core')['toValue']> | ||||
|     readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']> | ||||
|     readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']> | ||||
|     readonly tryOnBeforeUnmount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeUnmount']> | ||||
| @ -367,6 +393,19 @@ declare module 'vue' { | ||||
|     readonly unrefElement: UnwrapRef<typeof import('@vueuse/core')['unrefElement']> | ||||
|     readonly until: UnwrapRef<typeof import('@vueuse/core')['until']> | ||||
|     readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']> | ||||
|     readonly useAnimate: UnwrapRef<typeof import('@vueuse/core')['useAnimate']> | ||||
|     readonly useArrayDifference: UnwrapRef<typeof import('@vueuse/core')['useArrayDifference']> | ||||
|     readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']> | ||||
|     readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']> | ||||
|     readonly useArrayFind: UnwrapRef<typeof import('@vueuse/core')['useArrayFind']> | ||||
|     readonly useArrayFindIndex: UnwrapRef<typeof import('@vueuse/core')['useArrayFindIndex']> | ||||
|     readonly useArrayFindLast: UnwrapRef<typeof import('@vueuse/core')['useArrayFindLast']> | ||||
|     readonly useArrayIncludes: UnwrapRef<typeof import('@vueuse/core')['useArrayIncludes']> | ||||
|     readonly useArrayJoin: UnwrapRef<typeof import('@vueuse/core')['useArrayJoin']> | ||||
|     readonly useArrayMap: UnwrapRef<typeof import('@vueuse/core')['useArrayMap']> | ||||
|     readonly useArrayReduce: UnwrapRef<typeof import('@vueuse/core')['useArrayReduce']> | ||||
|     readonly useArraySome: UnwrapRef<typeof import('@vueuse/core')['useArraySome']> | ||||
|     readonly useArrayUnique: UnwrapRef<typeof import('@vueuse/core')['useArrayUnique']> | ||||
|     readonly useAsyncQueue: UnwrapRef<typeof import('@vueuse/core')['useAsyncQueue']> | ||||
|     readonly useAsyncState: UnwrapRef<typeof import('@vueuse/core')['useAsyncState']> | ||||
|     readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']> | ||||
| @ -377,8 +416,8 @@ declare module 'vue' { | ||||
|     readonly useBroadcastChannel: UnwrapRef<typeof import('@vueuse/core')['useBroadcastChannel']> | ||||
|     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']> | ||||
|     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']> | ||||
|     readonly useClamp: UnwrapRef<typeof import('@vueuse/core')['useClamp']> | ||||
|     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']> | ||||
|     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']> | ||||
|     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']> | ||||
|     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']> | ||||
|     readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']> | ||||
| @ -452,12 +491,18 @@ declare module 'vue' { | ||||
|     readonly useOnline: UnwrapRef<typeof import('@vueuse/core')['useOnline']> | ||||
|     readonly usePageLeave: UnwrapRef<typeof import('@vueuse/core')['usePageLeave']> | ||||
|     readonly useParallax: UnwrapRef<typeof import('@vueuse/core')['useParallax']> | ||||
|     readonly useParentElement: UnwrapRef<typeof import('@vueuse/core')['useParentElement']> | ||||
|     readonly usePerformanceObserver: UnwrapRef<typeof import('@vueuse/core')['usePerformanceObserver']> | ||||
|     readonly usePermission: UnwrapRef<typeof import('@vueuse/core')['usePermission']> | ||||
|     readonly usePointer: UnwrapRef<typeof import('@vueuse/core')['usePointer']> | ||||
|     readonly usePointerLock: UnwrapRef<typeof import('@vueuse/core')['usePointerLock']> | ||||
|     readonly usePointerSwipe: UnwrapRef<typeof import('@vueuse/core')['usePointerSwipe']> | ||||
|     readonly usePreferredColorScheme: UnwrapRef<typeof import('@vueuse/core')['usePreferredColorScheme']> | ||||
|     readonly usePreferredContrast: UnwrapRef<typeof import('@vueuse/core')['usePreferredContrast']> | ||||
|     readonly usePreferredDark: UnwrapRef<typeof import('@vueuse/core')['usePreferredDark']> | ||||
|     readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']> | ||||
|     readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']> | ||||
|     readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']> | ||||
|     readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']> | ||||
|     readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']> | ||||
|     readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']> | ||||
| @ -471,14 +516,17 @@ declare module 'vue' { | ||||
|     readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']> | ||||
|     readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']> | ||||
|     readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']> | ||||
|     readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']> | ||||
|     readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']> | ||||
|     readonly useSpeechSynthesis: UnwrapRef<typeof import('@vueuse/core')['useSpeechSynthesis']> | ||||
|     readonly useStepper: UnwrapRef<typeof import('@vueuse/core')['useStepper']> | ||||
|     readonly useStorage: UnwrapRef<typeof import('@vueuse/core')['useStorage']> | ||||
|     readonly useStorageAsync: UnwrapRef<typeof import('@vueuse/core')['useStorageAsync']> | ||||
|     readonly useStyleTag: UnwrapRef<typeof import('@vueuse/core')['useStyleTag']> | ||||
|     readonly useSupported: UnwrapRef<typeof import('@vueuse/core')['useSupported']> | ||||
|     readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']> | ||||
|     readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']> | ||||
|     readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']> | ||||
|     readonly useTextSelection: UnwrapRef<typeof import('@vueuse/core')['useTextSelection']> | ||||
|     readonly useTextareaAutosize: UnwrapRef<typeof import('@vueuse/core')['useTextareaAutosize']> | ||||
|     readonly useThrottle: UnwrapRef<typeof import('@vueuse/core')['useThrottle']> | ||||
| @ -490,6 +538,8 @@ declare module 'vue' { | ||||
|     readonly useTimeoutPoll: UnwrapRef<typeof import('@vueuse/core')['useTimeoutPoll']> | ||||
|     readonly useTimestamp: UnwrapRef<typeof import('@vueuse/core')['useTimestamp']> | ||||
|     readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']> | ||||
|     readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']> | ||||
|     readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']> | ||||
|     readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']> | ||||
|     readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']> | ||||
|     readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']> | ||||
| @ -510,8 +560,10 @@ declare module 'vue' { | ||||
|     readonly watchArray: UnwrapRef<typeof import('@vueuse/core')['watchArray']> | ||||
|     readonly watchAtMost: UnwrapRef<typeof import('@vueuse/core')['watchAtMost']> | ||||
|     readonly watchDebounced: UnwrapRef<typeof import('@vueuse/core')['watchDebounced']> | ||||
|     readonly watchDeep: UnwrapRef<typeof import('@vueuse/core')['watchDeep']> | ||||
|     readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']> | ||||
|     readonly watchIgnorable: UnwrapRef<typeof import('@vueuse/core')['watchIgnorable']> | ||||
|     readonly watchImmediate: UnwrapRef<typeof import('@vueuse/core')['watchImmediate']> | ||||
|     readonly watchOnce: UnwrapRef<typeof import('@vueuse/core')['watchOnce']> | ||||
|     readonly watchPausable: UnwrapRef<typeof import('@vueuse/core')['watchPausable']> | ||||
|     readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']> | ||||
|  | ||||
							
								
								
									
										5
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -56,7 +56,12 @@ declare module '@vue/runtime-core' { | ||||
|     HtmlEntities: typeof import('./src/tools/html-entities/html-entities.vue')['default'] | ||||
|     HtmlWysiwygEditor: typeof import('./src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue')['default'] | ||||
|     HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default'] | ||||
|     IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default'] | ||||
|     IconMdiClose: typeof import('~icons/mdi/close')['default'] | ||||
|     IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default'] | ||||
|     IconMdiEye: typeof import('~icons/mdi/eye')['default'] | ||||
|     IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default'] | ||||
|     IconMdiRefresh: typeof import('~icons/mdi/refresh')['default'] | ||||
|     InputCopyable: typeof import('./src/components/InputCopyable.vue')['default'] | ||||
|     IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default'] | ||||
|     Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default'] | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <template> | ||||
|   <n-form-item :label="inputLabel" v-bind="validationAttrs"> | ||||
|   <n-form-item :label="inputLabel" v-bind="validationAttrs as any"> | ||||
|     <n-input | ||||
|       ref="inputElement" | ||||
|       v-model:value="input" | ||||
| @ -10,7 +10,7 @@ | ||||
|       autocorrect="off" | ||||
|       autocapitalize="off" | ||||
|       spellcheck="false" | ||||
|       :input-props="{ 'data-test-id': 'input' }" | ||||
|       :input-props="{ 'data-test-id': 'input' } as any" | ||||
|     /> | ||||
|   </n-form-item> | ||||
|   <n-form-item :label="outputLabel"> | ||||
|  | ||||
| @ -1,21 +1,20 @@ | ||||
| <template> | ||||
|   <n-input v-model:value="value"> | ||||
|   <c-input-text v-model:value="value"> | ||||
|     <template #suffix> | ||||
|       <n-tooltip trigger="hover"> | ||||
|         <template #trigger> | ||||
|           <c-button circle variant="text" @click="onCopyClicked"> | ||||
|             <n-icon :component="ContentCopyFilled" /> | ||||
|           <c-button circle variant="text" size="small" @click="onCopyClicked"> | ||||
|             <icon-mdi-content-copy /> | ||||
|           </c-button> | ||||
|         </template> | ||||
|         {{ tooltipText }} | ||||
|       </n-tooltip> | ||||
|     </template> | ||||
|   </n-input> | ||||
|   </c-input-text> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { useVModel, useClipboard } from '@vueuse/core'; | ||||
| import { ContentCopyFilled } from '@vicons/material'; | ||||
| import { ref } from 'vue'; | ||||
| 
 | ||||
| const props = defineProps<{ value: string }>(); | ||||
| @ -35,9 +34,3 @@ function onCopyClicked() { | ||||
|   }, 2000); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| ::v-deep(.n-input-wrapper) { | ||||
|   padding-right: 5px; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| @ -20,7 +20,7 @@ | ||||
|   </c-card> | ||||
| 
 | ||||
|   <c-card title="Base64 to string"> | ||||
|     <n-form-item label="Base64 string to decode" v-bind="b64Validation.attrs"> | ||||
|     <n-form-item label="Base64 string to decode" v-bind="b64Validation.attrs as any"> | ||||
|       <n-input v-model:value="base64Input" type="textarea" placeholder="Your base64 string..." rows="5" /> | ||||
|     </n-form-item> | ||||
| 
 | ||||
|  | ||||
| @ -1,17 +1,15 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-form-item label="Username"> | ||||
|       <n-input v-model:value="username" placeholder="Your username..." clearable /> | ||||
|     </n-form-item> | ||||
|     <n-form-item label="Password"> | ||||
|       <n-input | ||||
|         v-model:value="password" | ||||
|         placeholder="Your password..." | ||||
|         type="password" | ||||
|         show-password-on="click" | ||||
|         clearable | ||||
|       /> | ||||
|     </n-form-item> | ||||
|     <c-input-text v-model:value="username" label="Username" placeholder="Your username..." clearable raw-text mb-5 /> | ||||
|     <c-input-text | ||||
|       v-model:value="password" | ||||
|       label="Password" | ||||
|       placeholder="Your password..." | ||||
|       clearable | ||||
|       raw-text | ||||
|       mb-2 | ||||
|       type="password" | ||||
|     /> | ||||
| 
 | ||||
|     <c-card> | ||||
|       <n-statistic label="Authorization header:" class="header"> | ||||
|  | ||||
| @ -1,21 +1,20 @@ | ||||
| <template> | ||||
|   <c-card title="Hash"> | ||||
|     <n-form label-width="120"> | ||||
|       <n-form-item label="Your string: " label-placement="left"> | ||||
|         <n-input | ||||
|           v-model:value="input" | ||||
|           placeholder="Your string to bcrypt..." | ||||
|           autocomplete="off" | ||||
|           autocorrect="off" | ||||
|           autocapitalize="off" | ||||
|           spellcheck="false" | ||||
|         /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="Salt count: " label-placement="left"> | ||||
|         <n-input-number v-model:value="saltCount" placeholder="Salt rounds..." :max="10" :min="0" w-full /> | ||||
|       </n-form-item> | ||||
|       <n-input :value="hashed" readonly style="text-align: center" /> | ||||
|     </n-form> | ||||
|     <c-input-text | ||||
|       v-model:value="input" | ||||
|       placeholder="Your string to bcrypt..." | ||||
|       raw-text | ||||
|       label="Your string: " | ||||
|       label-position="left" | ||||
|       label-width="120px" | ||||
|       mb-2 | ||||
|     /> | ||||
|     <n-form-item label="Salt count: " label-placement="left" label-width="120"> | ||||
|       <n-input-number v-model:value="saltCount" placeholder="Salt rounds..." :max="10" :min="0" w-full /> | ||||
|     </n-form-item> | ||||
| 
 | ||||
|     <c-input-text :value="hashed" readonly text-center /> | ||||
| 
 | ||||
|     <n-space justify="center" mt-5> | ||||
|       <c-button @click="copy"> Copy hash </c-button> | ||||
|     </n-space> | ||||
| @ -24,24 +23,10 @@ | ||||
|   <c-card title="Compare string with hash"> | ||||
|     <n-form label-width="120"> | ||||
|       <n-form-item label="Your string: " label-placement="left"> | ||||
|         <n-input | ||||
|           v-model:value="compareString" | ||||
|           placeholder="Your string to compare..." | ||||
|           autocomplete="off" | ||||
|           autocorrect="off" | ||||
|           autocapitalize="off" | ||||
|           spellcheck="false" | ||||
|         /> | ||||
|         <c-input-text v-model:value="compareString" placeholder="Your string to compare..." raw-text /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="Your hash: " label-placement="left"> | ||||
|         <n-input | ||||
|           v-model:value="compareHash" | ||||
|           placeholder="Your hahs to compare..." | ||||
|           autocomplete="off" | ||||
|           autocorrect="off" | ||||
|           autocapitalize="off" | ||||
|           spellcheck="false" | ||||
|         /> | ||||
|         <c-input-text v-model:value="compareHash" placeholder="Your hahs to compare..." raw-text /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="Do they match ? " label-placement="left" :show-feedback="false"> | ||||
|         <div class="compare-result" :class="{ positive: compareMatch }"> | ||||
|  | ||||
| @ -1,11 +1,15 @@ | ||||
| <template> | ||||
|   <n-scrollbar style="flex: 1" x-scrollable> | ||||
|     <n-space :wrap="false" style="flex: 1" justify="center" :size="0" mb-5> | ||||
|     <n-space :wrap="false" style="flex: 1" justify="center" :size="12" mb-5> | ||||
|       <div v-for="(suite, index) of suites" :key="index"> | ||||
|         <c-card style="width: 292px; margin: 0 8px 5px"> | ||||
|           <n-form-item label="Suite name:" :show-feedback="false" label-placement="left"> | ||||
|             <n-input v-model:value="suite.title" placeholder="Suite name..." /> | ||||
|           </n-form-item> | ||||
|         <c-card style="width: 294px"> | ||||
|           <c-input-text | ||||
|             v-model:value="suite.title" | ||||
|             label-position="left" | ||||
|             label="Suite name" | ||||
|             placeholder="Suite name..." | ||||
|             clearable | ||||
|           /> | ||||
| 
 | ||||
|           <n-divider></n-divider> | ||||
|           <n-form-item label="Suite values" :show-feedback="false"> | ||||
| @ -33,9 +37,7 @@ | ||||
|   <div style="flex: 0 0 100%"> | ||||
|     <div style="max-width: 600px; margin: 0 auto"> | ||||
|       <n-space justify="center"> | ||||
|         <n-form-item label="Unit:" label-placement="left"> | ||||
|           <n-input v-model:value="unit" placeholder="Unit (eg: ms)" /> | ||||
|         </n-form-item> | ||||
|         <c-input-text v-model:value="unit" placeholder="Unit (eg: ms)" label="Unit" label-position="left" mb-4 /> | ||||
| 
 | ||||
|         <c-button | ||||
|           @click=" | ||||
|  | ||||
| @ -16,7 +16,8 @@ | ||||
|           :validation-status="entropyValidation.status" | ||||
|         > | ||||
|           <n-input-group> | ||||
|             <n-input v-model:value="entropy" placeholder="Your string..." /> | ||||
|             <c-input-text v-model:value="entropy" placeholder="Your string..." /> | ||||
| 
 | ||||
|             <c-button @click="refreshEntropy"> | ||||
|               <n-icon size="22"> | ||||
|                 <Refresh /> | ||||
| @ -37,15 +38,7 @@ | ||||
|       :validation-status="mnemonicValidation.status" | ||||
|     > | ||||
|       <n-input-group> | ||||
|         <n-input | ||||
|           v-model:value="passphrase" | ||||
|           style="text-align: center; flex: 1" | ||||
|           placeholder="Your mnemonic..." | ||||
|           autocomplete="off" | ||||
|           autocorrect="off" | ||||
|           autocapitalize="off" | ||||
|           spellcheck="false" | ||||
|         /> | ||||
|         <c-input-text v-model:value="passphrase" placeholder="Your mnemonic..." raw-text /> | ||||
| 
 | ||||
|         <c-button @click="copyPassphrase"> | ||||
|           <n-icon size="22" :component="Copy" /> | ||||
|  | ||||
| @ -1,9 +1,15 @@ | ||||
| <template> | ||||
|   <c-card> | ||||
|     <n-form label-width="120" label-placement="left" :show-feedback="false"> | ||||
|       <n-form-item label="Your string:"> | ||||
|         <n-input v-model:value="input" /> | ||||
|       </n-form-item> | ||||
|       <c-input-text | ||||
|         v-model:value="input" | ||||
|         label="Your string" | ||||
|         label-position="left" | ||||
|         label-width="120px" | ||||
|         label-align="right" | ||||
|         placeholder="Your string..." | ||||
|         raw-text | ||||
|       /> | ||||
| 
 | ||||
|       <n-divider /> | ||||
| 
 | ||||
|  | ||||
| @ -4,8 +4,8 @@ | ||||
|       <div class="duration">{{ formatMs(counter) }}</div> | ||||
|     </c-card> | ||||
|     <n-space justify="center" mt-5> | ||||
|       <c-button v-if="!isRunning" secondary type="primary" @click="resume">Start</c-button> | ||||
|       <c-button v-else secondary type="warning" @click="pause">Stop</c-button> | ||||
|       <c-button v-if="!isRunning" type="primary" @click="resume">Start</c-button> | ||||
|       <c-button v-else type="warning" @click="pause">Stop</c-button> | ||||
| 
 | ||||
|       <c-button @click="counter = 0">Reset</c-button> | ||||
|     </n-space> | ||||
|  | ||||
| @ -9,25 +9,25 @@ | ||||
|         /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="color name:"> | ||||
|         <input-copyable v-model:value="name" :on-input="(v: string) => onInputUpdated(v, 'name')" /> | ||||
|         <input-copyable v-model:value="name" @update:value="(v: string) => onInputUpdated(v, 'name')" /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="hex:"> | ||||
|         <input-copyable v-model:value="hex" :on-input="(v: string) => onInputUpdated(v, 'hex')" /> | ||||
|         <input-copyable v-model:value="hex" @update:value="(v: string) => onInputUpdated(v, 'hex')" /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="rgb:"> | ||||
|         <input-copyable v-model:value="rgb" :on-input="(v: string) => onInputUpdated(v, 'rgb')" /> | ||||
|         <input-copyable v-model:value="rgb" @update:value="(v: string) => onInputUpdated(v, 'rgb')" /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="hsl:"> | ||||
|         <input-copyable v-model:value="hsl" :on-input="(v: string) => onInputUpdated(v, 'hsl')" /> | ||||
|         <input-copyable v-model:value="hsl" @update:value="(v: string) => onInputUpdated(v, 'hsl')" /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="hwb:"> | ||||
|         <input-copyable v-model:value="hwb" :on-input="(v: string) => onInputUpdated(v, 'hwb')" /> | ||||
|         <input-copyable v-model:value="hwb" @update:value="(v: string) => onInputUpdated(v, 'hwb')" /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="lch:"> | ||||
|         <input-copyable v-model:value="lch" :on-input="(v: string) => onInputUpdated(v, 'lch')" /> | ||||
|         <input-copyable v-model:value="lch" @update:value="(v: string) => onInputUpdated(v, 'lch')" /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="cmyk:"> | ||||
|         <input-copyable v-model:value="cmyk" :on-input="(v: string) => onInputUpdated(v, 'cmyk')" /> | ||||
|         <input-copyable v-model:value="cmyk" @update:value="(v: string) => onInputUpdated(v, 'cmyk')" /> | ||||
|       </n-form-item> | ||||
|     </n-form> | ||||
|   </c-card> | ||||
| @ -54,15 +54,19 @@ const cmyk = ref(''); | ||||
| const lch = ref(''); | ||||
| 
 | ||||
| function onInputUpdated(value: string, omit: string) { | ||||
|   const color = colord(value); | ||||
|   try { | ||||
|     const color = colord(value); | ||||
| 
 | ||||
|   if (omit !== 'name') name.value = color.toName({ closest: true }) ?? ''; | ||||
|   if (omit !== 'hex') hex.value = color.toHex(); | ||||
|   if (omit !== 'rgb') rgb.value = color.toRgbString(); | ||||
|   if (omit !== 'hsl') hsl.value = color.toHslString(); | ||||
|   if (omit !== 'hwb') hwb.value = color.toHwbString(); | ||||
|   if (omit !== 'cmyk') cmyk.value = color.toCmykString(); | ||||
|   if (omit !== 'lch') lch.value = color.toLchString(); | ||||
|     if (omit !== 'name') name.value = color.toName({ closest: true }) ?? ''; | ||||
|     if (omit !== 'hex') hex.value = color.toHex(); | ||||
|     if (omit !== 'rgb') rgb.value = color.toRgbString(); | ||||
|     if (omit !== 'hsl') hsl.value = color.toHslString(); | ||||
|     if (omit !== 'hwb') hwb.value = color.toHwbString(); | ||||
|     if (omit !== 'cmyk') cmyk.value = color.toCmykString(); | ||||
|     if (omit !== 'lch') lch.value = color.toLchString(); | ||||
|   } catch { | ||||
|     // | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| onInputUpdated(hex.value, 'hex'); | ||||
|  | ||||
| @ -1,13 +1,15 @@ | ||||
| <template> | ||||
|   <c-card> | ||||
|     <n-form-item | ||||
|       class="cron" | ||||
|       :show-label="false" | ||||
|       :feedback="cronValidation.message" | ||||
|       :validation-status="cronValidation.status" | ||||
|     > | ||||
|       <n-input v-model:value="cron" size="large" placeholder="* * * * *" /> | ||||
|     </n-form-item> | ||||
|     <div mx-auto max-w-sm> | ||||
|       <c-input-text | ||||
|         v-model:value="cron" | ||||
|         size="large" | ||||
|         placeholder="* * * * *" | ||||
|         :validation-rules="cronValidationRules" | ||||
|         mb-3 | ||||
|       /> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="cron-string"> | ||||
|       {{ cronString }} | ||||
|     </div> | ||||
| @ -86,7 +88,6 @@ | ||||
| import cronstrue from 'cronstrue'; | ||||
| import { isValidCron } from 'cron-validator'; | ||||
| import { computed, reactive, ref } from 'vue'; | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| import { useStyleStore } from '@/stores/style.store'; | ||||
| 
 | ||||
| function isCronValid(v: string) { | ||||
| @ -185,30 +186,20 @@ const cronString = computed(() => { | ||||
|   return ' '; | ||||
| }); | ||||
| 
 | ||||
| const cronValidation = useValidation({ | ||||
|   source: cron, | ||||
|   rules: [ | ||||
|     { | ||||
|       validator: (value) => isCronValid(value), | ||||
|       message: 'This cron is invalid', | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| const cronValidationRules = [ | ||||
|   { | ||||
|     validator: (value: string) => isCronValid(value), | ||||
|     message: 'This cron is invalid', | ||||
|   }, | ||||
| ]; | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
| .cron { | ||||
| ::v-deep(input) { | ||||
|   font-size: 30px; | ||||
|   font-family: monospace; | ||||
|   padding: 5px; | ||||
|   text-align: center; | ||||
| 
 | ||||
|   margin: auto; | ||||
|   max-width: 400px; | ||||
|   display: block; | ||||
| 
 | ||||
|   .n-input { | ||||
|     font-size: 30px; | ||||
|     font-family: monospace; | ||||
|     padding: 5px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .cron-string { | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-form-item :show-label="false" v-bind="validation.attrs"> | ||||
|     <n-form-item :show-label="false" v-bind="validation.attrs as any"> | ||||
|       <n-input-group> | ||||
|         <n-input | ||||
|           v-model:value="inputDate" | ||||
| @ -8,7 +8,7 @@ | ||||
|           :on-input="onDateInputChanged" | ||||
|           placeholder="Put you date string here..." | ||||
|           clearable | ||||
|           :input-props="{ 'data-test-id': 'date-time-converter-input' }" | ||||
|           :input-props="{ 'data-test-id': 'date-time-converter-input' } as any" | ||||
|         /> | ||||
| 
 | ||||
|         <n-select | ||||
| @ -20,16 +20,19 @@ | ||||
|       </n-input-group> | ||||
|     </n-form-item> | ||||
|     <n-divider style="margin-top: 0" /> | ||||
|     <div v-for="{ name, fromDate } in formats" :key="name" mt-1> | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="flex: 0 0 170px"> {{ name }}: </n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="formatDateUsingFormatter(fromDate, normalizedDate)" | ||||
|           placeholder="Invalid date..." | ||||
|           :input-props="{ 'data-test-id': name }" | ||||
|         /> | ||||
|       </n-input-group> | ||||
|     </div> | ||||
|     <input-copyable | ||||
|       v-for="{ name, fromDate } in formats" | ||||
|       :key="name" | ||||
|       :label="name" | ||||
|       label-width="150px" | ||||
|       label-position="left" | ||||
|       label-align="right" | ||||
|       :value="formatDateUsingFormatter(fromDate, normalizedDate)" | ||||
|       placeholder="Invalid date..." | ||||
|       :test-id="name" | ||||
|       readonly | ||||
|       mt-2 | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
|  | ||||
| @ -7,12 +7,15 @@ | ||||
|           type="textarea" | ||||
|           placeholder="The string to cypher" | ||||
|           :autosize="{ minRows: 4 }" | ||||
|           autocomplete="off" | ||||
|           autocorrect="off" | ||||
|           autocapitalize="off" | ||||
|           spellcheck="false" | ||||
|         /> | ||||
|       </n-form-item> | ||||
|       <n-space vertical> | ||||
|         <n-form-item label="Your secret key:" :show-feedback="false"> | ||||
|           <n-input v-model:value="cypherSecret" /> | ||||
|         </n-form-item> | ||||
|         <c-input-text v-model:value="cypherSecret" label="Your secret key:" clearable raw-text /> | ||||
| 
 | ||||
|         <n-form-item label="Encryption algorithm:" :show-feedback="false"> | ||||
|           <n-select | ||||
|             v-model:value="cypherAlgo" | ||||
| @ -43,12 +46,15 @@ | ||||
|           type="textarea" | ||||
|           placeholder="The string to cypher" | ||||
|           :autosize="{ minRows: 4 }" | ||||
|           autocomplete="off" | ||||
|           autocorrect="off" | ||||
|           autocapitalize="off" | ||||
|           spellcheck="false" | ||||
|         /> | ||||
|       </n-form-item> | ||||
|       <n-space vertical> | ||||
|         <n-form-item label="Your secret key:" :show-feedback="false"> | ||||
|           <n-input v-model:value="decryptSecret" /> | ||||
|         </n-form-item> | ||||
|         <c-input-text v-model:value="decryptSecret" label="Your secret key:" clearable raw-text /> | ||||
| 
 | ||||
|         <n-form-item label="Encryption algorithm:" :show-feedback="false"> | ||||
|           <n-select | ||||
|             v-model:value="decryptAlgo" | ||||
|  | ||||
| @ -22,59 +22,54 @@ | ||||
|       <n-alert v-if="error" style="margin-top: 25px" type="error">{{ error }}</n-alert> | ||||
|       <n-divider /> | ||||
| 
 | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="flex: 0 0 170px"> Binary (2): </n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 2 })" | ||||
|           readonly | ||||
|           placeholder="Binary version will be here..." | ||||
|         /> | ||||
|       </n-input-group> | ||||
|       <input-copyable | ||||
|         label="Binary (2)" | ||||
|         v-bind="inputProps" | ||||
|         :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 2 })" | ||||
|         placeholder="Binary version will be here..." | ||||
|       /> | ||||
| 
 | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="flex: 0 0 170px"> Octal (8): </n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 8 })" | ||||
|           readonly | ||||
|           placeholder="Octal version will be here..." | ||||
|         /> | ||||
|       </n-input-group> | ||||
|       <input-copyable | ||||
|         label="Octal (8)" | ||||
|         v-bind="inputProps" | ||||
|         :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 8 })" | ||||
|         placeholder="Octal version will be here..." | ||||
|       /> | ||||
| 
 | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="flex: 0 0 170px"> Decimal (10): </n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 10 })" | ||||
|           readonly | ||||
|           placeholder="Decimal version will be here..." | ||||
|         /> | ||||
|       </n-input-group> | ||||
|       <input-copyable | ||||
|         label="Decimal (10)" | ||||
|         v-bind="inputProps" | ||||
|         :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 10 })" | ||||
|         placeholder="Decimal version will be here..." | ||||
|       /> | ||||
| 
 | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="flex: 0 0 170px"> Hexadecimal (16): </n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 16 })" | ||||
|           readonly | ||||
|           placeholder="Decimal version will be here..." | ||||
|         /> | ||||
|       </n-input-group> | ||||
|       <input-copyable | ||||
|         label="Hexadecimal (16)" | ||||
|         v-bind="inputProps" | ||||
|         :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 16 })" | ||||
|         placeholder="Hexadecimal version will be here..." | ||||
|       /> | ||||
| 
 | ||||
|       <input-copyable | ||||
|         label="Base64 (64)" | ||||
|         v-bind="inputProps" | ||||
|         :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 64 })" | ||||
|         placeholder="Base64 version will be here..." | ||||
|       /> | ||||
| 
 | ||||
|       <div flex items-baseline> | ||||
|         <n-input-group style="width: 160px; margin-right: 10px"> | ||||
|           <n-input-group-label> Custom: </n-input-group-label> | ||||
|           <n-input-number v-model:value="outputBase" max="64" min="2" /> | ||||
|         </n-input-group> | ||||
| 
 | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="flex: 0 0 170px"> Base64 (64): </n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 64 })" | ||||
|           readonly | ||||
|           placeholder="Base64 version will be here..." | ||||
|         /> | ||||
|       </n-input-group> | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="flex: 0 0 85px"> Custom: </n-input-group-label> | ||||
|         <n-input-number v-model:value="outputBase" style="flex: 0 0 86px" max="64" min="2" /> | ||||
|         <input-copyable | ||||
|           flex-1 | ||||
|           v-bind="inputProps" | ||||
|           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: outputBase })" | ||||
|           readonly | ||||
|           :placeholder="`Base ${outputBase} will be here...`" | ||||
|         /> | ||||
|       </n-input-group> | ||||
|       </div> | ||||
|     </c-card> | ||||
|   </div> | ||||
| </template> | ||||
| @ -88,6 +83,14 @@ import InputCopyable from '../../components/InputCopyable.vue'; | ||||
| 
 | ||||
| const styleStore = useStyleStore(); | ||||
| 
 | ||||
| const inputProps = { | ||||
|   labelPosition: 'left', | ||||
|   labelWidth: '170px', | ||||
|   labelAlign: 'right', | ||||
|   readonly: true, | ||||
|   'mb-2': '', | ||||
| } as const; | ||||
| 
 | ||||
| const input = ref('42'); | ||||
| const inputBase = ref(10); | ||||
| const outputBase = ref(42); | ||||
|  | ||||
| @ -1,23 +1,20 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-form-item label="An ipv4 address:" v-bind="validationAttrs"> | ||||
|       <n-input v-model:value="rawIpAddress" placeholder="An ipv4 address..." /> | ||||
|     </n-form-item> | ||||
|     <c-input-text v-model:value="rawIpAddress" label="The ipv4 address:" placeholder="The ipv4 address..." readonly /> | ||||
| 
 | ||||
|     <n-divider style="margin-top: 0" mt-0 /> | ||||
|     <n-divider /> | ||||
| 
 | ||||
|     <n-form-item | ||||
|     <input-copyable | ||||
|       v-for="{ label, value } of convertedSections" | ||||
|       :key="label" | ||||
|       :label="label" | ||||
|       label-placement="left" | ||||
|       label-width="100" | ||||
|     > | ||||
|       <input-copyable | ||||
|         :value="validationAttrs.validationStatus === 'error' ? '' : value" | ||||
|         placeholder="Set a correct ipv4 address" | ||||
|       /> | ||||
|     </n-form-item> | ||||
|       label-position="left" | ||||
|       label-width="100px" | ||||
|       label-align="right" | ||||
|       mb-2 | ||||
|       :value="validationAttrs.validationStatus === 'error' ? '' : value" | ||||
|       placeholder="Set a correct ipv4 address" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| @ -33,7 +30,7 @@ const convertedSections = computed(() => { | ||||
| 
 | ||||
|   return [ | ||||
|     { | ||||
|       label: 'Decimal : ', | ||||
|       label: 'Decimal: ', | ||||
|       value: String(ipInDecimal), | ||||
|     }, | ||||
|     { | ||||
|  | ||||
| @ -3,10 +3,10 @@ | ||||
|     <n-space item-style="flex:1 1 0"> | ||||
|       <div> | ||||
|         <n-space item-style="flex:1 1 0"> | ||||
|           <n-form-item label="Start address" v-bind="startIpValidation.attrs"> | ||||
|           <n-form-item label="Start address" v-bind="startIpValidation.attrs as any"> | ||||
|             <n-input v-model:value="rawStartAddress" placeholder="Start IPv4 address..." /> | ||||
|           </n-form-item> | ||||
|           <n-form-item label="End address" v-bind="endIpValidation.attrs"> | ||||
|           <n-form-item label="End address" v-bind="endIpValidation.attrs as any"> | ||||
|             <n-input v-model:value="rawEndAddress" placeholder="End IPv4 address..." /> | ||||
|           </n-form-item> | ||||
|         </n-space> | ||||
|  | ||||
| @ -1,8 +1,12 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-form-item label="An IPv4 address with or without mask" v-bind="validationAttrs"> | ||||
|       <n-input v-model:value="ip" /> | ||||
|     </n-form-item> | ||||
|     <c-input-text | ||||
|       v-model:value="ip" | ||||
|       label="An IPv4 address with or without mask" | ||||
|       placeholder="The ipv4 address..." | ||||
|       :validation-rules="ipValidationRules" | ||||
|       mb-4 | ||||
|     /> | ||||
| 
 | ||||
|     <div v-if="networkInfo"> | ||||
|       <n-table> | ||||
| @ -37,7 +41,6 @@ | ||||
| import { computed } from 'vue'; | ||||
| import { Netmask } from 'netmask'; | ||||
| import { withDefaultOnError } from '@/utils/defaults'; | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| import { isNotThrowing } from '@/utils/boolean'; | ||||
| import { useStorage } from '@vueuse/core'; | ||||
| import { ArrowLeft, ArrowRight } from '@vicons/tabler'; | ||||
| @ -50,15 +53,12 @@ const getNetworkInfo = (address: string) => new Netmask(address.trim()); | ||||
| 
 | ||||
| const networkInfo = computed(() => withDefaultOnError(() => getNetworkInfo(ip.value), undefined)); | ||||
| 
 | ||||
| const { attrs: validationAttrs } = useValidation({ | ||||
|   source: ip, | ||||
|   rules: [ | ||||
|     { | ||||
|       message: 'We cannot parse this address, check the format', | ||||
|       validator: (value) => isNotThrowing(() => getNetworkInfo(value.trim())), | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| const ipValidationRules = [ | ||||
|   { | ||||
|     message: 'We cannot parse this address, check the format', | ||||
|     validator: (value: string) => isNotThrowing(() => getNetworkInfo(value.trim())), | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| const sections: { | ||||
|   label: string; | ||||
|  | ||||
| @ -1,30 +1,32 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-space vertical :size="50"> | ||||
|       <n-alert title="Info" type="info"> | ||||
|         This tool uses the first method suggested by IETF using the current timestamp plus the mac address, sha1 hashed, | ||||
|         and the lower 40 bits to generate your random ULA. | ||||
|       </n-alert> | ||||
|     <n-alert title="Info" type="info"> | ||||
|       This tool uses the first method suggested by IETF using the current timestamp plus the mac address, sha1 hashed, | ||||
|       and the lower 40 bits to generate your random ULA. | ||||
|     </n-alert> | ||||
| 
 | ||||
|       <n-form-item label="MAC address:" v-bind="validationAttrs"> | ||||
|         <n-input | ||||
|           v-model:value="macAddress" | ||||
|           size="large" | ||||
|           placeholder="Type a MAC address" | ||||
|           clearable | ||||
|           autocomplete="off" | ||||
|           autocorrect="off" | ||||
|           autocapitalize="off" | ||||
|           spellcheck="false" | ||||
|         /> | ||||
|       </n-form-item> | ||||
|     </n-space> | ||||
|     <c-input-text | ||||
|       v-model:value="macAddress" | ||||
|       placeholder="Type a MAC address" | ||||
|       clearable | ||||
|       label="MAC address:" | ||||
|       raw-text | ||||
|       my-8 | ||||
|       :validation="addressValidation" | ||||
|     /> | ||||
| 
 | ||||
|     <div v-if="validationAttrs.validationStatus !== 'error'"> | ||||
|       <n-input-group v-for="{ label, value } in calculatedSections" :key="label" style="margin: 5px 0"> | ||||
|         <n-input-group-label style="flex: 0 0 160px"> {{ label }} </n-input-group-label> | ||||
|         <input-copyable :value="value" readonly /> | ||||
|       </n-input-group> | ||||
|     <div v-if="addressValidation.isValid"> | ||||
|       <input-copyable | ||||
|         v-for="{ label, value } in calculatedSections" | ||||
|         :key="label" | ||||
|         :value="value" | ||||
|         :label="label" | ||||
|         label-width="160px" | ||||
|         label-align="right" | ||||
|         label-position="left" | ||||
|         readonly | ||||
|         mb-2 | ||||
|       /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @ -59,7 +61,7 @@ const calculatedSections = computed(() => { | ||||
|   ]; | ||||
| }); | ||||
| 
 | ||||
| const { attrs: validationAttrs } = macAddressValidation(macAddress); | ||||
| const addressValidation = macAddressValidation(macAddress); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped></style> | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <template> | ||||
|   <n-form-item label="Your first json" v-bind="leftJsonValidation.attrs"> | ||||
|   <n-form-item label="Your first json" v-bind="leftJsonValidation.attrs as any"> | ||||
|     <n-input | ||||
|       v-model:value="rawLeftJson" | ||||
|       placeholder="Paste your first json here..." | ||||
| @ -9,10 +9,10 @@ | ||||
|       autocorrect="off" | ||||
|       autocapitalize="off" | ||||
|       spellcheck="false" | ||||
|       :input-props="{ 'data-test-id': 'leftJson' }" | ||||
|       :input-props="{ 'data-test-id': 'leftJson' }  as any" | ||||
|     /> | ||||
|   </n-form-item> | ||||
|   <n-form-item label="Your json to compare" v-bind="rightJsonValidation.attrs"> | ||||
|   <n-form-item label="Your json to compare" v-bind="rightJsonValidation.attrs as any"> | ||||
|     <n-input | ||||
|       v-model:value="rawRightJson" | ||||
|       placeholder="Paste your json to compare here..." | ||||
| @ -22,7 +22,7 @@ | ||||
|       autocorrect="off" | ||||
|       autocapitalize="off" | ||||
|       spellcheck="false" | ||||
|       :input-props="{ 'data-test-id': 'rightJson' }" | ||||
|       :input-props="{ 'data-test-id': 'rightJson' }  as any" | ||||
|     /> | ||||
|   </n-form-item> | ||||
| 
 | ||||
|  | ||||
| @ -30,8 +30,8 @@ test.describe('Tool - List converter', () => { | ||||
|     3 | ||||
|     5`);
 | ||||
|     await page.getByTestId('removeDuplicates').check(); | ||||
|     await page.getByTestId('itemPrefix').locator('input').fill("'"); | ||||
|     await page.getByTestId('itemSuffix').locator('input').fill("'"); | ||||
|     await page.getByTestId('itemPrefix').fill("'"); | ||||
|     await page.getByTestId('itemSuffix').fill("'"); | ||||
| 
 | ||||
|     const result = await page.getByTestId('area-content').innerText(); | ||||
|     expect(result.trim()).toEqual("'1', '2', '4', '3', '5'"); | ||||
|  | ||||
| @ -36,37 +36,39 @@ | ||||
|               /> | ||||
|             </n-form-item> | ||||
| 
 | ||||
|             <n-form-item label="Separator" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||||
|               <n-input v-model:value="conversionConfig.separator" placeholder="," /> | ||||
|             </n-form-item> | ||||
|             <c-input-text | ||||
|               v-model:value="conversionConfig.separator" | ||||
|               label="Separator" | ||||
|               label-position="left" | ||||
|               label-width="120px" | ||||
|               label-align="right" | ||||
|               mb-2 | ||||
|               placeholder="," | ||||
|             /> | ||||
| 
 | ||||
|             <n-form-item label="Wrap item" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||||
|               <n-input-group> | ||||
|                 <n-input | ||||
|                   v-model:value="conversionConfig.itemPrefix" | ||||
|                   placeholder="Item prefix" | ||||
|                   data-test-id="itemPrefix" | ||||
|                 /> | ||||
|                 <n-input | ||||
|                   v-model:value="conversionConfig.itemSuffix" | ||||
|                   placeholder="Item suffix" | ||||
|                   data-test-id="itemSuffix" | ||||
|                 /> | ||||
|               </n-input-group> | ||||
|               <c-input-text | ||||
|                 v-model:value="conversionConfig.itemPrefix" | ||||
|                 placeholder="Item prefix" | ||||
|                 test-id="itemPrefix" | ||||
|               /> | ||||
|               <c-input-text | ||||
|                 v-model:value="conversionConfig.itemSuffix" | ||||
|                 placeholder="Item suffix" | ||||
|                 test-id="itemSuffix" | ||||
|               /> | ||||
|             </n-form-item> | ||||
|             <n-form-item label="Wrap list" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||||
|               <n-input-group> | ||||
|                 <n-input | ||||
|                   v-model:value="conversionConfig.listPrefix" | ||||
|                   placeholder="List prefix" | ||||
|                   data-test-id="listPrefix" | ||||
|                 /> | ||||
|                 <n-input | ||||
|                   v-model:value="conversionConfig.listSuffix" | ||||
|                   placeholder="List suffix" | ||||
|                   data-test-id="listSuffix" | ||||
|                 /> | ||||
|               </n-input-group> | ||||
|               <c-input-text | ||||
|                 v-model:value="conversionConfig.listPrefix" | ||||
|                 placeholder="List prefix" | ||||
|                 test-id="listPrefix" | ||||
|               /> | ||||
|               <c-input-text | ||||
|                 v-model:value="conversionConfig.listSuffix" | ||||
|                 placeholder="List suffix" | ||||
|                 test-id="listSuffix" | ||||
|               /> | ||||
|             </n-form-item> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-form-item label="MAC address:" v-bind="validationAttrs"> | ||||
|     <n-form-item label="MAC address:" v-bind="validationAttrs as any"> | ||||
|       <n-input | ||||
|         v-model:value="macAddress" | ||||
|         size="large" | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
| 
 | ||||
|       <n-input-group v-for="{ key, type, label, placeholder, ...element } of elements" :key="key"> | ||||
|         <n-input-group-label style="flex: 0 0 110px">{{ label }}</n-input-group-label> | ||||
|         <n-input v-if="type === 'input'" v-model:value="metadata[key]" :placeholder="placeholder" /> | ||||
|         <c-input-text v-if="type === 'input'" v-model:value="metadata[key]" :placeholder="placeholder" clearable /> | ||||
|         <n-dynamic-input | ||||
|           v-else-if="type === 'input-multiple'" | ||||
|           v-model:value="metadata[key]" | ||||
|  | ||||
| @ -1,19 +1,23 @@ | ||||
| <template> | ||||
|   <div style="max-width: 350px"> | ||||
|     <n-form-item label="Secret" v-bind="secretValidationAttrs"> | ||||
|       <n-input v-model:value="secret" placeholder="Paste your TOTP secret..."> | ||||
|         <template #suffix> | ||||
|           <n-tooltip trigger="hover"> | ||||
|             <template #trigger> | ||||
|               <c-button circle variant="text" @click="refreshSecret"> | ||||
|                 <n-icon :component="Refresh" /> | ||||
|               </c-button> | ||||
|             </template> | ||||
|             Generate secret token | ||||
|           </n-tooltip> | ||||
|         </template> | ||||
|       </n-input> | ||||
|     </n-form-item> | ||||
|     <c-input-text | ||||
|       v-model:value="secret" | ||||
|       label="Secret" | ||||
|       placeholder="Paste your TOTP secret..." | ||||
|       mb-5 | ||||
|       :validation-rules="secretValidationRules" | ||||
|     > | ||||
|       <template #suffix> | ||||
|         <n-tooltip trigger="hover"> | ||||
|           <template #trigger> | ||||
|             <c-button circle variant="text" size="small" @click="refreshSecret"> | ||||
|               <icon-mdi-refresh /> | ||||
|             </c-button> | ||||
|           </template> | ||||
|           Generate secret token | ||||
|         </n-tooltip> | ||||
|       </template> | ||||
|     </c-input-text> | ||||
| 
 | ||||
|     <div> | ||||
|       <token-display :tokens="tokens" style="margin-top: 2px" /> | ||||
| @ -27,49 +31,52 @@ | ||||
|     </n-space> | ||||
|   </div> | ||||
|   <div style="max-width: 350px"> | ||||
|     <n-form-item label="Secret in hexadecimal"> | ||||
|       <input-copyable :value="base32toHex(secret)" readonly placeholder="Secret in hex will be displayed here" /> | ||||
|     </n-form-item> | ||||
|     <input-copyable | ||||
|       label="Secret in hexadecimal" | ||||
|       :value="base32toHex(secret)" | ||||
|       readonly | ||||
|       placeholder="Secret in hex will be displayed here" | ||||
|       mb-5 | ||||
|     /> | ||||
| 
 | ||||
|     <n-form-item label="Epoch"> | ||||
|       <input-copyable | ||||
|         :value="Math.floor(now / 1000).toString()" | ||||
|         readonly | ||||
|         placeholder="Epoch in sec will be displayed here" | ||||
|       /> | ||||
|     </n-form-item> | ||||
|     <n-form-item label="Iteration" :show-feedback="false"> | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="width: 110px">Count:</n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="String(getCounterFromTime({ now, timeStep: 30 }))" | ||||
|           readonly | ||||
|           placeholder="Iteration count will be displayed here" | ||||
|         /> | ||||
|       </n-input-group> | ||||
|     </n-form-item> | ||||
|     <input-copyable | ||||
|       label="Epoch" | ||||
|       :value="Math.floor(now / 1000).toString()" | ||||
|       readonly | ||||
|       mb-5 | ||||
|       placeholder="Epoch in sec will be displayed here" | ||||
|     /> | ||||
| 
 | ||||
|     <n-form-item label="Iteration" :show-label="false" style="margin-top: 5px"> | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="width: 110px">Padded hex:</n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="getCounterFromTime({ now, timeStep: 30 }).toString(16).padStart(16, '0')" | ||||
|           readonly | ||||
|           placeholder="Iteration count in hex will be displayed here" | ||||
|         /> | ||||
|       </n-input-group> | ||||
|     </n-form-item> | ||||
|     <p>Iteration</p> | ||||
| 
 | ||||
|     <input-copyable | ||||
|       :value="String(getCounterFromTime({ now, timeStep: 30 }))" | ||||
|       readonly | ||||
|       label="Count:" | ||||
|       label-position="left" | ||||
|       label-width="90px" | ||||
|       label-align="right" | ||||
|       placeholder="Iteration count will be displayed here" | ||||
|     /> | ||||
| 
 | ||||
|     <input-copyable | ||||
|       :value="getCounterFromTime({ now, timeStep: 30 }).toString(16).padStart(16, '0')" | ||||
|       readonly | ||||
|       placeholder="Iteration count in hex will be displayed here" | ||||
|       label-position="left" | ||||
|       label-width="90px" | ||||
|       label-align="right" | ||||
|       label="Padded hex:" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { computed, ref } from 'vue'; | ||||
| import { Refresh } from '@vicons/tabler'; | ||||
| import { useTimestamp } from '@vueuse/core'; | ||||
| import { useThemeVars } from 'naive-ui'; | ||||
| import { useStyleStore } from '@/stores/style.store'; | ||||
| import InputCopyable from '@/components/InputCopyable.vue'; | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| import { computedRefreshable } from '@/composable/computedRefreshable'; | ||||
| import { generateTOTP, buildKeyUri, generateSecret, base32toHex, getCounterFromTime } from './otp.service'; | ||||
| import { useQRCode } from '../qr-code-generator/useQRCode'; | ||||
| @ -106,19 +113,16 @@ const { qrcode } = useQRCode({ | ||||
|   options: { width: 210 }, | ||||
| }); | ||||
| 
 | ||||
| const { attrs: secretValidationAttrs } = useValidation({ | ||||
|   source: secret, | ||||
|   rules: [ | ||||
|     { | ||||
|       message: 'Secret should be a base32 string', | ||||
|       validator: (value) => value.toUpperCase().match(/^[A-Z234567]+$/), | ||||
|     }, | ||||
|     { | ||||
|       message: 'Please set a secret', | ||||
|       validator: (value) => value !== '', | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| const secretValidationRules = [ | ||||
|   { | ||||
|     message: 'Secret should be a base32 string', | ||||
|     validator: (value: string) => value.toUpperCase().match(/^[A-Z234567]+$/), | ||||
|   }, | ||||
|   { | ||||
|     message: 'Please set a secret', | ||||
|     validator: (value: string) => value !== '', | ||||
|   }, | ||||
| ]; | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
|  | ||||
| @ -3,9 +3,14 @@ | ||||
|     <n-form-item label="Default country code:"> | ||||
|       <n-select v-model:value="defaultCountryCode" :options="countriesOptions" /> | ||||
|     </n-form-item> | ||||
|     <n-form-item label="Phone number:" v-bind="validation.attrs"> | ||||
|       <n-input v-model:value="rawPhone" placeholder="Enter a phone number" /> | ||||
|     </n-form-item> | ||||
| 
 | ||||
|     <c-input-text | ||||
|       v-model:value="rawPhone" | ||||
|       placeholder="Enter a phone number" | ||||
|       label="Phone number:" | ||||
|       :validation="validation" | ||||
|       mb-5 | ||||
|     /> | ||||
| 
 | ||||
|     <n-table v-if="parsedDetails"> | ||||
|       <tbody> | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|   <div> | ||||
|     <c-card title="Arabic to roman"> | ||||
|       <n-space align="center" justify="space-between"> | ||||
|         <n-form-item v-bind="validationNumeral"> | ||||
|         <n-form-item v-bind="validationNumeral as any"> | ||||
|           <n-input-number v-model:value="inputNumeral" :min="1" style="width: 200px" :show-button="false" /> | ||||
|         </n-form-item> | ||||
|         <div class="result"> | ||||
| @ -15,13 +15,12 @@ | ||||
|     </c-card> | ||||
|     <c-card title="Roman to arabic" mt-5> | ||||
|       <n-space align="center" justify="space-between"> | ||||
|         <n-form-item v-bind="validationRoman"> | ||||
|           <n-input v-model:value="inputRoman" style="width: 200px" /> | ||||
|         </n-form-item> | ||||
|         <c-input-text v-model:value="inputRoman" style="width: 200px" :validation="validationRoman" /> | ||||
| 
 | ||||
|         <div class="result"> | ||||
|           {{ outputNumeral }} | ||||
|         </div> | ||||
|         <c-button :disabled="validationRoman.validationStatus === 'error'" @click="copyArabic"> Copy </c-button> | ||||
|         <c-button :disabled="!validationRoman.isValid" @click="copyArabic"> Copy </c-button> | ||||
|       </n-space> | ||||
|     </c-card> | ||||
|   </div> | ||||
| @ -55,7 +54,7 @@ const { attrs: validationNumeral } = useValidation({ | ||||
| const inputRoman = ref('XLII'); | ||||
| const outputNumeral = computed(() => romanToArabic(inputRoman.value)); | ||||
| 
 | ||||
| const { attrs: validationRoman } = useValidation({ | ||||
| const validationRoman = useValidation({ | ||||
|   source: inputRoman, | ||||
|   rules: [ | ||||
|     { | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <template> | ||||
|   <div style="flex: 0 0 100%"> | ||||
|     <n-space item-style="flex: 1 1 0" style="margin: 0 auto; max-width: 600px" justify="center"> | ||||
|       <n-form-item label="Bits :" v-bind="bitsValidationAttrs" label-placement="left" label-width="100"> | ||||
|       <n-form-item label="Bits :" v-bind="bitsValidationAttrs as any" label-placement="left" label-width="100"> | ||||
|         <n-input-number v-model:value="bits" min="256" max="16384" step="8" /> | ||||
|       </n-form-item> | ||||
| 
 | ||||
|  | ||||
| @ -21,9 +21,15 @@ | ||||
|         <n-form-item label="Font size"> | ||||
|           <n-input-number v-model:value="fontSize" placeholder="Font size..." min="1" /> | ||||
|         </n-form-item> | ||||
|         <n-form-item label="Custom text"> | ||||
|           <n-input v-model:value="customText" :placeholder="`Default is ${width}x${height}`" /> | ||||
|         </n-form-item> | ||||
| 
 | ||||
|         <c-input-text | ||||
|           v-model:value="customText" | ||||
|           label="Custom text" | ||||
|           :placeholder="`Default is ${width}x${height}`" | ||||
|           label-position="left" | ||||
|           label-width="100px" | ||||
|           label-align="right" | ||||
|         /> | ||||
|       </n-space> | ||||
|       <n-form-item label="Use exact size" label-placement="left"> | ||||
|         <n-switch v-model:value="useExactSize" /> | ||||
|  | ||||
| @ -1,8 +1,12 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-form-item label="Your text to convert to NATO phonetic alphabet"> | ||||
|       <n-input v-model:value="input" placeholder="Put your text here..." clearable /> | ||||
|     </n-form-item> | ||||
|     <c-input-text | ||||
|       v-model:value="input" | ||||
|       label="Your text to convert to NATO phonetic alphabet" | ||||
|       placeholder="Put your text here..." | ||||
|       clearable | ||||
|       mb-5 | ||||
|     /> | ||||
| 
 | ||||
|     <n-space v-if="natoText" vertical> | ||||
|       <n-text>Your text in NATO phonetic alphabet</n-text> | ||||
|  | ||||
| @ -1,51 +1,59 @@ | ||||
| <template> | ||||
|   <c-card> | ||||
|     <n-form-item label="Your url to parse:" :feedback="validation.message" :validation-status="validation.status"> | ||||
|       <n-input v-model:value="urlToParse" placeholder="Your url to parse..." /> | ||||
|     </n-form-item> | ||||
|     <c-input-text | ||||
|       v-model:value="urlToParse" | ||||
|       label="Your url to parse:" | ||||
|       placeholder="Your url to parse..." | ||||
|       raw-text | ||||
|       :validation-rules="urlValidationRules" | ||||
|     /> | ||||
| 
 | ||||
|     <n-divider style="margin-top: 0" /> | ||||
|     <n-divider /> | ||||
| 
 | ||||
|     <n-form> | ||||
|       <n-input-group v-for="{ title, key } in properties" :key="key"> | ||||
|         <n-input-group-label style="flex: 0 0 120px"> {{ title }}: </n-input-group-label> | ||||
|         <input-copyable :value="(urlParsed?.[key] as string) ?? ''" readonly placeholder=" " /> | ||||
|       </n-input-group> | ||||
|     <input-copyable | ||||
|       v-for="{ title, key } in properties" | ||||
|       :key="key" | ||||
|       :label="title" | ||||
|       :value="(urlParsed?.[key] as string) ?? ''" | ||||
|       readonly | ||||
|       label-position="left" | ||||
|       label-width="110px" | ||||
|       mb-2 | ||||
|       placeholder=" " | ||||
|     /> | ||||
| 
 | ||||
|       <n-input-group | ||||
|         v-for="[k, v] in Object.entries(Object.fromEntries(urlParsed?.searchParams.entries() ?? []))" | ||||
|         :key="k" | ||||
|       > | ||||
|         <n-input-group-label style="flex: 0 0 120px"> | ||||
|           <n-icon :component="SubdirectoryArrowRightRound" /> | ||||
|         </n-input-group-label> | ||||
|         <input-copyable :value="k" readonly /> | ||||
|         <input-copyable :value="v" readonly /> | ||||
|       </n-input-group> | ||||
|     </n-form> | ||||
|     <div | ||||
|       v-for="[k, v] in Object.entries(Object.fromEntries(urlParsed?.searchParams.entries() ?? []))" | ||||
|       :key="k" | ||||
|       mb-2 | ||||
|       w-full | ||||
|       flex | ||||
|     > | ||||
|       <div style="flex: 1 0 110px"> | ||||
|         <icon-mdi-arrow-right-bottom /> | ||||
|       </div> | ||||
| 
 | ||||
|       <input-copyable :value="k" readonly /> | ||||
|       <input-copyable :value="v" readonly /> | ||||
|     </div> | ||||
|   </c-card> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| import { isNotThrowing } from '@/utils/boolean'; | ||||
| import { withDefaultOnError } from '@/utils/defaults'; | ||||
| import { SubdirectoryArrowRightRound } from '@vicons/material'; | ||||
| import { computed, ref } from 'vue'; | ||||
| import InputCopyable from '../../components/InputCopyable.vue'; | ||||
| 
 | ||||
| const urlToParse = ref('https://me:pwd@it-tools.tech:3000/url-parser?key1=value&key2=value2#the-hash'); | ||||
| 
 | ||||
| const urlParsed = computed(() => withDefaultOnError(() => new URL(urlToParse.value), undefined)); | ||||
| const validation = useValidation({ | ||||
|   source: urlToParse, | ||||
|   rules: [ | ||||
|     { | ||||
|       validator: (value) => isNotThrowing(() => new URL(value)), | ||||
|       message: 'Invalid url', | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| const urlValidationRules = [ | ||||
|   { | ||||
|     validator: (value: string) => isNotThrowing(() => new URL(value)), | ||||
|     message: 'Invalid url', | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| const properties: { title: string; key: keyof URL }[] = [ | ||||
|   { title: 'Protocol', key: 'protocol' }, | ||||
|  | ||||
| @ -25,6 +25,18 @@ | ||||
|       > | ||||
|         A | ||||
|       </c-button> | ||||
| 
 | ||||
|       <c-button | ||||
|         v-for="buttonType of buttonTypes" | ||||
|         :key="buttonType" | ||||
|         :variant="buttonVariant" | ||||
|         :type="buttonType" | ||||
|         :size="buttonSize" | ||||
|         circle | ||||
|         mx-1 | ||||
|       > | ||||
|         <icon-mdi-content-copy /> | ||||
|       </c-button> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @ -33,7 +45,7 @@ | ||||
| import _ from 'lodash'; | ||||
| 
 | ||||
| const buttonVariants = ['basic', 'text'] as const; | ||||
| const buttonTypes = ['default', 'primary'] as const; | ||||
| const buttonTypes = ['default', 'primary', 'warning'] as const; | ||||
| const buttonSizes = ['small', 'medium', 'large'] as const; | ||||
| </script> | ||||
| 
 | ||||
|  | ||||
| @ -56,10 +56,10 @@ const createTheme = ({ style }: { style: 'light' | 'dark' }) => { | ||||
|         pressedBackground: darken(theme.primary.colorFaded, 30), | ||||
|       }), | ||||
|       warning: createState({ | ||||
|         textColor: theme.text.baseColor, | ||||
|         backgroundColor: theme.warning.color, | ||||
|         hoverBackground: theme.warning.colorHover, | ||||
|         pressedBackground: theme.warning.colorPressed, | ||||
|         textColor: theme.warning.color, | ||||
|         backgroundColor: theme.warning.colorFaded, | ||||
|         hoverBackground: lighten(theme.warning.colorFaded, 30), | ||||
|         pressedBackground: darken(theme.warning.colorFaded, 30), | ||||
|       }), | ||||
|     }, | ||||
|     text: { | ||||
| @ -76,10 +76,10 @@ const createTheme = ({ style }: { style: 'light' | 'dark' }) => { | ||||
|         pressedBackground: darken(theme.primary.colorFaded, 30), | ||||
|       }), | ||||
|       warning: createState({ | ||||
|         textColor: theme.text.baseColor, | ||||
|         backgroundColor: theme.warning.color, | ||||
|         hoverBackground: theme.warning.colorHover, | ||||
|         pressedBackground: theme.warning.colorPressed, | ||||
|         textColor: darken(theme.warning.color, 20), | ||||
|         backgroundColor: 'transparent', | ||||
|         hoverBackground: theme.warning.colorFaded, | ||||
|         pressedBackground: darken(theme.warning.colorFaded, 30), | ||||
|       }), | ||||
|     }, | ||||
|   }; | ||||
|  | ||||
| @ -18,7 +18,7 @@ import { useAppTheme } from '../theme/themes'; | ||||
| 
 | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     type?: 'default' | 'primary'; | ||||
|     type?: 'default' | 'primary' | 'warning'; | ||||
|     variant?: 'basic' | 'text'; | ||||
|     disabled?: boolean; | ||||
|     round?: boolean; | ||||
|  | ||||
| @ -2,6 +2,9 @@ | ||||
|   <h2>Default</h2> | ||||
| 
 | ||||
|   <c-input-text value="qsd" /> | ||||
|   <c-input-text | ||||
|     value="Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum, est modi iusto repellendus fuga accusantium atque at magnam aliquam eum explicabo vero quia, nobis quasi quis! Earum amet quam a?" | ||||
|   /> | ||||
| 
 | ||||
|   <h2>With placeholder</h2> | ||||
| 
 | ||||
| @ -24,16 +27,50 @@ | ||||
| 
 | ||||
|   <h2>Validation</h2> | ||||
| 
 | ||||
|   <c-input-text | ||||
|     v-model:value="value" | ||||
|     :validation-rules="[{ message: 'Length must be > 10', validator: (value) => value.length > 10 }]" | ||||
|   /> | ||||
|   <c-input-text v-model:value="value" :validation-rules="validationRules" mb-2 /> | ||||
|   <c-input-text v-model:value="value" :validation-rules="validationRules" mb-2 label-position="left" label="Yo " /> | ||||
|   <c-input-text v-model:value="value" :validation="validation" /> | ||||
|   <c-input-text v-model:value="value" :validation="validation" multiline rows="3" /> | ||||
| 
 | ||||
|   <h2>Clearable</h2> | ||||
| 
 | ||||
|   <c-input-text v-model:value="value" clearable /> | ||||
| 
 | ||||
|   <h2>Type password</h2> | ||||
| 
 | ||||
|   <c-input-text value="value" type="password" /> | ||||
| 
 | ||||
|   <h2>Multiline</h2> | ||||
| 
 | ||||
|   <c-input-text value="value" multiline label="Label" mb-2 rows="1" /> | ||||
|   <c-input-text value="value" multiline label="Label" mb-2 /> | ||||
|   <c-input-text | ||||
|     value="Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum, est modi iusto repellendus fuga accusantium atque at magnam aliquam eum explicabo vero quia, nobis quasi quis! Earum amet quam a?" | ||||
|     multiline | ||||
|     mb-2 | ||||
|   /> | ||||
| 
 | ||||
|   <c-input-text v-model:value="valueLong" multiline autosize mb-2 rows="5" /> | ||||
| 
 | ||||
|   <c-input-text | ||||
|     value="Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum, est modi iusto repellendus fuga accusantium atque at magnam aliquam eum explicabo vero quia, nobis quasi quis! Earum amet quam a?" | ||||
|     multiline | ||||
|     clearable | ||||
|   /> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| 
 | ||||
| const value = ref('value'); | ||||
| const valueLong = ref( | ||||
|   'Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum, est modi iusto repellendus fuga accusantium atque at magnam aliquam eum explicabo vero quia, nobis quasi quis! Earum amet quam a?', | ||||
| ); | ||||
| 
 | ||||
| const validationRules = [{ message: 'Length must be > 10', validator: (value: string) => value.length > 10 }]; | ||||
| 
 | ||||
| const validation = useValidation({ | ||||
|   source: value, | ||||
|   rules: validationRules, | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| import { describe, expect, it, beforeEach } from 'vitest'; | ||||
| import { shallowMount } from '@vue/test-utils'; | ||||
| import { shallowMount, mount } from '@vue/test-utils'; | ||||
| import { setActivePinia, createPinia } from 'pinia'; | ||||
| import _ from 'lodash'; | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| import CInputText from './c-input-text.vue'; | ||||
| 
 | ||||
| describe('CInputText', () => { | ||||
| @ -71,10 +72,28 @@ describe('CInputText', () => { | ||||
| 
 | ||||
|   it('renders a feedback message for invalid rules', async () => { | ||||
|     const wrapper = shallowMount(CInputText, { | ||||
|       props: { rules: [{ validator: () => false, message: 'Message' }] }, | ||||
|       props: { validationRules: [{ validator: () => false, message: 'Message' }] }, | ||||
|     }); | ||||
| 
 | ||||
|     expect(wrapper.get('.feedback').text()).to.equal('Message'); | ||||
|     const feedback = wrapper.find('.feedback'); | ||||
|     expect(feedback.exists()).to.equal(true); | ||||
|     expect(feedback.text()).to.equal('Message'); | ||||
|   }); | ||||
| 
 | ||||
|   it('if the value become valid according to rules, the feedback disappear', async () => { | ||||
|     const wrapper = shallowMount(CInputText, { | ||||
|       props: { | ||||
|         validationRules: [{ validator: (value: string) => value === 'Hello', message: 'Value should be Hello' }], | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     const feedback = wrapper.find('.feedback'); | ||||
|     expect(feedback.exists()).to.equal(true); | ||||
|     expect(feedback.text()).to.equal('Value should be Hello'); | ||||
| 
 | ||||
|     await wrapper.setProps({ value: 'Hello' }); | ||||
| 
 | ||||
|     expect(wrapper.find('.feedback').exists()).to.equal(false); | ||||
|   }); | ||||
| 
 | ||||
|   it('feedback does not render for valid rules', async () => { | ||||
| @ -84,4 +103,58 @@ describe('CInputText', () => { | ||||
| 
 | ||||
|     expect(wrapper.find('.feedback').exists()).to.equal(false); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders a feedback message for invalid custom validation wrapper', async () => { | ||||
|     const wrapper = shallowMount(CInputText, { | ||||
|       props: { | ||||
|         validation: useValidation({ source: ref(), rules: [{ validator: () => false, message: 'Message' }] }), | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     const feedback = wrapper.find('.feedback'); | ||||
|     expect(feedback.exists()).to.equal(true); | ||||
|     expect(feedback.text()).to.equal('Message'); | ||||
|   }); | ||||
| 
 | ||||
|   it('feedback does not render for valid custom validation wrapper', async () => { | ||||
|     const wrapper = shallowMount(CInputText, { | ||||
|       props: { | ||||
|         validation: useValidation({ source: ref(), rules: [{ validator: () => true, message: 'Message' }] }), | ||||
|       }, | ||||
|     }); | ||||
|     expect(wrapper.find('.feedback').exists()).to.equal(false); | ||||
|   }); | ||||
| 
 | ||||
|   it('if the value become valid according to the custom validation wrapper, the feedback disappear', async () => { | ||||
|     const source = ref(''); | ||||
| 
 | ||||
|     const wrapper = shallowMount(CInputText, { | ||||
|       props: { | ||||
|         validation: useValidation({ | ||||
|           source, | ||||
|           rules: [{ validator: (value: string) => value === 'Hello', message: 'Value should be Hello' }], | ||||
|         }), | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     const feedback = wrapper.find('.feedback'); | ||||
|     expect(feedback.exists()).to.equal(true); | ||||
|     expect(feedback.text()).to.equal('Value should be Hello'); | ||||
| 
 | ||||
|     source.value = 'Hello'; | ||||
| 
 | ||||
|     await wrapper.vm.$nextTick(); | ||||
| 
 | ||||
|     expect(wrapper.find('.feedback').exists()).to.equal(false); | ||||
|   }); | ||||
| 
 | ||||
|   it('[prop:testId] renders a test id on the input', async () => { | ||||
|     const wrapper = mount(CInputText, { | ||||
|       props: { | ||||
|         testId: 'TEST', | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     expect(wrapper.get('input').attributes('data-test-id')).to.equal('TEST'); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -1,32 +1,60 @@ | ||||
| <template> | ||||
|   <div class="c-input-text" :class="{ disabled, error: !validation.isValid, 'label-left': labelPosition === 'left' }"> | ||||
|   <div | ||||
|     class="c-input-text" | ||||
|     :class="{ disabled, error: !validation.isValid, 'label-left': labelPosition === 'left', multiline }" | ||||
|   > | ||||
|     <label v-if="label" :for="id" class="label"> {{ label }} </label> | ||||
| 
 | ||||
|     <div class="input-wrapper"> | ||||
|       <slot name="prefix" /> | ||||
|     <div class="feedback-wrapper"> | ||||
|       <div ref="inputWrapperRef" class="input-wrapper"> | ||||
|         <slot name="prefix" /> | ||||
| 
 | ||||
|       <input | ||||
|         :id="id" | ||||
|         v-model="value" | ||||
|         type="text" | ||||
|         class="input" | ||||
|         :placeholder="placeholder" | ||||
|         :readonly="readonly" | ||||
|         :disabled="disabled" | ||||
|         :data-test-id="testId" | ||||
|         :autocapitalize="autocapitalize ?? (rawText ? 'off' : undefined)" | ||||
|         :autocomplete="autocomplete ?? (rawText ? 'off' : undefined)" | ||||
|         :autocorrect="autocorrect ?? (rawText ? 'off' : undefined)" | ||||
|         :spellcheck="spellcheck ?? (rawText ? false : undefined)" | ||||
|       /> | ||||
|         <textarea | ||||
|           v-if="multiline" | ||||
|           :id="id" | ||||
|           ref="textareaRef" | ||||
|           v-model="value" | ||||
|           class="input" | ||||
|           :placeholder="placeholder" | ||||
|           :readonly="readonly" | ||||
|           :disabled="disabled" | ||||
|           :data-test-id="testId" | ||||
|           :autocapitalize="autocapitalize ?? (rawText ? 'off' : undefined)" | ||||
|           :autocomplete="autocomplete ?? (rawText ? 'off' : undefined)" | ||||
|           :autocorrect="autocorrect ?? (rawText ? 'off' : undefined)" | ||||
|           :spellcheck="spellcheck ?? (rawText ? false : undefined)" | ||||
|           :rows="rows" | ||||
|         /> | ||||
| 
 | ||||
|       <c-button v-if="clearable && value" variant="text" circle size="small" @click="value = ''"> | ||||
|         <icon-mdi-close /> | ||||
|       </c-button> | ||||
|       <slot name="suffix" /> | ||||
|         <input | ||||
|           v-else | ||||
|           :id="id" | ||||
|           v-model="value" | ||||
|           :type="htmlInputType" | ||||
|           class="input" | ||||
|           size="1" | ||||
|           :placeholder="placeholder" | ||||
|           :readonly="readonly" | ||||
|           :disabled="disabled" | ||||
|           :data-test-id="testId" | ||||
|           :autocapitalize="autocapitalize ?? (rawText ? 'off' : undefined)" | ||||
|           :autocomplete="autocomplete ?? (rawText ? 'off' : undefined)" | ||||
|           :autocorrect="autocorrect ?? (rawText ? 'off' : undefined)" | ||||
|           :spellcheck="spellcheck ?? (rawText ? false : undefined)" | ||||
|         /> | ||||
| 
 | ||||
|         <c-button v-if="clearable && value" variant="text" circle size="small" @click="value = ''"> | ||||
|           <icon-mdi-close /> | ||||
|         </c-button> | ||||
| 
 | ||||
|         <c-button v-if="type === 'password'" variant="text" circle size="small" @click="showPassword = !showPassword"> | ||||
|           <icon-mdi-eye v-if="!showPassword" /> | ||||
|           <icon-mdi-eye-off v-if="showPassword" /> | ||||
|         </c-button> | ||||
|         <slot name="suffix" /> | ||||
|       </div> | ||||
|       <span v-if="!validation.isValid" class="feedback"> {{ validation.message }} </span> | ||||
|     </div> | ||||
| 
 | ||||
|     <span v-if="!validation.isValid" class="feedback"> {{ validation.message }} </span> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| @ -45,6 +73,7 @@ const props = withDefaults( | ||||
|     readonly?: boolean; | ||||
|     disabled?: boolean; | ||||
|     validationRules?: UseValidationRule<string>[]; | ||||
|     validation?: ReturnType<typeof useValidation>; | ||||
|     labelPosition?: 'top' | 'left'; | ||||
|     labelWidth?: string; | ||||
|     labelAlign?: 'left' | 'right'; | ||||
| @ -55,6 +84,10 @@ const props = withDefaults( | ||||
|     autocorrect?: 'on' | 'off' | string; | ||||
|     spellcheck?: 'true' | 'false' | boolean; | ||||
|     rawText?: boolean; | ||||
|     type?: 'text' | 'password'; | ||||
|     multiline?: boolean; | ||||
|     rows?: number | string; | ||||
|     autosize?: boolean; | ||||
|   }>(), | ||||
|   { | ||||
|     value: '', | ||||
| @ -64,6 +97,7 @@ const props = withDefaults( | ||||
|     readonly: false, | ||||
|     disabled: false, | ||||
|     validationRules: () => [], | ||||
|     validation: undefined, | ||||
|     labelPosition: 'top', | ||||
|     labelWidth: 'auto', | ||||
|     labelAlign: 'left', | ||||
| @ -74,20 +108,58 @@ const props = withDefaults( | ||||
|     autocorrect: undefined, | ||||
|     spellcheck: undefined, | ||||
|     rawText: false, | ||||
|     type: 'text', | ||||
|     multiline: false, | ||||
|     rows: 3, | ||||
|     autosize: false, | ||||
|   }, | ||||
| ); | ||||
| const emit = defineEmits(['update:value']); | ||||
| const value = useVModel(props, 'value', emit); | ||||
| const showPassword = ref(false); | ||||
| 
 | ||||
| const { id, placeholder, label, validationRules, labelPosition, labelWidth, labelAlign } = toRefs(props); | ||||
| const { id, placeholder, label, validationRules, labelPosition, labelWidth, labelAlign, autosize } = toRefs(props); | ||||
| 
 | ||||
| const validation = useValidation({ | ||||
|   rules: validationRules, | ||||
|   source: value, | ||||
| }); | ||||
| const validation = | ||||
|   props.validation ?? | ||||
|   useValidation({ | ||||
|     rules: validationRules, | ||||
|     source: value, | ||||
|   }); | ||||
| 
 | ||||
| const theme = useTheme(); | ||||
| const appTheme = useAppTheme(); | ||||
| 
 | ||||
| const textareaRef = ref<HTMLTextAreaElement>(); | ||||
| const inputWrapperRef = ref<HTMLElement>(); | ||||
| 
 | ||||
| watch( | ||||
|   value, | ||||
|   () => { | ||||
|     if (props.multiline && autosize.value) { | ||||
|       resizeTextarea(); | ||||
|     } | ||||
|   }, | ||||
|   { immediate: true }, | ||||
| ); | ||||
| 
 | ||||
| function resizeTextarea() { | ||||
|   if (!textareaRef.value || !inputWrapperRef.value) { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   const { scrollHeight } = textareaRef.value; | ||||
| 
 | ||||
|   inputWrapperRef.value.style.height = `${scrollHeight + 2}px`; | ||||
| } | ||||
| 
 | ||||
| const htmlInputType = computed(() => { | ||||
|   if (props.type === 'password' && !showPassword.value) { | ||||
|     return 'password'; | ||||
|   } | ||||
| 
 | ||||
|   return 'text'; | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
| @ -114,29 +186,55 @@ const appTheme = useAppTheme(); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     & > .feedback { | ||||
|     & .feedback { | ||||
|       color: v-bind('appTheme.error.color'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   & > .label { | ||||
|     flex-shrink: 0; | ||||
|     margin-bottom: 5px; | ||||
|     flex: 0 0 v-bind('labelWidth'); | ||||
|     text-align: v-bind('labelAlign'); | ||||
|     padding-right: 10px; | ||||
|     padding-right: 12px; | ||||
|   } | ||||
| 
 | ||||
|   .input-wrapper { | ||||
|   .feedback-wrapper { | ||||
|     flex: 1 1 0; | ||||
|     min-width: 0; | ||||
| 
 | ||||
|   } | ||||
|   .input-wrapper { | ||||
|     display: flex; | ||||
|     flex-direction: row; | ||||
|     align-items: center; | ||||
|     background-color: v-bind('theme.backgroundColor'); | ||||
|     color: transparent; | ||||
|     border: 1px solid v-bind('theme.borderColor'); | ||||
|     border-radius: 4px; | ||||
|     padding: 0 4px 0 12px; | ||||
|     transition: border-color 0.2s ease-in-out; | ||||
| 
 | ||||
|     .multiline& { | ||||
|       resize: vertical; | ||||
|       overflow: hidden; | ||||
| 
 | ||||
|       & > textarea { | ||||
|         height: 100%; | ||||
|         resize: none; | ||||
|         word-break: break-word; | ||||
|         white-space: pre-wrap; | ||||
|         overflow-wrap: break-word; | ||||
|         border: none; | ||||
|         outline: none; | ||||
|         font-family: inherit; | ||||
|         font-size: inherit; | ||||
|         color: v-bind('appTheme.text.baseColor'); | ||||
| 
 | ||||
|         &::placeholder { | ||||
|           color: v-bind('appTheme.text.mutedColor'); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     & > .input { | ||||
|       flex: 1 1 0; | ||||
| @ -144,7 +242,6 @@ const appTheme = useAppTheme(); | ||||
| 
 | ||||
|       padding: 8px 0; | ||||
|       outline: none; | ||||
|       transition: border-color 0.2s ease-in-out; | ||||
|       background-color: transparent; | ||||
|       background-image: none; | ||||
|       -webkit-box-shadow: none; | ||||
| @ -159,12 +256,13 @@ const appTheme = useAppTheme(); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     &:hover, | ||||
|     &:focus { | ||||
|     &:hover { | ||||
|       border-color: v-bind('appTheme.primary.color'); | ||||
|     } | ||||
| 
 | ||||
|     &:focus { | ||||
|     &:focus-within { | ||||
|       border-color: v-bind('appTheme.primary.color'); | ||||
| 
 | ||||
|       background-color: v-bind('theme.focus.backgroundColor'); | ||||
|     } | ||||
|   } | ||||
| @ -173,11 +271,11 @@ const appTheme = useAppTheme(); | ||||
|     border-color: v-bind('appTheme.error.color'); | ||||
| 
 | ||||
|     &:hover, | ||||
|     &:focus { | ||||
|     &:focus-within { | ||||
|       border-color: v-bind('appTheme.error.color'); | ||||
|     } | ||||
| 
 | ||||
|     &:focus { | ||||
|     &:focus-within { | ||||
|       background-color: v-bind('appTheme.error.color + 22'); | ||||
|     } | ||||
|   } | ||||
| @ -186,7 +284,7 @@ const appTheme = useAppTheme(); | ||||
|     opacity: 0.5; | ||||
| 
 | ||||
|     &:hover, | ||||
|     &:focus { | ||||
|     &:focus-within { | ||||
|       border-color: v-bind('theme.borderColor'); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -21,6 +21,7 @@ export const { themes: appThemes, useTheme: useAppTheme } = defineThemes({ | ||||
|       color: '#f59e0b', | ||||
|       colorHover: '#f59e0b', | ||||
|       colorPressed: '#f59e0b', | ||||
|       colorFaded: '#f59e0b2f', | ||||
|     }, | ||||
|     success: { | ||||
|       color: '#18a058', | ||||
| @ -55,6 +56,7 @@ export const { themes: appThemes, useTheme: useAppTheme } = defineThemes({ | ||||
|       color: '#f59e0b', | ||||
|       colorHover: '#f59e0b', | ||||
|       colorPressed: '#f59e0b', | ||||
|       colorFaded: '#f59e0b2f', | ||||
|     }, | ||||
|     success: { | ||||
|       color: '#18a058', | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user