Skip to content

Commit 74504a0

Browse files
committed
Merge branch 'feat/list-converter-enh' into chore/all-my-stuffs
# Conflicts: # components.d.ts
2 parents 950580d + 7bafd47 commit 74504a0

File tree

7 files changed

+199
-51
lines changed

7 files changed

+199
-51
lines changed

pnpm-lock.yaml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/list-converter/list-converter.models.test.ts

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,11 @@ describe('list-converter', () => {
66
describe('convert', () => {
77
it('should convert a given list', () => {
88
const options: ConvertOptions = {
9-
separator: ', ',
9+
itemsSeparator: ', ',
1010
trimItems: true,
1111
removeDuplicates: true,
1212
itemPrefix: '"',
1313
itemSuffix: '"',
14-
listPrefix: '',
15-
listSuffix: '',
16-
reverseList: false,
17-
sortList: null,
18-
lowerCase: false,
19-
keepLineBreaks: false,
2014
};
2115
const input = `
2216
1
@@ -31,34 +25,21 @@ describe('list-converter', () => {
3125

3226
it('should return an empty value for an empty input', () => {
3327
const options: ConvertOptions = {
34-
separator: ', ',
28+
itemsSeparator: ', ',
3529
trimItems: true,
3630
removeDuplicates: true,
37-
itemPrefix: '',
38-
itemSuffix: '',
39-
listPrefix: '',
40-
listSuffix: '',
41-
reverseList: false,
42-
sortList: null,
43-
lowerCase: false,
44-
keepLineBreaks: false,
4531
};
4632
expect(convert('', options)).toEqual('');
4733
});
4834

4935
it('should keep line breaks', () => {
5036
const options: ConvertOptions = {
51-
separator: '',
5237
trimItems: true,
5338
itemPrefix: '<li>',
5439
itemSuffix: '</li>',
5540
listPrefix: '<ul>',
5641
listSuffix: '</ul>',
5742
keepLineBreaks: true,
58-
lowerCase: false,
59-
removeDuplicates: false,
60-
reverseList: false,
61-
sortList: null,
6243
};
6344
const input = `
6445
1
@@ -72,5 +53,65 @@ describe('list-converter', () => {
7253
</ul>`;
7354
expect(convert(input, options)).toEqual(expected);
7455
});
56+
57+
it('should remove prefix and suffix', () => {
58+
const options: ConvertOptions = {
59+
trimItems: true,
60+
removeItemPrefix: '<li>',
61+
removeItemSuffix: '</li>',
62+
keepLineBreaks: true,
63+
};
64+
const input = `
65+
<li>1</li>
66+
<li>2</li>
67+
<li>3</li>
68+
`;
69+
const expected = `1
70+
2
71+
3`;
72+
expect(convert(input, options)).toEqual(expected);
73+
});
74+
75+
it('should split by separator', () => {
76+
const options: ConvertOptions = {
77+
trimItems: true,
78+
keepLineBreaks: true,
79+
splitBySeparator: ',',
80+
};
81+
const input = '1,2,3';
82+
const expected = `1
83+
2
84+
3`;
85+
expect(convert(input, options)).toEqual(expected);
86+
});
87+
88+
it('should sort by asc-num', () => {
89+
const options: ConvertOptions = {
90+
trimItems: true,
91+
keepLineBreaks: true,
92+
sortList: 'asc-num',
93+
};
94+
const input = `3
95+
20
96+
1`;
97+
const expected = `1
98+
3
99+
20`;
100+
expect(convert(input, options)).toEqual(expected);
101+
});
102+
it('should sort by desc', () => {
103+
const options: ConvertOptions = {
104+
trimItems: true,
105+
keepLineBreaks: true,
106+
sortList: 'desc',
107+
};
108+
const input = `1
109+
20
110+
3`;
111+
const expected = `3
112+
20
113+
1`;
114+
expect(convert(input, options)).toEqual(expected);
115+
});
75116
});
76117
});

src/tools/list-converter/list-converter.models.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,28 @@ import { byOrder } from '@/utils/array';
44

55
export { convert };
66

7-
function whenever<T, R>(condition: boolean, fn: (value: T) => R) {
7+
function whenever<T, R>(condition: boolean | undefined, fn: (value: T) => R) {
88
return (value: T) =>
99
condition ? fn(value) : value;
1010
}
1111

1212
function convert(list: string, options: ConvertOptions): string {
1313
const lineBreak = options.keepLineBreaks ? '\n' : '';
1414

15+
const splitSep = options.splitBySeparator ? `${options.splitBySeparator}|` : '';
16+
const splitRegExp = new RegExp(`(?:${splitSep}\\n)`, 'g');
1517
return _.chain(list)
1618
.thru(whenever(options.lowerCase, text => text.toLowerCase()))
17-
.split('\n')
19+
.split(splitRegExp)
1820
.thru(whenever(options.removeDuplicates, _.uniq))
1921
.thru(whenever(options.reverseList, _.reverse))
20-
.thru(whenever(!_.isNull(options.sortList), parts => parts.sort(byOrder({ order: options.sortList }))))
2122
.map(whenever(options.trimItems, _.trim))
23+
.thru(whenever(!!options.sortList, parts => parts.sort(byOrder({ order: options.sortList }))))
2224
.without('')
23-
.map(p => options.itemPrefix + p + options.itemSuffix)
24-
.join(options.separator + lineBreak)
25-
.thru(text => [options.listPrefix, text, options.listSuffix].join(lineBreak))
25+
.map(p => options.removeItemPrefix ? p.replace(new RegExp(`^${options.removeItemPrefix}`, 'g'), '') : p)
26+
.map(p => options.removeItemSuffix ? p.replace(new RegExp(`${options.removeItemSuffix}$`, 'g'), '') : p)
27+
.map(p => (options.itemPrefix || '') + p + (options.itemSuffix || ''))
28+
.join((options.itemsSeparator || '') + lineBreak)
29+
.thru(text => [options.listPrefix, text, options.listSuffix].filter(l => l).join(lineBreak))
2630
.value();
2731
}
Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
export type SortOrder = 'asc' | 'desc' | null;
1+
export type SortOrder = null | 'asc' | 'desc' | 'asc-num' | 'desc-num' | 'asc-bin' | 'desc-bin' | 'asc-upper' | 'desc-upper';
22

33
export interface ConvertOptions {
4-
lowerCase: boolean
5-
trimItems: boolean
6-
itemPrefix: string
7-
itemSuffix: string
8-
listPrefix: string
9-
listSuffix: string
10-
reverseList: boolean
11-
sortList: SortOrder
12-
removeDuplicates: boolean
13-
separator: string
14-
keepLineBreaks: boolean
4+
lowerCase?: boolean
5+
trimItems?: boolean
6+
itemPrefix?: string
7+
itemSuffix?: string
8+
removeItemPrefix?: string
9+
removeItemSuffix?: string
10+
listPrefix?: string
11+
listSuffix?: string
12+
reverseList?: boolean
13+
sortList?: SortOrder
14+
removeDuplicates?: boolean
15+
itemsSeparator?: string
16+
splitBySeparator?: string
17+
keepLineBreaks?: boolean
1518
}

src/tools/list-converter/list-converter.vue

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,41 @@ import { convert } from './list-converter.models';
44
import type { ConvertOptions } from './list-converter.types';
55
66
const sortOrderOptions = [
7+
{
8+
label: 'No Sort',
9+
value: null,
10+
},
711
{
812
label: 'Sort ascending',
913
value: 'asc',
10-
disabled: false,
1114
},
1215
{
1316
label: 'Sort descending',
1417
value: 'desc',
15-
disabled: false,
18+
},
19+
{
20+
label: 'Sort asc (Numeric)',
21+
value: 'asc-num',
22+
},
23+
{
24+
label: 'Sort desc (Numeric)',
25+
value: 'desc-num',
26+
},
27+
{
28+
label: 'Sort asc (Upper)',
29+
value: 'asc-upper',
30+
},
31+
{
32+
label: 'Sort desc (Upper)',
33+
value: 'desc-upper',
34+
},
35+
{
36+
label: 'Sort asc (Binary)',
37+
value: 'asc-bin',
38+
},
39+
{
40+
label: 'Sort desc (Binary)',
41+
value: 'desc-bin',
1642
},
1743
];
1844
@@ -23,11 +49,14 @@ const conversionConfig = useStorage<ConvertOptions>('list-converter:conversionCo
2349
keepLineBreaks: false,
2450
itemPrefix: '',
2551
itemSuffix: '',
52+
removeItemPrefix: '',
53+
removeItemSuffix: '',
2654
listPrefix: '',
2755
listSuffix: '',
2856
reverseList: false,
2957
sortList: null,
30-
separator: ', ',
58+
itemsSeparator: ', ',
59+
splitBySeparator: '',
3160
});
3261
3362
function transformer(value: string) {
@@ -39,7 +68,7 @@ function transformer(value: string) {
3968
<div style="flex: 0 0 100%">
4069
<div style="margin: 0 auto; max-width: 600px">
4170
<c-card>
42-
<div flex>
71+
<n-space>
4372
<div>
4473
<n-form-item label="Trim list items" label-placement="left" label-width="150" :show-feedback="false" mb-2>
4574
<n-switch v-model:value="conversionConfig.trimItems" />
@@ -60,7 +89,7 @@ function transformer(value: string) {
6089
<n-switch v-model:value="conversionConfig.keepLineBreaks" />
6190
</n-form-item>
6291
</div>
63-
<div flex-1>
92+
<div>
6493
<c-select
6594
v-model:value="conversionConfig.sortList"
6695
label="Sort list"
@@ -76,15 +105,38 @@ function transformer(value: string) {
76105
/>
77106

78107
<c-input-text
79-
v-model:value="conversionConfig.separator"
80-
label="Separator"
108+
v-model:value="conversionConfig.itemsSeparator"
109+
label="Items Separator"
110+
label-position="left"
111+
label-width="120px"
112+
label-align="right"
113+
mb-2
114+
placeholder="Items separator"
115+
/>
116+
117+
<c-input-text
118+
v-model:value="conversionConfig.splitBySeparator"
119+
label="Split Separator"
81120
label-position="left"
82121
label-width="120px"
83122
label-align="right"
84123
mb-2
85-
placeholder=","
124+
placeholder="Separator for splitting"
86125
/>
87126

127+
<n-form-item label="Unwrap item" label-placement="left" label-width="120" :show-feedback="false" mb-2>
128+
<c-input-text
129+
v-model:value="conversionConfig.removeItemPrefix"
130+
placeholder="Remove item prefix regex"
131+
test-id="removeItemPrefix"
132+
/>
133+
<c-input-text
134+
v-model:value="conversionConfig.removeItemSuffix"
135+
placeholder="Remove item suffix regex"
136+
test-id="removeItemSuffix"
137+
/>
138+
</n-form-item>
139+
88140
<n-form-item label="Wrap item" label-placement="left" label-width="120" :show-feedback="false" mb-2>
89141
<c-input-text
90142
v-model:value="conversionConfig.itemPrefix"
@@ -110,7 +162,7 @@ function transformer(value: string) {
110162
/>
111163
</n-form-item>
112164
</div>
113-
</div>
165+
</n-space>
114166
</c-card>
115167
</div>
116168
</div>

src/utils/array.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { type SortOrder, byOrder } from './array';
3+
4+
describe('array utils', () => {
5+
describe('byOrder', () => {
6+
it('should sort correctly', () => {
7+
const sortBy = (array: string[], order: SortOrder) => {
8+
return array.sort(byOrder({ order }));
9+
};
10+
11+
const strings = ['a', 'A', 'b', 'B', 'á', '1', '2', '10', '一', '阿'];
12+
13+
expect(sortBy(strings, null)).to.eql(strings);
14+
expect(sortBy(strings, undefined)).to.eql(strings);
15+
expect(sortBy(strings, 'asc')).to.eql(['1', '10', '2', 'a', 'A', 'á', 'b', 'B', '一', '阿']);
16+
expect(sortBy(strings, 'asc-num')).to.eql(['1', '2', '10', 'a', 'A', 'á', 'b', 'B', '一', '阿']);
17+
expect(sortBy(strings, 'asc-bin')).to.eql(['1', '10', '2', 'A', 'B', 'a', 'b', 'á', '一', '阿']);
18+
expect(sortBy(strings, 'asc-upper')).to.eql(['1', '10', '2', 'A', 'a', 'á', 'B', 'b', '一', '阿']);
19+
expect(sortBy(strings, 'desc')).to.eql(['阿', '一', 'B', 'b', 'á', 'A', 'a', '2', '10', '1']);
20+
expect(sortBy(strings, 'desc-num')).to.eql(['阿', '一', 'B', 'b', 'á', 'A', 'a', '10', '2', '1']);
21+
expect(sortBy(strings, 'desc-bin')).to.eql(['阿', '一', 'á', 'b', 'a', 'B', 'A', '2', '10', '1']);
22+
expect(sortBy(strings, 'desc-upper')).to.eql(['阿', '一', 'b', 'B', 'á', 'a', 'A', '2', '10', '1']);
23+
});
24+
});
25+
});

src/utils/array.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
1-
export { byOrder };
1+
export type SortOrder = 'asc' | 'desc' | 'asc-num' | 'desc-num' | 'asc-bin' | 'desc-bin' | 'asc-upper' | 'desc-upper' | null | undefined;
2+
3+
export function byOrder({ order }: { order: SortOrder }) {
4+
if (order === 'asc-bin' || order === 'desc-bin') {
5+
return (a: string, b: string) => {
6+
const compare = a > b ? 1 : (a < b ? -1 : 0); // NOSONAR
7+
return order === 'asc-bin' ? compare : -compare;
8+
};
9+
}
10+
if (order === 'asc-num' || order === 'desc-num') {
11+
return (a: string, b: string) => {
12+
const compare = a.localeCompare(b, undefined, {
13+
numeric: true,
14+
});
15+
return order === 'asc-num' ? compare : -compare;
16+
};
17+
}
18+
if (order === 'asc-upper' || order === 'desc-upper') {
19+
return (a: string, b: string) => {
20+
const compare = a.localeCompare(b, undefined, {
21+
caseFirst: 'upper',
22+
});
23+
return order === 'asc-upper' ? compare : -compare;
24+
};
25+
}
226

3-
function byOrder({ order }: { order: 'asc' | 'desc' | null | undefined }) {
427
return (a: string, b: string) => {
528
return order === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
629
};

0 commit comments

Comments
 (0)