mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Refactored many helpers to Typescript
This commit is contained in:
parent
7c67fa4149
commit
62df46d648
18 changed files with 145 additions and 113 deletions
15
package-lock.json
generated
15
package-lock.json
generated
|
@ -3090,6 +3090,12 @@
|
||||||
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
|
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/geojson": {
|
||||||
|
"version": "7946.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
|
||||||
|
"integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/glob": {
|
"@types/glob": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
|
||||||
|
@ -3299,6 +3305,15 @@
|
||||||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
|
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/leaflet": {
|
||||||
|
"version": "1.5.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.17.tgz",
|
||||||
|
"integrity": "sha512-2XYq9k6kNjhNI7PaTz8Rdxcc8Vzwu97OaS9CtcrTxnTSxFUGwjlGjTDvhTLJU+JRSfZ4lBwGcl0SjZHALdVr6g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/geojson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/minimatch": {
|
"@types/minimatch": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
"@types/classnames": "^2.2.10",
|
"@types/classnames": "^2.2.10",
|
||||||
"@types/enzyme": "^3.10.5",
|
"@types/enzyme": "^3.10.5",
|
||||||
"@types/jest": "^26.0.10",
|
"@types/jest": "^26.0.10",
|
||||||
|
"@types/leaflet": "^1.5.17",
|
||||||
"@types/moment": "^2.13.0",
|
"@types/moment": "^2.13.0",
|
||||||
"@types/ramda": "^0.27.14",
|
"@types/ramda": "^0.27.14",
|
||||||
"@types/react": "^16.9.46",
|
"@types/react": "^16.9.46",
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { homepage } from '../package.json';
|
||||||
import registerServiceWorker from './registerServiceWorker';
|
import registerServiceWorker from './registerServiceWorker';
|
||||||
import container from './container';
|
import container from './container';
|
||||||
import store from './container/store';
|
import store from './container/store';
|
||||||
import { fixLeafletIcons } from './utils/utils';
|
import { fixLeafletIcons } from './utils/helpers/leaflet';
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
|
|
@ -10,4 +10,4 @@ const formatDateFromFormat = (date?: NullableDate, format?: string): NullableDat
|
||||||
|
|
||||||
export const formatDate = (format = 'YYYY-MM-DD') => (date?: NullableDate) => formatDateFromFormat(date, format);
|
export const formatDate = (format = 'YYYY-MM-DD') => (date?: NullableDate) => formatDateFromFormat(date, format);
|
||||||
|
|
||||||
export const formatIsoDate = (date: NullableDate) => formatDateFromFormat(date, undefined);
|
export const formatIsoDate = (date?: NullableDate) => formatDateFromFormat(date, undefined);
|
||||||
|
|
16
src/utils/helpers/leaflet.js
Normal file
16
src/utils/helpers/leaflet.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// TODO Migrate this file to Typescript
|
||||||
|
|
||||||
|
import L from 'leaflet';
|
||||||
|
import marker2x from 'leaflet/dist/images/marker-icon-2x.png';
|
||||||
|
import marker from 'leaflet/dist/images/marker-icon.png';
|
||||||
|
import markerShadow from 'leaflet/dist/images/marker-shadow.png';
|
||||||
|
|
||||||
|
export const fixLeafletIcons = () => {
|
||||||
|
delete L.Icon.Default.prototype._getIconUrl;
|
||||||
|
|
||||||
|
L.Icon.Default.mergeOptions({
|
||||||
|
iconRetinaUrl: marker2x,
|
||||||
|
iconUrl: marker,
|
||||||
|
shadowUrl: markerShadow,
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,8 +0,0 @@
|
||||||
const TEN_ROUNDING_NUMBER = 10;
|
|
||||||
const { ceil } = Math;
|
|
||||||
|
|
||||||
const formatter = new Intl.NumberFormat('en-US');
|
|
||||||
|
|
||||||
export const prettify = (number) => formatter.format(number);
|
|
||||||
|
|
||||||
export const roundTen = (number) => ceil(number / TEN_ROUNDING_NUMBER) * TEN_ROUNDING_NUMBER;
|
|
7
src/utils/helpers/numbers.ts
Normal file
7
src/utils/helpers/numbers.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const TEN_ROUNDING_NUMBER = 10;
|
||||||
|
const { ceil } = Math;
|
||||||
|
const formatter = new Intl.NumberFormat('en-US');
|
||||||
|
|
||||||
|
export const prettify = (number: number) => formatter.format(number);
|
||||||
|
|
||||||
|
export const roundTen = (number: number) => ceil(number / TEN_ROUNDING_NUMBER) * TEN_ROUNDING_NUMBER;
|
|
@ -1,27 +0,0 @@
|
||||||
import { max, min, range } from 'ramda';
|
|
||||||
|
|
||||||
export const ELLIPSIS = '...';
|
|
||||||
|
|
||||||
export const progressivePagination = (currentPage, pageCount) => {
|
|
||||||
const delta = 2;
|
|
||||||
const pages = range(
|
|
||||||
max(delta, currentPage - delta),
|
|
||||||
min(pageCount - 1, currentPage + delta) + 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (currentPage - delta > delta) {
|
|
||||||
pages.unshift(ELLIPSIS);
|
|
||||||
}
|
|
||||||
if (currentPage + delta < pageCount - 1) {
|
|
||||||
pages.push(ELLIPSIS);
|
|
||||||
}
|
|
||||||
|
|
||||||
pages.unshift(1);
|
|
||||||
pages.push(pageCount);
|
|
||||||
|
|
||||||
return pages;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const keyForPage = (pageNumber, index) => pageNumber !== ELLIPSIS ? pageNumber : `${pageNumber}_${index}`;
|
|
||||||
|
|
||||||
export const isPageDisabled = (pageNumber) => pageNumber === ELLIPSIS;
|
|
29
src/utils/helpers/pagination.ts
Normal file
29
src/utils/helpers/pagination.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { max, min, range } from 'ramda';
|
||||||
|
|
||||||
|
export const ELLIPSIS = '...';
|
||||||
|
|
||||||
|
type NumberOrEllipsis = number | '...';
|
||||||
|
|
||||||
|
export const progressivePagination = (currentPage: number, pageCount: number): NumberOrEllipsis[] => {
|
||||||
|
const delta = 2;
|
||||||
|
const pages: NumberOrEllipsis[] = range(
|
||||||
|
max(delta, currentPage - delta),
|
||||||
|
min(pageCount - 1, currentPage + delta) + 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentPage - delta > delta) {
|
||||||
|
pages.unshift(ELLIPSIS);
|
||||||
|
}
|
||||||
|
if (currentPage + delta < pageCount - 1) {
|
||||||
|
pages.push(ELLIPSIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
pages.unshift(1);
|
||||||
|
pages.push(pageCount);
|
||||||
|
|
||||||
|
return pages;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const keyForPage = (pageNumber: NumberOrEllipsis, index: number) => pageNumber !== ELLIPSIS ? pageNumber : `${pageNumber}_${index}`;
|
||||||
|
|
||||||
|
export const isPageDisabled = (pageNumber: NumberOrEllipsis) => pageNumber === ELLIPSIS;
|
|
@ -1,27 +0,0 @@
|
||||||
import { compare } from 'compare-versions';
|
|
||||||
import { identity, memoizeWith } from 'ramda';
|
|
||||||
import { hasValue } from '../utils';
|
|
||||||
|
|
||||||
export const versionMatch = (versionToMatch, { maxVersion, minVersion }) => {
|
|
||||||
if (!hasValue(versionToMatch)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchesMinVersion = !minVersion || compare(versionToMatch, minVersion, '>=');
|
|
||||||
const matchesMaxVersion = !maxVersion || compare(versionToMatch, maxVersion, '<=');
|
|
||||||
|
|
||||||
return !!(matchesMaxVersion && matchesMinVersion);
|
|
||||||
};
|
|
||||||
|
|
||||||
const versionIsValidSemVer = memoizeWith(identity, (version) => {
|
|
||||||
try {
|
|
||||||
return compare(version, version, '=');
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const versionToPrintable = (version) => !versionIsValidSemVer(version) ? version : `v${version}`;
|
|
||||||
|
|
||||||
export const versionToSemVer = (defaultValue = 'latest') =>
|
|
||||||
(version) => versionIsValidSemVer(version) ? version : defaultValue;
|
|
32
src/utils/helpers/version.ts
Normal file
32
src/utils/helpers/version.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { compare } from 'compare-versions';
|
||||||
|
import { identity, memoizeWith } from 'ramda';
|
||||||
|
import { Empty, hasValue } from '../utils';
|
||||||
|
|
||||||
|
export interface Versions {
|
||||||
|
maxVersion?: string;
|
||||||
|
minVersion?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const versionMatch = (versionToMatch: string | Empty, { maxVersion, minVersion }: Versions): boolean => {
|
||||||
|
if (!hasValue(versionToMatch)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchesMinVersion = !minVersion || compare(versionToMatch, minVersion, '>=');
|
||||||
|
const matchesMaxVersion = !maxVersion || compare(versionToMatch, maxVersion, '<=');
|
||||||
|
|
||||||
|
return matchesMaxVersion && matchesMinVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
const versionIsValidSemVer = memoizeWith(identity, (version: string) => {
|
||||||
|
try {
|
||||||
|
return compare(version, version, '=');
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const versionToPrintable = (version: string) => !versionIsValidSemVer(version) ? version : `v${version}`;
|
||||||
|
|
||||||
|
export const versionToSemVer = (defaultValue = 'latest') =>
|
||||||
|
(version: string) => versionIsValidSemVer(version) ? version : defaultValue;
|
|
@ -1,32 +0,0 @@
|
||||||
import L from 'leaflet';
|
|
||||||
import marker2x from 'leaflet/dist/images/marker-icon-2x.png';
|
|
||||||
import marker from 'leaflet/dist/images/marker-icon.png';
|
|
||||||
import markerShadow from 'leaflet/dist/images/marker-shadow.png';
|
|
||||||
import { isEmpty, isNil, range } from 'ramda';
|
|
||||||
|
|
||||||
export const determineOrderDir = (clickedField, currentOrderField, currentOrderDir) => {
|
|
||||||
if (currentOrderField !== clickedField) {
|
|
||||||
return 'ASC';
|
|
||||||
}
|
|
||||||
|
|
||||||
const newOrderMap = {
|
|
||||||
ASC: 'DESC',
|
|
||||||
DESC: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
return currentOrderDir ? newOrderMap[currentOrderDir] : 'ASC';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fixLeafletIcons = () => {
|
|
||||||
delete L.Icon.Default.prototype._getIconUrl;
|
|
||||||
|
|
||||||
L.Icon.Default.mergeOptions({
|
|
||||||
iconRetinaUrl: marker2x,
|
|
||||||
iconUrl: marker,
|
|
||||||
shadowUrl: markerShadow,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const rangeOf = (size, mappingFn, startAt = 1) => range(startAt, size + 1).map(mappingFn);
|
|
||||||
|
|
||||||
export const hasValue = (value) => !isNil(value) && !isEmpty(value);
|
|
23
src/utils/utils.ts
Normal file
23
src/utils/utils.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { isEmpty, isNil, range } from 'ramda';
|
||||||
|
|
||||||
|
export type OrderDir = 'ASC' | 'DESC' | undefined;
|
||||||
|
|
||||||
|
export const determineOrderDir = (currentField: string, newField: string, currentOrderDir: OrderDir): OrderDir => {
|
||||||
|
if (currentField !== newField) {
|
||||||
|
return 'ASC';
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOrderMap: Record<'ASC' | 'DESC', OrderDir> = {
|
||||||
|
ASC: 'DESC',
|
||||||
|
DESC: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return currentOrderDir ? newOrderMap[currentOrderDir] : 'ASC';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const rangeOf = <T>(size: number, mappingFn: (value: number) => T, startAt = 1): T[] =>
|
||||||
|
range(startAt, size + 1).map(mappingFn);
|
||||||
|
|
||||||
|
export type Empty = null | undefined | '' | never[];
|
||||||
|
|
||||||
|
export const hasValue = <T>(value: T | Empty): value is T => !isNil(value) && !isEmpty(value);
|
19
test/utils/helpers/leaflet.test.js
Normal file
19
test/utils/helpers/leaflet.test.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import L from 'leaflet';
|
||||||
|
import marker2x from 'leaflet/dist/images/marker-icon-2x.png';
|
||||||
|
import marker from 'leaflet/dist/images/marker-icon.png';
|
||||||
|
import markerShadow from 'leaflet/dist/images/marker-shadow.png';
|
||||||
|
import { fixLeafletIcons } from '../../../src/utils/helpers/leaflet';
|
||||||
|
|
||||||
|
describe('leaflet', () => {
|
||||||
|
describe('fixLeafletIcons', () => {
|
||||||
|
it('updates icons used by leaflet', () => {
|
||||||
|
fixLeafletIcons();
|
||||||
|
|
||||||
|
const { iconRetinaUrl, iconUrl, shadowUrl } = L.Icon.Default.prototype.options;
|
||||||
|
|
||||||
|
expect(iconRetinaUrl).toEqual(marker2x);
|
||||||
|
expect(iconUrl).toEqual(marker);
|
||||||
|
expect(shadowUrl).toEqual(markerShadow);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,8 +1,4 @@
|
||||||
import L from 'leaflet';
|
import { determineOrderDir, rangeOf } from '../../src/utils/utils';
|
||||||
import marker2x from 'leaflet/dist/images/marker-icon-2x.png';
|
|
||||||
import marker from 'leaflet/dist/images/marker-icon.png';
|
|
||||||
import markerShadow from 'leaflet/dist/images/marker-shadow.png';
|
|
||||||
import { determineOrderDir, fixLeafletIcons, rangeOf } from '../../src/utils/utils';
|
|
||||||
|
|
||||||
describe('utils', () => {
|
describe('utils', () => {
|
||||||
describe('determineOrderDir', () => {
|
describe('determineOrderDir', () => {
|
||||||
|
@ -27,18 +23,6 @@ describe('utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fixLeafletIcons', () => {
|
|
||||||
it('updates icons used by leaflet', () => {
|
|
||||||
fixLeafletIcons();
|
|
||||||
|
|
||||||
const { iconRetinaUrl, iconUrl, shadowUrl } = L.Icon.Default.prototype.options;
|
|
||||||
|
|
||||||
expect(iconRetinaUrl).toEqual(marker2x);
|
|
||||||
expect(iconUrl).toEqual(marker);
|
|
||||||
expect(shadowUrl).toEqual(markerShadow);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('rangeOf', () => {
|
describe('rangeOf', () => {
|
||||||
const func = (i) => `result_${i}`;
|
const func = (i) => `result_${i}`;
|
||||||
const size = 5;
|
const size = 5;
|
||||||
|
|
Loading…
Reference in a new issue