189 lines
5.9 KiB
TypeScript
189 lines
5.9 KiB
TypeScript
import { Id, ITrackable } from '../listing/models/trackable';
|
|
import { UntypedFormGroup } from '@angular/forms';
|
|
import { forOwn, has, isEqual, isPlainObject, transform } from 'lodash-es';
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|
import { inject } from '@angular/core';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
|
|
export function capitalize(value: string | String): string {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
}
|
|
|
|
export function humanize(value: string, lowercase = true): string {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
|
|
const words = (lowercase ? value.toLowerCase() : value).split(/[ \-_]+/);
|
|
return words.map(capitalize).join(' ');
|
|
}
|
|
|
|
export function humanizeCamelCase(value: string): string {
|
|
return value.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase();
|
|
}
|
|
|
|
export function _log(value: unknown, message = '') {
|
|
console.log(`%c[${dayjs().format('mm:ss.SSS')}] ${message}`, 'color: yellow;', value);
|
|
}
|
|
|
|
export function toNumber(str: string): number {
|
|
try {
|
|
return parseInt(`${str}`.replace(/\D/g, ''), 10);
|
|
} catch (e) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
export function trackByFactory<T extends ITrackable<PrimaryKey>, PrimaryKey extends Id = T['id']>() {
|
|
return (_index: number, item: T): Id => item.id;
|
|
}
|
|
|
|
export function hasFormChanged(form: UntypedFormGroup, initialFormValue: Record<string, string>): boolean {
|
|
if (form && initialFormValue) {
|
|
for (const key of Object.keys(form.getRawValue())) {
|
|
const initialValue = initialFormValue[key];
|
|
const updatedValue = form.get(key)?.value;
|
|
|
|
if (initialValue == null && updatedValue != null) {
|
|
const updatedValueType = typeof updatedValue;
|
|
if (updatedValueType !== 'string' && updatedValueType !== 'boolean') {
|
|
return true;
|
|
} else if (updatedValueType === 'string' && updatedValue.length > 0) {
|
|
return true;
|
|
} else if (updatedValueType === 'boolean' && updatedValue === true) {
|
|
return true;
|
|
}
|
|
} else if (initialValue !== updatedValue) {
|
|
if (Array.isArray(updatedValue)) {
|
|
if (JSON.stringify(initialValue) !== JSON.stringify(updatedValue)) {
|
|
return true;
|
|
}
|
|
} else if (updatedValue instanceof dayjs) {
|
|
if (!(updatedValue as Dayjs).isSame(dayjs(initialValue), 'day')) {
|
|
return true;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const HOURS_IN_A_DAY = 24;
|
|
const MINUTES_IN_AN_HOUR = 60;
|
|
|
|
export function getLeftDateTime(ISOString: string) {
|
|
const date = dayjs(ISOString);
|
|
const now = new Date(Date.now());
|
|
|
|
const daysLeft = date.diff(now, 'days');
|
|
const hoursFromNow = date.diff(now, 'hours');
|
|
const hoursLeft = hoursFromNow - HOURS_IN_A_DAY * daysLeft;
|
|
const minutesFromNow = date.diff(now, 'minutes');
|
|
const minutesLeft = minutesFromNow - HOURS_IN_A_DAY * MINUTES_IN_AN_HOUR * daysLeft;
|
|
|
|
return { daysLeft, hoursLeft, minutesLeft };
|
|
}
|
|
|
|
export function deepDiffObj(base: Record<string, unknown>, object: Record<string, unknown>) {
|
|
if (!object) {
|
|
throw new Error(`The object compared should be an object: ${object}`);
|
|
}
|
|
|
|
if (!base) {
|
|
return object;
|
|
}
|
|
|
|
const res = transform(object, (result: Record<string, unknown>, value: any, key: string) => {
|
|
if (!has(base, key)) {
|
|
result[key] = value;
|
|
} // fix edge case: not defined to explicitly defined as undefined
|
|
if (!isEqual(value, base[key])) {
|
|
result[key] =
|
|
isPlainObject(value) && isPlainObject(base[key])
|
|
? deepDiffObj(base[key] as Record<string, unknown>, value as Record<string, unknown>)
|
|
: value;
|
|
}
|
|
});
|
|
// map removed fields to undefined
|
|
forOwn(base, (_value: any, key: string) => {
|
|
if (!has(object, key)) {
|
|
res[key] = undefined;
|
|
}
|
|
});
|
|
|
|
return res;
|
|
}
|
|
|
|
export function bool(value: unknown): boolean {
|
|
if (typeof value !== 'string') {
|
|
return Boolean(value);
|
|
}
|
|
|
|
const _value = value.toLowerCase().trim();
|
|
if (_value === 'true') {
|
|
return true;
|
|
}
|
|
|
|
if (_value === 'false') {
|
|
return false;
|
|
}
|
|
|
|
return Boolean(_value);
|
|
}
|
|
|
|
declare global {
|
|
interface String {
|
|
capitalize(): string;
|
|
}
|
|
|
|
interface Array<T> {
|
|
/**
|
|
* Returns a new array with all falsy values removed.
|
|
* The values false, null, 0, "", undefined, and NaN are considered falsy.
|
|
* @param and - Additional function that is called for each truthy element in the array.
|
|
* The value returned from the function determines whether the element is kept or removed.
|
|
*/
|
|
filterTruthy(and?: (value: T) => boolean): T[];
|
|
}
|
|
|
|
interface Console {
|
|
/**
|
|
* Logs a beautifully formatted message to the console.
|
|
* @param value - The object to log.
|
|
* @param message - Additional message.
|
|
*/
|
|
write(value: unknown, message?: string): void;
|
|
}
|
|
}
|
|
|
|
console.write = _log;
|
|
|
|
String.prototype.capitalize = function _capitalize(this: string): string {
|
|
return capitalize(this);
|
|
};
|
|
|
|
Array.prototype.filterTruthy = function <T>(this: T[], predicate: (value: T) => boolean = () => true): T[] {
|
|
return this.filter(value => !!value && predicate(value));
|
|
};
|
|
|
|
/**
|
|
* Use this in field initialization or in constructor of a service / component
|
|
* @param param
|
|
* @param route
|
|
*/
|
|
export function getParam(param: string, route = inject(ActivatedRoute)): string | null {
|
|
if (route.snapshot.paramMap.has(param)) {
|
|
return route.snapshot.paramMap.get(param);
|
|
}
|
|
if (route.parent) {
|
|
return getParam(param, route.parent);
|
|
}
|
|
return null;
|
|
}
|