Merge a3206b4249 into 07eea0f484
				
					
				
			This commit is contained in:
		
						commit
						fcce0d0ed0
					
				
							
								
								
									
										4
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -141,8 +141,10 @@ declare module '@vue/runtime-core' { | |||||||
|     NLayout: typeof import('naive-ui')['NLayout'] |     NLayout: typeof import('naive-ui')['NLayout'] | ||||||
|     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] |     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] | ||||||
|     NMenu: typeof import('naive-ui')['NMenu'] |     NMenu: typeof import('naive-ui')['NMenu'] | ||||||
|  |     NScrollbar: typeof import('naive-ui')['NScrollbar'] | ||||||
|  |     NSlider: typeof import('naive-ui')['NSlider'] | ||||||
|     NSpace: typeof import('naive-ui')['NSpace'] |     NSpace: typeof import('naive-ui')['NSpace'] | ||||||
|     NTable: typeof import('naive-ui')['NTable'] |     NSwitch: typeof import('naive-ui')['NSwitch'] | ||||||
|     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] |     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] | ||||||
|     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] |     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] | ||||||
|     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] |     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] | ||||||
|  | |||||||
| @ -6,17 +6,11 @@ describe('list-converter', () => { | |||||||
|   describe('convert', () => { |   describe('convert', () => { | ||||||
|     it('should convert a given list', () => { |     it('should convert a given list', () => { | ||||||
|       const options: ConvertOptions = { |       const options: ConvertOptions = { | ||||||
|         separator: ', ', |         itemsSeparator: ', ', | ||||||
|         trimItems: true, |         trimItems: true, | ||||||
|         removeDuplicates: true, |         removeDuplicates: true, | ||||||
|         itemPrefix: '"', |         itemPrefix: '"', | ||||||
|         itemSuffix: '"', |         itemSuffix: '"', | ||||||
|         listPrefix: '', |  | ||||||
|         listSuffix: '', |  | ||||||
|         reverseList: false, |  | ||||||
|         sortList: null, |  | ||||||
|         lowerCase: false, |  | ||||||
|         keepLineBreaks: false, |  | ||||||
|       }; |       }; | ||||||
|       const input = ` |       const input = ` | ||||||
|         1 |         1 | ||||||
| @ -31,34 +25,21 @@ describe('list-converter', () => { | |||||||
| 
 | 
 | ||||||
|     it('should return an empty value for an empty input', () => { |     it('should return an empty value for an empty input', () => { | ||||||
|       const options: ConvertOptions = { |       const options: ConvertOptions = { | ||||||
|         separator: ', ', |         itemsSeparator: ', ', | ||||||
|         trimItems: true, |         trimItems: true, | ||||||
|         removeDuplicates: true, |         removeDuplicates: true, | ||||||
|         itemPrefix: '', |  | ||||||
|         itemSuffix: '', |  | ||||||
|         listPrefix: '', |  | ||||||
|         listSuffix: '', |  | ||||||
|         reverseList: false, |  | ||||||
|         sortList: null, |  | ||||||
|         lowerCase: false, |  | ||||||
|         keepLineBreaks: false, |  | ||||||
|       }; |       }; | ||||||
|       expect(convert('', options)).toEqual(''); |       expect(convert('', options)).toEqual(''); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('should keep line breaks', () => { |     it('should keep line breaks', () => { | ||||||
|       const options: ConvertOptions = { |       const options: ConvertOptions = { | ||||||
|         separator: '', |  | ||||||
|         trimItems: true, |         trimItems: true, | ||||||
|         itemPrefix: '<li>', |         itemPrefix: '<li>', | ||||||
|         itemSuffix: '</li>', |         itemSuffix: '</li>', | ||||||
|         listPrefix: '<ul>', |         listPrefix: '<ul>', | ||||||
|         listSuffix: '</ul>', |         listSuffix: '</ul>', | ||||||
|         keepLineBreaks: true, |         keepLineBreaks: true, | ||||||
|         lowerCase: false, |  | ||||||
|         removeDuplicates: false, |  | ||||||
|         reverseList: false, |  | ||||||
|         sortList: null, |  | ||||||
|       }; |       }; | ||||||
|       const input = ` |       const input = ` | ||||||
|         1 |         1 | ||||||
| @ -72,5 +53,65 @@ describe('list-converter', () => { | |||||||
| </ul>`;
 | </ul>`;
 | ||||||
|       expect(convert(input, options)).toEqual(expected); |       expect(convert(input, options)).toEqual(expected); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     it('should remove prefix and suffix', () => { | ||||||
|  |       const options: ConvertOptions = { | ||||||
|  |         trimItems: true, | ||||||
|  |         removeItemPrefix: '<li>', | ||||||
|  |         removeItemSuffix: '</li>', | ||||||
|  |         keepLineBreaks: true, | ||||||
|  |       }; | ||||||
|  |       const input = ` | ||||||
|  | <li>1</li> | ||||||
|  | <li>2</li> | ||||||
|  | <li>3</li> | ||||||
|  |         `;
 | ||||||
|  |       const expected = `1
 | ||||||
|  | 2 | ||||||
|  | 3`;
 | ||||||
|  |       expect(convert(input, options)).toEqual(expected); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should split by separator', () => { | ||||||
|  |       const options: ConvertOptions = { | ||||||
|  |         trimItems: true, | ||||||
|  |         keepLineBreaks: true, | ||||||
|  |         splitBySeparator: ',', | ||||||
|  |       }; | ||||||
|  |       const input = '1,2,3'; | ||||||
|  |       const expected = `1
 | ||||||
|  | 2 | ||||||
|  | 3`;
 | ||||||
|  |       expect(convert(input, options)).toEqual(expected); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should sort by asc-num', () => { | ||||||
|  |       const options: ConvertOptions = { | ||||||
|  |         trimItems: true, | ||||||
|  |         keepLineBreaks: true, | ||||||
|  |         sortList: 'asc-num', | ||||||
|  |       }; | ||||||
|  |       const input = `3
 | ||||||
|  | 20 | ||||||
|  | 1`;
 | ||||||
|  |       const expected = `1
 | ||||||
|  | 3 | ||||||
|  | 20`;
 | ||||||
|  |       expect(convert(input, options)).toEqual(expected); | ||||||
|  |     }); | ||||||
|  |     it('should sort by desc', () => { | ||||||
|  |       const options: ConvertOptions = { | ||||||
|  |         trimItems: true, | ||||||
|  |         keepLineBreaks: true, | ||||||
|  |         sortList: 'desc', | ||||||
|  |       }; | ||||||
|  |       const input = `1
 | ||||||
|  | 20 | ||||||
|  | 3`;
 | ||||||
|  |       const expected = `3
 | ||||||
|  | 20 | ||||||
|  | 1`;
 | ||||||
|  |       expect(convert(input, options)).toEqual(expected); | ||||||
|  |     }); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ import { byOrder } from '@/utils/array'; | |||||||
| 
 | 
 | ||||||
| export { convert }; | export { convert }; | ||||||
| 
 | 
 | ||||||
| function whenever<T, R>(condition: boolean, fn: (value: T) => R) { | function whenever<T, R>(condition: boolean | undefined, fn: (value: T) => R) { | ||||||
|   return (value: T) => |   return (value: T) => | ||||||
|     condition ? fn(value) : value; |     condition ? fn(value) : value; | ||||||
| } | } | ||||||
| @ -12,16 +12,20 @@ function whenever<T, R>(condition: boolean, fn: (value: T) => R) { | |||||||
| function convert(list: string, options: ConvertOptions): string { | function convert(list: string, options: ConvertOptions): string { | ||||||
|   const lineBreak = options.keepLineBreaks ? '\n' : ''; |   const lineBreak = options.keepLineBreaks ? '\n' : ''; | ||||||
| 
 | 
 | ||||||
|  |   const splitSep = options.splitBySeparator ? `${options.splitBySeparator}|` : ''; | ||||||
|  |   const splitRegExp = new RegExp(`(?:${splitSep}\\n)`, 'g'); | ||||||
|   return _.chain(list) |   return _.chain(list) | ||||||
|     .thru(whenever(options.lowerCase, text => text.toLowerCase())) |     .thru(whenever(options.lowerCase, text => text.toLowerCase())) | ||||||
|     .split('\n') |     .split(splitRegExp) | ||||||
|     .thru(whenever(options.removeDuplicates, _.uniq)) |     .thru(whenever(options.removeDuplicates, _.uniq)) | ||||||
|     .thru(whenever(options.reverseList, _.reverse)) |     .thru(whenever(options.reverseList, _.reverse)) | ||||||
|     .thru(whenever(!_.isNull(options.sortList), parts => parts.sort(byOrder({ order: options.sortList })))) |  | ||||||
|     .map(whenever(options.trimItems, _.trim)) |     .map(whenever(options.trimItems, _.trim)) | ||||||
|  |     .thru(whenever(!!options.sortList, parts => parts.sort(byOrder({ order: options.sortList })))) | ||||||
|     .without('') |     .without('') | ||||||
|     .map(p => options.itemPrefix + p + options.itemSuffix) |     .map(p => options.removeItemPrefix ? p.replace(new RegExp(`^${options.removeItemPrefix}`, 'g'), '') : p) | ||||||
|     .join(options.separator + lineBreak) |     .map(p => options.removeItemSuffix ? p.replace(new RegExp(`${options.removeItemSuffix}$`, 'g'), '') : p) | ||||||
|     .thru(text => [options.listPrefix, text, options.listSuffix].join(lineBreak)) |     .map(p => (options.itemPrefix || '') + p + (options.itemSuffix || '')) | ||||||
|  |     .join((options.itemsSeparator || '') + lineBreak) | ||||||
|  |     .thru(text => [options.listPrefix, text, options.listSuffix].filter(l => l).join(lineBreak)) | ||||||
|     .value(); |     .value(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,15 +1,18 @@ | |||||||
| export type SortOrder = 'asc' | 'desc' | null; | export type SortOrder = null | 'asc' | 'desc' | 'asc-num' | 'desc-num' | 'asc-bin' | 'desc-bin' | 'asc-upper' | 'desc-upper'; | ||||||
| 
 | 
 | ||||||
| export interface ConvertOptions { | export interface ConvertOptions { | ||||||
|   lowerCase: boolean |   lowerCase?: boolean | ||||||
|   trimItems: boolean |   trimItems?: boolean | ||||||
|   itemPrefix: string |   itemPrefix?: string | ||||||
|   itemSuffix: string |   itemSuffix?: string | ||||||
|   listPrefix: string |   removeItemPrefix?: string | ||||||
|   listSuffix: string |   removeItemSuffix?: string | ||||||
|   reverseList: boolean |   listPrefix?: string | ||||||
|   sortList: SortOrder |   listSuffix?: string | ||||||
|   removeDuplicates: boolean |   reverseList?: boolean | ||||||
|   separator: string |   sortList?: SortOrder | ||||||
|   keepLineBreaks: boolean |   removeDuplicates?: boolean | ||||||
|  |   itemsSeparator?: string | ||||||
|  |   splitBySeparator?: string | ||||||
|  |   keepLineBreaks?: boolean | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,15 +4,41 @@ import { convert } from './list-converter.models'; | |||||||
| import type { ConvertOptions } from './list-converter.types'; | import type { ConvertOptions } from './list-converter.types'; | ||||||
| 
 | 
 | ||||||
| const sortOrderOptions = [ | const sortOrderOptions = [ | ||||||
|  |   { | ||||||
|  |     label: 'No Sort', | ||||||
|  |     value: null, | ||||||
|  |   }, | ||||||
|   { |   { | ||||||
|     label: 'Sort ascending', |     label: 'Sort ascending', | ||||||
|     value: 'asc', |     value: 'asc', | ||||||
|     disabled: false, |  | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     label: 'Sort descending', |     label: 'Sort descending', | ||||||
|     value: 'desc', |     value: 'desc', | ||||||
|     disabled: false, |   }, | ||||||
|  |   { | ||||||
|  |     label: 'Sort asc (Numeric)', | ||||||
|  |     value: 'asc-num', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: 'Sort desc (Numeric)', | ||||||
|  |     value: 'desc-num', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: 'Sort asc (Upper)', | ||||||
|  |     value: 'asc-upper', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: 'Sort desc (Upper)', | ||||||
|  |     value: 'desc-upper', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: 'Sort asc (Binary)', | ||||||
|  |     value: 'asc-bin', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: 'Sort desc (Binary)', | ||||||
|  |     value: 'desc-bin', | ||||||
|   }, |   }, | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| @ -23,11 +49,14 @@ const conversionConfig = useStorage<ConvertOptions>('list-converter:conversionCo | |||||||
|   keepLineBreaks: false, |   keepLineBreaks: false, | ||||||
|   itemPrefix: '', |   itemPrefix: '', | ||||||
|   itemSuffix: '', |   itemSuffix: '', | ||||||
|  |   removeItemPrefix: '', | ||||||
|  |   removeItemSuffix: '', | ||||||
|   listPrefix: '', |   listPrefix: '', | ||||||
|   listSuffix: '', |   listSuffix: '', | ||||||
|   reverseList: false, |   reverseList: false, | ||||||
|   sortList: null, |   sortList: null, | ||||||
|   separator: ', ', |   itemsSeparator: ', ', | ||||||
|  |   splitBySeparator: '', | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| function transformer(value: string) { | function transformer(value: string) { | ||||||
| @ -39,7 +68,7 @@ function transformer(value: string) { | |||||||
|   <div style="flex: 0 0 100%"> |   <div style="flex: 0 0 100%"> | ||||||
|     <div style="margin: 0 auto; max-width: 600px"> |     <div style="margin: 0 auto; max-width: 600px"> | ||||||
|       <c-card> |       <c-card> | ||||||
|         <div flex> |         <n-space> | ||||||
|           <div> |           <div> | ||||||
|             <n-form-item label="Trim list items" label-placement="left" label-width="150" :show-feedback="false" mb-2> |             <n-form-item label="Trim list items" label-placement="left" label-width="150" :show-feedback="false" mb-2> | ||||||
|               <n-switch v-model:value="conversionConfig.trimItems" /> |               <n-switch v-model:value="conversionConfig.trimItems" /> | ||||||
| @ -60,7 +89,7 @@ function transformer(value: string) { | |||||||
|               <n-switch v-model:value="conversionConfig.keepLineBreaks" /> |               <n-switch v-model:value="conversionConfig.keepLineBreaks" /> | ||||||
|             </n-form-item> |             </n-form-item> | ||||||
|           </div> |           </div> | ||||||
|           <div flex-1> |           <div> | ||||||
|             <c-select |             <c-select | ||||||
|               v-model:value="conversionConfig.sortList" |               v-model:value="conversionConfig.sortList" | ||||||
|               label="Sort list" |               label="Sort list" | ||||||
| @ -76,15 +105,38 @@ function transformer(value: string) { | |||||||
|             /> |             /> | ||||||
| 
 | 
 | ||||||
|             <c-input-text |             <c-input-text | ||||||
|               v-model:value="conversionConfig.separator" |               v-model:value="conversionConfig.itemsSeparator" | ||||||
|               label="Separator" |               label="Items Separator" | ||||||
|               label-position="left" |               label-position="left" | ||||||
|               label-width="120px" |               label-width="120px" | ||||||
|               label-align="right" |               label-align="right" | ||||||
|               mb-2 |               mb-2 | ||||||
|               placeholder="," |               placeholder="Items separator" | ||||||
|             /> |             /> | ||||||
| 
 | 
 | ||||||
|  |             <c-input-text | ||||||
|  |               v-model:value="conversionConfig.splitBySeparator" | ||||||
|  |               label="Split Separator" | ||||||
|  |               label-position="left" | ||||||
|  |               label-width="120px" | ||||||
|  |               label-align="right" | ||||||
|  |               mb-2 | ||||||
|  |               placeholder="Separator for splitting" | ||||||
|  |             /> | ||||||
|  | 
 | ||||||
|  |             <n-form-item label="Unwrap item" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||||||
|  |               <c-input-text | ||||||
|  |                 v-model:value="conversionConfig.removeItemPrefix" | ||||||
|  |                 placeholder="Remove item prefix regex" | ||||||
|  |                 test-id="removeItemPrefix" | ||||||
|  |               /> | ||||||
|  |               <c-input-text | ||||||
|  |                 v-model:value="conversionConfig.removeItemSuffix" | ||||||
|  |                 placeholder="Remove item suffix regex" | ||||||
|  |                 test-id="removeItemSuffix" | ||||||
|  |               /> | ||||||
|  |             </n-form-item> | ||||||
|  | 
 | ||||||
|             <n-form-item label="Wrap item" label-placement="left" label-width="120" :show-feedback="false" mb-2> |             <n-form-item label="Wrap item" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||||||
|               <c-input-text |               <c-input-text | ||||||
|                 v-model:value="conversionConfig.itemPrefix" |                 v-model:value="conversionConfig.itemPrefix" | ||||||
| @ -110,7 +162,7 @@ function transformer(value: string) { | |||||||
|               /> |               /> | ||||||
|             </n-form-item> |             </n-form-item> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </n-space> | ||||||
|       </c-card> |       </c-card> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|  | |||||||
							
								
								
									
										25
									
								
								src/utils/array.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/utils/array.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | import { describe, expect, it } from 'vitest'; | ||||||
|  | import { type SortOrder, byOrder } from './array'; | ||||||
|  | 
 | ||||||
|  | describe('array utils', () => { | ||||||
|  |   describe('byOrder', () => { | ||||||
|  |     it('should sort correctly', () => { | ||||||
|  |       const sortBy = (array: string[], order: SortOrder) => { | ||||||
|  |         return array.sort(byOrder({ order })); | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       const strings = ['a', 'A', 'b', 'B', 'á', '1', '2', '10', '一', '阿']; | ||||||
|  | 
 | ||||||
|  |       expect(sortBy(strings, null)).to.eql(strings); | ||||||
|  |       expect(sortBy(strings, undefined)).to.eql(strings); | ||||||
|  |       expect(sortBy(strings, 'asc')).to.eql(['1', '10', '2', 'a', 'A', 'á', 'b', 'B', '一', '阿']); | ||||||
|  |       expect(sortBy(strings, 'asc-num')).to.eql(['1', '2', '10', 'a', 'A', 'á', 'b', 'B', '一', '阿']); | ||||||
|  |       expect(sortBy(strings, 'asc-bin')).to.eql(['1', '10', '2', 'A', 'B', 'a', 'b', 'á', '一', '阿']); | ||||||
|  |       expect(sortBy(strings, 'asc-upper')).to.eql(['1', '10', '2', 'A', 'a', 'á', 'B', 'b', '一', '阿']); | ||||||
|  |       expect(sortBy(strings, 'desc')).to.eql(['阿', '一', 'B', 'b', 'á', 'A', 'a', '2', '10', '1']); | ||||||
|  |       expect(sortBy(strings, 'desc-num')).to.eql(['阿', '一', 'B', 'b', 'á', 'A', 'a', '10', '2', '1']); | ||||||
|  |       expect(sortBy(strings, 'desc-bin')).to.eql(['阿', '一', 'á', 'b', 'a', 'B', 'A', '2', '10', '1']); | ||||||
|  |       expect(sortBy(strings, 'desc-upper')).to.eql(['阿', '一', 'b', 'B', 'á', 'a', 'A', '2', '10', '1']); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @ -1,6 +1,29 @@ | |||||||
| export { byOrder }; | export type SortOrder = 'asc' | 'desc' | 'asc-num' | 'desc-num' | 'asc-bin' | 'desc-bin' | 'asc-upper' | 'desc-upper' | null | undefined; | ||||||
|  | 
 | ||||||
|  | export function byOrder({ order }: { order: SortOrder }) { | ||||||
|  |   if (order === 'asc-bin' || order === 'desc-bin') { | ||||||
|  |     return (a: string, b: string) => { | ||||||
|  |       const compare = a > b ? 1 : (a < b ? -1 : 0); // NOSONAR
 | ||||||
|  |       return order === 'asc-bin' ? compare : -compare; | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |   if (order === 'asc-num' || order === 'desc-num') { | ||||||
|  |     return (a: string, b: string) => { | ||||||
|  |       const compare = a.localeCompare(b, undefined, { | ||||||
|  |         numeric: true, | ||||||
|  |       }); | ||||||
|  |       return order === 'asc-num' ? compare : -compare; | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |   if (order === 'asc-upper' || order === 'desc-upper') { | ||||||
|  |     return (a: string, b: string) => { | ||||||
|  |       const compare = a.localeCompare(b, undefined, { | ||||||
|  |         caseFirst: 'upper', | ||||||
|  |       }); | ||||||
|  |       return order === 'asc-upper' ? compare : -compare; | ||||||
|  |     }; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
| function byOrder({ order }: { order: 'asc' | 'desc' | null | undefined }) { |  | ||||||
|   return (a: string, b: string) => { |   return (a: string, b: string) => { | ||||||
|     return order === 'asc' ? a.localeCompare(b) : b.localeCompare(a); |     return order === 'asc' ? a.localeCompare(b) : b.localeCompare(a); | ||||||
|   }; |   }; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user