Skip to content

Commit

Permalink
clean up style creation (#383)
Browse files Browse the repository at this point in the history
* clean up style creation

* fix tests
  • Loading branch information
mathieudutour authored Sep 20, 2018
1 parent da83cde commit 45a34db
Show file tree
Hide file tree
Showing 17 changed files with 358 additions and 436 deletions.
2 changes: 1 addition & 1 deletion __tests__/jest/jsonUtils/shapeLayers.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('makeShapeGroup', () => {
const layers = [{ baz: 'qux' }];
const fills = ['foo', 'bar'];

const shapeGroup = makeShapeGroup(frame, layers, fills);
const shapeGroup = makeShapeGroup(frame, layers, undefined, undefined, fills);

expect(shapeGroup).toHaveProperty('frame', frame);
expect(shapeGroup).toHaveProperty('layers', layers);
Expand Down
6 changes: 3 additions & 3 deletions __tests__/jest/sharedStyles/TextStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,10 @@ describe('get', () => {
TextStyles.create({ context }, styles);

expect(TextStyles.get('foo')).toEqual(styles.foo);
expect(TextStyles.get('baz')).toEqual({});
expect(TextStyles.get('baz')).toEqual(undefined);
});

it('returns an empty object when not found', () => {
it('returns undefined when not found', () => {
const styles = {
foo: {
fontSize: 'bar',
Expand All @@ -218,7 +218,7 @@ describe('get', () => {

TextStyles.create({ context }, styles);

expect(TextStyles.get('baz')).toEqual({});
expect(TextStyles.get('baz')).toEqual(undefined);
});
});

Expand Down
8 changes: 4 additions & 4 deletions docs/styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Components use CSS styles + flexbox layout.

| property | type | supported? |
| ------------------------- | ------------------------------------------------------------------------------------------- | ---------- |
| `shadowColor` | `Color` | ⛔️ |
| `shadowColor` | `Color` | |
| `shadowOffset` | `{ width: number, height: number }` ||
| `shadowOpacity` | `number` ||
| `shadowRadius` | `number` | `percentage` ||
Expand Down Expand Up @@ -82,9 +82,9 @@ Components use CSS styles + flexbox layout.
| `fontStyle` | `normal` | `italic` ||
| `fontWeight` | `string` | `number` ||
| `textDecorationLine` | `none` | `underline` | `double` | `line-through` ||
| `textShadowOffset` | `{ width: number, height: number }` | ⛔️ |
| `textShadowRadius` | `number` | ⛔️ |
| `textShadowColor` | `Color` | ⛔️ |
| `textShadowOffset` | `{ width: number, height: number }` | |
| `textShadowRadius` | `number` | |
| `textShadowColor` | `Color` | |
| `letterSpacing` | `number` ||
| `lineHeight` | `number` ||
| `textAlign` | `auto` | `left` | `right` | `center` | `justify` ||
Expand Down
22 changes: 10 additions & 12 deletions src/components/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import { or } from 'airbnb-prop-types';
import StyleSheet from '../stylesheet';
import ResizingConstraintPropTypes from './ResizingConstraintPropTypes';
import ResizeModePropTypes from './ResizeModePropTypes';
import ImageStylePropTypes from './ImageStylePropTypes';
import { ViewPropTypes } from './View';

const ImageURISourcePropType = PropTypes.shape({
uri: PropTypes.string.isRequired,
Expand Down Expand Up @@ -34,19 +34,17 @@ const ResizeModes = {
none: 'Fill',
};

export const ImagePropTypes = {
...ViewPropTypes,
style: or([PropTypes.shape(ImageStylePropTypes), PropTypes.number]),
defaultSource: ImageSourcePropType,
resizeMode: ResizeModePropTypes,
source: ImageSourcePropType,
};

// $FlowFixMe
export default class Image extends React.Component {
static propTypes = {
name: PropTypes.string,
children: PropTypes.node,
defaultSource: ImageSourcePropType,
resizeMode: ResizeModePropTypes,
source: ImageSourcePropType,
style: or([PropTypes.shape(ImageStylePropTypes), PropTypes.number]),
resizingConstraint: PropTypes.shape({
...ResizingConstraintPropTypes,
}),
};
static propTypes = ImagePropTypes;

static defaultProps = {
name: 'Image',
Expand Down
21 changes: 7 additions & 14 deletions src/components/Text.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import * as PropTypes from 'prop-types';
import { or } from 'airbnb-prop-types';
import StyleSheet from '../stylesheet';
import TextStylePropTypes from './TextStylePropTypes';
import ViewStylePropTypes from './ViewStylePropTypes';
import ResizingConstraintPropTypes from './ResizingConstraintPropTypes';
import { ViewPropTypes } from './View';

export const TextPropTypes = {
...ViewPropTypes,
style: or([PropTypes.shape(TextStylePropTypes), PropTypes.number]),
};

/**
* @example
Expand All @@ -15,18 +19,7 @@ import ResizingConstraintPropTypes from './ResizingConstraintPropTypes';
*/
// $FlowFixMe
export default class Text extends React.Component {
static propTypes = {
// TODO(lmr): do some nice warning stuff like RN does
style: or([
PropTypes.shape({ ...ViewStylePropTypes, ...TextStylePropTypes }),
PropTypes.number,
]),
name: PropTypes.string,
resizingConstraint: PropTypes.shape({
...ResizingConstraintPropTypes,
}),
children: PropTypes.node,
};
static propTypes = TextPropTypes;

render() {
return (
Expand Down
6 changes: 5 additions & 1 deletion src/components/TextStylePropTypes.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
// @flow
export default {};
import ViewStylePropTypes from './ViewStylePropTypes';

export default {
...ViewStylePropTypes,
};
136 changes: 136 additions & 0 deletions src/jsonUtils/borders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// @flow
import { BorderPosition, FillType } from 'sketch-constants';
import type { SJShapeGroupLayer } from 'sketchapp-json-flow-types';
import { makeColorFromCSS } from './models';
import type { ViewStyle, LayoutInfo } from '../types';
import same from '../utils/same';
import { makeVerticalBorder, makeHorizontalBorder } from './shapeLayers';
import { makeBorderOptions } from './style';

const DEFAULT_BORDER_COLOR = 'transparent';
const DEFAULT_BORDER_STYLE = 'solid';

/* eslint-disable no-param-reassign */
// eslint-disable-next-line import/prefer-default-export
export const createBorders = (
content: SJShapeGroupLayer,
layout: LayoutInfo,
style?: ViewStyle,
): Array<SJShapeGroupLayer> => {
if (!style) {
return [content];
}

const {
borderTopWidth = 0,
borderRightWidth = 0,
borderBottomWidth = 0,
borderLeftWidth = 0,

borderTopColor = DEFAULT_BORDER_COLOR,
borderRightColor = DEFAULT_BORDER_COLOR,
borderBottomColor = DEFAULT_BORDER_COLOR,
borderLeftColor = DEFAULT_BORDER_COLOR,

borderTopStyle = DEFAULT_BORDER_STYLE,
borderRightStyle = DEFAULT_BORDER_STYLE,
borderBottomStyle = DEFAULT_BORDER_STYLE,
borderLeftStyle = DEFAULT_BORDER_STYLE,
} = style;

if (
same(borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidth) &&
same(borderTopColor, borderRightColor, borderBottomColor, borderLeftColor) &&
same(borderTopStyle, borderRightStyle, borderBottomStyle, borderLeftStyle)
) {
// all sides have same border width
// in this case, we can do everything with just a single shape.
if (borderTopStyle !== undefined) {
const borderOptions = makeBorderOptions(borderTopStyle, borderTopWidth);
if (borderOptions) {
content.style.borderOptions = borderOptions;
}
}

if (borderTopWidth > 0) {
content.style.borders = [
{
_class: 'border',
isEnabled: true,
color: makeColorFromCSS(borderTopColor),
fillType: FillType.Solid,
position: BorderPosition.Inside,
thickness: borderTopWidth,
},
];
}

return [content];
}

content.hasClippingMask = true;

const layers = [content];

if (borderTopWidth > 0) {
const topBorder = makeHorizontalBorder(0, 0, layout.width, borderTopWidth, borderTopColor);
topBorder.name = 'Border (top)';

const borderOptions = makeBorderOptions(borderTopStyle, borderTopWidth);
if (borderOptions) {
topBorder.style.borderOptions = borderOptions;
}

layers.push(topBorder);
}

if (borderRightWidth > 0) {
const rightBorder = makeVerticalBorder(
layout.width - borderRightWidth,
0,
layout.height,
borderRightWidth,
borderRightColor,
);
rightBorder.name = 'Border (right)';

const borderOptions = makeBorderOptions(borderRightStyle, borderRightWidth);
if (borderOptions) {
rightBorder.style.borderOptions = borderOptions;
}

layers.push(rightBorder);
}

if (borderBottomWidth > 0) {
const bottomBorder = makeHorizontalBorder(
0,
layout.height - borderBottomWidth,
layout.width,
borderBottomWidth,
borderBottomColor,
);
bottomBorder.name = 'Border (bottom)';

const borderOptions = makeBorderOptions(borderBottomStyle, borderBottomWidth);
if (borderOptions) {
bottomBorder.style.borderOptions = borderOptions;
}

layers.push(bottomBorder);
}

if (borderLeftWidth > 0) {
const leftBorder = makeVerticalBorder(0, 0, layout.height, borderLeftWidth, borderLeftColor);
leftBorder.name = 'Border (left)';

const borderOptions = makeBorderOptions(borderLeftStyle, borderLeftWidth);
if (borderOptions) {
leftBorder.style.borderOptions = borderOptions;
}

layers.push(leftBorder);
}

return layers;
};
15 changes: 6 additions & 9 deletions src/jsonUtils/hacksForJSONImpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { toSJSON } from 'sketchapp-json-plugin';
import findFont from '../utils/findFont';
import getSketchVersion from '../utils/getSketchVersion';
import type { TextNodes, TextNode, TextStyle, ResizeConstraints, LayoutInfo } from '../types';
import { generateID, makeColorFromCSS } from './models';
import { makeColorFromCSS } from './models';
import { makeStyle } from './style';

export const TEXT_ALIGN = {
auto: TextAlignment.Left,
Expand Down Expand Up @@ -296,14 +297,10 @@ export function makeTextStyle(textStyle: TextStyle) {
},
};

return {
_class: 'style',
sharedObjectID: generateID(),
miterLimit: 10,
startDecorationType: 0,
endDecorationType: 0,
textStyle: value,
};
const json = makeStyle(textStyle);
json.textStyle = value;

return json;
}

export function makeSvgLayer(layout: LayoutInfo, name: string, svg: string) {
Expand Down
66 changes: 56 additions & 10 deletions src/jsonUtils/shapeLayers.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// @flow
import type { SJFill, SJPath, SJRect, SJShapeGroupLayer } from 'sketchapp-json-flow-types';
import { BorderPosition } from 'sketch-constants';
import { makeResizeConstraint } from './hacksForJSONImpl';
import { generateID, makeRect } from './models';
import type { ResizeConstraints } from '../types';
import { generateID, makeRect, makeColorFromCSS } from './models';
import { makeStyle } from './style';
import type { Color, ResizeConstraints, ViewStyle } from '../types';

type Radii = Array<number>;

Expand Down Expand Up @@ -169,7 +171,9 @@ export const makeRectShapeLayer = (
export const makeShapeGroup = (
frame: SJRect,
layers: Array<any> = [],
fills?: Array<SJFill> = [],
style?: ViewStyle,
shadows?: Array<ViewStyle>,
fills?: Array<SJFill>,
resizingConstraint?: ResizeConstraints,
): SJShapeGroupLayer => ({
_class: 'shapeGroup',
Expand All @@ -183,16 +187,58 @@ export const makeShapeGroup = (
resizingType: 0,
rotation: 0,
shouldBreakMaskChain: false,
style: {
_class: 'style',
endDecorationType: 0,
fills,
miterLimit: 10,
startDecorationType: 0,
},
style: makeStyle(style, fills),
hasClickThrough: false,
layers,
clippingMaskMode: 0,
hasClippingMask: false,
windingRule: 1,
});

export const makeVerticalBorder = (
x: number,
y: number,
length: number,
thickness: number,
color: Color,
): SJShapeGroupLayer => {
const frame = makeRect(x, y, thickness, length);
const shapeFrame = makeRect(0, 0, thickness, length);
const shapePath = makeShapePath(shapeFrame, makeVerticalPath());
const content = makeShapeGroup(frame, [shapePath]);
content.style.borders = [
{
_class: 'border',
isEnabled: true,
color: makeColorFromCSS(color),
fillType: 0,
position: BorderPosition.Center,
thickness,
},
];
return content;
};

export const makeHorizontalBorder = (
x: number,
y: number,
length: number,
thickness: number,
color: Color,
): SJShapeGroupLayer => {
const frame = makeRect(x, y, length, thickness);
const shapeFrame = makeRect(0, 0, length, thickness);
const shapePath = makeShapePath(shapeFrame, makeHorizontalPath());
const content = makeShapeGroup(frame, [shapePath]);
content.style.borders = [
{
_class: 'border',
isEnabled: true,
color: makeColorFromCSS(color),
fillType: 0,
position: BorderPosition.Center,
thickness,
},
];
return content;
};
Loading

0 comments on commit 45a34db

Please sign in to comment.