Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/156 #288

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ const Comps = () => {
</Box>
</Container>
</Section>
<Box style={{ width: '100', height: 'm', animation: 'loading' }} />
<Section>
<Container>
<Typography variant="h2">Grid...</Typography>
Expand Down
11 changes: 10 additions & 1 deletion packages/cli/src/constants/maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ type SliceToBeParsed = {
/**
* The values of these slices can't be represented as css variables
*/
export const SLICES_TO_BE_EXCLUDED: ThemeKey[] = ['components', 'mediaQueries'];
export const SLICES_TO_BE_EXCLUDED: ThemeKey[] = [
'keyframes',
'components',
'mediaQueries',
];

/**
* The value of those theme slices has to be parsed before to become css variables
Expand All @@ -30,4 +34,9 @@ export const SLICES_TO_BE_PARSED: SliceToBeParsed[] = [
styleProp: 'border',
property: 'border',
},
{
name: 'animations',
styleProp: 'animation',
property: 'animation',
},
];
3 changes: 3 additions & 0 deletions packages/cli/src/constants/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import { ThemeKey } from '@morfeo/web';
export const THEME_KEYS: ThemeKey[] = [
'sizes',
'radii',
'fonts',
'colors',
'borders',
'shadows',
'zIndices',
'spacings',
'opacities',
'gradients',
'fontSizes',
'animations',
'transitions',
'breakpoints',
'lineHeights',
Expand Down
8 changes: 6 additions & 2 deletions packages/cli/src/utils/buildTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { safeWrite } from './safeWrite';
import { parseSlice } from './parseSlice';
import { BuildConfig } from '../types';
import { getClassesCSS } from './getClassesCSS';
import { getKeyFrames } from './getKeyFrames';

function getStylePaths(buildPath: string, themeName: string) {
const themeNameInParamCase = paramCase(themeName);
Expand Down Expand Up @@ -80,13 +81,16 @@ export function buildTheme({ name, buildPath }: BuildConfig) {
name,
);

safeWrite(variablesPath, wrapWithScope(name, cssText));

let variablesCss = wrapWithScope(name, cssText);
/**
* Setting the new theme where values of slices are css variables
*/
theme.set(newTheme);

variablesCss += `\n${getKeyFrames()}\n`;

safeWrite(variablesPath, variablesCss);

/**
* getComponentsCSS will return a css class for each component and for each variant
*/
Expand Down
10 changes: 3 additions & 7 deletions packages/cli/src/utils/getClassesCSS.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Theme,
theme,
Style,
ThemeKey,
Expand All @@ -9,7 +8,7 @@ import {
} from '@morfeo/web';
import { paramCase } from 'change-case';

function getPropertiesOfSlice<Key extends ThemeKey>(sliceName: Key) {
function getPropertiesOfSlice(sliceName: string) {
const keys = Object.keys(allProperties) as Property[];
return keys.filter(key => allProperties[key] === sliceName);
}
Expand All @@ -25,10 +24,7 @@ function generateCss(className: string, style: Style) {
return sheet.toString();
}

function getClasses<Key extends ThemeKey>(
sliceName: Key,
value: keyof Theme[Key],
) {
function getClasses(sliceName: string, value: string) {
const properties = getPropertiesOfSlice(sliceName);

const mapped = properties.map(property => {
Expand All @@ -43,7 +39,7 @@ function getClasses<Key extends ThemeKey>(

export function getSliceClasses<Key extends ThemeKey>(sliceName: Key) {
const slice = theme.getSlice(sliceName);
const aliases = Object.keys(slice) as (keyof Theme[Key])[];
const aliases = Object.keys(slice);

const css = aliases.reduce((acc, curr) => {
const classes = getClasses(sliceName, curr);
Expand Down
22 changes: 22 additions & 0 deletions packages/cli/src/utils/getKeyFrames.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getStyleSheet, theme } from '@morfeo/web';

function getKeyFramesStyle() {
const currentTheme = theme.get();
if (!currentTheme || !currentTheme.keyframes) {
return {};
}

return Object.keys(currentTheme.keyframes).reduce(
(acc, curr) => ({
...acc,
[`@keyframes ${curr}`]: currentTheme.keyframes[curr],
}),
{},
);
}

export function getKeyFrames() {
const style = getKeyFramesStyle();
const stylesheet = getStyleSheet({ '@global': style });
return stylesheet.toString();
}
26 changes: 9 additions & 17 deletions packages/cli/src/utils/parseSlice.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { Theme, theme, parsers, ThemeKey } from '@morfeo/web';
import { theme, parsers, ThemeKey } from '@morfeo/web';
import { paramCase } from 'change-case';
import { SLICES_TO_BE_PARSED } from '../constants';

function getVariableName<Key extends ThemeKey>(
sliceName: Key,
value: keyof Theme[Key],
) {
return `--${paramCase(`${sliceName}-${String(value)}`)}`;
function getVariableName(sliceName: string, value: string) {
return `--${paramCase(`${sliceName}-${value}`)}`;
}

export function getCssValue<Key extends ThemeKey>(
sliceName: Key,
attribute: keyof Theme[Key],
) {
export function getCssValue(sliceName: string, attribute: string) {
const toBeParsed = SLICES_TO_BE_PARSED.find(
config => config.name === sliceName,
);
Expand All @@ -23,27 +17,25 @@ export function getCssValue<Key extends ThemeKey>(

return style[toBeParsed.property];
}

// @ts-expect-error
return theme.getValue(sliceName, attribute);
}

function getValue<Key extends ThemeKey>(
sliceName: Key,
attribute: keyof Theme[Key],
) {
function getValue(sliceName: string, attribute: string) {
const variableName = getVariableName(sliceName, attribute);

const toBeParsed = SLICES_TO_BE_PARSED.find(
config => config.name === sliceName,
);
return toBeParsed
? theme.getValue(sliceName, attribute)
? // @ts-expect-error
theme.getValue(sliceName, attribute)
: `var(${variableName})`;
}

export function parseSlice<Key extends ThemeKey>(sliceName: Key) {
const slice = theme.getSlice(sliceName);
const aliases = Object.keys(slice) as (keyof Theme[Key])[];
const aliases = Object.keys(slice);
let css: string[] = [];
let object = {};

Expand Down
11 changes: 11 additions & 0 deletions packages/cli/tests/config/theme.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default {
colors: {
primary: 'black',
secondary: 'white',
},
fonts: {
regular: 'Roboto',
Expand Down Expand Up @@ -94,6 +95,16 @@ export default {
elevation: 4,
},
},
keyframes: {
default: {
from: {
bg: 'primary',
},
to: {
bg: 'secondary',
},
},
},
breakpoints: {
xs: '300px',
sm: '600px',
Expand Down
5 changes: 1 addition & 4 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": false,
"outDir": "build",
"rootDir": "src",
"module": "commonjs",
},
"include": [
"src/**/*"
]
"include": ["src/**/*"]
}
38 changes: 38 additions & 0 deletions packages/jss/src/getKeyframesStyle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { morfeo, Style, ThemeName } from '@morfeo/core';

function getGetKeyFramesStyle() {
const cache = new Map<ThemeName, Style>();

return function getKeyFramesStyle(
newTheme: any,
): Record<string, Style> | undefined {
const { keyframes } = newTheme;
if (!keyframes) {
return undefined;
}
const keyframesKeys = Object.keys(keyframes);

if (keyframesKeys.length === 0) {
return undefined;
}

const currentTheme = morfeo.getCurrentTheme();
if (cache.has(currentTheme)) {
return cache.get(currentTheme);
}

const keyframesStyle = Object.keys(keyframes).reduce(
(acc, key) => ({
...acc,
[`@keyframes ${key}`]: keyframes[key],
}),
{},
);

cache.set(currentTheme, keyframesStyle);

return keyframesStyle;
};
}

export const getKeyFramesStyle = getGetKeyFramesStyle();
17 changes: 17 additions & 0 deletions packages/jss/src/getStyleElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function createGetStyleElement() {
let styleElement: HTMLStyleElement;

function getStyleElement() {
if (!styleElement && globalThis.document) {
styleElement = document.createElement('style');
styleElement.setAttribute('data-global', 'morfeo');
document.head.appendChild(styleElement);
}

return styleElement;
}

return getStyleElement;
}

export const getStyleElement = createGetStyleElement();
2 changes: 2 additions & 0 deletions packages/jss/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import './keyframes';

export * from './getStyles';
export * from './morfeoJSS';
export { default } from './initJSS';
15 changes: 15 additions & 0 deletions packages/jss/src/keyframes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { theme } from '@morfeo/core';
import { getKeyFramesStyle } from './getKeyframesStyle';
import { getStyleElement } from './getStyleElement';
import { getStyleSheet } from './getStyles';

function updateKeyframesOnThemeChanges(newTheme: any) {
const style = getKeyFramesStyle(newTheme);
const styleElement = getStyleElement();
if (style && styleElement) {
const stylesheet = getStyleSheet({ '@global': style });
const globalCSS = stylesheet.toString();
styleElement.innerHTML = globalCSS;
}
}
theme.subscribe(updateKeyframesOnThemeChanges);
90 changes: 90 additions & 0 deletions packages/jss/tests/keyframes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* @jest-environment jsdom
*/
import { Theme, theme } from '@morfeo/core';
import { getStyleSheet } from '../src';
import { getKeyFramesStyle } from '../src/getKeyframesStyle';

const THEME: Theme = {
colors: {
primary: '#e3e3e3',
secondary: '#000',
},
keyframes: {
default: {
from: {
bg: 'primary',
},
to: {
bg: 'secondary',
},
},
},
animations: {
default: {
name: 'default',
},
},
} as any;

describe('keyframes', () => {
beforeEach(() => {
theme.set(THEME);
});

test('should generate the style element', () => {
getStyleSheet({
className: {
// @ts-expect-error
animation: 'default',
},
});

const styleElement = document.querySelector('style[data-global="morfeo"]');

expect(styleElement).toBeDefined();
});

test('the style element should contain the keyframes', () => {
getStyleSheet({
className: {
// @ts-expect-error
animation: 'default',
},
});
const styleElement = document.querySelector('style[data-global="morfeo"]');

expect(styleElement?.innerHTML).toContain(`@keyframes default {
from {
background-color: #e3e3e3;
}
to {
background-color: #000;
}
}`);
});
});

describe('getKeyframesStyle', () => {
test('should return undefined in case no keyframes exists', () => {
theme.reset();
// @ts-expect-error
theme.set({ keyframes: {} });

expect(getKeyFramesStyle(theme.get())).not.toBeDefined();
});

test('should return return the cached value', () => {
theme.set(THEME);
const firstValue = getKeyFramesStyle(theme.get());
const secondValue = getKeyFramesStyle(theme.get());
/**
* Checking if the references of the objects are the same
* so we are sure that is the cached value and not
* an object with the same shape
*/
expect(firstValue).toBe(secondValue);
expect(typeof firstValue).toBe('object');
expect(typeof secondValue).toBe('object');
});
});
Loading