apphouse
Version:
Component library for React that uses observable state management and theme-able components.
183 lines (158 loc) • 4.52 kB
text/typescript
import { uuidv4 } from '@firebase/util';
import { makeAutoObservable } from 'mobx';
import { getColorFromColorString } from './utils/color.utils';
import { Color } from './Color';
import colorSort from 'color-sorter';
import { ColorType } from './utils/color.interface';
import {
Colors,
DEFAULT_THEME_MODE,
PaletteType,
ThemeModeType
} from './palette.interface';
export class Palette {
title: string;
id: string;
description?: string;
mode: ThemeModeType;
colors: Colors;
sortColorsBy: 'key' | 'color';
/**
* The theme id for which this palette is for
*/
themeId: string;
/**
* When palette is copied or imported from another theme it will have a
* reference to the original theme id
*/
referenceThemeId: string;
constructor(palette?: PaletteType) {
this.title = palette?.title || 'Untitled Palette';
this.id = palette?.id || Palette.autoGenId(this.title);
this.description = palette?.description;
this.mode = palette?.mode || DEFAULT_THEME_MODE;
this.sortColorsBy = 'key';
this.colors = Palette.colorify(palette?.colors);
this.referenceThemeId = palette?.referenceThemeId || '';
this.themeId = palette?.themeId || '';
makeAutoObservable(this);
}
get objectify(): PaletteType {
const colors: ColorType[] = [];
Object.keys(this.colors).forEach((key) => {
colors.push(this.colors[key].objectify);
});
return {
title: this.title || 'Untitled Palette',
id: this.id,
description: this.description || '',
mode: this.mode,
colors: colors,
themeId: this.themeId || '',
referenceThemeId: this.referenceThemeId || ''
};
}
static autoGenId = (title: string) => {
if (!title) {
return uuidv4();
}
return title?.split(' ').join('-').toLocaleLowerCase();
};
get sortedColors() {
const colors = this.colorsList;
const colorsBykey: Colors = {};
colors.forEach((color) => {
if (color.rgbString) {
colorsBykey[color.rgbString] = color;
} else {
console.warn('Attempted to sort colors that do not exist');
}
});
const rgbaColors = Object.keys(colorsBykey);
const sortedColorKeys: string[] = colorSort(rgbaColors);
const sortedColors: Color[] = [];
sortedColorKeys.forEach((colorKey) => {
sortedColors.push(colorsBykey[colorKey]);
});
return sortedColors;
}
setTitle = (value: string) => {
this.title = value;
this.setId(Palette.autoGenId(value));
};
setId = (id: string) => {
this.id = id;
};
addColors = (colors: ColorType[]) => {
colors.forEach((color) => {
this.colors[color.id] = new Color(color);
});
};
setColors = (colors: Record<string, ColorType>) => {
Object.keys(colors).forEach((key) => {
const color = colors[key];
if (typeof color === 'string') {
// user may be attempting to add a raw color
// let's attempt to normalize it
const nColor = getColorFromColorString(color, key);
if (nColor) {
this.colors[key] = nColor;
}
} else {
this.colors[key] = new Color(color);
}
});
};
appendColors = (colors: Colors) => {
Object.keys(colors).forEach((key) => {
let color = colors[key];
this.setColor(color);
});
};
setColor = (color: Color) => {
this.colors[color.id] = color;
};
setMode = (mode: ThemeModeType) => {
this.mode = mode;
};
setDescription = (value: string) => {
this.description = value;
};
setReferenceThemeId = (id: string) => {
this.referenceThemeId = id;
};
get colorsList() {
return Object.keys(this.colors).map((key) => this.colors[key]);
}
addColor = (key: string, value: ColorType) => {
const color = new Color(value);
this.colors[key] = color;
};
removeColor = (key: string) => {
if (this.colors[key]) {
delete this.colors[key];
}
};
deleteAllColors = () => {
this.colors = {};
};
getColorFromKey = (key: string): string | undefined => {
const color = this.colors[key];
return color?.rgbString;
};
/**
* ColorType[] --> Record<string, Color>
* @param colors colors list
* @returns the colors list in Color
*/
static colorify = (colors?: ColorType[]): Colors => {
if (!colors) {
return {};
}
const _colors: Colors = {};
colors.forEach((color: ColorType) => {
_colors[color.id] = new Color(color);
});
return _colors;
};
}