mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-14 00:32:35 +10:00
🚀 release v3.0.0
This commit is contained in:
42
client/utils/date.ts
Normal file
42
client/utils/date.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { DateRange } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import isString from 'lodash/isString';
|
||||
|
||||
export const dateFormatOptions: string[] = [
|
||||
'MMMM DD, YYYY',
|
||||
'DD MMMM YYYY',
|
||||
'DD.MM.YYYY',
|
||||
'DD/MM/YYYY',
|
||||
'MM.DD.YYYY',
|
||||
'MM/DD/YYYY',
|
||||
'YYYY.MM.DD',
|
||||
'YYYY/MM/DD',
|
||||
'MMMM YYYY',
|
||||
'MMM YYYY',
|
||||
'YYYY',
|
||||
];
|
||||
|
||||
export const getRelativeTime = (timestamp: dayjs.ConfigType): string => dayjs(timestamp).toNow(true);
|
||||
|
||||
export const formatDateString = (date: string | DateRange, formatStr: string): string | null => {
|
||||
if (isEmpty(date)) return null;
|
||||
|
||||
// If `date` is a string
|
||||
if (isString(date)) {
|
||||
if (!dayjs(date).isValid()) return null;
|
||||
|
||||
return dayjs(date).format(formatStr);
|
||||
}
|
||||
|
||||
// If `date` is a DateRange
|
||||
if (isEmpty(date.start)) return null;
|
||||
|
||||
if (!dayjs(date.start).isValid()) return null;
|
||||
|
||||
if (!isEmpty(date.end) && dayjs(date.end).isValid()) {
|
||||
return `${dayjs(date.start).format(formatStr)} - ${dayjs(date.end).format(formatStr)}`;
|
||||
}
|
||||
|
||||
return dayjs(date.start).format(formatStr);
|
||||
};
|
||||
9
client/utils/getGravatarUrl.ts
Normal file
9
client/utils/getGravatarUrl.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import md5Hex from 'md5-hex';
|
||||
|
||||
const getGravatarUrl = (email: string, size: number) => {
|
||||
const hash = md5Hex(email);
|
||||
|
||||
return `https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon`;
|
||||
};
|
||||
|
||||
export default getGravatarUrl;
|
||||
37
client/utils/getProfileIcon.tsx
Normal file
37
client/utils/getProfileIcon.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { Link } from '@mui/icons-material';
|
||||
import get from 'lodash/get';
|
||||
import {
|
||||
FaBehance,
|
||||
FaDribbble,
|
||||
FaFacebookF,
|
||||
FaGithub,
|
||||
FaGitlab,
|
||||
FaInstagram,
|
||||
FaLinkedinIn,
|
||||
FaSkype,
|
||||
FaSoundcloud,
|
||||
FaStackOverflow,
|
||||
FaTelegram,
|
||||
FaTwitter,
|
||||
FaYoutube,
|
||||
} from 'react-icons/fa';
|
||||
|
||||
const profileIconMap: Record<string, JSX.Element> = {
|
||||
facebook: <FaFacebookF />,
|
||||
twitter: <FaTwitter />,
|
||||
linkedin: <FaLinkedinIn />,
|
||||
dribbble: <FaDribbble />,
|
||||
soundcloud: <FaSoundcloud />,
|
||||
github: <FaGithub />,
|
||||
instagram: <FaInstagram />,
|
||||
stackoverflow: <FaStackOverflow />,
|
||||
behance: <FaBehance />,
|
||||
gitlab: <FaGitlab />,
|
||||
telegram: <FaTelegram />,
|
||||
skype: <FaSkype />,
|
||||
youtube: <FaYoutube />,
|
||||
};
|
||||
|
||||
const getProfileIcon = (network: string): JSX.Element => get(profileIconMap, network.toLowerCase(), <Link />);
|
||||
|
||||
export default getProfileIcon;
|
||||
35
client/utils/getResumeUrl.ts
Normal file
35
client/utils/getResumeUrl.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Resume } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
|
||||
type Options = {
|
||||
withHost?: boolean;
|
||||
shortUrl?: boolean;
|
||||
buildUrl?: boolean;
|
||||
};
|
||||
|
||||
const defaultOptions: Options = {
|
||||
withHost: false,
|
||||
shortUrl: false,
|
||||
buildUrl: false,
|
||||
};
|
||||
|
||||
const getResumeUrl = (resume: Resume, options: Options = defaultOptions): string => {
|
||||
const username: string = get(resume, 'user.username');
|
||||
const shortId: string = get(resume, 'shortId');
|
||||
const slug: string = get(resume, 'slug');
|
||||
|
||||
let url = '';
|
||||
let hostname = '';
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
hostname = window.location.origin;
|
||||
}
|
||||
|
||||
url = options.withHost ? `${hostname}` : url;
|
||||
url = options.shortUrl ? `${url}/r/${shortId}` : `${url}/${username}/${slug}`;
|
||||
url = options.buildUrl ? `${url}/build` : url;
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
export default getResumeUrl;
|
||||
3
client/utils/isBrowser.ts
Normal file
3
client/utils/isBrowser.ts
Normal file
@ -0,0 +1,3 @@
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
export default isBrowser;
|
||||
66
client/utils/styles.ts
Normal file
66
client/utils/styles.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { Theme, Typography } from '@reactive-resume/schema';
|
||||
import { RgbColor } from 'react-colorful';
|
||||
|
||||
import { hexColorPattern } from '@/config/colors';
|
||||
|
||||
export const generateTypographyStyles = ({ family, size }: Typography): string => `
|
||||
font-size: ${size.body}px;
|
||||
font-family: ${family.body};
|
||||
|
||||
svg { font-size: ${size.body}px; }
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: bold;
|
||||
font-family: ${family.heading};
|
||||
}
|
||||
|
||||
h1 { font-size: ${size.heading}px; line-height: ${size.heading}px; }
|
||||
h2 { font-size: ${size.heading / 1.5}px; line-height: ${size.heading / 1.5}px; }
|
||||
h3 { font-size: ${size.heading / 2}px; line-height: ${size.heading / 2}px; }
|
||||
h4 { font-size: ${size.heading / 2.5}px; line-height: ${size.heading / 2.5}px; }
|
||||
h5 { font-size: ${size.heading / 3}px; line-height: ${size.heading / 3}px; }
|
||||
h6 { font-size: ${size.heading / 3.5}px; line-height: ${size.heading / 3.5}px; }
|
||||
`;
|
||||
|
||||
export const generateThemeStyles = ({ text, background, primary }: Theme): string => `
|
||||
color: ${text};
|
||||
background-color: ${background};
|
||||
--primary-color: ${primary};
|
||||
|
||||
svg {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
`;
|
||||
|
||||
export const hexToRgb = (hex: string): RgbColor | null => {
|
||||
const result = hexColorPattern.exec(hex);
|
||||
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16),
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
export const getContrastColor = (color: string): 'dark' | 'light' => {
|
||||
const rgb = hexToRgb(color);
|
||||
|
||||
if (rgb) {
|
||||
const { r, g, b } = rgb;
|
||||
|
||||
if (r * 0.299 + g * 0.587 + b * 0.114 > 186) {
|
||||
return 'dark';
|
||||
} else {
|
||||
return 'light';
|
||||
}
|
||||
}
|
||||
|
||||
return 'light';
|
||||
};
|
||||
63
client/utils/template.ts
Normal file
63
client/utils/template.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { ListItem, Location, PhotoFilters } from '@reactive-resume/schema';
|
||||
import clsx from 'clsx';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
export type Required<T, K extends keyof T> = T & { [P in K]-?: T[P] };
|
||||
|
||||
export type PageProps = {
|
||||
page: number;
|
||||
};
|
||||
|
||||
export const formatLocation = (location?: Location): string => {
|
||||
if (!location) return '';
|
||||
|
||||
const locationArr = [location.address, location.city, location.region, location.postalCode, location.country];
|
||||
const filteredLocationArr = locationArr.filter((x) => !isEmpty(x));
|
||||
|
||||
return filteredLocationArr.join(', ');
|
||||
};
|
||||
|
||||
export const addHttp = (url: string) => {
|
||||
if (url.search(/^http[s]?:\/\//) == -1) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
export const isValidUrl = (string: string): boolean => {
|
||||
let url: URL;
|
||||
|
||||
try {
|
||||
url = new URL(string);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return url.protocol === 'http:' || url.protocol === 'https:';
|
||||
};
|
||||
|
||||
type Separator = ', ' | ' / ' | ' | ';
|
||||
|
||||
export const parseListItemPath = (item: ListItem, path: string | string[], separator: Separator = ', '): string => {
|
||||
if (isArray(path)) {
|
||||
const value = path.map((_path) => get(item, _path));
|
||||
|
||||
return value.join(separator);
|
||||
} else {
|
||||
const value = get(item, path);
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
export const getPhotoClassNames = (filters: PhotoFilters) =>
|
||||
clsx({
|
||||
'object-cover': true,
|
||||
grayscale: filters.grayscale,
|
||||
'!border-[4px] !border-solid': filters.border,
|
||||
'rounded-lg': filters.shape === 'rounded-square',
|
||||
'rounded-full': filters.shape === 'circle',
|
||||
});
|
||||
Reference in New Issue
Block a user