apphouse
Version:
Component library for React that uses observable state management and theme-able components.
269 lines (244 loc) • 7.09 kB
text/typescript
import { values } from '../../utils/obj/values';
import { isObject } from '../../utils/obj/isObject';
import { isEmpty } from '../../utils/obj/isEmpty';
import { Palette } from '../Palette';
import { Token } from '../Token';
import { ensureAllLowerCase } from './string.utils';
import ColorUtils from './color.utils';
import { ThemeTokens } from '../../styles/defaults/themes.interface';
import { ColorType } from './color.interface';
import { COLOR, StyleTokenReference, TOKEN } from '../style.interface';
import { TOKEN_KEY_SEPARATOR } from '../token.interface';
import { ThemeModeType } from '../palette.interface';
/**
* Convert Tokens [Record<string, Token>] into object
* @param value Record<string, Token> Tokens to be transformed
* @returns object
*/
export const toTokensObject = (value: Record<string, Token>): object => {
// TODO: update from any to ThemeTokens
const obj: any = {};
values(value).forEach((token) => {
if (!obj[token.type]) {
obj[token.type] = {};
}
if (!obj[token.type][token.key]) {
obj[token.type][token.key] = token.value;
}
});
return obj;
};
/**
* Convert Tokens [Record<string, Token>] into Themes Things Tokens
* @param value Record<string, Token> Tokens to be transformed
* @returns object
*/
export const toApphouseTokens = (value: Record<string, Token>): ThemeTokens => {
// TODO: update from any to ThemeTokens
const obj: any = toTokensObject(value);
return obj as ThemeTokens;
};
const toUpperCaseifHex = (value: any) => {
if (typeof value === 'string') {
if (value.startsWith('#')) {
return value.toLocaleUpperCase();
}
}
return value;
};
/**
* Find the first match for this value in the lookup table
* @param value string value to be matched
* @param lookup lookup table
* @param namespace string if namespace is provided it will help
* finding a value when there are multiple keys with the same value
* in the lookup table
* @returns
*/
export const getValueReferenceStringFromObject = (
value: string | number,
lookup: { [value: string]: string },
namespace?: string
) => {
let v = toUpperCaseifHex(value);
const hasLookupValues = !isEmpty(lookup);
if (!hasLookupValues) {
return v;
}
if (hasLookupValues) {
let found = v;
Object.keys(lookup).forEach((key) => {
if (namespace) {
if (key.includes(namespace)) {
if (toUpperCaseifHex(lookup[key]) === v) {
found = key;
return;
}
}
}
if (toUpperCaseifHex(lookup[key]) === v) {
found = key;
return;
}
});
return found;
}
};
/**
* The reference here is key colors.onBase
* @param obj object, usually referring to styles or tokens
*/
export const getReferenceTokenFromObject = (
obj: object,
path?: string
): StyleTokenReference | undefined => {
const keys = Object.keys(obj);
if (keys.length > 1) {
console.warn('only one key can be referenced at a time');
return undefined;
}
if (keys.length === 1) {
const key = keys[0];
const value = Object.values(obj)[0];
const k = path ? `${path}.${key}` : key;
const reference: StyleTokenReference | undefined = {
type: getStyleTokenReferenceType(key),
value: value,
key: k
};
return reference;
}
console.warn('no key was found in this object');
return undefined;
};
/**
* Discover is a key has the word 'color' in it
* @param key string
* @returns if the key contains 'color' it will return true, otherwise it will be false
*/
export const hasColorReference = (key: string): boolean => {
return (
`${key}`.toLocaleLowerCase().indexOf('color') >= 0 ||
`${key}`.toLocaleLowerCase().indexOf('theme') >= 0
);
};
/**
*
* @param key string
* @returns if the key contains 'color' it will return PALETTE, otherwise it will be TOKEN
*/
export const getStyleTokenReferenceType = (key: string): 'color' | 'token' => {
return hasColorReference(key) ? COLOR : TOKEN;
};
/**
* Create lookup table for value reference
* It only keeps unique objects
* @param obj
* @param root
* @param lookup
* @returns
*/
export const getLookupTable = (
obj: {
[id: string]: any;
},
lookup: { [id: string]: string } = {},
root?: string
): { [id: string]: string } => {
const lookupCopy = lookup;
if (!obj) {
return {};
}
Object.keys(obj).map((key) => {
const value = obj[key];
const id = root ? `${root}.${key}` : key;
// only create lookup values for unique values
if (!isObject(value)) {
if (!lookupCopy[id]) {
lookupCopy[id] = value;
} else {
delete lookupCopy[id];
}
} else {
return getLookupTable(value, lookupCopy, id);
}
});
return lookupCopy;
};
export const getTokenListId = (token: any) => {
const type = token?.type;
const key = token?.key;
if (type && key && typeof key === 'string' && typeof type === 'string') {
const nKey = ensureAllLowerCase(key).trim();
const ntype = type.trim();
return `${ntype}${TOKEN_KEY_SEPARATOR}${nKey}`.trim();
}
console.warn('token is not valid when trying to get token list id');
return 'unknownTokenId';
};
export const getColorTokensLookupReferenceFromPalettes = (
palette: Record<string, Palette>,
basePaletteId: string,
colorPaletteId?: string | undefined
): { [id: string]: string } => {
let baseColorsLookup;
if (palette[basePaletteId]) {
const baseColors = palette[basePaletteId].objectify.colors;
baseColorsLookup = getLookupTableFromColorType(
baseColors || [],
palette[basePaletteId].mode
);
}
if (!colorPaletteId && baseColorsLookup) {
return baseColorsLookup;
}
if (colorPaletteId) {
if (palette[colorPaletteId] && palette[colorPaletteId].mode === 'theme') {
const colors = palette[colorPaletteId].objectify.colors;
const colorsLookup = getLookupTableFromColorType(
colors || [],
palette[colorPaletteId].mode,
colorPaletteId
);
return colorsLookup;
}
}
return {};
};
export const getLookupTableFromColorType = (
colors: ColorType[],
mode: ThemeModeType,
paletteId?: string
) => {
const lookup: { [id: string]: string } = {};
colors.forEach((color) => {
if (!color) {
return;
}
const id = paletteId
? `${mode}.${paletteId}.${color.id}`
: `${mode}.${color.id}`;
const hex = color.color.hex;
if (!lookup[id]) {
lookup[id] = hex;
}
const rgbaString = ColorUtils.toRgbaStringFromRgbaObject(color.color.rgb);
if (rgbaString && !lookup[rgbaString]) {
lookup[id] = rgbaString;
}
});
return lookup;
};
export interface hashedObjectReturnType {
lookup: { [id: string]: any };
hashedObject: { [id: string]: any };
}
// Letter spacing
export const toRemsLetterSpacing = (value: string | number): string => {
return `${value}rem`;
};
export const toEmLetterSpacing = () => {
return 'em';
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
export const toPtLettersSpacing = () => {};