apphouse
Version:
Component library for React that uses observable state management and theme-able components.
404 lines (351 loc) • 10.5 kB
text/typescript
import { Style } from '../Style';
import { getCssWithThemedTokens } from './theme.utils';
import { Token } from '../Token';
import { ThemeStyles } from '../../styles/defaults/themes.interface';
import { htmlTags } from '../style.interface';
/**
* Helper function to determine if a property key is a selector
* @param styleKey
* @returns
*/
export const isSelector = (property: string): boolean => {
const selectorPrefixes = [':', '&:', '>', '.', '::', '@'];
const found = selectorPrefixes.find((prefix) => property.startsWith(prefix));
return found !== undefined;
};
/**
* Force first letter uppercase on a word
* @param string string
* @returns
*/
export const makeFirstLetterUppercase = (string: string) => {
if (string.length === 0) {
return string;
}
return string[0].toUpperCase() + string.substring(1);
};
export const createStylesFromObject = (
objectStyles: any
): Record<string, Style> => {
const styles: any = {};
// use default styles
const keys = Object.keys(objectStyles);
keys.forEach((key) => {
const value = objectStyles[key];
const values = [];
if (Array.isArray(value)) {
value.forEach((entry: any) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
styles[key] = values.push(createStylesFromObject(entry));
});
} else {
styles[key] = new Style({
id: key,
value,
baseComponent: '',
variant: 'default'
});
}
});
return styles;
};
export const getStyleValue = () => {};
export const toStylesObject = ({
value,
withColorsFromPaletteId,
lookup
}: {
value: Record<string, Style>;
withColorsFromPaletteId?: string; // this is actually the paletteId ('dark' or 'light')
lookup: { [id: string]: string };
}): object => {
const obj: any = {};
Object.keys(value).forEach((key) => {
const style = value[key];
if (style.baseComponent) {
if (!obj[style.baseComponent]) {
obj[style.baseComponent] = {};
}
}
});
Object.keys(value).forEach((key) => {
const style = value[key];
if (style.baseComponent) {
obj[style.baseComponent][style.variant] = getCssWithThemedTokens({
value: style.value,
lookup,
withColorsFromPaletteId,
withRawValue: true
});
}
});
return obj;
};
export const toApphouseStyles = ({
value,
withColorsFromPaletteId,
lookup
}: {
value: Record<string, Style>;
withColorsFromPaletteId?: string;
lookup: { [id: string]: string };
}): ThemeStyles => {
const baseStyles: any = toStylesObject({
value,
withColorsFromPaletteId,
lookup
});
let styles = JSON.stringify(baseStyles);
Object.keys(lookup).forEach((key) => {
styles = styles.replaceAll(`${key}"`, `${lookup[key]}"`);
});
return JSON.parse(styles) as ThemeStyles;
};
export const objectifyStylesWithBaseColors = ({
value
}: {
value: Record<string, Style>;
theme?: 'dark' | 'light';
colors: { [colorId: string]: string };
tokens: Record<string, Token>;
}): object => {
const obj: any = {};
Object.keys(value).forEach((key) => {
const style = value[key];
if (style.baseComponent) {
if (!obj[style.baseComponent]) {
obj[style.baseComponent] = {};
}
}
});
Object.keys(value).forEach((key) => {
const style = value[key];
if (style.baseComponent) {
obj[style.baseComponent][style.variant] = {};
}
});
return obj;
};
export const camelcaseifyCssProperty = (property: string): string => {
const words = property.replaceAll('_', '-').split('-');
let camelcaseProperty = '';
words.forEach((word: string, i: number) => {
if (i === 0) {
camelcaseProperty = word;
} else {
camelcaseProperty = camelcaseProperty + makeFirstLetterUppercase(word);
}
});
return camelcaseProperty;
};
// import { CSSProperties } from "glamor";
// import { CssPropertyStyle, Style } from "./Style";
// export const getPropertyValue = (value: any) => {
// try {
// const localStyles: any = {};
// if (typeof value !== "object") {
// return value;
// }
// Object.keys(value).forEach((key) => {
// const it = value[key];
// if (!it && it !== 0) {
// return;
// }
// if (it === 0) {
// //console.warn("x342", { it, itKey: key });
// localStyles[key] = 0;
// return;
// }
// const stylePropertyValues = getPropertyValue(value[key]);
// const hasChildren = Object.keys(stylePropertyValues)?.length > 0;
// if (!hasChildren) {
// // accumulate styles
// localStyles[key] = stylePropertyValues;
// }
// if (hasChildren) {
// localStyles[key] = {};
// localStyles[key] = getPropertyValue(stylePropertyValues);
// }
// });
// return localStyles;
// } catch (error) {
// console.log({ Error: "Error getting property values", error });
// return {};
// }
// };
// export const isSelector = (styleKey: string): boolean => {
// let selector = false;
// if (styleKey.startsWith(":") || styleKey.startsWith("&:")) {
// selector = true;
// }
// return selector;
// };
// export const getPropertyValuePairs = (
// cssStyles: CSSProperties
// ): CssPropertyStyle[] => {
// let propertyStyles: CssPropertyStyle[] = [];
// Object.keys(cssStyles).forEach((key: string) => {
// const selector = isSelector(key);
// const ps: CssPropertyStyle = {
// isSelector: selector,
// property: key,
// value: selector ? cssStyles[key] : cssStyles[key],
// };
// propertyStyles = [...propertyStyles, ps];
// });
// return propertyStyles;
// };
// export const makeFirstLetterUppercase = (string: string) => {
// if (string.length === 0) {
// return string;
// }
// return string[0].toUpperCase() + string.substring(1);
// };
// export const depthOf = (object: any) => {
// let level = 1;
// for (let key in object) {
// if (!object.hasOwnProperty(key)) continue;
// if (typeof object[key] == "object") {
// const depth = depthOf(object[key]) + 1;
// level = Math.max(depth, level);
// }
// }
// return level;
// };
// const getHtmlFromId = (id: string) => {
// if (id.indexOf("button") >= 0) {
// return "button";
// }
// if (id.indexOf("heading") >= 0) {
// return "h1";
// }
// if (id.indexOf("text") >= 0) {
// return "p";
// }
// if (id.indexOf("input") >= 0) {
// return "input";
// }
// if (id.indexOf("label") >= 0) {
// return "label";
// }
// return "div";
// };
// export const getFlattenedStyles = (cssStyles: any): Record<string, Style[]> => {
// let localStyles: Record<string, Style[]> = {};
// const getStyles = (
// current: any,
// level: number,
// currentPath?: string
// ): Style[] => {
// let localStyle: Style[] = [];
// const keys = Object.keys(current);
// keys?.forEach((key) => {
// const currentStyles = current[key];
// const variant = level === 0 ? "default" : key;
// const localDepth = depthOf(localStyle);
// let id = key;
// if (currentPath && level !== 0) {
// id = `${currentPath}${makeFirstLetterUppercase(key)}`;
// }
// const childDepth = depthOf(currentStyles);
// if (childDepth > 1) {
// const s = getPropertyValuePairs(currentStyles);
// const hasSelectors = s.find((item) => item.isSelector);
// if (!hasSelectors) {
// return mergeStyles(currentStyles, level + 1, key);
// }
// }
// if (localDepth === 1) {
// const style = new Style(
// id,
// getPropertyValuePairs(currentStyles),
// currentPath || key,
// variant,
// undefined,
// undefined,
// getHtmlFromId(id)
// );
// if (localStyles[id]) {
// localStyles[id] = [...localStyles[id], style];
// } else {
// localStyles[id] = [style];
// }
// }
// });
// return localStyle;
// };
// mergeStyles(cssStyles, 0);
// return localStyles;
// };
// const cssStyles = toJS(styles);
// export const defaultsStylesRaw = getPropertyValue(cssStyles);
// export const defaultsStyles = getFlattenedStyles(defaultsStylesRaw);
/**
* Attempt to get a preview tag based on an item from the base component list
* @param baseComponent string from baseComponent list
* @returns an html tag (it will default to div if no match is found)
*/
export const getPreviewWithTagFromBaseComponent = (baseComponent: string) => {
let previewWithTag = htmlTags.includes(baseComponent)
? baseComponent
: undefined;
if (!previewWithTag) {
if (baseComponent === 'button') {
previewWithTag = 'button';
} else if (baseComponent === 'list') {
previewWithTag = 'ul';
} else if (baseComponent === 'link') {
previewWithTag = 'a';
} else if (baseComponent === 'typography') {
previewWithTag = 'p';
} else if (baseComponent === 'custom') {
previewWithTag = 'SearchInput';
} else {
previewWithTag = 'div';
}
}
return previewWithTag;
};
/**
* Attempt to get a preview tag based on the property keys on a object
* @param cssObj object representing the styles
* @returns an html tag (it will default to div if no match is found)
*/
export const getPreviewWithTagBasedOnObjectProperties = (
obj: {
[id: string]: any;
},
k?: string
): string => {
if (k && k.toLocaleLowerCase().includes('button')) {
return 'button';
}
let previewWithTag = 'div';
Object.keys(obj).forEach((property) => {
const value = obj[property];
if (typeof value === 'string') {
const key = property.toLocaleLowerCase();
if (
key.indexOf('font') >= 0 ||
key.indexOf('typography') >= 0 ||
key.indexOf('text') >= 0
) {
previewWithTag = 'p';
}
if (key.indexOf('input') >= 0) {
previewWithTag = 'input';
}
if (key.indexOf('button') >= 0) {
previewWithTag = 'button';
}
}
if (typeof value === 'object') {
// if we have not found a matching at the upper object keys, let's look at the children
if (previewWithTag === 'div') {
previewWithTag = getPreviewWithTagBasedOnObjectProperties(value);
}
}
});
return previewWithTag;
};