Skip to content

Commit

Permalink
Initial things
Browse files Browse the repository at this point in the history
  • Loading branch information
JoelBesada committed Sep 6, 2019
0 parents commit 0499b2c
Show file tree
Hide file tree
Showing 23 changed files with 6,794 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
**/node_modules/*
*.d.ts
30 changes: 30 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"extends": [
"plugin:shopify/typescript",
"plugin:shopify/react",
"plugin:shopify/jest",
"plugin:shopify/prettier"
],
"parserOptions": {
"project": "tsconfig.json"
},
"rules": {
"import/extensions": "off",
"jsx-a11y/control-has-associated-label": "off",
"node/no-extraneous-require": "off",
"import/no-cycle": "off",
"jest/require-tothrow-message": "off",
"callback-return": "off",
"jest/no-if": "off",
"import/named": "off",
"func-style": "off",
"react/display-name": "off",
"shopify/restrict-full-import": ["error", "lodash"],
"shopify/jest/no-vague-titles": [
"error",
{
"allow": ["all"]
}
]
}
}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.DS_Store

node_modules
npm-debug.log

dist
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"arrowParens": "avoid",
"singleQuote": true,
"bracketSpacing": false,
"trailingComma": "all"
}
29 changes: 29 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"javascript.validate.enable": false,
"css.validate": false,
"scss.validate": false,
"typescript.tsdk": "./node_modules/typescript/lib",
"flow.enabled": false,
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/node_modules": true,
"**/.DS_Store": true,
"build": true,
".dev": true
},
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"build": true,
"**/*.js": true
},
"editor.formatOnSave": true,
"prettier.stylelintIntegration": true,
"prettier.eslintIntegration": true,
"eslint.validate": ["typescript", "typescriptreact"],
"javascript.implicitProjectConfig.experimentalDecorators": true
}
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- `@shopify/style-system` package
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `@shopify/style-system`

A system for building constraint-based UI components

## Installation

```bash
$ yarn add @shopify/style-system
```

## Usage
37 changes: 37 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@shopify/style-system",
"version": "0.0.1",
"license": "MIT",
"description": "A system for building constraint-based UI components",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"sideEffects": false,
"scripts": {
"build": "tsc -p tsconfig.json",
"lint": "yarn eslint '**/*.{ts,tsx}' && yarn tsc -p tsconfig.json --noEmit",
"lint-fix": "yarn eslint --fix '**/*.{ts,tsx}'"
},
"publishConfig": {
"access": "public",
"@shopify:registry": "https://registry.npmjs.org"
},
"author": "Shopify Inc.",
"dependencies": {},
"devDependencies": {
"@types/react": "^16.9.2",
"@types/react-native": "^0.60.10",
"eslint": "^5.16.0",
"eslint-plugin-shopify": "^30.0.1",
"prettier": "^1.18.2",
"react": "^16.9.0",
"react-native": "0.60.5",
"typescript": "~3.2.1"
},
"peerDependencies": {
"react": "^16.8.0",
"react-native": "^0.59.0"
},
"files": [
"dist/*"
]
}
44 changes: 44 additions & 0 deletions src/composeStyleFunctions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {StyleFunctionContainer, BaseTheme, Dimensions} from './types';

const composeStyleFunctions = (
styleFunctions: (StyleFunctionContainer | StyleFunctionContainer[])[],
) => {
const flattenedStyleFunctions = styleFunctions.reduce(
(acc: StyleFunctionContainer[], item) => {
return acc.concat(item);
},
[],
) as StyleFunctionContainer[];

const properties = flattenedStyleFunctions.map(styleFunc => {
return styleFunc.property;
});
const funcs = flattenedStyleFunctions
.sort(
(styleFuncA, styleFuncB) =>
Number(styleFuncB.variant) - Number(styleFuncA.variant),
)
.map(styleFunc => {
return styleFunc.func;
});

const buildStyle = ({
props,
theme,
dimensions,
}: {
props: {[key: string]: any};
theme: BaseTheme;
dimensions: Dimensions;
}) => {
return funcs.reduce((acc, func) => {
return {...acc, ...func(props, {theme, dimensions})};
}, {});
};
return {
buildStyle,
properties,
};
};

export default composeStyleFunctions;
16 changes: 16 additions & 0 deletions src/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import {BaseTheme} from 'types';

export const ThemeContext = React.createContext({
colors: {},
spacing: {},
breakpoints: {},
});

export const ThemeProvider = ({
theme,
children,
}: {
theme: BaseTheme;
children: React.ReactNode;
}) => <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
42 changes: 42 additions & 0 deletions src/createBox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import createStyleSystemComponent from './createStyleSystemComponent';
import {BaseTheme} from './types';
import {
backgroundColor,
opacity,
layout,
spacing,
border,
position,
BackgroundColorProps,
OpacityProps,
LayoutProps,
SpacingProps,
BorderProps,
PositionProps,
visible,
VisibleProps,
} from './styleFunctions';

export type BoxProps<Theme extends BaseTheme> = BackgroundColorProps<Theme> &
OpacityProps<Theme> &
VisibleProps<Theme> &
LayoutProps<Theme> &
SpacingProps<Theme> &
BorderProps<Theme> &
PositionProps<Theme>;

export const boxStyleFunctions = [
backgroundColor,
opacity,
visible,
layout,
spacing,
border,
position,
];

const createBox = <Theme extends BaseTheme>() => {
return createStyleSystemComponent<BoxProps<Theme>>(boxStyleFunctions);
};

export default createBox;
102 changes: 102 additions & 0 deletions src/createStyleFunction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
ResponsiveValue,
BaseTheme,
Dimensions,
StyleFunctionContainer,
} from './types';

type StyleTransformFunction = (params: {
value: any;
theme: BaseTheme;
themeKey?: string;
}) => any;

const getValueForScreenSize = ({
responsiveValue,
breakpoints,
dimensions,
}: {
responsiveValue: {[key in keyof BaseTheme['breakpoints']]: any};
breakpoints: BaseTheme['breakpoints'];
dimensions: Dimensions;
}) => {
const sortedBreakpoints = Object.entries(breakpoints).sort((valA, valB) => {
return valA[1] - valB[1];
});
const {width} = dimensions;
return sortedBreakpoints.reduce((acc, [breakpoint, minWidth]) => {
if (width >= minWidth && responsiveValue[breakpoint] !== undefined)
return responsiveValue[breakpoint];
return acc;
}, null);
};

const isResponsiveObjectValue = <Theme extends BaseTheme>(
val: ResponsiveValue<any, Theme>,
theme: Theme,
): val is {[key: string]: any} => {
if (typeof val !== 'object') return false;
return Object.keys(val).reduce((acc: boolean, key) => {
return acc && theme.breakpoints[key] !== undefined;
}, true);
};

const getValue = <Theme extends BaseTheme>(
propValue: ResponsiveValue<string | number, Theme>,
{
theme,
transform,
dimensions,
themeKey,
}: {
theme: Theme;
transform?: StyleTransformFunction;
dimensions: Dimensions;
themeKey?: string;
},
) => {
const val = isResponsiveObjectValue(propValue, theme)
? getValueForScreenSize({
responsiveValue: propValue,
breakpoints: theme.breakpoints,
dimensions,
})
: propValue;
if (transform) return transform({value: val, theme, themeKey});
return themeKey && theme[themeKey] && val ? theme[themeKey][val] : val;
};

const createStyleFunction = ({
property,
transform,
styleProperty = property,
themeKey,
}: {
property: string;
transform?: StyleTransformFunction;
styleProperty?: string;
themeKey?: string;
}): StyleFunctionContainer => {
return {
property,
themeKey,
variant: false,
func: (
props: any,
{theme, dimensions}: {theme: BaseTheme; dimensions: Dimensions},
): {[key: string]: any} => {
const value = getValue(props[property], {
theme,
dimensions,
themeKey,
transform,
});
if (value === undefined) return {};
return {
[styleProperty]: value,
};
},
};
};

export default createStyleFunction;
31 changes: 31 additions & 0 deletions src/createStyleSystemComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import {View, ViewProps} from 'react-native';
import {StyleFunctionContainer} from './types';
import useStyleSystem from './hooks/useStyleSystem';

const createStyleSystemComponent = <
Props extends {},
BaseComponentProps = ViewProps
>(
styleFunctions: (StyleFunctionContainer | StyleFunctionContainer[])[],
baseComponent: React.ComponentType<BaseComponentProps> = View as any,
) => {
const StyleSystemComponent = <
OverrideComponentProps extends BaseComponentProps
>({
component = baseComponent as any,
...rest
}: {
component?: React.ComponentType<OverrideComponentProps>;
children?: React.ReactNode;
style?: any;
} & OverrideComponentProps &
Props) => {
const Component = component;
const props = useStyleSystem(styleFunctions, rest);
return <Component {...(props as OverrideComponentProps)} />;
};
return StyleSystemComponent;
};

export default createStyleSystemComponent;
Loading

0 comments on commit 0499b2c

Please sign in to comment.