🚀 release v3.0.0

This commit is contained in:
Amruth Pillai
2022-03-06 22:48:29 +01:00
parent 00505a9e5d
commit 9c1380f401
373 changed files with 12050 additions and 15783 deletions

42
client/utils/date.ts Normal file
View 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);
};

View 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;

View 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;

View 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;

View File

@ -0,0 +1,3 @@
const isBrowser = typeof window !== 'undefined';
export default isBrowser;

66
client/utils/styles.ts Normal file
View 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
View 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',
});