@wikicasa-dev/utilities 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/utilities.cjs +4 -4
- package/dist/utilities.iife.js +2 -2
- package/dist/utilities.mjs +57 -51
- package/dist/utils/DateUtils.d.ts +1 -0
- package/package.json +2 -5
- package/src/custom/constants.ts +3 -0
- package/src/custom/icons.ts +63 -0
- package/src/custom/leaflet_map.ts +946 -0
- package/src/index.ts +173 -0
- package/src/services/agencyAPI.ts +105 -0
- package/src/services/geographyAPI.ts +129 -0
- package/src/services/insightsAPI.ts +20 -0
- package/src/services/mailAPI.ts +89 -0
- package/src/services/placesAPI.ts +72 -0
- package/src/services/portfolioCustomerAPI.ts +16 -0
- package/src/services/publicUserAPI.ts +216 -0
- package/src/services/realEstateAPI.ts +133 -0
- package/src/services/requestAPI.ts +40 -0
- package/src/services/servicesUtils.ts +27 -0
- package/src/services/statisticsAPI.ts +84 -0
- package/src/services/valuationAPI.ts +45 -0
- package/src/services/wikicasaPro.ts +25 -0
- package/src/utils/AppRedirectUtils.ts +26 -0
- package/src/utils/ArrayUtils.ts +2 -0
- package/src/utils/ColorUtils.ts +11 -0
- package/src/utils/CookieUtils.ts +43 -0
- package/src/utils/CurrencyUtils.ts +18 -0
- package/src/utils/DOMUtils.ts +28 -0
- package/src/utils/DateUtils.ts +9 -0
- package/src/utils/DeviceDetectionUtils.ts +17 -0
- package/src/utils/EmailUtils.ts +45 -0
- package/src/utils/FavoriteUtils.ts +19 -0
- package/src/utils/FunctionUtils.ts +29 -0
- package/src/utils/GAEvents.ts +414 -0
- package/src/utils/GAutocompleteUtils.ts +70 -0
- package/src/utils/GenericUtils.ts +37 -0
- package/src/utils/LazyLoadingBg.ts +18 -0
- package/src/utils/MapUtils.ts +118 -0
- package/src/utils/NumberUtils.ts +90 -0
- package/src/utils/ObjectUtils.ts +34 -0
- package/src/utils/ObserverUtils.ts +32 -0
- package/src/utils/PermissionUtils.ts +41 -0
- package/src/utils/RESB_UrlBuilder.ts +99 -0
- package/src/utils/RequestUtils.ts +20 -0
- package/src/utils/StringUtils.ts +67 -0
- package/src/utils/URLBuilderUtils.ts +21 -0
- package/src/utils/URLPagesFactory.ts +20 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +38 -0
- package/vite.config.ts +42 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { LatLng } from "leaflet";
|
|
2
|
+
import { encode } from "google-polyline";
|
|
3
|
+
|
|
4
|
+
import Leaflet_map from "../custom/leaflet_map";
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
interface Window {
|
|
8
|
+
_mapRange?: number;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function parsePoints(polygonFromMap: string): Array<LatLng> | undefined {
|
|
13
|
+
if (!polygonFromMap) return;
|
|
14
|
+
|
|
15
|
+
const numbers: RegExpMatchArray | null =
|
|
16
|
+
polygonFromMap.match(/[-]?[\d]*[.]?[\d]+/g);
|
|
17
|
+
|
|
18
|
+
if (!numbers) return;
|
|
19
|
+
|
|
20
|
+
const latLngs: Array<LatLng> = [];
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < numbers.length; i++) {
|
|
23
|
+
latLngs.push({
|
|
24
|
+
lat: parseFloat(numbers[i + 1]),
|
|
25
|
+
lng: parseFloat(numbers[i]),
|
|
26
|
+
} as LatLng);
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return latLngs;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param polygon An array of latitude and polygon that will be used to design an area in a map
|
|
35
|
+
* @param forUrl If true, the output string will be encoded as a URI so respecting the UTF-8 encoding
|
|
36
|
+
* */
|
|
37
|
+
export function encodePolygon(polygon: Array<LatLng>, forUrl = true): string {
|
|
38
|
+
const polygonArr: Array<any> = [];
|
|
39
|
+
polygon.forEach((p: LatLng) => {
|
|
40
|
+
polygonArr.push([p.lat, p.lng]);
|
|
41
|
+
});
|
|
42
|
+
const encodedPolygon = encode(polygonArr);
|
|
43
|
+
return forUrl ? encodeURIComponent(encodedPolygon) : encodedPolygon;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getMinMaxLatLng(polygon: Array<LatLng>): {
|
|
47
|
+
miny: number;
|
|
48
|
+
maxy: number;
|
|
49
|
+
minx: number;
|
|
50
|
+
maxx: number;
|
|
51
|
+
} {
|
|
52
|
+
const minmax = {
|
|
53
|
+
miny: polygon[0].lat,
|
|
54
|
+
maxy: polygon[0].lat,
|
|
55
|
+
minx: polygon[0].lng,
|
|
56
|
+
maxx: polygon[0].lng,
|
|
57
|
+
};
|
|
58
|
+
polygon.forEach((p: LatLng) => {
|
|
59
|
+
minmax.miny = Math.min(minmax.miny, p.lat);
|
|
60
|
+
minmax.maxy = Math.max(minmax.maxy, p.lat);
|
|
61
|
+
minmax.minx = Math.min(minmax.minx, p.lng);
|
|
62
|
+
minmax.maxx = Math.max(minmax.maxx, p.lng);
|
|
63
|
+
});
|
|
64
|
+
return minmax;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function removeDuplicatePoints(latLngs: Array<LatLng>): Array<LatLng> {
|
|
68
|
+
if (latLngs.length <= 0) return [];
|
|
69
|
+
|
|
70
|
+
const uniqueCoordinates = new Set();
|
|
71
|
+
const unique = latLngs.filter((x) => {
|
|
72
|
+
const isDuplicate = uniqueCoordinates.has(x.lat + x.lng);
|
|
73
|
+
const coordinate = x.lat + x.lng;
|
|
74
|
+
uniqueCoordinates.add(coordinate);
|
|
75
|
+
return !isDuplicate;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return unique;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function calcPolygonCenter(points: Array<LatLng>): LatLng {
|
|
82
|
+
let lat = 0;
|
|
83
|
+
let lng = 0;
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < points.length; i++) {
|
|
86
|
+
const point = points[i];
|
|
87
|
+
|
|
88
|
+
lat += point.lat;
|
|
89
|
+
lng += point.lng;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { lat: lat / points.length, lng: lng / points.length } as LatLng;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function validateMap(map: Leaflet_map): boolean {
|
|
96
|
+
const mapRange = window._mapRange || 10000;
|
|
97
|
+
|
|
98
|
+
if (map) {
|
|
99
|
+
const center = map.getShape()?.getCenter();
|
|
100
|
+
const points = map.getShapePoints();
|
|
101
|
+
if (points) {
|
|
102
|
+
for (let i = 0; i < points.length; i++) {
|
|
103
|
+
if (map.getDistance(points[i], center) > mapRange) return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getPolygonString(shapePoints: Array<LatLng>): string {
|
|
112
|
+
let polygonString = "POLYGON((";
|
|
113
|
+
shapePoints.forEach((point: LatLng) => {
|
|
114
|
+
polygonString += `${point.lng} ${point.lat}, `;
|
|
115
|
+
});
|
|
116
|
+
polygonString += `${shapePoints[0].lng} ${shapePoints[0].lat}))`;
|
|
117
|
+
return polygonString;
|
|
118
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a localized number to a float.
|
|
3
|
+
* @param stringNumber - the localized number
|
|
4
|
+
* @param locale - [optional] the locale that the number is represented in. Omit this parameter to use the current locale.
|
|
5
|
+
*/
|
|
6
|
+
export const parseLocaleNumber = (
|
|
7
|
+
stringNumber: string | number,
|
|
8
|
+
locale?: string
|
|
9
|
+
): number | string => {
|
|
10
|
+
if (!locale) locale = navigator.language;
|
|
11
|
+
|
|
12
|
+
const thousandSeparator = Intl.NumberFormat(locale)
|
|
13
|
+
.format(1111)
|
|
14
|
+
.replace(/\p{Number}/gu, "");
|
|
15
|
+
const decimalSeparator = Intl.NumberFormat(locale)
|
|
16
|
+
.format(1.1)
|
|
17
|
+
.replace(/\p{Number}/gu, "");
|
|
18
|
+
const parsedNumber = parseFloat(
|
|
19
|
+
`${stringNumber}`
|
|
20
|
+
.replace(new RegExp("\\" + thousandSeparator, "g"), "")
|
|
21
|
+
.replace(new RegExp("\\" + decimalSeparator), ".")
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
/* In case the parsed number is NaN, we will return an empty string! */
|
|
25
|
+
if (isNaN(parsedNumber)) return "";
|
|
26
|
+
|
|
27
|
+
return parsedNumber;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const formatLocaleNumber = (
|
|
31
|
+
numberToFormat?: number | string,
|
|
32
|
+
props?: { currency: boolean }
|
|
33
|
+
): string | undefined | null => {
|
|
34
|
+
if (numberToFormat === undefined || numberToFormat === null) return;
|
|
35
|
+
|
|
36
|
+
numberToFormat = parseFloat(`${numberToFormat}`);
|
|
37
|
+
|
|
38
|
+
/* If the number to format is NaN, we won't return anything */
|
|
39
|
+
if (isNaN(numberToFormat)) return "";
|
|
40
|
+
|
|
41
|
+
const currencyOptions = {
|
|
42
|
+
style: "currency",
|
|
43
|
+
currency: "EUR",
|
|
44
|
+
};
|
|
45
|
+
const defaultOptions = {
|
|
46
|
+
maximumFractionDigits: 0,
|
|
47
|
+
};
|
|
48
|
+
let options = { ...defaultOptions };
|
|
49
|
+
if (props?.currency) options = { ...options, ...currencyOptions };
|
|
50
|
+
|
|
51
|
+
return new Intl.NumberFormat(navigator.language, {
|
|
52
|
+
...options,
|
|
53
|
+
}).format(numberToFormat);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export function formatInteger(
|
|
57
|
+
value: number | undefined,
|
|
58
|
+
round: boolean = false
|
|
59
|
+
): string {
|
|
60
|
+
if (value == null) return "";
|
|
61
|
+
|
|
62
|
+
return formatNumber(round ? Math.round(value) : Math.floor(value), 0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function formatNumber(
|
|
66
|
+
value: number | undefined,
|
|
67
|
+
digits?: number
|
|
68
|
+
): string {
|
|
69
|
+
if (!value && value !== 0) return "";
|
|
70
|
+
|
|
71
|
+
return value.toLocaleString(
|
|
72
|
+
"it-IT", // usare una stringa tipo 'en-US' per sovrascrivere la lingua del browser
|
|
73
|
+
{
|
|
74
|
+
minimumFractionDigits: digits,
|
|
75
|
+
maximumFractionDigits: digits,
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function formatFloat(
|
|
81
|
+
value: number | undefined,
|
|
82
|
+
digits: number = 2
|
|
83
|
+
): string {
|
|
84
|
+
return formatNumber(value, digits);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const formatterNumberObj = {
|
|
88
|
+
formatFn: formatLocaleNumber,
|
|
89
|
+
unFormatFn: parseLocaleNumber,
|
|
90
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function shallowCopyObjectTo(
|
|
2
|
+
from: Record<string, any> | undefined,
|
|
3
|
+
to: Record<string, any>
|
|
4
|
+
): void {
|
|
5
|
+
if (!from) return;
|
|
6
|
+
|
|
7
|
+
for (const k in from) {
|
|
8
|
+
to[k] = from[k];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isEmptyObject(obj: any): boolean {
|
|
13
|
+
return !obj || !Object.keys(obj).length;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Makes the deep copy of the input object.
|
|
18
|
+
* For performance purposes, if the values of the properties of the object to copy are all primitive types then we will use the spread operator (faster).
|
|
19
|
+
* Otherwise, to ensure the deep copy of nested objects and arrays, we will use JSON.parse and JSON.stringify (slower).
|
|
20
|
+
* */
|
|
21
|
+
export function deepCopy<T>(objToCopy: T): T | null {
|
|
22
|
+
if (!objToCopy) return null;
|
|
23
|
+
|
|
24
|
+
for (const key in objToCopy) {
|
|
25
|
+
const prop = objToCopy[key];
|
|
26
|
+
if (!prop) continue;
|
|
27
|
+
|
|
28
|
+
//Sebastian R.S: If the property value is a primitive type, then we will continue!
|
|
29
|
+
if (Array.isArray(prop) || Object.keys(prop).length > 0) {
|
|
30
|
+
return JSON.parse(JSON.stringify(objToCopy));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { ...objToCopy };
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observes if an element is visible in the viewport. Once it is visible, the listener is removed
|
|
3
|
+
* */
|
|
4
|
+
export function observeOnce(
|
|
5
|
+
selector: string,
|
|
6
|
+
fn: (Element: Node, IntersectionObserver: IntersectionObserver) => void,
|
|
7
|
+
options?: IntersectionObserverInit
|
|
8
|
+
): void {
|
|
9
|
+
const elems = [].slice.call(document.querySelectorAll(selector));
|
|
10
|
+
|
|
11
|
+
const observer = new IntersectionObserver(
|
|
12
|
+
(
|
|
13
|
+
entries: Array<IntersectionObserverEntry>,
|
|
14
|
+
observer: IntersectionObserver
|
|
15
|
+
) => {
|
|
16
|
+
for (let i = 0; i < entries.length; i++) {
|
|
17
|
+
const entry = entries[i];
|
|
18
|
+
|
|
19
|
+
if (entry.isIntersecting) {
|
|
20
|
+
const elem = entry.target;
|
|
21
|
+
fn(elem, observer);
|
|
22
|
+
observer.unobserve(elem);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
options
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
elems.forEach((elem: HTMLElement) => {
|
|
30
|
+
observer.observe(elem);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type PermissionStates = "granted" | "denied" | "prompt";
|
|
2
|
+
export type PermissionChangeEvent = CustomEvent<{
|
|
3
|
+
permissionState: PermissionStates;
|
|
4
|
+
}>;
|
|
5
|
+
export type NotificationPermissionChangeHandler = (
|
|
6
|
+
e: PermissionChangeEvent
|
|
7
|
+
) => void | Promise<void>;
|
|
8
|
+
|
|
9
|
+
let permissionsListenerInit = false;
|
|
10
|
+
|
|
11
|
+
export const onNotificationsPermissionChange = (
|
|
12
|
+
cb: NotificationPermissionChangeHandler
|
|
13
|
+
): void => {
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
document.addEventListener("notification-permission-changed", cb);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Add an event listener which emits the event "notification-permission-changed" when the user changes the state of the notifications
|
|
20
|
+
* */
|
|
21
|
+
export const addPermissionStateListener = (): void => {
|
|
22
|
+
if (!navigator.permissions || !navigator.permissions.query) return;
|
|
23
|
+
|
|
24
|
+
//setting the listener ONCE
|
|
25
|
+
!permissionsListenerInit &&
|
|
26
|
+
navigator.permissions
|
|
27
|
+
.query({ name: "notifications" })
|
|
28
|
+
.then(function (permission) {
|
|
29
|
+
// Initial status is available at permission.state
|
|
30
|
+
permission.onchange = function (): void {
|
|
31
|
+
// Whenever there's a change, updated status is available at this.state
|
|
32
|
+
document.dispatchEvent(
|
|
33
|
+
new CustomEvent("notification-permission-changed", {
|
|
34
|
+
detail: { permissionState: this.state },
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
permissionsListenerInit = true;
|
|
41
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { RealEstateSearchBean } from "@wikicasa-dev/types";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CONTRACT,
|
|
5
|
+
searchBeanMap,
|
|
6
|
+
searchBeanMapAgencyFilter,
|
|
7
|
+
} from "@wikicasa-dev/types";
|
|
8
|
+
|
|
9
|
+
declare global {
|
|
10
|
+
interface Window {
|
|
11
|
+
_auctionLabel: string;
|
|
12
|
+
_rentLabel: string;
|
|
13
|
+
_saleLabel: string;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function buildURL(
|
|
18
|
+
cityURL: string,
|
|
19
|
+
listingTypology: string,
|
|
20
|
+
contractType: number | undefined | null,
|
|
21
|
+
zone?: string | null,
|
|
22
|
+
seoFilter?: string | null,
|
|
23
|
+
agencyAndPlace?: string,
|
|
24
|
+
district?: string | null
|
|
25
|
+
): string {
|
|
26
|
+
let URL = "/";
|
|
27
|
+
|
|
28
|
+
if (contractType) {
|
|
29
|
+
URL +=
|
|
30
|
+
(contractType == CONTRACT.AUCTION
|
|
31
|
+
? window["_auctionLabel"]
|
|
32
|
+
: contractType == CONTRACT.RENT
|
|
33
|
+
? window["_rentLabel"]
|
|
34
|
+
: window["_saleLabel"]) + "-";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
URL += `${listingTypology}/`;
|
|
38
|
+
|
|
39
|
+
if (agencyAndPlace) URL += `${agencyAndPlace}/`;
|
|
40
|
+
|
|
41
|
+
if (cityURL) URL += `${cityURL.replace(/^-+|-+$/g, "")}/`;
|
|
42
|
+
|
|
43
|
+
if (zone) {
|
|
44
|
+
URL += `${zone.replace(/^_+|_+$/g, "")}/`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!zone && district) {
|
|
48
|
+
URL += `${district.replace(/^_+|_+$/g, "")}/`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (seoFilter) {
|
|
52
|
+
URL += `${seoFilter.replace(/^_+|_+$/g, "")}/`;
|
|
53
|
+
}
|
|
54
|
+
return URL;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function buildParams(searchBean: RealEstateSearchBean): string {
|
|
58
|
+
const params = Object.keys(searchBeanMap)
|
|
59
|
+
.map((key) => {
|
|
60
|
+
if (searchBean[key]) {
|
|
61
|
+
return (
|
|
62
|
+
encodeURIComponent(searchBeanMap[key]) +
|
|
63
|
+
"=" +
|
|
64
|
+
encodeURIComponent(searchBean[key])
|
|
65
|
+
);
|
|
66
|
+
} else return null;
|
|
67
|
+
})
|
|
68
|
+
.filter((param) => param !== null);
|
|
69
|
+
|
|
70
|
+
return params.length ? `?${params.join("&")}` : "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function buildParamsForAgency(searchBean: RealEstateSearchBean): string {
|
|
74
|
+
const params = Object.keys(searchBeanMapAgencyFilter)
|
|
75
|
+
.map((key) => {
|
|
76
|
+
if (searchBean[key]) {
|
|
77
|
+
if (Array.isArray(searchBean[key])) {
|
|
78
|
+
const arr = searchBean[key];
|
|
79
|
+
const nameField = searchBeanMapAgencyFilter[key];
|
|
80
|
+
let strToReturn = "";
|
|
81
|
+
for (let i = 0; i < arr.length; i++) {
|
|
82
|
+
strToReturn +=
|
|
83
|
+
encodeURIComponent(nameField) + "=" + encodeURIComponent(arr[i]);
|
|
84
|
+
strToReturn += i < arr.length - 1 ? "&" : "";
|
|
85
|
+
}
|
|
86
|
+
return strToReturn;
|
|
87
|
+
} else {
|
|
88
|
+
return (
|
|
89
|
+
encodeURIComponent(searchBeanMapAgencyFilter[key]) +
|
|
90
|
+
"=" +
|
|
91
|
+
encodeURIComponent(searchBean[key])
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
} else return null;
|
|
95
|
+
})
|
|
96
|
+
.filter((param) => param !== null);
|
|
97
|
+
|
|
98
|
+
return params.length ? `?${params.join("&")}` : "";
|
|
99
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
import type { RequestGeneric } from "@wikicasa-dev/types";
|
|
4
|
+
|
|
5
|
+
export function sendRequestGeneric(rg: RequestGeneric): Promise<unknown> {
|
|
6
|
+
return new Promise(
|
|
7
|
+
(
|
|
8
|
+
resolve: (...args: Array<unknown>) => void,
|
|
9
|
+
reject: (error: Error) => void
|
|
10
|
+
) =>
|
|
11
|
+
axios
|
|
12
|
+
.post("/rest/request/addRequestGenericValuation", rg, {
|
|
13
|
+
headers: { "Content-Type": "application/json", dataType: "json" },
|
|
14
|
+
})
|
|
15
|
+
.then(() => {
|
|
16
|
+
resolve();
|
|
17
|
+
})
|
|
18
|
+
.catch((error: any) => reject(error))
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export function replaceAll(str: string, find: string, replace: string): string {
|
|
2
|
+
return str.replace(new RegExp(find, "g"), replace);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function capitalizeFirstLetter(string: string): string {
|
|
6
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function formatAddress(
|
|
10
|
+
streetName: string,
|
|
11
|
+
streetNumber: string,
|
|
12
|
+
cityName: string,
|
|
13
|
+
provinceAcronym?: string
|
|
14
|
+
): string {
|
|
15
|
+
const components: Array<string> = [];
|
|
16
|
+
|
|
17
|
+
if (streetName && streetName !== "ND") {
|
|
18
|
+
if (streetNumber && streetNumber !== "ND" && streetNumber !== "0")
|
|
19
|
+
components.push(`${streetName} ${streetNumber}`);
|
|
20
|
+
else components.push(streetName);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (cityName && provinceAcronym) {
|
|
24
|
+
components.push(`${cityName} (${provinceAcronym})`);
|
|
25
|
+
} else {
|
|
26
|
+
components.push(cityName);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const address = components
|
|
30
|
+
.filter((component: string) => !!component)
|
|
31
|
+
.join(", ");
|
|
32
|
+
|
|
33
|
+
return capitalizeFirstLetter(address);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function decodeTextWithEntities(strWithEntities: string | null): string {
|
|
37
|
+
if (!strWithEntities) return "";
|
|
38
|
+
|
|
39
|
+
const a = document.createElement("span");
|
|
40
|
+
a.innerHTML = strWithEntities;
|
|
41
|
+
return a.textContent || "";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Replaces a given string with the values of the object
|
|
46
|
+
* @param str string to replace
|
|
47
|
+
* @param mapObj Object whose keys are the tokens to replace and whose values are the ones that will replace that matched token
|
|
48
|
+
* e.g. replaceAllToken('Here we go', {Here: Now, go: eat}) returns 'Now we eat'
|
|
49
|
+
* */
|
|
50
|
+
export function replaceAllTokens(
|
|
51
|
+
str: string,
|
|
52
|
+
mapObj: Record<string, string>
|
|
53
|
+
): string {
|
|
54
|
+
const re = new RegExp(
|
|
55
|
+
Object.keys(mapObj)
|
|
56
|
+
.map((key) => key.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"))
|
|
57
|
+
.join("|"),
|
|
58
|
+
"gi"
|
|
59
|
+
);
|
|
60
|
+
return str.replace(re, function (matched) {
|
|
61
|
+
return mapObj[matched.toLowerCase()];
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function cleanASCII(str: string): string {
|
|
66
|
+
return str.replace(/[\uA78C\uA78B]/g, "'").replace(/[^\x00-\x7F]/g, "");
|
|
67
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <p>Adds a query string to the provided URL using the params object</p>
|
|
3
|
+
* <p>e.g. Input => URL: www.sample.com, params = {userId: 56, name: "Jonas"}</p>
|
|
4
|
+
* Output => www.sample.com?userId=56&name=Jonas
|
|
5
|
+
* @param url The url that will contain the query string
|
|
6
|
+
* @param params The object that will be transformed into a query string
|
|
7
|
+
* @return A new url instance containing the query string
|
|
8
|
+
* */
|
|
9
|
+
export const appendQueryString = (
|
|
10
|
+
url: URL,
|
|
11
|
+
params: Record<string, any> = {}
|
|
12
|
+
): URL => {
|
|
13
|
+
if (Object.keys(params).length === 0)
|
|
14
|
+
throw "The params object can't be empty";
|
|
15
|
+
|
|
16
|
+
const urlWithQueryString = new URL(url);
|
|
17
|
+
for (const key in params)
|
|
18
|
+
urlWithQueryString.searchParams.append(key, `${params[key]}`);
|
|
19
|
+
|
|
20
|
+
return urlWithQueryString;
|
|
21
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PAGES } from "@wikicasa-dev/types";
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
interface Window {
|
|
5
|
+
_baseURL: string;
|
|
6
|
+
_privateURL: string;
|
|
7
|
+
_saveSearchesURL: string;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const getURLPage = (page: PAGES): string => {
|
|
12
|
+
const baseUrl = `${window["_baseURL"]}`;
|
|
13
|
+
|
|
14
|
+
switch (page) {
|
|
15
|
+
case "SAVED_SEARCHES":
|
|
16
|
+
return `${baseUrl}/${window["_privateURL"]}/${window["_saveSearchesURL"]}`;
|
|
17
|
+
default:
|
|
18
|
+
throw new Error(`There is no matching URL for ${page}`);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"baseUrl": "src",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"useDefineForClassFields": true,
|
|
8
|
+
"module": "ESNext",
|
|
9
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"paths": {
|
|
12
|
+
"@utils/*": ["./utils/*"],
|
|
13
|
+
"@services/*": ["./services/*"]
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
"sourceMap": true,
|
|
17
|
+
"esModuleInterop": true,
|
|
18
|
+
"noImplicitReturns": true,
|
|
19
|
+
"forceConsistentCasingInFileNames": true,
|
|
20
|
+
|
|
21
|
+
/* Bundler mode */
|
|
22
|
+
"moduleResolution": "Node",
|
|
23
|
+
"allowImportingTsExtensions": true,
|
|
24
|
+
"resolveJsonModule": true,
|
|
25
|
+
"isolatedModules": true,
|
|
26
|
+
"noEmit": true,
|
|
27
|
+
|
|
28
|
+
/* Linting */
|
|
29
|
+
"strict": true,
|
|
30
|
+
"noUnusedLocals": true,
|
|
31
|
+
"noUnusedParameters": true,
|
|
32
|
+
"noFallthroughCasesInSwitch": true,
|
|
33
|
+
|
|
34
|
+
"strictPropertyInitialization": false
|
|
35
|
+
},
|
|
36
|
+
"include": ["src"],
|
|
37
|
+
"exclude": ["node_modules"]
|
|
38
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// vite.config.ts
|
|
2
|
+
import { fileURLToPath, URL } from "url";
|
|
3
|
+
import { resolve } from "path";
|
|
4
|
+
import { defineConfig } from "vite";
|
|
5
|
+
import dts from "vite-plugin-dts";
|
|
6
|
+
|
|
7
|
+
const getPackageName = () => {
|
|
8
|
+
return "utilities";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const fileName = {
|
|
12
|
+
es: `${getPackageName()}.mjs`,
|
|
13
|
+
cjs: `${getPackageName()}.cjs`,
|
|
14
|
+
iife: `${getPackageName()}.iife.js`,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const formats = Object.keys(fileName) as Array<keyof typeof fileName>;
|
|
18
|
+
|
|
19
|
+
// https://vitejs.dev/guide/build.html#library-mode
|
|
20
|
+
export default defineConfig({
|
|
21
|
+
build: {
|
|
22
|
+
lib: {
|
|
23
|
+
entry: resolve(__dirname, "src/index.ts"),
|
|
24
|
+
name: getPackageName(),
|
|
25
|
+
formats,
|
|
26
|
+
fileName: (format) => fileName[format],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
resolve: {
|
|
30
|
+
alias: [
|
|
31
|
+
{
|
|
32
|
+
find: "@utils",
|
|
33
|
+
replacement: fileURLToPath(new URL("./src/utils", import.meta.url)),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
find: "@services",
|
|
37
|
+
replacement: fileURLToPath(new URL("./src/services", import.meta.url)),
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
plugins: [dts()],
|
|
42
|
+
});
|