You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					135 lines
				
				3.4 KiB
			
		
		
			
		
	
	
					135 lines
				
				3.4 KiB
			| 
											3 years ago
										 | // @flow | ||
|  | import PolishedError from '../internalHelpers/_errors' | ||
|  | 
 | ||
|  | import type { FontFaceConfiguration } from '../types/fontFaceConfiguration' | ||
|  | import type { Styles } from '../types/style' | ||
|  | 
 | ||
|  | const dataURIRegex = /^\s*data:([a-z]+\/[a-z-]+(;[a-z-]+=[a-z-]+)?)?(;charset=[a-z0-9-]+)?(;base64)?,[a-z0-9!$&',()*+,;=\-._~:@/?%\s]*\s*$/i | ||
|  | 
 | ||
|  | const formatHintMap = { | ||
|  |   woff: 'woff', | ||
|  |   woff2: 'woff2', | ||
|  |   ttf: 'truetype', | ||
|  |   otf: 'opentype', | ||
|  |   eot: 'embedded-opentype', | ||
|  |   svg: 'svg', | ||
|  |   svgz: 'svg', | ||
|  | } | ||
|  | 
 | ||
|  | function generateFormatHint(format: string, formatHint: boolean): string { | ||
|  |   if (!formatHint) return '' | ||
|  |   return ` format("${formatHintMap[format]}")` | ||
|  | } | ||
|  | 
 | ||
|  | function isDataURI(fontFilePath: string): boolean { | ||
|  |   return !!fontFilePath.replace(/\s+/g, ' ').match(dataURIRegex) | ||
|  | } | ||
|  | 
 | ||
|  | function generateFileReferences( | ||
|  |   fontFilePath: string, | ||
|  |   fileFormats: Array<string>, | ||
|  |   formatHint: boolean | ||
|  | ): string { | ||
|  |   if (isDataURI(fontFilePath)) { | ||
|  |     return `url("${fontFilePath}")${generateFormatHint(fileFormats[0], formatHint)}` | ||
|  |   } | ||
|  | 
 | ||
|  |   const fileFontReferences = fileFormats.map( | ||
|  |     format => `url("${fontFilePath}.${format}")${generateFormatHint(format, formatHint)}` | ||
|  |   ) | ||
|  |   return fileFontReferences.join(', ') | ||
|  | } | ||
|  | 
 | ||
|  | function generateLocalReferences(localFonts: Array<string>): string { | ||
|  |   const localFontReferences = localFonts.map(font => `local("${font}")`) | ||
|  |   return localFontReferences.join(', ') | ||
|  | } | ||
|  | 
 | ||
|  | function generateSources( | ||
|  |   fontFilePath?: string, | ||
|  |   localFonts?: Array<string>, | ||
|  |   fileFormats: Array<string>, | ||
|  |   formatHint: boolean | ||
|  | ): string { | ||
|  |   const fontReferences = [] | ||
|  |   if (localFonts) fontReferences.push(generateLocalReferences(localFonts)) | ||
|  |   if (fontFilePath) { | ||
|  |     fontReferences.push(generateFileReferences(fontFilePath, fileFormats, formatHint)) | ||
|  |   } | ||
|  |   return fontReferences.join(', ') | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * CSS for a @font-face declaration. | ||
|  |  * | ||
|  |  * @example | ||
|  |  * // Styles as object basic usage | ||
|  |  * const styles = { | ||
|  |  *    ...fontFace({ | ||
|  |  *      'fontFamily': 'Sans-Pro', | ||
|  |  *      'fontFilePath': 'path/to/file' | ||
|  |  *    }) | ||
|  |  * } | ||
|  |  * | ||
|  |  * // styled-components basic usage | ||
|  |  * const GlobalStyle = createGlobalStyle`${ | ||
|  |  *   fontFace({ | ||
|  |  *     'fontFamily': 'Sans-Pro', | ||
|  |  *     'fontFilePath': 'path/to/file' | ||
|  |  *   } | ||
|  |  * )}` | ||
|  |  * | ||
|  |  * // CSS as JS Output | ||
|  |  * | ||
|  |  * '@font-face': { | ||
|  |  *   'fontFamily': 'Sans-Pro', | ||
|  |  *   'src': 'url("path/to/file.eot"), url("path/to/file.woff2"), url("path/to/file.woff"), url("path/to/file.ttf"), url("path/to/file.svg")', | ||
|  |  * } | ||
|  |  */ | ||
|  | 
 | ||
|  | export default function fontFace({ | ||
|  |   fontFamily, | ||
|  |   fontFilePath, | ||
|  |   fontStretch, | ||
|  |   fontStyle, | ||
|  |   fontVariant, | ||
|  |   fontWeight, | ||
|  |   fileFormats = ['eot', 'woff2', 'woff', 'ttf', 'svg'], | ||
|  |   formatHint = false, | ||
|  |   localFonts, | ||
|  |   unicodeRange, | ||
|  |   fontDisplay, | ||
|  |   fontVariationSettings, | ||
|  |   fontFeatureSettings, | ||
|  | }: FontFaceConfiguration): Styles { | ||
|  |   // Error Handling | ||
|  |   if (!fontFamily) throw new PolishedError(55) | ||
|  |   if (!fontFilePath && !localFonts) { | ||
|  |     throw new PolishedError(52) | ||
|  |   } | ||
|  |   if (localFonts && !Array.isArray(localFonts)) { | ||
|  |     throw new PolishedError(53) | ||
|  |   } | ||
|  |   if (!Array.isArray(fileFormats)) { | ||
|  |     throw new PolishedError(54) | ||
|  |   } | ||
|  | 
 | ||
|  |   const fontFaceDeclaration = { | ||
|  |     '@font-face': { | ||
|  |       fontFamily, | ||
|  |       src: generateSources(fontFilePath, localFonts, fileFormats, formatHint), | ||
|  |       unicodeRange, | ||
|  |       fontStretch, | ||
|  |       fontStyle, | ||
|  |       fontVariant, | ||
|  |       fontWeight, | ||
|  |       fontDisplay, | ||
|  |       fontVariationSettings, | ||
|  |       fontFeatureSettings, | ||
|  |     }, | ||
|  |   } | ||
|  | 
 | ||
|  |   // Removes undefined fields for cleaner css object. | ||
|  |   return JSON.parse(JSON.stringify(fontFaceDeclaration)) | ||
|  | } |