convert to emotion
Summary: My benchmarks have shown react-emotion to be faster than the current implementation of `styled`. For this reason, I am converting all styling to [emotion](https://emotion.sh). Benchmark results: {F136839093} The syntax is very similar between the two libraries. The main difference is that emotion only allows a single function for the whole style attribute, whereas the old implementation had functions for every style-attirbute. Before: ``` { color: props => props.color, fontSize: props => props.size, } ``` After: ``` props => ({ color: props.color, fontSize: props.size, }) ``` Reviewed By: jknoxville Differential Revision: D9479893 fbshipit-source-id: 2c39e4618f7e52ceacb67bbec8ae26114025723f
This commit is contained in:
committed by
Facebook Github Bot
parent
4151c73409
commit
726966fdc0
60
flow-typed/npm/react-emotion_vx.x.x.js
vendored
Normal file
60
flow-typed/npm/react-emotion_vx.x.x.js
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// flow-typed signature: 69f0585b3bbc433c0c0eb3d242636c6d
|
||||
// flow-typed version: <<STUB>>/react-emotion_v9.2.6/flow_v0.76.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'react-emotion'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'react-emotion' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module 'react-emotion/dist/emotion.umd.min' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-emotion/dist/index.cjs' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-emotion/dist/index.esm' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-emotion/macro' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-emotion/src/index' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module 'react-emotion/dist/emotion.umd.min.js' {
|
||||
declare module.exports: $Exports<'react-emotion/dist/emotion.umd.min'>;
|
||||
}
|
||||
declare module 'react-emotion/dist/index.cjs.js' {
|
||||
declare module.exports: $Exports<'react-emotion/dist/index.cjs'>;
|
||||
}
|
||||
declare module 'react-emotion/dist/index.esm.js' {
|
||||
declare module.exports: $Exports<'react-emotion/dist/index.esm'>;
|
||||
}
|
||||
declare module 'react-emotion/macro.js' {
|
||||
declare module.exports: $Exports<'react-emotion/macro'>;
|
||||
}
|
||||
declare module 'react-emotion/src/index.js' {
|
||||
declare module.exports: $Exports<'react-emotion/src/index'>;
|
||||
}
|
||||
@@ -10,7 +10,14 @@ import type Client from './Client.js';
|
||||
import type BaseDevice from './devices/BaseDevice.js';
|
||||
|
||||
import {SonarDevicePlugin} from './plugin.js';
|
||||
import {ErrorBoundary, Component, FlexColumn, FlexRow, colors} from 'sonar';
|
||||
import {
|
||||
ErrorBoundary,
|
||||
Component,
|
||||
FlexColumn,
|
||||
FlexRow,
|
||||
colors,
|
||||
styled,
|
||||
} from 'sonar';
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {setPluginState} from './reducers/pluginStates.js';
|
||||
@@ -18,14 +25,14 @@ import {devicePlugins} from './device-plugins/index.js';
|
||||
import plugins from './plugins/index.js';
|
||||
import {activateMenuItems} from './MenuBar.js';
|
||||
|
||||
const Container = FlexColumn.extends({
|
||||
const Container = styled(FlexColumn)({
|
||||
width: 0,
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
backgroundColor: colors.white,
|
||||
});
|
||||
|
||||
const SidebarContainer = FlexRow.extends({
|
||||
const SidebarContainer = styled(FlexRow)({
|
||||
backgroundColor: colors.light02,
|
||||
height: '100%',
|
||||
overflow: 'scroll',
|
||||
|
||||
@@ -5,19 +5,27 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {FlexRow, Text, colors, LoadingIndicator, Glyph, Component} from 'sonar';
|
||||
import {
|
||||
FlexRow,
|
||||
Text,
|
||||
colors,
|
||||
LoadingIndicator,
|
||||
Glyph,
|
||||
Component,
|
||||
styled,
|
||||
} from 'sonar';
|
||||
import {remote} from 'electron';
|
||||
import isProduction from '../utils/isProduction.js';
|
||||
import config from '../fb-stubs/config.js';
|
||||
const version = remote.app.getVersion();
|
||||
|
||||
const VersionText = Text.extends({
|
||||
const VersionText = styled(Text)({
|
||||
color: colors.light50,
|
||||
marginLeft: 4,
|
||||
marginTop: 2,
|
||||
});
|
||||
|
||||
const Container = FlexRow.extends({
|
||||
const Container = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
styled,
|
||||
} from 'sonar';
|
||||
|
||||
const Container = FlexColumn.extends({
|
||||
const Container = styled(FlexColumn)({
|
||||
padding: 10,
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ const textareaStyle = {
|
||||
marginBottom: 10,
|
||||
};
|
||||
|
||||
const DialogContainer = styled.view({
|
||||
const DialogContainer = styled('div')({
|
||||
width: 400,
|
||||
height: 300,
|
||||
position: 'absolute',
|
||||
@@ -45,25 +45,25 @@ const DialogContainer = styled.view({
|
||||
boxShadow: '0 1px 10px rgba(0, 0, 0, 0.1)',
|
||||
});
|
||||
|
||||
const TitleInput = Input.extends({
|
||||
const TitleInput = styled(Input)({
|
||||
...textareaStyle,
|
||||
height: 30,
|
||||
});
|
||||
|
||||
const DescriptionTextarea = Textarea.extends({
|
||||
const DescriptionTextarea = styled(Textarea)({
|
||||
...textareaStyle,
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
const SubmitButtonContainer = styled.view({
|
||||
const SubmitButtonContainer = styled('div')({
|
||||
marginLeft: 'auto',
|
||||
});
|
||||
|
||||
const Footer = FlexRow.extends({
|
||||
const Footer = styled(FlexRow)({
|
||||
lineHeight: '24px',
|
||||
});
|
||||
|
||||
const CloseDoneButton = Button.extends({
|
||||
const CloseDoneButton = styled(Button)({
|
||||
width: 50,
|
||||
margin: '10px auto',
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
colors,
|
||||
} from 'sonar';
|
||||
|
||||
const Heading = Text.extends({
|
||||
const Heading = styled(Text)({
|
||||
display: 'block',
|
||||
backgroundColor: colors.white,
|
||||
color: colors.light30,
|
||||
@@ -26,7 +26,7 @@ const Heading = Text.extends({
|
||||
padding: '4px 8px 0',
|
||||
});
|
||||
|
||||
const PopoverItem = FlexRow.extends({
|
||||
const PopoverItem = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
borderBottom: `1px solid ${colors.light05}`,
|
||||
height: 50,
|
||||
@@ -35,7 +35,7 @@ const PopoverItem = FlexRow.extends({
|
||||
},
|
||||
});
|
||||
|
||||
const ItemTitle = Text.extends({
|
||||
const ItemTitle = styled(Text)({
|
||||
display: 'block',
|
||||
fontSize: 14,
|
||||
fontWeight: 400,
|
||||
@@ -46,7 +46,7 @@ const ItemTitle = Text.extends({
|
||||
marginBottom: 1,
|
||||
});
|
||||
|
||||
const ItemSubtitle = Text.extends({
|
||||
const ItemSubtitle = styled(Text)({
|
||||
display: 'block',
|
||||
fontWeight: 400,
|
||||
fontSize: 11,
|
||||
@@ -57,20 +57,20 @@ const ItemSubtitle = Text.extends({
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
|
||||
const ItemImage = FlexBox.extends({
|
||||
const ItemImage = styled(FlexBox)({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 40,
|
||||
flexShrink: 0,
|
||||
});
|
||||
|
||||
const ItemContent = styled.view({
|
||||
const ItemContent = styled('div')({
|
||||
minWidth: 0,
|
||||
paddingRight: 5,
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
const Section = styled.view({
|
||||
const Section = styled('div')({
|
||||
maxWidth: 260,
|
||||
borderBottom: `1px solid ${colors.light05}`,
|
||||
'&:last-child': {
|
||||
@@ -78,7 +78,7 @@ const Section = styled.view({
|
||||
},
|
||||
});
|
||||
|
||||
const Action = Button.extends({
|
||||
const Action = styled(Button)({
|
||||
border: `1px solid ${colors.macOSTitleBarButtonBorder}`,
|
||||
background: 'transparent',
|
||||
color: colors.macOSTitleBarIconSelected,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import {styled, colors} from 'sonar';
|
||||
|
||||
const ErrorBarContainer = styled.view({
|
||||
const ErrorBarContainer = styled('div')({
|
||||
backgroundColor: colors.cherry,
|
||||
bottom: 0,
|
||||
color: '#fff',
|
||||
|
||||
@@ -17,11 +17,11 @@ import {
|
||||
Component,
|
||||
Sidebar,
|
||||
FlexBox,
|
||||
ClickableListItem,
|
||||
colors,
|
||||
brandColors,
|
||||
Text,
|
||||
Glyph,
|
||||
styled,
|
||||
} from 'sonar';
|
||||
import React from 'react';
|
||||
import {devicePlugins} from '../device-plugins/index.js';
|
||||
@@ -29,15 +29,22 @@ import plugins from '../plugins/index.js';
|
||||
import {selectPlugin} from '../reducers/connections.js';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
const CustomClickableListItem = ClickableListItem.extends({
|
||||
const ListItem = styled('div')(({active}) => ({
|
||||
paddingLeft: 10,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: 2,
|
||||
flexShrink: 0,
|
||||
});
|
||||
backgroundColor: active ? colors.macOSTitleBarIconSelected : 'none',
|
||||
color: active ? colors.white : colors.macOSSidebarSectionItem,
|
||||
lineHeight: '25px',
|
||||
padding: '0 10px',
|
||||
'&[disabled]': {
|
||||
color: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
}));
|
||||
|
||||
const SidebarHeader = FlexBox.extends({
|
||||
const SidebarHeader = styled(FlexBox)({
|
||||
display: 'block',
|
||||
alignItems: 'center',
|
||||
padding: 3,
|
||||
@@ -51,23 +58,18 @@ const SidebarHeader = FlexBox.extends({
|
||||
flexShrink: 0,
|
||||
});
|
||||
|
||||
const PluginShape = FlexBox.extends(
|
||||
{
|
||||
marginRight: 5,
|
||||
backgroundColor: props => props.backgroundColor,
|
||||
borderRadius: 3,
|
||||
flexShrink: 0,
|
||||
width: 18,
|
||||
height: 18,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['backgroundColor'],
|
||||
},
|
||||
);
|
||||
const PluginShape = styled(FlexBox)(({backgroundColor}) => ({
|
||||
marginRight: 5,
|
||||
backgroundColor,
|
||||
borderRadius: 3,
|
||||
flexShrink: 0,
|
||||
width: 18,
|
||||
height: 18,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}));
|
||||
|
||||
const PluginName = Text.extends({
|
||||
const PluginName = styled(Text)({
|
||||
minWidth: 0,
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
@@ -75,17 +77,19 @@ const PluginName = Text.extends({
|
||||
});
|
||||
|
||||
function PluginIcon({
|
||||
isActive,
|
||||
backgroundColor,
|
||||
name,
|
||||
color,
|
||||
}: {
|
||||
isActive: boolean,
|
||||
backgroundColor: string,
|
||||
name: string,
|
||||
color: string,
|
||||
}) {
|
||||
return (
|
||||
<PluginShape backgroundColor={backgroundColor}>
|
||||
<Glyph size={12} name={name} color={color} />
|
||||
<Glyph size={12} name={name} color={isActive ? colors.white : color} />
|
||||
</PluginShape>
|
||||
);
|
||||
}
|
||||
@@ -118,14 +122,15 @@ class PluginSidebarListItem extends Component<{
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomClickableListItem active={isActive} onClick={this.props.onClick}>
|
||||
<ListItem active={isActive} onClick={this.props.onClick}>
|
||||
<PluginIcon
|
||||
isActive={isActive}
|
||||
name={plugin.icon}
|
||||
backgroundColor={iconColor}
|
||||
color={colors.white}
|
||||
/>
|
||||
<PluginName>{plugin.title}</PluginName>
|
||||
</CustomClickableListItem>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,18 +63,18 @@ type State = {
|
||||
searchCompleted: boolean,
|
||||
};
|
||||
|
||||
const Container = FlexBox.extends({
|
||||
const Container = styled(FlexBox)({
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
background: colors.light02,
|
||||
overflowY: 'scroll',
|
||||
});
|
||||
|
||||
const Title = Text.extends({
|
||||
const Title = styled(Text)({
|
||||
fontWeight: 500,
|
||||
});
|
||||
|
||||
const Plugin = FlexColumn.extends({
|
||||
const Plugin = styled(FlexColumn)({
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 4,
|
||||
padding: 15,
|
||||
@@ -82,20 +82,20 @@ const Plugin = FlexColumn.extends({
|
||||
boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
|
||||
});
|
||||
|
||||
const SectionTitle = styled.text({
|
||||
const SectionTitle = styled('span')({
|
||||
fontWeight: 'bold',
|
||||
fontSize: 24,
|
||||
margin: 15,
|
||||
marginLeft: 20,
|
||||
});
|
||||
|
||||
const Loading = FlexBox.extends({
|
||||
const Loading = styled(FlexBox)({
|
||||
padding: 50,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
});
|
||||
|
||||
const RestartRequired = FlexBox.extends({
|
||||
const RestartRequired = styled(FlexBox)({
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center',
|
||||
fontWeight: 500,
|
||||
@@ -105,22 +105,22 @@ const RestartRequired = FlexBox.extends({
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
const TitleRow = FlexRow.extends({
|
||||
const TitleRow = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
marginBottom: 10,
|
||||
fontSize: '1.1em',
|
||||
});
|
||||
|
||||
const Description = FlexRow.extends({
|
||||
const Description = styled(FlexRow)({
|
||||
marginBottom: 15,
|
||||
lineHeight: '130%',
|
||||
});
|
||||
|
||||
const PluginGlyph = Glyph.extends({
|
||||
const PluginGlyph = styled(Glyph)({
|
||||
marginRight: 5,
|
||||
});
|
||||
|
||||
const PluginLoading = LoadingIndicator.extends({
|
||||
const PluginLoading = styled(LoadingIndicator)({
|
||||
marginLeft: 5,
|
||||
marginTop: 5,
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
colors,
|
||||
} from 'sonar';
|
||||
|
||||
const Anchor = styled.image({
|
||||
const Anchor = styled('img')({
|
||||
zIndex: 6,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
@@ -24,7 +24,7 @@ const Anchor = styled.image({
|
||||
transform: 'translate(-50%, calc(100% + 2px))',
|
||||
});
|
||||
|
||||
const PopoverContainer = FlexColumn.extends({
|
||||
const PopoverContainer = styled(FlexColumn)({
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 7,
|
||||
border: '1px solid rgba(0,0,0,0.3)',
|
||||
@@ -50,7 +50,7 @@ const PopoverContainer = FlexColumn.extends({
|
||||
},
|
||||
});
|
||||
|
||||
const Heading = Text.extends({
|
||||
const Heading = styled(Text)({
|
||||
display: 'block',
|
||||
backgroundColor: colors.white,
|
||||
color: colors.light30,
|
||||
@@ -60,7 +60,7 @@ const Heading = Text.extends({
|
||||
padding: '4px 8px 0',
|
||||
});
|
||||
|
||||
const PopoverItem = FlexRow.extends({
|
||||
const PopoverItem = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
borderBottom: `1px solid ${colors.light05}`,
|
||||
height: 50,
|
||||
@@ -69,7 +69,7 @@ const PopoverItem = FlexRow.extends({
|
||||
},
|
||||
});
|
||||
|
||||
const ItemTitle = Text.extends({
|
||||
const ItemTitle = styled(Text)({
|
||||
display: 'block',
|
||||
fontSize: 14,
|
||||
fontWeight: 400,
|
||||
@@ -80,7 +80,7 @@ const ItemTitle = Text.extends({
|
||||
marginBottom: 1,
|
||||
});
|
||||
|
||||
const ItemSubtitle = Text.extends({
|
||||
const ItemSubtitle = styled(Text)({
|
||||
display: 'block',
|
||||
fontWeight: 400,
|
||||
fontSize: 11,
|
||||
@@ -91,27 +91,27 @@ const ItemSubtitle = Text.extends({
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
|
||||
const ItemImage = FlexBox.extends({
|
||||
const ItemImage = styled(FlexBox)({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 40,
|
||||
flexShrink: 0,
|
||||
});
|
||||
|
||||
const ItemContent = styled.view({
|
||||
const ItemContent = styled('div')({
|
||||
minWidth: 0,
|
||||
paddingRight: 5,
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
const Section = styled.view({
|
||||
const Section = styled('div')({
|
||||
borderBottom: `1px solid ${colors.light05}`,
|
||||
'&:last-child': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
const Action = Button.extends({
|
||||
const Action = styled(Button)({
|
||||
border: `1px solid ${colors.macOSTitleBarButtonBorder}`,
|
||||
background: 'transparent',
|
||||
color: colors.macOSTitleBarIconSelected,
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
Component,
|
||||
Spacer,
|
||||
GK,
|
||||
styled,
|
||||
} from 'sonar';
|
||||
import {connect} from 'react-redux';
|
||||
import {
|
||||
@@ -26,34 +27,24 @@ import ScreenCaptureButtons from './ScreenCaptureButtons.js';
|
||||
import AutoUpdateVersion from './AutoUpdateVersion.js';
|
||||
import config from '../fb-stubs/config.js';
|
||||
|
||||
const TitleBar = FlexRow.extends(
|
||||
{
|
||||
background: props =>
|
||||
props.focused
|
||||
? `linear-gradient(to bottom, ${
|
||||
colors.macOSTitleBarBackgroundTop
|
||||
} 0%, ${colors.macOSTitleBarBackgroundBottom} 100%)`
|
||||
: colors.macOSTitleBarBackgroundBlur,
|
||||
borderBottom: props =>
|
||||
`1px solid ${
|
||||
props.focused
|
||||
? colors.macOSTitleBarBorder
|
||||
: colors.macOSTitleBarBorderBlur
|
||||
}`,
|
||||
height: 38,
|
||||
flexShrink: 0,
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
paddingLeft: 80,
|
||||
paddingRight: 10,
|
||||
justifyContent: 'space-between',
|
||||
// $FlowFixMe
|
||||
WebkitAppRegion: 'drag',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['focused'],
|
||||
},
|
||||
);
|
||||
const TitleBar = styled(FlexRow)(({focused}) => ({
|
||||
background: focused
|
||||
? `linear-gradient(to bottom, ${colors.macOSTitleBarBackgroundTop} 0%, ${
|
||||
colors.macOSTitleBarBackgroundBottom
|
||||
} 100%)`
|
||||
: colors.macOSTitleBarBackgroundBlur,
|
||||
borderBottom: `1px solid ${
|
||||
focused ? colors.macOSTitleBarBorder : colors.macOSTitleBarBorderBlur
|
||||
}`,
|
||||
height: 38,
|
||||
flexShrink: 0,
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
paddingLeft: 80,
|
||||
paddingRight: 10,
|
||||
justifyContent: 'space-between',
|
||||
WebkitAppRegion: 'drag',
|
||||
}));
|
||||
|
||||
type Props = {|
|
||||
windowIsFocused: boolean,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {Component, FlexRow, colors, LoadingIndicator} from 'sonar';
|
||||
import {Component, FlexRow, colors, LoadingIndicator, styled} from 'sonar';
|
||||
import {version} from '../../package.json';
|
||||
import {remote} from 'electron';
|
||||
import * as path from 'path';
|
||||
@@ -24,7 +24,7 @@ export default class Version extends Component<{}, VersionState> {
|
||||
status: 'unknown',
|
||||
};
|
||||
|
||||
static Container = FlexRow.extends({
|
||||
static Container = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
marginRight: 7,
|
||||
marginLeft: 7,
|
||||
@@ -32,7 +32,7 @@ export default class Version extends Component<{}, VersionState> {
|
||||
color: colors.light50,
|
||||
});
|
||||
|
||||
static UpdatedContainer = FlexRow.extends({
|
||||
static UpdatedContainer = styled(FlexRow)({
|
||||
backgroundColor: colors.blackAlpha10,
|
||||
borderRadius: '999em',
|
||||
padding: '2px 6px',
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import isProduction from '../utils/isProduction.js';
|
||||
import {shell, remote} from 'electron';
|
||||
|
||||
const Container = FlexColumn.extends({
|
||||
const Container = styled(FlexColumn)({
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
@@ -26,23 +26,18 @@ const Container = FlexColumn.extends({
|
||||
backgroundColor: colors.light02,
|
||||
});
|
||||
|
||||
const Welcome = FlexColumn.extends(
|
||||
{
|
||||
width: 460,
|
||||
background: colors.white,
|
||||
borderRadius: 10,
|
||||
boxShadow: '0 1px 3px rgba(0,0,0,0.25)',
|
||||
overflow: 'hidden',
|
||||
opacity: props => (props.isMounted ? 1 : 0),
|
||||
transform: props => `translateY(${props.isMounted ? 0 : 20}px)`,
|
||||
transition: '0.6s all ease-out',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['isMounted'],
|
||||
},
|
||||
);
|
||||
const Welcome = styled(FlexColumn)(({isMounted}) => ({
|
||||
width: 460,
|
||||
background: colors.white,
|
||||
borderRadius: 10,
|
||||
boxShadow: '0 1px 3px rgba(0,0,0,0.25)',
|
||||
overflow: 'hidden',
|
||||
opacity: isMounted ? 1 : 0,
|
||||
transform: `translateY(${isMounted ? 0 : 20}px)`,
|
||||
transition: '0.6s all ease-out',
|
||||
}));
|
||||
|
||||
const Title = Text.extends({
|
||||
const Title = styled(Text)({
|
||||
fontSize: 24,
|
||||
fontWeight: 300,
|
||||
textAlign: 'center',
|
||||
@@ -50,7 +45,7 @@ const Title = Text.extends({
|
||||
marginBottom: 16,
|
||||
});
|
||||
|
||||
const Version = Text.extends({
|
||||
const Version = styled(Text)({
|
||||
textAlign: 'center',
|
||||
fontSize: 11,
|
||||
fontWeight: 300,
|
||||
@@ -58,7 +53,7 @@ const Version = Text.extends({
|
||||
marginBottom: 60,
|
||||
});
|
||||
|
||||
const Item = FlexRow.extends({
|
||||
const Item = styled(FlexRow)({
|
||||
padding: 10,
|
||||
cursor: 'pointer',
|
||||
alignItems: 'center',
|
||||
@@ -69,23 +64,23 @@ const Item = FlexRow.extends({
|
||||
},
|
||||
});
|
||||
|
||||
const ItemTitle = Text.extends({
|
||||
const ItemTitle = styled(Text)({
|
||||
color: colors.light50,
|
||||
fontSize: 15,
|
||||
});
|
||||
|
||||
const ItemSubTitle = Text.extends({
|
||||
const ItemSubTitle = styled(Text)({
|
||||
color: colors.light30,
|
||||
fontSize: 11,
|
||||
marginTop: 2,
|
||||
});
|
||||
|
||||
const Icon = Glyph.extends({
|
||||
const Icon = styled(Glyph)({
|
||||
marginRight: 11,
|
||||
marginLeft: 6,
|
||||
});
|
||||
|
||||
const Logo = styled.image({
|
||||
const Logo = styled('img')({
|
||||
width: 128,
|
||||
height: 128,
|
||||
alignSelf: 'center',
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
ManagedTable,
|
||||
Button,
|
||||
colors,
|
||||
styled,
|
||||
} from 'sonar';
|
||||
|
||||
export type Counter = {
|
||||
@@ -55,7 +56,7 @@ const Columns = {
|
||||
},
|
||||
};
|
||||
|
||||
const Count = Text.extends({
|
||||
const Count = styled(Text)({
|
||||
alignSelf: 'center',
|
||||
background: colors.macOSHighlightActive,
|
||||
color: colors.white,
|
||||
@@ -68,7 +69,7 @@ const Count = Text.extends({
|
||||
marginLeft: 'auto',
|
||||
});
|
||||
|
||||
const Checkbox = Input.extends({
|
||||
const Checkbox = styled(Input)({
|
||||
lineHeight: '100%',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
@@ -76,11 +77,11 @@ const Checkbox = Input.extends({
|
||||
alignSelf: 'center',
|
||||
});
|
||||
|
||||
const ExpressionInput = Input.extends({
|
||||
const ExpressionInput = styled(Input)({
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
const WatcherPanel = Panel.extends({
|
||||
const WatcherPanel = styled(Panel)({
|
||||
minHeight: 200,
|
||||
});
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
FlexColumn,
|
||||
Glyph,
|
||||
SonarSidebar,
|
||||
styled,
|
||||
} from 'sonar';
|
||||
import {SonarDevicePlugin, SearchableTable} from 'sonar';
|
||||
import textContent from '../../utils/textContent.js';
|
||||
@@ -47,7 +48,7 @@ type LogsState = {|
|
||||
counters: Array<Counter>,
|
||||
|};
|
||||
|
||||
const Icon = Glyph.extends({
|
||||
const Icon = styled(Glyph)({
|
||||
marginTop: 5,
|
||||
});
|
||||
|
||||
@@ -202,7 +203,7 @@ const DEFAULT_FILTERS = [
|
||||
},
|
||||
];
|
||||
|
||||
const HiddenScrollText = Text.extends({
|
||||
const HiddenScrollText = styled(Text)({
|
||||
alignSelf: 'baseline',
|
||||
userSelect: 'none',
|
||||
lineHeight: '130%',
|
||||
@@ -212,23 +213,18 @@ const HiddenScrollText = Text.extends({
|
||||
},
|
||||
});
|
||||
|
||||
const LogCount = HiddenScrollText.extends(
|
||||
{
|
||||
backgroundColor: props => props.color,
|
||||
borderRadius: '999em',
|
||||
fontSize: 11,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 4,
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: colors.white,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['color'],
|
||||
},
|
||||
);
|
||||
const LogCount = styled(HiddenScrollText)(({color}) => ({
|
||||
backgroundColor: color,
|
||||
borderRadius: '999em',
|
||||
fontSize: 11,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 4,
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: colors.white,
|
||||
}));
|
||||
|
||||
function pad(chunk: mixed, len: number): string {
|
||||
let str = String(chunk);
|
||||
@@ -529,7 +525,7 @@ export default class LogTable extends SonarDevicePlugin<LogsState> {
|
||||
);
|
||||
};
|
||||
|
||||
static ContextMenu = ContextMenu.extends({
|
||||
static ContextMenu = styled(ContextMenu)({
|
||||
flex: 1,
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
export {default as styled} from './ui/styled/index.js';
|
||||
export {default as styled} from 'react-emotion';
|
||||
export * from './ui/index.js';
|
||||
export * from './utils/index.js';
|
||||
|
||||
|
||||
@@ -106,18 +106,18 @@ type SearchResultTree = {|
|
||||
axElement: Element,
|
||||
|};
|
||||
|
||||
const LoadingSpinner = LoadingIndicator.extends({
|
||||
const LoadingSpinner = styled(LoadingIndicator)({
|
||||
marginRight: 4,
|
||||
marginLeft: 3,
|
||||
marginTop: -1,
|
||||
});
|
||||
|
||||
const Center = FlexRow.extends({
|
||||
const Center = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
});
|
||||
|
||||
const SearchIconContainer = styled.view({
|
||||
const SearchIconContainer = styled('div')({
|
||||
marginRight: 9,
|
||||
marginTop: -3,
|
||||
marginLeft: 4,
|
||||
@@ -131,7 +131,7 @@ class LayoutSearchInput extends Component<
|
||||
value: string,
|
||||
},
|
||||
> {
|
||||
static TextInput = styled.textInput({
|
||||
static TextInput = styled('input')({
|
||||
width: '100%',
|
||||
marginLeft: 6,
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
Checkbox,
|
||||
SonarPlugin,
|
||||
Button,
|
||||
styled,
|
||||
} from 'sonar';
|
||||
import type {ElementID, Element} from 'sonar';
|
||||
import {processLeaks} from './processLeakString';
|
||||
@@ -42,12 +43,12 @@ export type Leak = {
|
||||
retainedSize: string,
|
||||
};
|
||||
|
||||
const Window = FlexRow.extends({
|
||||
const Window = styled(FlexRow)({
|
||||
height: '100%',
|
||||
flex: 1,
|
||||
});
|
||||
|
||||
const ToolbarItem = FlexRow.extends({
|
||||
const ToolbarItem = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
marginLeft: '8px',
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ import {getHeaderValue} from './index.js';
|
||||
|
||||
import querystring from 'querystring';
|
||||
|
||||
const WrappingText = Text.extends({
|
||||
const WrappingText = styled(Text)({
|
||||
wordWrap: 'break-word',
|
||||
width: '100%',
|
||||
lineHeight: '125%',
|
||||
@@ -84,7 +84,7 @@ function decompress(body: string): string {
|
||||
}
|
||||
|
||||
export default class RequestDetails extends Component<RequestDetailsProps> {
|
||||
static Container = FlexColumn.extends({
|
||||
static Container = styled(FlexColumn)({
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
});
|
||||
@@ -274,7 +274,7 @@ class HeaderInspector extends Component<
|
||||
}
|
||||
}
|
||||
|
||||
const BodyContainer = styled.view({
|
||||
const BodyContainer = styled('div')({
|
||||
paddingTop: 10,
|
||||
paddingBottom: 20,
|
||||
});
|
||||
@@ -338,7 +338,7 @@ class ResponseBodyInspector extends Component<{
|
||||
}
|
||||
}
|
||||
|
||||
const MediaContainer = FlexColumn.extends({
|
||||
const MediaContainer = styled(FlexColumn)({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
@@ -354,14 +354,14 @@ type ImageWithSizeState = {
|
||||
};
|
||||
|
||||
class ImageWithSize extends Component<ImageWithSizeProps, ImageWithSizeState> {
|
||||
static Image = styled.image({
|
||||
static Image = styled('img')({
|
||||
objectFit: 'scale-down',
|
||||
maxWidth: 500,
|
||||
maxHeight: 500,
|
||||
marginBottom: 10,
|
||||
});
|
||||
|
||||
static Text = Text.extends({
|
||||
static Text = styled(Text)({
|
||||
color: colors.dark70,
|
||||
fontSize: 14,
|
||||
});
|
||||
@@ -408,7 +408,7 @@ class ImageFormatter {
|
||||
}
|
||||
|
||||
class VideoFormatter {
|
||||
static Video = styled.customHTMLTag('video', {
|
||||
static Video = styled('video')({
|
||||
maxWidth: 500,
|
||||
maxHeight: 500,
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
colors,
|
||||
PureComponent,
|
||||
SonarSidebar,
|
||||
styled,
|
||||
} from 'sonar';
|
||||
import {SonarPlugin, SearchableTable} from 'sonar';
|
||||
import RequestDetails from './RequestDetails.js';
|
||||
@@ -100,7 +101,7 @@ export function formatBytes(count: number): string {
|
||||
return count + ' B';
|
||||
}
|
||||
|
||||
const TextEllipsis = Text.extends({
|
||||
const TextEllipsis = styled(Text)({
|
||||
overflowX: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
maxWidth: '100%',
|
||||
@@ -287,7 +288,7 @@ function calculateState(
|
||||
}
|
||||
|
||||
class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
||||
static ContextMenu = ContextMenu.extends({
|
||||
static ContextMenu = styled(ContextMenu)({
|
||||
flex: 1,
|
||||
});
|
||||
|
||||
@@ -338,7 +339,7 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
||||
}
|
||||
}
|
||||
|
||||
const Icon = Glyph.extends({
|
||||
const Icon = styled(Glyph)({
|
||||
marginTop: -3,
|
||||
marginRight: 3,
|
||||
});
|
||||
@@ -367,7 +368,7 @@ class DurationColumn extends PureComponent<{
|
||||
request: Request,
|
||||
response: ?Response,
|
||||
}> {
|
||||
static Text = Text.extends({
|
||||
static Text = styled(Text)({
|
||||
flex: 1,
|
||||
textAlign: 'right',
|
||||
paddingRight: 10,
|
||||
@@ -389,7 +390,7 @@ class DurationColumn extends PureComponent<{
|
||||
class SizeColumn extends PureComponent<{
|
||||
response: ?Response,
|
||||
}> {
|
||||
static Text = Text.extends({
|
||||
static Text = styled(Text)({
|
||||
flex: 1,
|
||||
textAlign: 'right',
|
||||
paddingRight: 10,
|
||||
|
||||
@@ -20,13 +20,13 @@ type SandboxState = {|
|
||||
showFeedback: boolean,
|
||||
|};
|
||||
|
||||
const BigButton = Button.extends({
|
||||
const BigButton = styled(Button)({
|
||||
flexGrow: 1,
|
||||
fontSize: 24,
|
||||
padding: 20,
|
||||
});
|
||||
|
||||
const ButtonContainer = FlexColumn.extends({
|
||||
const ButtonContainer = styled(FlexColumn)({
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
});
|
||||
@@ -42,7 +42,7 @@ export default class SandboxView extends SonarPlugin<SandboxState> {
|
||||
static id = 'Sandbox';
|
||||
static icon = 'translate';
|
||||
|
||||
static TextInput = styled.textInput({
|
||||
static TextInput = styled('input')({
|
||||
border: `1px solid ${colors.light10}`,
|
||||
fontSize: '1em',
|
||||
padding: '0 5px',
|
||||
@@ -52,13 +52,13 @@ export default class SandboxView extends SonarPlugin<SandboxState> {
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
static FeedbackMessage = styled.text({
|
||||
static FeedbackMessage = styled('span')({
|
||||
fontSize: '1.2em',
|
||||
paddingTop: '10px',
|
||||
color: 'green',
|
||||
});
|
||||
|
||||
static TextInputLayout = FlexColumn.extends({
|
||||
static TextInputLayout = styled(FlexColumn)({
|
||||
float: 'left',
|
||||
justifyContent: 'center',
|
||||
flexGrow: 1,
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default styled.view({
|
||||
export default styled('div')({
|
||||
display: 'block',
|
||||
});
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
*/
|
||||
|
||||
import FlexBox from './FlexBox.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default FlexBox.extends({
|
||||
export default styled(FlexBox)({
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
position: 'relative',
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
|
||||
import Glyph from './Glyph.js';
|
||||
import styled from '../styled/index.js';
|
||||
import type {StyledComponent} from '../styled/index.js';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import {colors} from './colors.js';
|
||||
import {connect} from 'react-redux';
|
||||
import electron from 'electron';
|
||||
import {keyframes} from 'react-emotion';
|
||||
|
||||
const borderColor = props => {
|
||||
if (!props.windowIsFocused) {
|
||||
@@ -35,129 +35,122 @@ const borderBottomColor = props => {
|
||||
}
|
||||
};
|
||||
|
||||
const StyledButton = styled.view(
|
||||
{
|
||||
backgroundColor: props => {
|
||||
if (!props.windowIsFocused) {
|
||||
return colors.macOSTitleBarButtonBackgroundBlur;
|
||||
} else {
|
||||
return colors.white;
|
||||
}
|
||||
},
|
||||
backgroundImage: props => {
|
||||
if (props.windowIsFocused) {
|
||||
if (props.depressed) {
|
||||
return `linear-gradient(to bottom, ${
|
||||
colors.macOSTitleBarBorderBlur
|
||||
} 1px, ${colors.macOSTitleBarButtonBorderBlur} 0%, ${
|
||||
colors.macOSTitleBarButtonBackgroundActive
|
||||
} 100%)`;
|
||||
} else {
|
||||
return `linear-gradient(to bottom, transparent 0%,${
|
||||
colors.macOSTitleBarButtonBackground
|
||||
} 100%)`;
|
||||
}
|
||||
} else {
|
||||
return 'none';
|
||||
}
|
||||
},
|
||||
const backgroundImage = props => {
|
||||
if (props.windowIsFocused) {
|
||||
if (props.depressed) {
|
||||
return `linear-gradient(to bottom, ${
|
||||
colors.macOSTitleBarBorderBlur
|
||||
} 1px, ${colors.macOSTitleBarButtonBorderBlur} 0%, ${
|
||||
colors.macOSTitleBarButtonBackgroundActive
|
||||
} 100%)`;
|
||||
} else {
|
||||
return `linear-gradient(to bottom, transparent 0%,${
|
||||
colors.macOSTitleBarButtonBackground
|
||||
} 100%)`;
|
||||
}
|
||||
} else {
|
||||
return 'none';
|
||||
}
|
||||
};
|
||||
|
||||
const color = props => {
|
||||
if (props.type === 'danger' && props.windowIsFocused) {
|
||||
return colors.red;
|
||||
} else if (props.disabled) {
|
||||
return colors.macOSTitleBarIconBlur;
|
||||
} else {
|
||||
return colors.light50;
|
||||
}
|
||||
};
|
||||
|
||||
const pulse = keyframes({
|
||||
'0%': {
|
||||
boxShadow: `0 0 4px 0 ${colors.macOSTitleBarIconSelected}`,
|
||||
},
|
||||
'70%': {
|
||||
boxShadow: '0 0 4px 6px transparent',
|
||||
},
|
||||
'100%': {
|
||||
boxShadow: '0 0 4px 0 transparent',
|
||||
},
|
||||
});
|
||||
|
||||
const StyledButton = styled('div')(props => ({
|
||||
backgroundColor: !props.windowIsFocused
|
||||
? colors.macOSTitleBarButtonBackgroundBlur
|
||||
: colors.white,
|
||||
backgroundImage: backgroundImage(props),
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: borderColor(props),
|
||||
borderBottomColor: borderBottomColor(props),
|
||||
fontSize: props.compact === true ? 11 : '1em',
|
||||
color: color(props),
|
||||
borderRadius: 4,
|
||||
position: 'relative',
|
||||
padding: '0 6px',
|
||||
height: props.compact === true ? 24 : 28,
|
||||
margin: 0,
|
||||
marginLeft: props.inButtonGroup === true ? 0 : 10,
|
||||
minWidth: 34,
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
|
||||
boxShadow:
|
||||
props.pulse && props.windowIsFocused
|
||||
? `0 0 0 ${colors.macOSTitleBarIconSelected}`
|
||||
: '',
|
||||
animation: props.pulse && props.windowIsFocused ? `${pulse} 1s infinite` : '',
|
||||
|
||||
'&:not(:first-child)': {
|
||||
borderTopLeftRadius: props.inButtonGroup === true ? 0 : 4,
|
||||
borderBottomLeftRadius: props.inButtonGroup === true ? 0 : 4,
|
||||
},
|
||||
|
||||
'&:not(:last-child)': {
|
||||
borderTopRightRadius: props.inButtonGroup === true ? 0 : 4,
|
||||
borderBottomRightRadius: props.inButtonGroup === true ? 0 : 4,
|
||||
borderRight: props.inButtonGroup === true ? 0 : '',
|
||||
},
|
||||
|
||||
'&:first-of-type': {
|
||||
marginLeft: 0,
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
borderColor: colors.macOSTitleBarButtonBorder,
|
||||
borderBottomColor: colors.macOSTitleBarButtonBorderBottom,
|
||||
background: `linear-gradient(to bottom, ${
|
||||
colors.macOSTitleBarButtonBackgroundActiveHighlight
|
||||
} 1px, ${colors.macOSTitleBarButtonBackgroundActive} 0%, ${
|
||||
colors.macOSTitleBarButtonBorderBlur
|
||||
} 100%)`,
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
borderColor: borderColor(props),
|
||||
borderBottomColor: borderBottomColor(props),
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
|
||||
'&:hover::before': {
|
||||
content: props.dropdown ? "''" : '',
|
||||
position: 'absolute',
|
||||
bottom: 1,
|
||||
right: 2,
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor,
|
||||
borderBottomColor,
|
||||
fontSize: props => (props.compact === true ? 11 : '1em'),
|
||||
color: props => {
|
||||
if (props.type === 'danger' && props.windowIsFocused) {
|
||||
return colors.red;
|
||||
} else if (props.disabled) {
|
||||
return colors.macOSTitleBarIconBlur;
|
||||
} else {
|
||||
return colors.light50;
|
||||
}
|
||||
},
|
||||
borderRadius: 4,
|
||||
position: 'relative',
|
||||
padding: '0 6px',
|
||||
height: props => (props.compact === true ? 24 : 28),
|
||||
margin: 0,
|
||||
marginLeft: props => (props.inButtonGroup === true ? 0 : 10),
|
||||
minWidth: 34,
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
|
||||
boxShadow: props =>
|
||||
props.pulse && props.windowIsFocused
|
||||
? `0 0 0 ${colors.macOSTitleBarIconSelected}`
|
||||
: '',
|
||||
animation: props =>
|
||||
props.pulse && props.windowIsFocused ? 'pulse 1s infinite' : '',
|
||||
|
||||
'&:not(:first-child)': {
|
||||
borderTopLeftRadius: props => (props.inButtonGroup === true ? 0 : 4),
|
||||
borderBottomLeftRadius: props => (props.inButtonGroup === true ? 0 : 4),
|
||||
},
|
||||
|
||||
'&:not(:last-child)': {
|
||||
borderTopRightRadius: props => (props.inButtonGroup === true ? 0 : 4),
|
||||
borderBottomRightRadius: props => (props.inButtonGroup === true ? 0 : 4),
|
||||
borderRight: props => (props.inButtonGroup === true ? 0 : ''),
|
||||
},
|
||||
|
||||
'&:first-of-type': {
|
||||
marginLeft: 0,
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
borderColor: colors.macOSTitleBarButtonBorder,
|
||||
borderBottomColor: colors.macOSTitleBarButtonBorderBottom,
|
||||
background: `linear-gradient(to bottom, ${
|
||||
colors.macOSTitleBarButtonBackgroundActiveHighlight
|
||||
} 1px, ${colors.macOSTitleBarButtonBackgroundActive} 0%, ${
|
||||
colors.macOSTitleBarButtonBorderBlur
|
||||
} 100%)`,
|
||||
},
|
||||
|
||||
'&:disabled': {
|
||||
borderColor,
|
||||
borderBottomColor,
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
|
||||
'&:hover::before': {
|
||||
content: props => (props.dropdown ? "''" : ''),
|
||||
position: 'absolute',
|
||||
bottom: 1,
|
||||
right: 2,
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '4px 3px 0 3px',
|
||||
borderColor: props =>
|
||||
`${colors.macOSTitleBarIcon} transparent transparent transparent`,
|
||||
},
|
||||
borderWidth: '4px 3px 0 3px',
|
||||
borderColor: `${
|
||||
colors.macOSTitleBarIcon
|
||||
} transparent transparent transparent`,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: [
|
||||
'dropdown',
|
||||
'dispatch',
|
||||
'compact',
|
||||
'large',
|
||||
'windowIsFocused',
|
||||
'inButtonGroup',
|
||||
'danger',
|
||||
'pulse',
|
||||
],
|
||||
},
|
||||
);
|
||||
}));
|
||||
|
||||
const Icon = Glyph.extends(
|
||||
{
|
||||
marginRight: props => (props.hasText ? 3 : 0),
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['hasText', 'type'],
|
||||
},
|
||||
);
|
||||
const Icon = styled(Glyph)(({hasText}) => ({
|
||||
marginRight: hasText ? 3 : 0,
|
||||
}));
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
@@ -252,7 +245,7 @@ type State = {
|
||||
* @example Disabled button
|
||||
* <Button disabled={true}>Click me</Button>
|
||||
*/
|
||||
class Button extends styled.StylableComponent<
|
||||
class Button extends React.Component<
|
||||
Props & {windowIsFocused: boolean},
|
||||
State,
|
||||
> {
|
||||
@@ -350,25 +343,6 @@ class Button extends styled.StylableComponent<
|
||||
inButtonGroup={this.context.inButtonGroup}>
|
||||
{iconComponent}
|
||||
{children}
|
||||
{this.props.pulse === true && (
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 4px 0 ${colors.macOSTitleBarIconSelected};
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 4px 6px transparent;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 4px 0 transparent;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</StyledButton>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {Component} from 'react';
|
||||
|
||||
const PropTypes = require('prop-types');
|
||||
|
||||
const ButtonGroupContainer = styled.view({
|
||||
const ButtonGroupContainer = styled('div')({
|
||||
display: 'inline-flex',
|
||||
marginLeft: 10,
|
||||
'&:first-child': {
|
||||
|
||||
@@ -13,7 +13,7 @@ type CheckboxProps = {
|
||||
onChange: (checked: boolean) => void,
|
||||
};
|
||||
|
||||
const CheckboxContainer = styled.textInput({
|
||||
const CheckboxContainer = styled('input')({
|
||||
display: 'inline-block',
|
||||
marginRight: 5,
|
||||
verticalAlign: 'middle',
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default styled.view({
|
||||
marginBottom: 10,
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
import {colors} from './colors.js';
|
||||
|
||||
export default styled.view(
|
||||
{
|
||||
backgroundColor: ({active, windowFocused}) => {
|
||||
if (active && windowFocused) {
|
||||
return colors.macOSTitleBarIconSelected;
|
||||
} else if (active && !windowFocused) {
|
||||
return colors.macOSTitleBarBorderBlur;
|
||||
} else {
|
||||
return 'none';
|
||||
}
|
||||
},
|
||||
color: ({active, windowFocused}) =>
|
||||
active && windowFocused ? colors.white : colors.macOSSidebarSectionItem,
|
||||
lineHeight: '25px',
|
||||
padding: '0 10px',
|
||||
'&[disabled]': {
|
||||
color: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['active', 'windowFocused'],
|
||||
},
|
||||
);
|
||||
@@ -7,6 +7,6 @@
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default styled.view({
|
||||
export default styled('div')({
|
||||
fontFamily: 'monospace',
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
import FlexColumn from './FlexColumn.js';
|
||||
import styled from '../styled/index.js';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
type MenuTemplate = Array<Electron$MenuItemOptions>;
|
||||
@@ -18,7 +17,7 @@ type Props = {
|
||||
component: React.ComponentType<any> | string,
|
||||
};
|
||||
|
||||
export default class ContextMenu extends styled.StylablePureComponent<Props> {
|
||||
export default class ContextMenu extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
component: FlexColumn,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ const PropTypes = require('prop-types');
|
||||
|
||||
type MenuTemplate = Array<Electron$MenuItemOptions>;
|
||||
|
||||
const Container = styled.view({
|
||||
const Container = styled('div')({
|
||||
display: 'contents',
|
||||
});
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
*/
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
import React from 'react';
|
||||
import CodeBlock from './CodeBlock.js';
|
||||
|
||||
const ErrorBlockContainer = CodeBlock.extends({
|
||||
const ErrorBlockContainer = styled(CodeBlock)({
|
||||
backgroundColor: '#f2dede',
|
||||
border: '1px solid #ebccd1',
|
||||
borderRadius: 4,
|
||||
@@ -17,7 +18,7 @@ const ErrorBlockContainer = CodeBlock.extends({
|
||||
padding: 10,
|
||||
});
|
||||
|
||||
export default class ErrorBlock extends styled.StylableComponent<{
|
||||
export default class ErrorBlock extends React.Component<{
|
||||
error: Error | string | void,
|
||||
className?: string,
|
||||
}> {
|
||||
|
||||
@@ -11,13 +11,14 @@ import Heading from './Heading.js';
|
||||
import Button from './Button.js';
|
||||
import View from './View.js';
|
||||
import LogManager from '../../fb-stubs/Logger.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
const ErrorBoundaryContainer = View.extends({
|
||||
const ErrorBoundaryContainer = styled(View)({
|
||||
overflow: 'auto',
|
||||
padding: 10,
|
||||
});
|
||||
|
||||
const ErrorBoundaryStack = ErrorBlock.extends({
|
||||
const ErrorBoundaryStack = styled(ErrorBlock)({
|
||||
marginBottom: 10,
|
||||
whiteSpace: 'pre',
|
||||
});
|
||||
|
||||
@@ -6,13 +6,9 @@
|
||||
*/
|
||||
|
||||
import View from './View.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default View.extends(
|
||||
{
|
||||
display: 'flex',
|
||||
flexShrink: props => (props.shrink == null || props.shrink ? 1 : 0),
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['shrink'],
|
||||
},
|
||||
);
|
||||
export default styled(View)(({shrink}) => ({
|
||||
display: 'flex',
|
||||
flexShrink: shrink == null || shrink ? 1 : 0,
|
||||
}));
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
*/
|
||||
|
||||
import View from './View.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default View.extends({
|
||||
export default styled(View)({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
*/
|
||||
|
||||
import FlexBox from './FlexBox.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default FlexBox.extends({
|
||||
export default styled(FlexBox)({
|
||||
flexDirection: 'column',
|
||||
});
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
*/
|
||||
|
||||
import FlexBox from './FlexBox.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default FlexBox.extends({
|
||||
export default styled(FlexBox)({
|
||||
flexDirection: 'row',
|
||||
});
|
||||
|
||||
@@ -8,21 +8,17 @@
|
||||
import {Component} from 'react';
|
||||
import Box from './Box.js';
|
||||
import {colors} from './colors';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
const FocusableBoxBorder = Box.extends(
|
||||
{
|
||||
border: `1px solid ${colors.highlight}`,
|
||||
bottom: '0',
|
||||
left: '0',
|
||||
pointerEvents: 'none',
|
||||
position: 'absolute',
|
||||
right: '0',
|
||||
top: '0',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: [],
|
||||
},
|
||||
);
|
||||
const FocusableBoxBorder = styled(Box)({
|
||||
border: `1px solid ${colors.highlight}`,
|
||||
bottom: '0',
|
||||
left: '0',
|
||||
pointerEvents: 'none',
|
||||
position: 'absolute',
|
||||
right: '0',
|
||||
top: '0',
|
||||
});
|
||||
|
||||
export default class FocusableBox extends Component<
|
||||
Object,
|
||||
|
||||
@@ -5,39 +5,28 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from '../styled/index.js';
|
||||
const PropTypes = require('prop-types');
|
||||
import {getIconUrl} from '../../utils/icons.js';
|
||||
|
||||
const ColoredIconBlack = styled.image(
|
||||
{
|
||||
height: props => props.size,
|
||||
verticalAlign: 'middle',
|
||||
width: props => props.size,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['size'],
|
||||
},
|
||||
);
|
||||
const ColoredIconBlack = styled('img')(({size}) => ({
|
||||
height: size,
|
||||
verticalAlign: 'middle',
|
||||
width: size,
|
||||
}));
|
||||
|
||||
const ColoredIconCustom = styled.view(
|
||||
{
|
||||
height: props => props.size,
|
||||
verticalAlign: 'middle',
|
||||
width: props => props.size,
|
||||
backgroundColor: props => props.color,
|
||||
display: 'inline-block',
|
||||
maskImage: props => `url('${props.src}')`,
|
||||
maskSize: '100% 100%',
|
||||
// $FlowFixMe: prefixed property
|
||||
WebkitMaskImage: props => `url('${props.src}')`,
|
||||
// $FlowFixMe: prefixed property
|
||||
WebkitMaskSize: '100% 100%',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['color', 'size', 'src'],
|
||||
},
|
||||
);
|
||||
const ColoredIconCustom = styled('div')(props => ({
|
||||
height: props.size,
|
||||
verticalAlign: 'middle',
|
||||
width: props.size,
|
||||
backgroundColor: props.color,
|
||||
display: 'inline-block',
|
||||
maskImage: `url('${props.src}')`,
|
||||
maskSize: '100% 100%',
|
||||
WebkitMaskImage: `url('${props.src}')`,
|
||||
WebkitMaskSize: '100% 100%',
|
||||
}));
|
||||
|
||||
export function ColoredIcon(
|
||||
props: {|
|
||||
@@ -84,7 +73,7 @@ ColoredIcon.contextTypes = {
|
||||
glyphColor: PropTypes.string,
|
||||
};
|
||||
|
||||
export default class Glyph extends styled.StylablePureComponent<{
|
||||
export default class Glyph extends React.Component<{
|
||||
name: string,
|
||||
size?: 8 | 10 | 12 | 16 | 18 | 20 | 24 | 32,
|
||||
variant?: 'filled' | 'outline',
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
const LargeHeading = styled.view({
|
||||
const LargeHeading = styled('div')({
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
lineHeight: '20px',
|
||||
@@ -15,7 +15,7 @@ const LargeHeading = styled.view({
|
||||
marginBottom: 10,
|
||||
});
|
||||
|
||||
const SmallHeading = styled.view({
|
||||
const SmallHeading = styled('div')({
|
||||
fontSize: 12,
|
||||
color: '#90949c',
|
||||
fontWeight: 'bold',
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default styled.view({
|
||||
export default styled('div')({
|
||||
backgroundColor: '#c9ced4',
|
||||
height: 1,
|
||||
margin: '5px 0',
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
import styled from '../styled/index.js';
|
||||
import {colors} from './colors.js';
|
||||
|
||||
export const inputStyle = {
|
||||
export const inputStyle = (compact: boolean) => ({
|
||||
border: `1px solid ${colors.light15}`,
|
||||
borderRadius: 4,
|
||||
font: 'inherit',
|
||||
fontSize: '1em',
|
||||
height: (props: Object) => (props.compact ? '17px' : '28px'),
|
||||
lineHeight: (props: Object) => (props.compact ? '17px' : '28px'),
|
||||
height: compact ? '17px' : '28px',
|
||||
lineHeight: compact ? '17px' : '28px',
|
||||
marginRight: 5,
|
||||
|
||||
'&:disabled': {
|
||||
@@ -22,17 +22,12 @@ export const inputStyle = {
|
||||
borderColor: '#ccc',
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const Input = styled.textInput(
|
||||
{
|
||||
...inputStyle,
|
||||
padding: props => (props.compact ? '0 5px' : '0 10px'),
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['compact'],
|
||||
},
|
||||
);
|
||||
const Input = styled('input')(({compact}) => ({
|
||||
...inputStyle(compact),
|
||||
padding: compact ? '0 5px' : '0 10px',
|
||||
}));
|
||||
|
||||
Input.defaultProps = {
|
||||
type: 'text',
|
||||
|
||||
@@ -86,11 +86,11 @@ type InteractiveState = {|
|
||||
resizingInitialCursor: ?CursorState,
|
||||
|};
|
||||
|
||||
const InteractiveContainer = styled.view({
|
||||
const InteractiveContainer = styled('div')({
|
||||
willChange: 'transform, height, width, z-index',
|
||||
});
|
||||
|
||||
export default class Interactive extends styled.StylableComponent<
|
||||
export default class Interactive extends React.Component<
|
||||
InteractiveProps,
|
||||
InteractiveState,
|
||||
> {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default styled.view({
|
||||
export default styled('div')({
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
});
|
||||
|
||||
@@ -10,18 +10,13 @@ import {colors} from './colors.js';
|
||||
import {Component} from 'react';
|
||||
import {shell} from 'electron';
|
||||
|
||||
const StyledLink = styled.text(
|
||||
{
|
||||
color: colors.highlight,
|
||||
'&:hover': {
|
||||
cursor: 'pointer',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
const StyledLink = styled('span')({
|
||||
color: colors.highlight,
|
||||
'&:hover': {
|
||||
cursor: 'pointer',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: [],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
export default class Link extends Component<{
|
||||
href: string,
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {StyledComponent} from '../styled/index.js';
|
||||
import styled from '../styled/index.js';
|
||||
import {keyframes} from 'react-emotion';
|
||||
|
||||
const animation = styled.keyframes({
|
||||
const animation = keyframes({
|
||||
'0%': {
|
||||
transform: 'rotate(0deg)',
|
||||
},
|
||||
@@ -17,23 +17,16 @@ const animation = styled.keyframes({
|
||||
},
|
||||
});
|
||||
|
||||
const LoadingIndicator: StyledComponent<{
|
||||
size?: number,
|
||||
}> = styled.view(
|
||||
{
|
||||
animation: `${animation} 1s infinite linear`,
|
||||
width: props => props.size,
|
||||
height: props => props.size,
|
||||
minWidth: props => props.size,
|
||||
minHeight: props => props.size,
|
||||
borderRadius: '50%',
|
||||
border: props => `${props.size / 6}px solid rgba(0, 0, 0, 0.2)`,
|
||||
borderLeftColor: 'rgba(0, 0, 0, 0.4)',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['size'],
|
||||
},
|
||||
);
|
||||
const LoadingIndicator = styled('div')(props => ({
|
||||
animation: `${animation} 1s infinite linear`,
|
||||
width: props.size,
|
||||
height: props.size,
|
||||
minWidth: props.size,
|
||||
minHeight: props.size,
|
||||
borderRadius: '50%',
|
||||
border: `${props.size / 6}px solid rgba(0, 0, 0, 0.2)`,
|
||||
borderLeftColor: 'rgba(0, 0, 0, 0.4)',
|
||||
}));
|
||||
|
||||
LoadingIndicator.defaultProps = {
|
||||
size: 50,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import styled from '../styled/index.js';
|
||||
import {Component} from 'react';
|
||||
|
||||
const Overlay = styled.view({
|
||||
const Overlay = styled('div')({
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
||||
bottom: 0,
|
||||
|
||||
@@ -37,19 +37,13 @@ type TabSizes = {
|
||||
[key: string]: Rect,
|
||||
};
|
||||
|
||||
const OrderableContainer = styled.view({
|
||||
const OrderableContainer = styled('div')({
|
||||
position: 'relative',
|
||||
});
|
||||
|
||||
const OrderableItemContainer = styled.view(
|
||||
{
|
||||
display: props =>
|
||||
props.orientation === 'vertical' ? 'block' : 'inline-block',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['orientation'],
|
||||
},
|
||||
);
|
||||
const OrderableItemContainer = styled('div')(props => ({
|
||||
display: props.orientation === 'vertical' ? 'block' : 'inline-block',
|
||||
}));
|
||||
|
||||
class OrderableItem extends Component<{
|
||||
orientation: OrderableOrientation,
|
||||
@@ -79,7 +73,7 @@ class OrderableItem extends Component<{
|
||||
}
|
||||
}
|
||||
|
||||
export default class Orderable extends styled.StylableComponent<
|
||||
export default class Orderable extends React.Component<
|
||||
OrderableProps,
|
||||
OrderableState,
|
||||
> {
|
||||
|
||||
@@ -5,18 +5,16 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import FlexColumn from './FlexColumn.js';
|
||||
|
||||
import React from 'react';
|
||||
import styled from '../styled/index.js';
|
||||
import FlexColumn from './FlexColumn.js';
|
||||
import FlexBox from './FlexBox.js';
|
||||
|
||||
import {colors} from './colors.js';
|
||||
import Glyph from './Glyph.js';
|
||||
|
||||
const BORDER = '1px solid #dddfe2';
|
||||
const ignoreAttributes = ['floating', 'padded'];
|
||||
|
||||
const Chevron = Glyph.extends({
|
||||
const Chevron = styled(Glyph)({
|
||||
marginRight: 4,
|
||||
marginLeft: -2,
|
||||
marginBottom: 1,
|
||||
@@ -25,7 +23,7 @@ const Chevron = Glyph.extends({
|
||||
/**
|
||||
* A Panel component.
|
||||
*/
|
||||
export default class Panel extends styled.StylableComponent<
|
||||
export default class Panel extends React.Component<
|
||||
{|
|
||||
/**
|
||||
* Class name to customise styling.
|
||||
@@ -84,46 +82,37 @@ export default class Panel extends styled.StylableComponent<
|
||||
collapsable: true,
|
||||
};
|
||||
|
||||
static PanelContainer = FlexColumn.extends(
|
||||
{
|
||||
flexShrink: 0,
|
||||
padding: props => (props.floating ? 10 : 0),
|
||||
borderBottom: props => (props.collapsed ? 'none' : BORDER),
|
||||
},
|
||||
{ignoreAttributes: ['collapsed', ...ignoreAttributes]},
|
||||
);
|
||||
static PanelContainer = styled(FlexColumn)(props => ({
|
||||
flexShrink: 0,
|
||||
padding: props.floating ? 10 : 0,
|
||||
borderBottom: props.collapsed ? 'none' : BORDER,
|
||||
}));
|
||||
|
||||
static PanelHeader = FlexBox.extends(
|
||||
{
|
||||
backgroundColor: '#f6f7f9',
|
||||
border: props => (props.floating ? BORDER : 'none'),
|
||||
borderBottom: BORDER,
|
||||
borderTopLeftRadius: 2,
|
||||
borderTopRightRadius: 2,
|
||||
justifyContent: 'space-between',
|
||||
lineHeight: '27px',
|
||||
fontWeight: 500,
|
||||
flexShrink: 0,
|
||||
padding: props => (props.padded ? '0 10px' : 0),
|
||||
'&:not(:first-child)': {
|
||||
borderTop: BORDER,
|
||||
},
|
||||
static PanelHeader = styled(FlexBox)(props => ({
|
||||
backgroundColor: '#f6f7f9',
|
||||
border: props.floating ? BORDER : 'none',
|
||||
borderBottom: BORDER,
|
||||
borderTopLeftRadius: 2,
|
||||
borderTopRightRadius: 2,
|
||||
justifyContent: 'space-between',
|
||||
lineHeight: '27px',
|
||||
fontWeight: 500,
|
||||
flexShrink: 0,
|
||||
padding: props.padded ? '0 10px' : 0,
|
||||
'&:not(:first-child)': {
|
||||
borderTop: BORDER,
|
||||
},
|
||||
{ignoreAttributes},
|
||||
);
|
||||
}));
|
||||
|
||||
static PanelBody = FlexColumn.extends(
|
||||
{
|
||||
backgroundColor: '#fff',
|
||||
border: props => (props.floating ? BORDER : 'none'),
|
||||
borderBottomLeftRadius: 2,
|
||||
borderBottomRightRadius: 2,
|
||||
borderTop: 'none',
|
||||
flexGrow: 1,
|
||||
padding: props => (props.padded ? 10 : 0),
|
||||
},
|
||||
{ignoreAttributes},
|
||||
);
|
||||
static PanelBody = styled(FlexColumn)(props => ({
|
||||
backgroundColor: '#fff',
|
||||
border: props.floating ? BORDER : 'none',
|
||||
borderBottomLeftRadius: 2,
|
||||
borderBottomRightRadius: 2,
|
||||
borderTop: 'none',
|
||||
flexGrow: 1,
|
||||
padding: props.padded ? 10 : 0,
|
||||
}));
|
||||
state = {
|
||||
collapsed: this.props.collapsed == null ? false : this.props.collapsed,
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import FlexColumn from './FlexColumn.js';
|
||||
import styled from '../styled/index.js';
|
||||
import {colors} from './colors.js';
|
||||
|
||||
const Anchor = styled.image({
|
||||
const Anchor = styled('img')({
|
||||
zIndex: 6,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
@@ -18,7 +18,7 @@ const Anchor = styled.image({
|
||||
transform: 'translate(-50%, calc(100% + 2px))',
|
||||
});
|
||||
|
||||
const PopoverContainer = FlexColumn.extends({
|
||||
const PopoverContainer = styled(FlexColumn)({
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 7,
|
||||
border: '1px solid rgba(0,0,0,0.3)',
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import styled from '../styled/index.js';
|
||||
import {Component} from 'react';
|
||||
|
||||
const IFrame = styled.customHTMLTag('iframe', {
|
||||
const IFrame = styled('iframe')({
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
border: 'none',
|
||||
|
||||
@@ -5,43 +5,30 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {StyledComponent} from '../styled/index.js';
|
||||
import Interactive from './Interactive.js';
|
||||
import FlexColumn from './FlexColumn.js';
|
||||
import {colors} from './colors';
|
||||
import {Component} from 'react';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
const SidebarInteractiveContainer = Interactive.extends({
|
||||
const SidebarInteractiveContainer = styled(Interactive)({
|
||||
flex: 'none',
|
||||
});
|
||||
|
||||
type SidebarPosition = 'left' | 'top' | 'right' | 'bottom';
|
||||
|
||||
const SidebarContainer: StyledComponent<{
|
||||
position: SidebarPosition,
|
||||
overflow?: boolean,
|
||||
}> = FlexColumn.extends(
|
||||
{
|
||||
backgroundColor: props =>
|
||||
props.backgroundColor || colors.macOSTitleBarBackgroundBlur,
|
||||
borderLeft: props =>
|
||||
props.position === 'right' ? '1px solid #b3b3b3' : 'none',
|
||||
borderTop: props =>
|
||||
props.position === 'bottom' ? '1px solid #b3b3b3' : 'none',
|
||||
borderRight: props =>
|
||||
props.position === 'left' ? '1px solid #b3b3b3' : 'none',
|
||||
borderBottom: props =>
|
||||
props.position === 'top' ? '1px solid #b3b3b3' : 'none',
|
||||
height: '100%',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
textOverflow: props => (props.overflow ? 'ellipsis' : 'auto'),
|
||||
whiteSpace: props => (props.overflow ? 'nowrap' : 'normal'),
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['backgroundColor', 'position'],
|
||||
},
|
||||
);
|
||||
const SidebarContainer = styled(FlexColumn)(props => ({
|
||||
backgroundColor: props.backgroundColor || colors.macOSTitleBarBackgroundBlur,
|
||||
borderLeft: props.position === 'right' ? '1px solid #b3b3b3' : 'none',
|
||||
borderTop: props.position === 'bottom' ? '1px solid #b3b3b3' : 'none',
|
||||
borderRight: props.position === 'left' ? '1px solid #b3b3b3' : 'none',
|
||||
borderBottom: props.position === 'top' ? '1px solid #b3b3b3' : 'none',
|
||||
height: '100%',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
textOverflow: props.overflow ? 'ellipsis' : 'auto',
|
||||
whiteSpace: props.overflow ? 'nowrap' : 'normal',
|
||||
}));
|
||||
|
||||
type SidebarProps = {
|
||||
/**
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
|
||||
import {colors} from './colors.js';
|
||||
import Label from './Label.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default Label.extends({
|
||||
export default styled(Label)({
|
||||
color: colors.blackAlpha30,
|
||||
fontSize: 12,
|
||||
padding: 10,
|
||||
|
||||
@@ -12,45 +12,38 @@ import FlexRow from './FlexRow.js';
|
||||
import {colors} from './colors.js';
|
||||
import Tab from './Tab.js';
|
||||
|
||||
const TabList = FlexRow.extends({
|
||||
const TabList = styled(FlexRow)({
|
||||
alignItems: 'stretch',
|
||||
});
|
||||
|
||||
const TabListItem = styled.view(
|
||||
{
|
||||
backgroundColor: props => (props.active ? colors.light15 : colors.light02),
|
||||
borderBottom: '1px solid #dddfe2',
|
||||
boxShadow: props =>
|
||||
props.active ? 'inset 0px 0px 3px rgba(0,0,0,0.25)' : 'none',
|
||||
color: colors.dark80,
|
||||
flex: 1,
|
||||
fontSize: 13,
|
||||
lineHeight: '28px',
|
||||
overflow: 'hidden',
|
||||
padding: '0 10px',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
const TabListItem = styled('div')(props => ({
|
||||
backgroundColor: props.active ? colors.light15 : colors.light02,
|
||||
borderBottom: '1px solid #dddfe2',
|
||||
boxShadow: props.active ? 'inset 0px 0px 3px rgba(0,0,0,0.25)' : 'none',
|
||||
color: colors.dark80,
|
||||
flex: 1,
|
||||
fontSize: 13,
|
||||
lineHeight: '28px',
|
||||
overflow: 'hidden',
|
||||
padding: '0 10px',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: props =>
|
||||
props.active ? colors.light15 : colors.light05,
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: props.active ? colors.light15 : colors.light05,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['active'],
|
||||
},
|
||||
);
|
||||
}));
|
||||
|
||||
const TabListAddItem = TabListItem.extends({
|
||||
const TabListAddItem = styled(TabListItem)({
|
||||
borderRight: 'none',
|
||||
flex: 0,
|
||||
flexGrow: 0,
|
||||
fontWeight: 'bold',
|
||||
});
|
||||
|
||||
const CloseButton = styled.view({
|
||||
const CloseButton = styled('div')({
|
||||
color: '#000',
|
||||
float: 'right',
|
||||
fontSize: 10,
|
||||
@@ -69,11 +62,11 @@ const CloseButton = styled.view({
|
||||
},
|
||||
});
|
||||
|
||||
const OrderableContainer = styled.view({
|
||||
const OrderableContainer = styled('div')({
|
||||
display: 'inline-block',
|
||||
});
|
||||
|
||||
const TabContent = styled.view({
|
||||
const TabContent = styled('div')({
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
width: '100%',
|
||||
|
||||
@@ -5,155 +5,31 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {StyledComponent} from '../styled/index.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
/**
|
||||
* A Text component.
|
||||
*/
|
||||
const Text: StyledComponent<{
|
||||
/**
|
||||
* Color of text.
|
||||
*/
|
||||
color?: string,
|
||||
/**
|
||||
* Whether this text is bold. Equivalent to the following CSS:
|
||||
*
|
||||
* font-weight: bold;
|
||||
*/
|
||||
bold?: boolean,
|
||||
/**
|
||||
* Whether this text is italic. Equivalent to the following CSS:
|
||||
*
|
||||
* font-style: italic;
|
||||
*/
|
||||
italic?: boolean,
|
||||
/**
|
||||
* Whether to format the text as code. Equivalent to the following CSS:
|
||||
*
|
||||
* font-size: Andale Mono, monospace;
|
||||
* overflow: auto;
|
||||
* user-select: text;
|
||||
* white-space: pre-wrap;
|
||||
* word-wrap: break-word;
|
||||
*/
|
||||
code?: boolean,
|
||||
/**
|
||||
* Whether this text is underlined. Equivalent to the following CSS:
|
||||
*
|
||||
* text-decoration: underline;
|
||||
*/
|
||||
underline?: boolean,
|
||||
/**
|
||||
* Whether this text is striked. Equivalent to the following CSS:
|
||||
*
|
||||
* text-decoration: line-through;
|
||||
*/
|
||||
strike?: boolean,
|
||||
/**
|
||||
* Whether this text is selectable by the cursor. Equivalent to the following CSS:
|
||||
*
|
||||
* user-select: text;
|
||||
*/
|
||||
selectable?: boolean,
|
||||
/**
|
||||
* Alignment of the text. Equivalent to the `text-align` CSS rule.
|
||||
*/
|
||||
align?: 'left' | 'center' | 'right',
|
||||
/**
|
||||
* Font size to use. Equivalent to the `font-size` CSS rule.
|
||||
*/
|
||||
size?: string | number,
|
||||
/**
|
||||
* Font family to use. Equivalent to the `font-family` CSS rule.
|
||||
*/
|
||||
family?: string,
|
||||
/**
|
||||
* Word wrap to use. Equivalent to the `word-wrap` CSS rule.
|
||||
*/
|
||||
wordWrap?: string,
|
||||
/**
|
||||
* White space to use. Equivalent to the `white-space` CSS rule.
|
||||
*/
|
||||
whiteSpace?: string,
|
||||
}> = styled.text(
|
||||
{
|
||||
color: props => (props.color ? props.color : 'inherit'),
|
||||
display: 'inline',
|
||||
fontWeight: props => (props.bold ? 'bold' : 'inherit'),
|
||||
fontStyle: props => (props.italic ? 'italic' : 'normal'),
|
||||
textAlign: props => props.align || 'left',
|
||||
fontSize: props => {
|
||||
if (props.size == null && props.code) {
|
||||
return 12;
|
||||
} else {
|
||||
return props.size;
|
||||
}
|
||||
},
|
||||
fontFamily: props => {
|
||||
if (props.code) {
|
||||
return 'SF Mono, Monaco, Andale Mono, monospace';
|
||||
} else {
|
||||
return props.family;
|
||||
}
|
||||
},
|
||||
overflow: props => {
|
||||
if (props.code) {
|
||||
return 'auto';
|
||||
} else {
|
||||
return 'visible';
|
||||
}
|
||||
},
|
||||
textDecoration: props => {
|
||||
if (props.underline) {
|
||||
return 'underline';
|
||||
} else if (props.strike) {
|
||||
return 'line-through';
|
||||
} else {
|
||||
return 'none';
|
||||
}
|
||||
},
|
||||
userSelect: props => {
|
||||
if (
|
||||
props.selectable ||
|
||||
(props.code && typeof props.selectable === 'undefined')
|
||||
) {
|
||||
return 'text';
|
||||
} else {
|
||||
return 'none';
|
||||
}
|
||||
},
|
||||
wordWrap: props => {
|
||||
if (props.code) {
|
||||
return 'break-word';
|
||||
} else {
|
||||
return props.wordWrap;
|
||||
}
|
||||
},
|
||||
whiteSpace: props => {
|
||||
if (props.code && typeof props.whiteSpace === 'undefined') {
|
||||
return 'pre';
|
||||
} else {
|
||||
return props.whiteSpace;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
ignoreAttributes: [
|
||||
'selectable',
|
||||
'whiteSpace',
|
||||
'wordWrap',
|
||||
'align',
|
||||
'code',
|
||||
'family',
|
||||
'size',
|
||||
'bold',
|
||||
'italic',
|
||||
'strike',
|
||||
'underline',
|
||||
'color',
|
||||
],
|
||||
},
|
||||
);
|
||||
const Text = styled('span')(props => ({
|
||||
color: props.color ? props.color : 'inherit',
|
||||
display: 'inline',
|
||||
fontWeight: props.bold ? 'bold' : 'inherit',
|
||||
fontStyle: props.italic ? 'italic' : 'normal',
|
||||
textAlign: props.align || 'left',
|
||||
fontSize: props.size == null && props.code ? 12 : props.size,
|
||||
fontFamily: props.code
|
||||
? 'SF Mono, Monaco, Andale Mono, monospace'
|
||||
: props.family,
|
||||
overflow: props.code ? 'auto' : 'visible',
|
||||
userSelect:
|
||||
props.selectable || (props.code && typeof props.selectable === 'undefined')
|
||||
? 'text'
|
||||
: 'none',
|
||||
wordWrap: props.code ? 'break-word' : props.wordWrap,
|
||||
whiteSpace:
|
||||
props.code && typeof props.whiteSpace === 'undefined'
|
||||
? 'pre'
|
||||
: props.whiteSpace,
|
||||
}));
|
||||
|
||||
export default Text;
|
||||
|
||||
@@ -10,7 +10,7 @@ import styled from '../styled/index.js';
|
||||
/**
|
||||
* A TextParagraph component.
|
||||
*/
|
||||
const TextParagraph = styled.view({
|
||||
const TextParagraph = styled('div')({
|
||||
marginBottom: 10,
|
||||
|
||||
'&:last-child': {
|
||||
|
||||
@@ -8,15 +8,9 @@
|
||||
import styled from '../styled/index.js';
|
||||
import {inputStyle} from './Input.js';
|
||||
|
||||
export default styled.customHTMLTag(
|
||||
'textarea',
|
||||
{
|
||||
...inputStyle,
|
||||
lineHeight: 'normal',
|
||||
padding: props => (props.compact ? '5px' : '8px'),
|
||||
resize: 'none',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['compact'],
|
||||
},
|
||||
);
|
||||
export default styled('textarea')(({compact}) => ({
|
||||
...inputStyle(compact),
|
||||
lineHeight: 'normal',
|
||||
padding: compact ? '5px' : '8px',
|
||||
resize: 'none',
|
||||
}));
|
||||
|
||||
@@ -9,11 +9,11 @@ import React from 'react';
|
||||
import styled from '../styled/index.js';
|
||||
import {colors} from './colors.js';
|
||||
|
||||
export const StyledButton = styled.view({
|
||||
export const StyledButton = styled('div')(props => ({
|
||||
cursor: 'pointer',
|
||||
width: '30px',
|
||||
height: '16px',
|
||||
background: props => (props.toggled ? colors.green : colors.grey),
|
||||
background: props.toggled ? colors.green : colors.grey,
|
||||
display: 'block',
|
||||
borderRadius: '100px',
|
||||
position: 'relative',
|
||||
@@ -22,14 +22,14 @@ export const StyledButton = styled.view({
|
||||
content: `''`,
|
||||
position: 'absolute',
|
||||
top: '3px',
|
||||
left: props => (props.toggled ? '18px' : '3px'),
|
||||
left: props.toggled ? '18px' : '3px',
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
background: 'white',
|
||||
borderRadius: '100px',
|
||||
transition: 'all cubic-bezier(0.3, 1.5, 0.7, 1) 0.3s',
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ type Props = {
|
||||
* <ToggleButton onClick={handler} toggled={boolean}/>
|
||||
* ```
|
||||
*/
|
||||
export default class ToggleButton extends styled.StylableComponent<Props> {
|
||||
export default class ToggleButton extends React.Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<StyledButton toggled={this.props.toggled} onClick={this.props.onClick} />
|
||||
|
||||
@@ -5,44 +5,33 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {StyledComponent} from '../styled/index.js';
|
||||
import {colors} from './colors.js';
|
||||
import FlexRow from './FlexRow.js';
|
||||
import FlexBox from './FlexBox.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
/**
|
||||
* A toolbar.
|
||||
*/
|
||||
const Toolbar: StyledComponent<{
|
||||
/**
|
||||
* Position of the toolbar. Dictates the location of the border.
|
||||
*/
|
||||
position?: 'top' | 'bottom',
|
||||
compact?: boolean,
|
||||
}> = FlexRow.extends(
|
||||
{
|
||||
backgroundColor: colors.light02,
|
||||
borderBottom: props =>
|
||||
props.position === 'bottom'
|
||||
? 'none'
|
||||
: `1px solid ${colors.sectionHeaderBorder}`,
|
||||
borderTop: props =>
|
||||
props.position === 'bottom'
|
||||
? `1px solid ${colors.sectionHeaderBorder}`
|
||||
: 'none',
|
||||
flexShrink: 0,
|
||||
height: props => (props.compact ? 28 : 42),
|
||||
lineHeight: '32px',
|
||||
alignItems: 'center',
|
||||
padding: 6,
|
||||
width: '100%',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['position'],
|
||||
},
|
||||
);
|
||||
const Toolbar = styled(FlexRow)(props => ({
|
||||
backgroundColor: colors.light02,
|
||||
borderBottom:
|
||||
props.position === 'bottom'
|
||||
? 'none'
|
||||
: `1px solid ${colors.sectionHeaderBorder}`,
|
||||
borderTop:
|
||||
props.position === 'bottom'
|
||||
? `1px solid ${colors.sectionHeaderBorder}`
|
||||
: 'none',
|
||||
flexShrink: 0,
|
||||
height: props.compact ? 28 : 42,
|
||||
lineHeight: '32px',
|
||||
alignItems: 'center',
|
||||
padding: 6,
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
export const Spacer = FlexBox.extends({
|
||||
export const Spacer = styled(FlexBox)({
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import {Component} from 'react';
|
||||
|
||||
const PropTypes = require('prop-types');
|
||||
|
||||
const TooltipContainer = styled.view({
|
||||
const TooltipContainer = styled('div')({
|
||||
display: 'contents',
|
||||
});
|
||||
|
||||
|
||||
@@ -10,25 +10,20 @@ import {Component} from 'react';
|
||||
|
||||
const PropTypes = require('prop-types');
|
||||
|
||||
const TooltipBubble = styled.view(
|
||||
{
|
||||
backgroundColor: '#000',
|
||||
lineHeight: '25px',
|
||||
padding: '0 6px',
|
||||
borderRadius: 4,
|
||||
position: 'absolute',
|
||||
width: 'auto',
|
||||
top: props => props.top,
|
||||
left: props => props.left,
|
||||
zIndex: 99999999999,
|
||||
pointerEvents: 'none',
|
||||
color: '#fff',
|
||||
marginTop: '-30px',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['top', 'left'],
|
||||
},
|
||||
);
|
||||
const TooltipBubble = styled('div')(props => ({
|
||||
backgroundColor: '#000',
|
||||
lineHeight: '25px',
|
||||
padding: '0 6px',
|
||||
borderRadius: 4,
|
||||
position: 'absolute',
|
||||
width: 'auto',
|
||||
top: props.top,
|
||||
left: props.left,
|
||||
zIndex: 99999999999,
|
||||
pointerEvents: 'none',
|
||||
color: '#fff',
|
||||
marginTop: '-30px',
|
||||
}));
|
||||
|
||||
type TooltipProps = {
|
||||
children: React$Node,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
export default styled.view({
|
||||
export default styled('div')({
|
||||
backgroundColor: '#c9ced4',
|
||||
width: 3,
|
||||
margin: '0',
|
||||
|
||||
@@ -7,16 +7,11 @@
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
const View = styled.view(
|
||||
{
|
||||
height: props => (props.fill ? '100%' : 'auto'),
|
||||
overflow: props => (props.scrollable ? 'auto' : 'visible'),
|
||||
position: 'relative',
|
||||
width: props => (props.fill ? '100%' : 'auto'),
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['fill', 'scrollable'],
|
||||
},
|
||||
);
|
||||
const View = styled('div')(props => ({
|
||||
height: props.fill ? '100%' : 'auto',
|
||||
overflow: props.scrollable ? 'auto' : 'visible',
|
||||
position: 'relative',
|
||||
width: props.fill ? '100%' : 'auto',
|
||||
}));
|
||||
|
||||
export default View;
|
||||
|
||||
@@ -8,33 +8,24 @@
|
||||
import FlexColumn from './FlexColumn.js';
|
||||
import {Component} from 'react';
|
||||
import View from './View.js';
|
||||
import styled from '../styled/index.js';
|
||||
|
||||
const Inner = FlexColumn.extends(
|
||||
{
|
||||
alignItems: 'flex-start',
|
||||
height: props => props.height,
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
overflow: 'visible',
|
||||
width: '100%',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['height'],
|
||||
},
|
||||
);
|
||||
const Inner = styled(FlexColumn)(({height}) => ({
|
||||
alignItems: 'flex-start',
|
||||
height,
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
overflow: 'visible',
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
const Content = FlexColumn.extends(
|
||||
{
|
||||
alignItems: 'flex-start',
|
||||
height: '100%',
|
||||
marginTop: props => props.top,
|
||||
minWidth: '100%',
|
||||
overflow: 'visible',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['top'],
|
||||
},
|
||||
);
|
||||
const Content = styled(FlexColumn)(({top}) => ({
|
||||
alignItems: 'flex-start',
|
||||
height: '100%',
|
||||
marginTop: top,
|
||||
minWidth: '100%',
|
||||
overflow: 'visible',
|
||||
}));
|
||||
|
||||
type VirtualListProps = {|
|
||||
data: Array<any>,
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
Input,
|
||||
View,
|
||||
} from '../index';
|
||||
import styled from '../styled/index';
|
||||
import type {TableBodyRow, TableRows} from 'sonar';
|
||||
import type {PluginClient} from '../../plugin';
|
||||
|
||||
@@ -50,7 +51,7 @@ class ConsoleError extends Component<{
|
||||
error: Error | string | void,
|
||||
className?: string,
|
||||
}> {
|
||||
static Container = CodeBlock.extends({
|
||||
static Container = styled(CodeBlock)({
|
||||
backgroundColor: colors.redTint,
|
||||
color: colors.red,
|
||||
overflow: 'auto',
|
||||
@@ -81,11 +82,11 @@ export class Console extends Component<Props, State> {
|
||||
},
|
||||
};
|
||||
|
||||
static Window = FlexColumn.extends({
|
||||
static Window = styled(FlexColumn)({
|
||||
padding: '15px',
|
||||
flexGrow: 1,
|
||||
});
|
||||
static Input = Input.extends({
|
||||
static Input = styled(Input)({
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
|
||||
@@ -14,55 +14,50 @@ import Popover from '../Popover.js';
|
||||
import {colors} from '../colors.js';
|
||||
import Input from '../Input.js';
|
||||
|
||||
const NullValue = styled.text({
|
||||
const NullValue = styled('span')({
|
||||
color: 'rgb(128, 128, 128)',
|
||||
});
|
||||
|
||||
const UndefinedValue = styled.text({
|
||||
const UndefinedValue = styled('span')({
|
||||
color: 'rgb(128, 128, 128)',
|
||||
});
|
||||
|
||||
const StringValue = styled.text({
|
||||
const StringValue = styled('span')({
|
||||
color: colors.cherryDark1,
|
||||
});
|
||||
|
||||
const ColorValue = styled.text({
|
||||
const ColorValue = styled('span')({
|
||||
color: colors.blueGrey,
|
||||
});
|
||||
|
||||
const SymbolValue = styled.text({
|
||||
const SymbolValue = styled('span')({
|
||||
color: 'rgb(196, 26, 22)',
|
||||
});
|
||||
|
||||
const NumberValue = styled.text({
|
||||
const NumberValue = styled('span')({
|
||||
color: colors.tealDark1,
|
||||
});
|
||||
|
||||
const ColorBox = styled.text(
|
||||
{
|
||||
backgroundColor: props => props.color,
|
||||
boxShadow: 'inset 0 0 1px rgba(0, 0, 0, 1)',
|
||||
display: 'inline-block',
|
||||
height: 12,
|
||||
marginRight: 5,
|
||||
verticalAlign: 'middle',
|
||||
width: 12,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['color'],
|
||||
},
|
||||
);
|
||||
const ColorBox = styled('span')(props => ({
|
||||
backgroundColor: props.color,
|
||||
boxShadow: 'inset 0 0 1px rgba(0, 0, 0, 1)',
|
||||
display: 'inline-block',
|
||||
height: 12,
|
||||
marginRight: 5,
|
||||
verticalAlign: 'middle',
|
||||
width: 12,
|
||||
}));
|
||||
|
||||
const FunctionKeyword = styled.text({
|
||||
const FunctionKeyword = styled('span')({
|
||||
color: 'rgb(170, 13, 145)',
|
||||
fontStyle: 'italic',
|
||||
});
|
||||
|
||||
const FunctionName = styled.text({
|
||||
const FunctionName = styled('span')({
|
||||
fontStyle: 'italic',
|
||||
});
|
||||
|
||||
const ColorPickerDescription = styled.view({
|
||||
const ColorPickerDescription = styled('div')({
|
||||
display: 'inline',
|
||||
position: 'relative',
|
||||
});
|
||||
|
||||
@@ -18,34 +18,29 @@ import {clipboard} from 'electron';
|
||||
|
||||
const deepEqual = require('deep-equal');
|
||||
|
||||
const BaseContainer = styled.view(
|
||||
{
|
||||
fontFamily: 'Menlo, monospace',
|
||||
fontSize: 11,
|
||||
lineHeight: '17px',
|
||||
filter: props => (props.disabled ? 'grayscale(100%)' : ''),
|
||||
margin: props => (props.depth === 0 ? '7.5px 0' : '0'),
|
||||
paddingLeft: 10,
|
||||
userSelect: 'text',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['depth', 'disabled'],
|
||||
},
|
||||
);
|
||||
const BaseContainer = styled('div')(props => ({
|
||||
fontFamily: 'Menlo, monospace',
|
||||
fontSize: 11,
|
||||
lineHeight: '17px',
|
||||
filter: props.disabled ? 'grayscale(100%)' : '',
|
||||
margin: props.depth === 0 ? '7.5px 0' : '0',
|
||||
paddingLeft: 10,
|
||||
userSelect: 'text',
|
||||
}));
|
||||
|
||||
const RecursiveBaseWrapper = styled.text({
|
||||
const RecursiveBaseWrapper = styled('span')({
|
||||
color: colors.red,
|
||||
});
|
||||
|
||||
const Wrapper = styled.text({
|
||||
const Wrapper = styled('span')({
|
||||
color: '#555',
|
||||
});
|
||||
|
||||
const PropertyContainer = styled.text({
|
||||
const PropertyContainer = styled('span')({
|
||||
paddingTop: '2px',
|
||||
});
|
||||
|
||||
const ExpandControl = styled.text({
|
||||
const ExpandControl = styled('span')({
|
||||
color: '#6e6e6e',
|
||||
fontSize: 10,
|
||||
marginLeft: -11,
|
||||
@@ -53,7 +48,7 @@ const ExpandControl = styled.text({
|
||||
whiteSpace: 'pre',
|
||||
});
|
||||
|
||||
export const InspectorName = styled.text({
|
||||
export const InspectorName = styled('span')({
|
||||
color: colors.grapeDark1,
|
||||
});
|
||||
|
||||
@@ -474,10 +469,10 @@ export default class DataInspector extends Component<DataInspectorProps> {
|
||||
|
||||
const keys = getSortedKeys({...value, ...diffValue});
|
||||
|
||||
const Added = styled.view({
|
||||
const Added = styled('div')({
|
||||
backgroundColor: colors.tealTint70,
|
||||
});
|
||||
const Removed = styled.view({
|
||||
const Removed = styled('div')({
|
||||
backgroundColor: colors.cherryTint70,
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import styled from '../../styled/index.js';
|
||||
import {getSortedKeys} from './utils.js';
|
||||
import {PureComponent} from 'react';
|
||||
|
||||
const PreviewContainer = styled.text({
|
||||
const PreviewContainer = styled('span')({
|
||||
fontStyle: 'italic',
|
||||
});
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {colors, darkColors} from './colors.js';
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const DesktopDropdownContainer = styled.view({
|
||||
const DesktopDropdownContainer = styled('div')({
|
||||
borderBottom: `1px solid ${darkColors.dividers}`,
|
||||
lineHeight: '25px',
|
||||
marginTop: 5,
|
||||
@@ -45,22 +45,15 @@ export function DesktopDropdown(props: {|
|
||||
);
|
||||
}
|
||||
|
||||
const DesktopDropdownItemContainer = styled.view(
|
||||
{
|
||||
listStyle: 'none',
|
||||
opacity: props => (props.onClick || props.onHover ? 1 : 0.5),
|
||||
padding: '0 20px',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: props =>
|
||||
props.onClick || props.onHover ? colors.highlight : '',
|
||||
color: props => (props.onClick || props.onHover ? '#fff' : 'inherit'),
|
||||
},
|
||||
const DesktopDropdownItemContainer = styled('div')(props => ({
|
||||
listStyle: 'none',
|
||||
opacity: props.onClick || props.onHover ? 1 : 0.5,
|
||||
padding: '0 20px',
|
||||
'&:hover': {
|
||||
backgroundColor: props.onClick || props.onHover ? colors.highlight : '',
|
||||
color: props.onClick || props.onHover ? '#fff' : 'inherit',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: [],
|
||||
},
|
||||
);
|
||||
}));
|
||||
|
||||
type DesktopDropdownItemState = {|hovered: boolean|};
|
||||
|
||||
@@ -71,7 +64,7 @@ type DesktopDropdownItemProps = {
|
||||
deactivate?: () => void,
|
||||
};
|
||||
|
||||
export class DesktopDropdownItem extends styled.StylableComponent<
|
||||
export class DesktopDropdownItem extends React.Component<
|
||||
DesktopDropdownItemProps,
|
||||
DesktopDropdownItemState,
|
||||
> {
|
||||
@@ -121,7 +114,7 @@ export class DesktopDropdownItem extends styled.StylableComponent<
|
||||
}
|
||||
}
|
||||
|
||||
export const DesktopDropdownSelectedItem = DesktopDropdownItem.extends({
|
||||
export const DesktopDropdownSelectedItem = styled(DesktopDropdownItem)({
|
||||
position: 'relative',
|
||||
|
||||
'&::before': {
|
||||
|
||||
@@ -25,54 +25,51 @@ import {FixedSizeList as List} from 'react-window';
|
||||
|
||||
const ROW_HEIGHT = 23;
|
||||
|
||||
const ElementsRowContainer = ContextMenu.extends(
|
||||
{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: props => {
|
||||
if (props.selected) {
|
||||
return colors.macOSTitleBarIconSelected;
|
||||
} else if (props.focused) {
|
||||
return colors.lime;
|
||||
} else if (props.even) {
|
||||
return colors.light02;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
color: props =>
|
||||
props.selected || props.focused ? colors.white : colors.grapeDark3,
|
||||
flexShrink: 0,
|
||||
flexWrap: 'nowrap',
|
||||
height: ROW_HEIGHT,
|
||||
minWidth: '100%',
|
||||
paddingLeft: props => (props.level - 1) * 12,
|
||||
paddingRight: 20,
|
||||
position: 'relative',
|
||||
const backgroundColor = props => {
|
||||
if (props.selected) {
|
||||
return colors.macOSTitleBarIconSelected;
|
||||
} else if (props.focused) {
|
||||
return colors.lime;
|
||||
} else if (props.even) {
|
||||
return colors.light02;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
'& *': {
|
||||
color: props =>
|
||||
props.selected || props.focused ? `${colors.white} !important` : '',
|
||||
},
|
||||
const backgroundColorHover = props => {
|
||||
if (props.selected) {
|
||||
return colors.macOSTitleBarIconSelected;
|
||||
} else if (props.focused) {
|
||||
return colors.lime;
|
||||
} else {
|
||||
return '#EBF1FB';
|
||||
}
|
||||
};
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: props => {
|
||||
if (props.selected) {
|
||||
return colors.macOSTitleBarIconSelected;
|
||||
} else if (props.focused) {
|
||||
return colors.lime;
|
||||
} else {
|
||||
return '#EBF1FB';
|
||||
}
|
||||
},
|
||||
},
|
||||
const ElementsRowContainer = styled(ContextMenu)(props => ({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: backgroundColor(props),
|
||||
color: props.selected || props.focused ? colors.white : colors.grapeDark3,
|
||||
flexShrink: 0,
|
||||
flexWrap: 'nowrap',
|
||||
height: ROW_HEIGHT,
|
||||
minWidth: '100%',
|
||||
paddingLeft: (props.level - 1) * 12,
|
||||
paddingRight: 20,
|
||||
position: 'relative',
|
||||
|
||||
'& *': {
|
||||
color: props.selected || props.focused ? `${colors.white} !important` : '',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['level', 'selected', 'even', 'focused'],
|
||||
},
|
||||
);
|
||||
|
||||
const ElementsRowDecoration = FlexRow.extends({
|
||||
'&:hover': {
|
||||
backgroundColor: backgroundColorHover(props),
|
||||
},
|
||||
}));
|
||||
|
||||
const ElementsRowDecoration = styled(FlexRow)({
|
||||
flexShrink: 0,
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
@@ -82,29 +79,24 @@ const ElementsRowDecoration = FlexRow.extends({
|
||||
top: -1,
|
||||
});
|
||||
|
||||
const ElementsLine = styled.view(
|
||||
{
|
||||
backgroundColor: colors.light20,
|
||||
height: props => props.childrenCount * ROW_HEIGHT - 4,
|
||||
position: 'absolute',
|
||||
right: 3,
|
||||
top: ROW_HEIGHT - 3,
|
||||
zIndex: 2,
|
||||
width: 2,
|
||||
borderRadius: '999em',
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['childrenCount'],
|
||||
},
|
||||
);
|
||||
const ElementsLine = styled('div')(props => ({
|
||||
backgroundColor: colors.light20,
|
||||
height: props.childrenCount * ROW_HEIGHT - 4,
|
||||
position: 'absolute',
|
||||
right: 3,
|
||||
top: ROW_HEIGHT - 3,
|
||||
zIndex: 2,
|
||||
width: 2,
|
||||
borderRadius: '999em',
|
||||
}));
|
||||
|
||||
const DecorationImage = styled.image({
|
||||
const DecorationImage = styled('img')({
|
||||
height: 12,
|
||||
marginRight: 5,
|
||||
width: 12,
|
||||
});
|
||||
|
||||
const NoShrinkText = Text.extends({
|
||||
const NoShrinkText = styled(Text)({
|
||||
flexShrink: 0,
|
||||
flexWrap: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
@@ -112,17 +104,17 @@ const NoShrinkText = Text.extends({
|
||||
fontWeight: 400,
|
||||
});
|
||||
|
||||
const ElementsRowAttributeContainer = NoShrinkText.extends({
|
||||
const ElementsRowAttributeContainer = styled(NoShrinkText)({
|
||||
color: colors.dark80,
|
||||
fontWeight: 300,
|
||||
marginLeft: 5,
|
||||
});
|
||||
|
||||
const ElementsRowAttributeKey = styled.text({
|
||||
const ElementsRowAttributeKey = styled('span')({
|
||||
color: colors.tomato,
|
||||
});
|
||||
|
||||
const ElementsRowAttributeValue = styled.text({
|
||||
const ElementsRowAttributeValue = styled('span')({
|
||||
color: colors.slateDark3,
|
||||
});
|
||||
|
||||
@@ -131,11 +123,10 @@ class PartialHighlight extends PureComponent<{
|
||||
highlighted: ?string,
|
||||
content: string,
|
||||
}> {
|
||||
static HighlightedText = styled.text({
|
||||
static HighlightedText = styled('span')(({selected}) => ({
|
||||
backgroundColor: '#ffff33',
|
||||
color: props =>
|
||||
props.selected ? `${colors.grapeDark3} !important` : 'auto',
|
||||
});
|
||||
color: selected ? `${colors.grapeDark3} !important` : 'auto',
|
||||
}));
|
||||
|
||||
render() {
|
||||
const {highlighted, content, selected} = this.props;
|
||||
@@ -380,14 +371,14 @@ class ElementsRow extends PureComponent<ElementsRowProps, ElementsRowState> {
|
||||
}
|
||||
}
|
||||
|
||||
const ElementsContainer = FlexColumn.extends({
|
||||
const ElementsContainer = styled(FlexColumn)({
|
||||
backgroundColor: colors.white,
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
overflow: 'auto',
|
||||
});
|
||||
|
||||
const ElementsBox = FlexColumn.extends({
|
||||
const ElementsBox = styled(FlexColumn)({
|
||||
alignItems: 'flex-start',
|
||||
flex: 1,
|
||||
overflow: 'auto',
|
||||
|
||||
@@ -12,36 +12,31 @@ import textContent from '../../../utils/textContent.js';
|
||||
import styled from '../../styled/index.js';
|
||||
import {colors} from '../colors.js';
|
||||
|
||||
const FilterText = styled.view(
|
||||
{
|
||||
display: 'flex',
|
||||
alignSelf: 'baseline',
|
||||
userSelect: 'none',
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
maxWidth: '100%',
|
||||
'&:hover': {
|
||||
color: colors.white,
|
||||
},
|
||||
'&:hover::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: 3,
|
||||
bottom: -2,
|
||||
left: -6,
|
||||
right: -6,
|
||||
borderRadius: '999em',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
'&:hover *': {
|
||||
color: `${colors.white} !important`,
|
||||
zIndex: 2,
|
||||
},
|
||||
const FilterText = styled('div')({
|
||||
display: 'flex',
|
||||
alignSelf: 'baseline',
|
||||
userSelect: 'none',
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
maxWidth: '100%',
|
||||
'&:hover': {
|
||||
color: colors.white,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['filterKey', 'addFilter'],
|
||||
'&:hover::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: 3,
|
||||
bottom: -2,
|
||||
left: -6,
|
||||
right: -6,
|
||||
borderRadius: '999em',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
);
|
||||
'&:hover *': {
|
||||
color: `${colors.white} !important`,
|
||||
zIndex: 2,
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
children: React.Node,
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
PureComponent,
|
||||
} from 'sonar';
|
||||
|
||||
const Containter = FlexColumn.extends({
|
||||
const Containter = styled(FlexColumn)({
|
||||
fontSize: 17,
|
||||
justifyContent: 'center',
|
||||
marginLeft: 60,
|
||||
@@ -30,12 +30,12 @@ const Containter = FlexColumn.extends({
|
||||
minWidth: 450,
|
||||
});
|
||||
|
||||
const TitleRow = FlexRow.extends({
|
||||
const TitleRow = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
marginBottom: 40,
|
||||
});
|
||||
|
||||
const Icon = FlexBox.extends({
|
||||
const Icon = styled(FlexBox)({
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: brandColors.Sonar,
|
||||
@@ -45,13 +45,13 @@ const Icon = FlexBox.extends({
|
||||
borderRadius: 6,
|
||||
});
|
||||
|
||||
const Title = Text.extends({
|
||||
const Title = styled(Text)({
|
||||
fontSize: 30,
|
||||
fontWeight: 300,
|
||||
paddingLeft: 10,
|
||||
});
|
||||
|
||||
const Button = View.extends({
|
||||
const Button = styled(View)({
|
||||
marginTop: 40,
|
||||
marginBottom: 30,
|
||||
borderRadius: 6,
|
||||
@@ -64,7 +64,7 @@ const Button = View.extends({
|
||||
alignSelf: 'flex-start',
|
||||
});
|
||||
|
||||
const Screenshot = styled.customHTMLTag('img', {
|
||||
const Screenshot = styled('img')({
|
||||
alignSelf: 'center',
|
||||
boxShadow: '0 5px 35px rgba(0,0,0,0.3)',
|
||||
borderRadius: 5,
|
||||
|
||||
@@ -13,58 +13,47 @@ import {findDOMNode} from 'react-dom';
|
||||
import {colors} from '../colors.js';
|
||||
import electron from 'electron';
|
||||
|
||||
const Token = Text.extends(
|
||||
{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
backgroundColor: props =>
|
||||
props.focused
|
||||
? colors.macOSHighlightActive
|
||||
: props.color || colors.macOSHighlight,
|
||||
borderRadius: 4,
|
||||
marginRight: 4,
|
||||
padding: 4,
|
||||
paddingLeft: 6,
|
||||
height: 21,
|
||||
color: props => (props.focused ? 'white' : 'inherit'),
|
||||
'&:active': {
|
||||
backgroundColor: colors.macOSHighlightActive,
|
||||
color: colors.white,
|
||||
},
|
||||
'&:first-of-type': {
|
||||
marginLeft: 3,
|
||||
},
|
||||
const Token = styled(Text)(props => ({
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
backgroundColor: props.focused
|
||||
? colors.macOSHighlightActive
|
||||
: props.color || colors.macOSHighlight,
|
||||
borderRadius: 4,
|
||||
marginRight: 4,
|
||||
padding: 4,
|
||||
paddingLeft: 6,
|
||||
height: 21,
|
||||
color: props.focused ? 'white' : 'inherit',
|
||||
'&:active': {
|
||||
backgroundColor: colors.macOSHighlightActive,
|
||||
color: colors.white,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['focused', 'color'],
|
||||
'&:first-of-type': {
|
||||
marginLeft: 3,
|
||||
},
|
||||
);
|
||||
}));
|
||||
|
||||
const Key = Text.extends(
|
||||
{
|
||||
position: 'relative',
|
||||
fontWeight: 500,
|
||||
paddingRight: 12,
|
||||
textTransform: 'capitalize',
|
||||
lineHeight: '21px',
|
||||
'&:after': {
|
||||
content: props => (props.type === 'exclude' ? '"≠"' : '"="'),
|
||||
paddingLeft: 5,
|
||||
position: 'absolute',
|
||||
top: -1,
|
||||
right: 0,
|
||||
fontSize: 14,
|
||||
},
|
||||
'&:active:after': {
|
||||
backgroundColor: colors.macOSHighlightActive,
|
||||
},
|
||||
const Key = styled(Text)(props => ({
|
||||
position: 'relative',
|
||||
fontWeight: 500,
|
||||
paddingRight: 12,
|
||||
textTransform: 'capitalize',
|
||||
lineHeight: '21px',
|
||||
'&:after': {
|
||||
content: props.type === 'exclude' ? '"≠"' : '"="',
|
||||
paddingLeft: 5,
|
||||
position: 'absolute',
|
||||
top: -1,
|
||||
right: 0,
|
||||
fontSize: 14,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['type', 'focused'],
|
||||
'&:active:after': {
|
||||
backgroundColor: colors.macOSHighlightActive,
|
||||
},
|
||||
);
|
||||
}));
|
||||
|
||||
const Value = Text.extends({
|
||||
const Value = styled(Text)({
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: 160,
|
||||
overflow: 'hidden',
|
||||
@@ -73,29 +62,24 @@ const Value = Text.extends({
|
||||
paddingLeft: 3,
|
||||
});
|
||||
|
||||
const Chevron = styled.view(
|
||||
{
|
||||
const Chevron = styled('div')(props => ({
|
||||
border: 0,
|
||||
paddingLeft: 3,
|
||||
paddingRight: 1,
|
||||
marginRight: 0,
|
||||
fontSize: 16,
|
||||
backgroundColor: 'transparent',
|
||||
position: 'relative',
|
||||
top: -2,
|
||||
height: 'auto',
|
||||
lineHeight: 'initial',
|
||||
color: props.focused ? colors.white : 'inherit',
|
||||
'&:hover, &:active, &:focus': {
|
||||
color: 'inherit',
|
||||
border: 0,
|
||||
paddingLeft: 3,
|
||||
paddingRight: 1,
|
||||
marginRight: 0,
|
||||
fontSize: 16,
|
||||
backgroundColor: 'transparent',
|
||||
position: 'relative',
|
||||
top: -2,
|
||||
height: 'auto',
|
||||
lineHeight: 'initial',
|
||||
color: props => (props.focused ? colors.white : 'inherit'),
|
||||
'&:hover, &:active, &:focus': {
|
||||
color: 'inherit',
|
||||
border: 0,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['focused'],
|
||||
},
|
||||
);
|
||||
}));
|
||||
|
||||
type Props = {|
|
||||
filter: Filter,
|
||||
|
||||
@@ -16,15 +16,16 @@ import FlexBox from '../FlexBox.js';
|
||||
import Glyph from '../Glyph.js';
|
||||
import FilterToken from './FilterToken.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from '../../styled/index.js';
|
||||
|
||||
const SEARCHABLE_STORAGE_KEY = (key: string) => `SEARCHABLE_STORAGE_KEY_${key}`;
|
||||
|
||||
const SearchBar = Toolbar.extends({
|
||||
const SearchBar = styled(Toolbar)({
|
||||
height: 42,
|
||||
padding: 6,
|
||||
});
|
||||
|
||||
export const SearchBox = FlexBox.extends({
|
||||
export const SearchBox = styled(FlexBox)({
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: '999em',
|
||||
border: `1px solid ${colors.light15}`,
|
||||
@@ -34,8 +35,8 @@ export const SearchBox = FlexBox.extends({
|
||||
paddingLeft: 4,
|
||||
});
|
||||
|
||||
export const SearchInput = Input.extends({
|
||||
border: props => (props.focus ? '1px solid black' : 0),
|
||||
export const SearchInput = styled(Input)(props => ({
|
||||
border: props.focus ? '1px solid black' : 0,
|
||||
padding: 0,
|
||||
fontSize: '1em',
|
||||
flexGrow: 1,
|
||||
@@ -47,9 +48,9 @@ export const SearchInput = Input.extends({
|
||||
color: colors.placeholder,
|
||||
fontWeight: 300,
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
const Clear = Text.extends({
|
||||
const Clear = styled(Text)({
|
||||
position: 'absolute',
|
||||
right: 6,
|
||||
top: '50%',
|
||||
@@ -68,14 +69,14 @@ const Clear = Text.extends({
|
||||
},
|
||||
});
|
||||
|
||||
export const SearchIcon = Glyph.extends({
|
||||
export const SearchIcon = styled(Glyph)({
|
||||
marginRight: 3,
|
||||
marginLeft: 3,
|
||||
marginTop: -1,
|
||||
minWidth: 16,
|
||||
});
|
||||
|
||||
const Actions = FlexRow.extends({
|
||||
const Actions = styled(FlexRow)({
|
||||
marginLeft: 8,
|
||||
flexShrink: 0,
|
||||
});
|
||||
|
||||
@@ -117,13 +117,11 @@ type ManagedTableState = {|
|
||||
shouldScrollToBottom: boolean,
|
||||
|};
|
||||
|
||||
/**
|
||||
* Wrapper around `Table` that handles row state.
|
||||
*
|
||||
* If you require lower level access to the state then use [`<Table>`]()
|
||||
* directly.
|
||||
*/
|
||||
class ManagedTable extends styled.StylableComponent<
|
||||
const Container = styled(FlexColumn)({
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
class ManagedTable extends React.Component<
|
||||
ManagedTableProps,
|
||||
ManagedTableState,
|
||||
> {
|
||||
@@ -446,7 +444,7 @@ class ManagedTable extends styled.StylableComponent<
|
||||
.filter(Boolean);
|
||||
|
||||
return (
|
||||
<FlexColumn style={{flexGrow: 1}}>
|
||||
<Container>
|
||||
<TableHead
|
||||
columnOrder={columnOrder}
|
||||
onColumnOrder={this.onColumnOrder}
|
||||
@@ -456,10 +454,7 @@ class ManagedTable extends styled.StylableComponent<
|
||||
columnSizes={columnSizes}
|
||||
onSort={this.onSort}
|
||||
/>
|
||||
<FlexColumn
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
}}>
|
||||
<Container>
|
||||
<AutoSizer>
|
||||
{({width, height}) => (
|
||||
<ContextMenu buildItems={this.buildContextMenuItems}>
|
||||
@@ -500,8 +495,8 @@ class ManagedTable extends styled.StylableComponent<
|
||||
</ContextMenu>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</FlexColumn>
|
||||
</FlexColumn>
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ const invariant = require('invariant');
|
||||
|
||||
type MenuTemplate = Array<Electron$MenuItemOptions>;
|
||||
|
||||
const TableHeaderArrow = styled.text({
|
||||
const TableHeaderArrow = styled('span')({
|
||||
float: 'right',
|
||||
});
|
||||
|
||||
const TableHeaderColumnInteractive = Interactive.extends({
|
||||
const TableHeaderColumnInteractive = styled(Interactive)({
|
||||
display: 'inline-block',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
@@ -39,11 +39,11 @@ const TableHeaderColumnInteractive = Interactive.extends({
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
const TableHeaderColumnContainer = styled.view({
|
||||
const TableHeaderColumnContainer = styled('div')({
|
||||
padding: '0 8px',
|
||||
});
|
||||
|
||||
const TableHeadContainer = FlexRow.extends({
|
||||
const TableHeadContainer = styled(FlexRow)({
|
||||
borderBottom: `1px solid ${colors.sectionHeaderBorder}`,
|
||||
color: colors.light50,
|
||||
flexShrink: 0,
|
||||
@@ -56,33 +56,28 @@ const TableHeadContainer = FlexRow.extends({
|
||||
zIndex: 2,
|
||||
});
|
||||
|
||||
const TableHeadColumnContainer = styled.view(
|
||||
{
|
||||
position: 'relative',
|
||||
backgroundColor: colors.white,
|
||||
flexShrink: props => (props.width === 'flex' ? 1 : 0),
|
||||
height: 23,
|
||||
lineHeight: '23px',
|
||||
fontSize: '0.85em',
|
||||
fontWeight: 500,
|
||||
width: props => (props.width === 'flex' ? '100%' : props.width),
|
||||
'&::after': {
|
||||
position: 'absolute',
|
||||
content: '""',
|
||||
right: 0,
|
||||
top: 5,
|
||||
height: 13,
|
||||
width: 1,
|
||||
background: colors.light15,
|
||||
},
|
||||
'&:last-child::after': {
|
||||
display: 'none',
|
||||
},
|
||||
const TableHeadColumnContainer = styled('div')(props => ({
|
||||
position: 'relative',
|
||||
backgroundColor: colors.white,
|
||||
flexShrink: props.width === 'flex' ? 1 : 0,
|
||||
height: 23,
|
||||
lineHeight: '23px',
|
||||
fontSize: '0.85em',
|
||||
fontWeight: 500,
|
||||
width: props.width === 'flex' ? '100%' : props.width,
|
||||
'&::after': {
|
||||
position: 'absolute',
|
||||
content: '""',
|
||||
right: 0,
|
||||
top: 5,
|
||||
height: 13,
|
||||
width: 1,
|
||||
background: colors.light15,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['width'],
|
||||
'&:last-child::after': {
|
||||
display: 'none',
|
||||
},
|
||||
);
|
||||
}));
|
||||
|
||||
const RIGHT_RESIZABLE = {right: true};
|
||||
|
||||
|
||||
@@ -20,81 +20,60 @@ import {colors} from '../colors.js';
|
||||
import {normaliseColumnWidth} from './utils.js';
|
||||
import {DEFAULT_ROW_HEIGHT} from './types';
|
||||
|
||||
const TableBodyRowContainer = FlexRow.extends(
|
||||
{
|
||||
backgroundColor: props => {
|
||||
if (props.highlighted) {
|
||||
if (props.highlightedBackgroundColor) {
|
||||
return props.highlightedBackgroundColor;
|
||||
} else {
|
||||
return colors.macOSTitleBarIconSelected;
|
||||
}
|
||||
} else {
|
||||
if (props.backgroundColor) {
|
||||
return props.backgroundColor;
|
||||
} else if (props.even && props.zebra) {
|
||||
return colors.light02;
|
||||
} else {
|
||||
return 'transparent';
|
||||
}
|
||||
}
|
||||
},
|
||||
boxShadow: props => (props.zebra ? 'none' : 'inset 0 -1px #E9EBEE'),
|
||||
color: props =>
|
||||
props.highlighted ? colors.white : props.color || 'inherit',
|
||||
'& *': {
|
||||
color: props => (props.highlighted ? `${colors.white} !important` : null),
|
||||
},
|
||||
'& img': {
|
||||
backgroundColor: props =>
|
||||
props.highlighted ? `${colors.white} !important` : 'none',
|
||||
},
|
||||
height: props => (props.multiline ? 'auto' : props.rowLineHeight),
|
||||
lineHeight: props =>
|
||||
`${String(props.rowLineHeight || DEFAULT_ROW_HEIGHT)}px`,
|
||||
fontWeight: props => props.fontWeight || 'inherit',
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
userSelect: 'none',
|
||||
flexShrink: 0,
|
||||
'&:hover': {
|
||||
backgroundColor: props =>
|
||||
!props.highlighted && props.highlightOnHover ? colors.light02 : 'none',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignoreAttributes: [
|
||||
'highlightedBackgroundColor',
|
||||
'highlightOnHover',
|
||||
'backgroundColor',
|
||||
'rowLineHeight',
|
||||
'highlighted',
|
||||
'multiline',
|
||||
'hasHover',
|
||||
'zebra',
|
||||
'even',
|
||||
],
|
||||
},
|
||||
);
|
||||
const backgroundColor = props => {
|
||||
if (props.highlighted) {
|
||||
if (props.highlightedBackgroundColor) {
|
||||
return props.highlightedBackgroundColor;
|
||||
} else {
|
||||
return colors.macOSTitleBarIconSelected;
|
||||
}
|
||||
} else {
|
||||
if (props.backgroundColor) {
|
||||
return props.backgroundColor;
|
||||
} else if (props.even && props.zebra) {
|
||||
return colors.light02;
|
||||
} else {
|
||||
return 'transparent';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const TableBodyColumnContainer = styled.view(
|
||||
{
|
||||
display: 'flex',
|
||||
flexShrink: props => (props.width === 'flex' ? 1 : 0),
|
||||
overflow: 'hidden',
|
||||
padding: '0 8px',
|
||||
userSelect: 'none',
|
||||
textOverflow: 'ellipsis',
|
||||
verticalAlign: 'top',
|
||||
whiteSpace: props => (props.multiline ? 'normal' : 'nowrap'),
|
||||
wordWrap: props => (props.multiline ? 'break-word' : 'normal'),
|
||||
width: props => (props.width === 'flex' ? '100%' : props.width),
|
||||
maxWidth: '100%',
|
||||
const TableBodyRowContainer = styled(FlexRow)(props => ({
|
||||
backgroundColor: backgroundColor(props),
|
||||
boxShadow: props.zebra ? 'none' : 'inset 0 -1px #E9EBEE',
|
||||
color: props.highlighted ? colors.white : props.color || 'inherit',
|
||||
'& *': {
|
||||
color: props.highlighted ? `${colors.white} !important` : null,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['multiline', 'width'],
|
||||
'& img': {
|
||||
backgroundColor: props.highlighted ? `${colors.white} !important` : 'none',
|
||||
},
|
||||
);
|
||||
height: props.multiline ? 'auto' : props.rowLineHeight,
|
||||
lineHeight: `${String(props.rowLineHeight || DEFAULT_ROW_HEIGHT)}px`,
|
||||
fontWeight: props.fontWeight || 'inherit',
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
userSelect: 'none',
|
||||
flexShrink: 0,
|
||||
'&:hover': {
|
||||
backgroundColor:
|
||||
!props.highlighted && props.highlightOnHover ? colors.light02 : 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
const TableBodyColumnContainer = styled('div')(props => ({
|
||||
display: 'flex',
|
||||
flexShrink: props.width === 'flex' ? 1 : 0,
|
||||
overflow: 'hidden',
|
||||
padding: '0 8px',
|
||||
userSelect: 'none',
|
||||
textOverflow: 'ellipsis',
|
||||
verticalAlign: 'top',
|
||||
whiteSpace: props.multiline ? 'normal' : 'nowrap',
|
||||
wordWrap: props.multiline ? 'break-word' : 'normal',
|
||||
width: props.width === 'flex' ? '100%' : props.width,
|
||||
maxWidth: '100%',
|
||||
}));
|
||||
|
||||
type Props = {
|
||||
columnSizes: TableColumnSizes,
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
export type {StyledComponent} from './styled/index.js';
|
||||
|
||||
//
|
||||
export {default as Button} from './components/Button.js';
|
||||
export {default as ToggleButton} from './components/ToggleSwitch.js';
|
||||
export {
|
||||
@@ -26,10 +24,6 @@ export {default as LoadingIndicator} from './components/LoadingIndicator.js';
|
||||
//
|
||||
export {default as Popover} from './components/Popover.js';
|
||||
|
||||
//
|
||||
export {default as ClickableList} from './components/ClickableList.js';
|
||||
export {default as ClickableListItem} from './components/ClickableListItem.js';
|
||||
|
||||
//
|
||||
export type {
|
||||
TableColumns,
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {Tracker} from '../index.js';
|
||||
import {GarbageCollector} from '../gc.js';
|
||||
import {StyleSheet} from '../sheet.js';
|
||||
|
||||
function createGC(): {|
|
||||
gc: GarbageCollector,
|
||||
tracker: Tracker,
|
||||
|} {
|
||||
const sheet = new StyleSheet();
|
||||
const tracker = new Map();
|
||||
const rulesToClass = new WeakMap();
|
||||
|
||||
const gc = new GarbageCollector(sheet, tracker, rulesToClass);
|
||||
return {gc, tracker};
|
||||
}
|
||||
|
||||
test('register classes to be garbage collected when no references exist', () => {
|
||||
const {gc} = createGC();
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
expect(gc.getCollectionQueue()).toEqual([]);
|
||||
|
||||
gc.deregisterClassUse('foo');
|
||||
expect(gc.getCollectionQueue()).toEqual(['foo']);
|
||||
});
|
||||
|
||||
test('cancel garbage collection for classes used before actual collection happens', () => {
|
||||
const {gc} = createGC();
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
expect(gc.getCollectionQueue()).toEqual([]);
|
||||
|
||||
gc.deregisterClassUse('foo');
|
||||
expect(gc.getCollectionQueue()).toEqual(['foo']);
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
expect(gc.getCollectionQueue()).toEqual([]);
|
||||
});
|
||||
|
||||
test('garbage collector removes unreferenced classes', () => {
|
||||
const {gc, tracker} = createGC();
|
||||
|
||||
tracker.set('foo', {
|
||||
displayName: 'foo',
|
||||
namespace: '',
|
||||
selector: '',
|
||||
style: {},
|
||||
rules: {},
|
||||
});
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
expect(gc.getCollectionQueue()).toEqual([]);
|
||||
|
||||
gc.deregisterClassUse('foo');
|
||||
expect(gc.getCollectionQueue()).toEqual(['foo']);
|
||||
expect(gc.hasQueuedCollection()).toBe(true);
|
||||
expect(tracker.has('foo')).toBe(true);
|
||||
|
||||
gc.collectGarbage();
|
||||
expect(gc.hasQueuedCollection()).toBe(false);
|
||||
expect(gc.getCollectionQueue()).toEqual([]);
|
||||
expect(tracker.has('foo')).toBe(false);
|
||||
});
|
||||
|
||||
test('properly tracks reference counts', () => {
|
||||
const {gc} = createGC();
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
gc.registerClassUse('foo');
|
||||
gc.registerClassUse('bar');
|
||||
expect(gc.getReferenceCount('foo')).toBe(2);
|
||||
expect(gc.getReferenceCount('bar')).toBe(1);
|
||||
|
||||
gc.deregisterClassUse('bar');
|
||||
expect(gc.getReferenceCount('bar')).toBe(0);
|
||||
|
||||
gc.deregisterClassUse('foo');
|
||||
expect(gc.getReferenceCount('foo')).toBe(1);
|
||||
|
||||
gc.deregisterClassUse('foo');
|
||||
expect(gc.getReferenceCount('foo')).toBe(0);
|
||||
});
|
||||
|
||||
test("gracefully handle deregistering classes we don't have a count for", () => {
|
||||
const {gc} = createGC();
|
||||
gc.deregisterClassUse('not-tracking');
|
||||
});
|
||||
|
||||
test('only halt garbage collection if there is nothing left in the queue', () => {
|
||||
const {gc} = createGC();
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
expect(gc.hasQueuedCollection()).toBe(false);
|
||||
|
||||
gc.deregisterClassUse('foo');
|
||||
expect(gc.hasQueuedCollection()).toBe(true);
|
||||
|
||||
gc.registerClassUse('bar');
|
||||
expect(gc.hasQueuedCollection()).toBe(true);
|
||||
|
||||
gc.deregisterClassUse('bar');
|
||||
expect(gc.hasQueuedCollection()).toBe(true);
|
||||
|
||||
gc.registerClassUse('bar');
|
||||
expect(gc.hasQueuedCollection()).toBe(true);
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
expect(gc.hasQueuedCollection()).toBe(false);
|
||||
});
|
||||
|
||||
test('ensure garbage collection happens', () => {
|
||||
const {gc} = createGC();
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
gc.deregisterClassUse('foo');
|
||||
expect(gc.hasQueuedCollection()).toBe(true);
|
||||
expect(gc.getCollectionQueue()).toEqual(['foo']);
|
||||
|
||||
jest.runAllTimers();
|
||||
expect(gc.hasQueuedCollection()).toBe(false);
|
||||
expect(gc.getCollectionQueue()).toEqual([]);
|
||||
});
|
||||
|
||||
test('flush', () => {
|
||||
const {gc} = createGC();
|
||||
|
||||
gc.registerClassUse('bar');
|
||||
gc.deregisterClassUse('bar');
|
||||
expect(gc.getCollectionQueue()).toEqual(['bar']);
|
||||
expect(gc.getReferenceCount('bar')).toBe(0);
|
||||
|
||||
gc.registerClassUse('foo');
|
||||
expect(gc.getReferenceCount('foo')).toBe(1);
|
||||
|
||||
gc.flush();
|
||||
expect(gc.getCollectionQueue()).toEqual([]);
|
||||
expect(gc.getReferenceCount('foo')).toBe(0);
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import hash from '../hash.js';
|
||||
|
||||
test('hash', () => {
|
||||
expect(hash('f')).toBe('1xwd1rk');
|
||||
expect(hash('foobar')).toBe('slolri');
|
||||
expect(hash('foobar2')).toBe('34u6r4');
|
||||
});
|
||||
@@ -1,387 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import styled, {buildKeyframes, flush, gc, tracker} from '../index.js';
|
||||
|
||||
const ReactTestRenderer = require('react-test-renderer');
|
||||
const invariant = require('invariant');
|
||||
const React = require('react'); // eslint-disable-line
|
||||
|
||||
const BasicComponent = styled.view({
|
||||
color: 'red',
|
||||
});
|
||||
|
||||
const DynamicComponent = styled.view({
|
||||
color: props => props.color,
|
||||
});
|
||||
|
||||
test('can create a basic component without any errors', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
component = ReactTestRenderer.create(<BasicComponent />);
|
||||
component.toJSON();
|
||||
component.unmount();
|
||||
gc.flush();
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('can create a basic component and garbage collect', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
component = ReactTestRenderer.create(<BasicComponent />);
|
||||
const tree = component.toJSON();
|
||||
|
||||
expect(tree.type).toBe('div');
|
||||
|
||||
const className = tree.props.className;
|
||||
expect(gc.hasQueuedCollection()).toBe(false);
|
||||
expect(gc.getReferenceCount(className)).toBe(1);
|
||||
|
||||
component.unmount();
|
||||
expect(gc.getReferenceCount(className)).toBe(0);
|
||||
expect(gc.hasQueuedCollection()).toBe(true);
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('remove outdated classes when updating component', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
component = ReactTestRenderer.create(<DynamicComponent color="red" />);
|
||||
const tree = component.toJSON();
|
||||
|
||||
const className = tree.props.className;
|
||||
expect(gc.hasQueuedCollection()).toBe(false);
|
||||
expect(gc.getReferenceCount(className)).toBe(1);
|
||||
|
||||
// updating with the same props should generate the same style and not trigger a collection
|
||||
component.update(<DynamicComponent color="red" />);
|
||||
expect(gc.hasQueuedCollection()).toBe(false);
|
||||
expect(gc.getReferenceCount(className)).toBe(1);
|
||||
|
||||
// change style
|
||||
component.update(<DynamicComponent color="blue" />);
|
||||
expect(gc.hasQueuedCollection()).toBe(true);
|
||||
expect(gc.getReferenceCount(className)).toBe(0);
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('extra class names should be preserved', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
component = ReactTestRenderer.create(<BasicComponent className="foo" />);
|
||||
const tree = component.toJSON();
|
||||
expect(tree.props.className.split(' ').includes('foo')).toBe(true);
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('should inherit component when passed as first arg to styled', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
const InheritComponent = BasicComponent.extends({
|
||||
backgroundColor: 'black',
|
||||
});
|
||||
|
||||
component = ReactTestRenderer.create(<InheritComponent />);
|
||||
const tree = component.toJSON();
|
||||
|
||||
const rules = tracker.get(tree.props.className);
|
||||
invariant(rules, 'expected rules');
|
||||
expect(rules.style).toEqual({
|
||||
'background-color': 'black',
|
||||
color: 'red',
|
||||
});
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('when passed class name of another styled component its rules should be inherited', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
class BaseComponent extends styled.StylableComponent<{
|
||||
className: string,
|
||||
}> {
|
||||
render() {
|
||||
return <BasicComponent className={this.props.className} />;
|
||||
}
|
||||
}
|
||||
|
||||
const InheritComponent = BaseComponent.extends({
|
||||
backgroundColor: 'black',
|
||||
});
|
||||
|
||||
component = ReactTestRenderer.create(<InheritComponent />);
|
||||
const tree = component.toJSON();
|
||||
|
||||
const rules = tracker.get(tree.props.className);
|
||||
invariant(rules, 'expected rules');
|
||||
expect(rules.style).toEqual({
|
||||
'background-color': 'black',
|
||||
color: 'red',
|
||||
});
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('supports pseudo selectors', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
const Component = styled.view({
|
||||
'&:hover': {
|
||||
color: 'red',
|
||||
},
|
||||
});
|
||||
|
||||
component = ReactTestRenderer.create(<Component />);
|
||||
const tree = component.toJSON();
|
||||
|
||||
const rules = tracker.get(tree.props.className);
|
||||
invariant(rules, 'expected rules');
|
||||
expect(rules.style).toEqual({
|
||||
color: 'red',
|
||||
});
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('supports multiple pseudo selectors', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
const Component = styled.view({
|
||||
'&:active': {
|
||||
color: 'blue',
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
color: 'red',
|
||||
},
|
||||
});
|
||||
|
||||
component = ReactTestRenderer.create(<Component />);
|
||||
const tree = component.toJSON();
|
||||
|
||||
const classes = tree.props.className.split(' ');
|
||||
expect(classes.length).toBe(2);
|
||||
|
||||
const hoverRules = tracker.get(classes[1]);
|
||||
invariant(hoverRules, 'expected hoverRules');
|
||||
expect(hoverRules.style).toEqual({
|
||||
color: 'red',
|
||||
});
|
||||
expect(hoverRules.namespace).toBe('&:hover');
|
||||
expect(hoverRules.selector.endsWith(':hover')).toBe(true);
|
||||
|
||||
const activeRules = tracker.get(classes[0]);
|
||||
invariant(activeRules, 'expected activeRules');
|
||||
expect(activeRules.style).toEqual({
|
||||
color: 'blue',
|
||||
});
|
||||
expect(activeRules.namespace).toBe('&:active');
|
||||
expect(activeRules.selector.endsWith(':active')).toBe(true);
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('supports child selectors', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
const Component = styled.view({
|
||||
'> li': {
|
||||
color: 'red',
|
||||
},
|
||||
});
|
||||
|
||||
component = ReactTestRenderer.create(<Component />);
|
||||
const tree = component.toJSON();
|
||||
|
||||
const classes = tree.props.className.split(' ');
|
||||
expect(classes.length).toBe(1);
|
||||
|
||||
const rules = tracker.get(classes[0]);
|
||||
invariant(rules, 'expected rules');
|
||||
|
||||
expect(rules.style).toEqual({
|
||||
color: 'red',
|
||||
});
|
||||
expect(rules.namespace).toBe('> li');
|
||||
expect(rules.selector.endsWith(' > li')).toBe(true);
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('flush', () => {
|
||||
flush();
|
||||
});
|
||||
|
||||
test('innerRef works on styled components', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
const Component = styled.view({});
|
||||
|
||||
let called = false;
|
||||
const innerRef = ref => {
|
||||
called = true;
|
||||
};
|
||||
ReactTestRenderer.create(<Component innerRef={innerRef} />);
|
||||
expect(called).toBe(true);
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
|
||||
test('ignoreAttributes', () => {
|
||||
let component;
|
||||
|
||||
try {
|
||||
const Component = styled.view(
|
||||
{
|
||||
color: props => props.color,
|
||||
},
|
||||
{
|
||||
ignoreAttributes: ['color'],
|
||||
},
|
||||
);
|
||||
|
||||
component = ReactTestRenderer.create(<Component color="red" />);
|
||||
const tree = component.toJSON();
|
||||
|
||||
expect(tree.props.color).toBe(undefined);
|
||||
|
||||
const rules = tracker.get(tree.props.className);
|
||||
invariant(rules, 'expected rules');
|
||||
expect(rules.style).toEqual({
|
||||
color: 'red',
|
||||
});
|
||||
} finally {
|
||||
if (component) {
|
||||
component.unmount();
|
||||
}
|
||||
gc.flush();
|
||||
}
|
||||
});
|
||||
test('buildKeyframes', () => {
|
||||
const css = buildKeyframes({
|
||||
'0%': {
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'50%': {
|
||||
height: 50,
|
||||
opacity: 0.8,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
expect(css).toBe(
|
||||
[
|
||||
' 0% {',
|
||||
' opacity: 0;',
|
||||
' }',
|
||||
' 50% {',
|
||||
' height: 50px;',
|
||||
' opacity: 0.8;',
|
||||
' }',
|
||||
' 100% {',
|
||||
' opacity: 1;',
|
||||
' }',
|
||||
].join('\n'),
|
||||
);
|
||||
});
|
||||
|
||||
test('keyframes', () => {
|
||||
const className = styled.keyframes({
|
||||
'0%': {
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'50%': {
|
||||
opacity: 0.8,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
expect(typeof className).toBe('string');
|
||||
});
|
||||
|
||||
test('buildKeyframes only accepts string property values', () => {
|
||||
expect(() => {
|
||||
buildKeyframes({
|
||||
// $FlowFixMe: ignore
|
||||
'0%': {
|
||||
fn: () => {},
|
||||
},
|
||||
});
|
||||
}).toThrow('Keyframe objects must only have strings values');
|
||||
});
|
||||
|
||||
test('buildKeyframes only accepts object specs', () => {
|
||||
expect(() => {
|
||||
buildKeyframes({
|
||||
// $FlowFixMe: ignore
|
||||
'0%': () => {
|
||||
return '';
|
||||
},
|
||||
});
|
||||
}).toThrow('Keyframe spec must only have objects');
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {buildRules, normaliseRules} from '../rules.js';
|
||||
|
||||
describe('normaliseRules', () => {
|
||||
test('ensure top level values are expanded', () => {
|
||||
const normalisedRules = normaliseRules({height: '4px'});
|
||||
expect(normalisedRules['&'].height).toBe('4px');
|
||||
});
|
||||
|
||||
test('ensure keys are dashed', () => {
|
||||
const normalisedRules = normaliseRules({
|
||||
// $FlowFixMe: ignore
|
||||
'&:hover': {
|
||||
lineHeight: '4px',
|
||||
WebkitAppRegion: 'drag',
|
||||
},
|
||||
});
|
||||
const hoverRules = normalisedRules['&:hover'];
|
||||
expect(Object.keys(hoverRules).length).toBe(2);
|
||||
expect(hoverRules['line-height']).toBe('4px');
|
||||
expect(hoverRules['-webkit-app-region']).toBe('drag');
|
||||
});
|
||||
|
||||
test('exclude empty objects', () => {
|
||||
const normalisedRules = normaliseRules({
|
||||
'&:hover': {},
|
||||
});
|
||||
|
||||
expect(normalisedRules['&:hover']).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildRules', () => {
|
||||
test('ensure null values are left out', () => {
|
||||
const builtRules = buildRules({height: (null: any)}, {}, {});
|
||||
expect('height' in builtRules).toBe(false);
|
||||
|
||||
const builtRules2 = buildRules(
|
||||
{
|
||||
height() {
|
||||
return (null: any);
|
||||
},
|
||||
},
|
||||
{},
|
||||
{},
|
||||
);
|
||||
expect('height' in builtRules2).toBe(false);
|
||||
});
|
||||
|
||||
test('ensure numbers are appended with px', () => {
|
||||
expect(buildRules({height: 40}, {}, {}).height).toBe('40px');
|
||||
});
|
||||
|
||||
test("ensure unitless numbers aren't appended with px", () => {
|
||||
expect(buildRules({'z-index': 4}, {}, {})['z-index']).toBe('4');
|
||||
});
|
||||
|
||||
test('ensure functions are called with props', () => {
|
||||
const thisProps = {};
|
||||
expect(
|
||||
buildRules(
|
||||
{
|
||||
border: props => (props === thisProps ? 'foo' : 'bar'),
|
||||
},
|
||||
thisProps,
|
||||
{},
|
||||
).border,
|
||||
).toBe('foo');
|
||||
});
|
||||
});
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {StyleSheet} from '../sheet.js';
|
||||
|
||||
describe('flush', () => {
|
||||
test('should remove all rules', () => {
|
||||
const sheet = new StyleSheet();
|
||||
expect(sheet.getRuleCount()).toBe(0);
|
||||
|
||||
sheet.insert('foo', 'div {color: red;}');
|
||||
expect(sheet.getRuleCount()).toBe(1);
|
||||
|
||||
sheet.flush();
|
||||
expect(sheet.getRuleCount()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inject', () => {
|
||||
test("throw's an error when already injected", () => {
|
||||
const sheet = new StyleSheet();
|
||||
|
||||
expect(() => {
|
||||
sheet.inject();
|
||||
sheet.inject();
|
||||
}).toThrow('already injected stylesheet!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('insert', () => {
|
||||
test('non-speedy', () => {
|
||||
const sheet = new StyleSheet();
|
||||
|
||||
expect(sheet.getRuleCount()).toBe(0);
|
||||
sheet.insert('foo', 'div {color: red;}');
|
||||
expect(sheet.getRuleCount()).toBe(1);
|
||||
});
|
||||
|
||||
test('speedy', () => {
|
||||
const sheet = new StyleSheet(true);
|
||||
|
||||
expect(sheet.getRuleCount()).toBe(0);
|
||||
sheet.insert('foo', 'div {color: red;}');
|
||||
expect(sheet.getRuleCount()).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
test('non-speedy', () => {
|
||||
const sheet = new StyleSheet();
|
||||
|
||||
expect(sheet.getRuleCount()).toBe(0);
|
||||
sheet.insert('foo', 'div {color: red;}');
|
||||
expect(sheet.getRuleCount()).toBe(1);
|
||||
|
||||
sheet.delete('foo');
|
||||
expect(sheet.getRuleCount()).toBe(0);
|
||||
});
|
||||
|
||||
test('speedy', () => {
|
||||
const sheet = new StyleSheet(true);
|
||||
|
||||
expect(sheet.getRuleCount()).toBe(0);
|
||||
sheet.insert('foo', 'div {color: red;}');
|
||||
expect(sheet.getRuleCount()).toBe(1);
|
||||
|
||||
sheet.delete('foo');
|
||||
expect(sheet.getRuleCount()).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -1,114 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {Tracker, RulesToClass} from './index.js';
|
||||
import type {StyleSheet} from './sheet.js';
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
export class GarbageCollector {
|
||||
constructor(sheet: StyleSheet, tracker: Tracker, rulesToClass: RulesToClass) {
|
||||
this.sheet = sheet;
|
||||
this.tracker = tracker;
|
||||
|
||||
// used to keep track of what classes are actively in use
|
||||
this.usedClasses = new Map();
|
||||
|
||||
// classes to be removed, we put this in a queue and perform it in bulk rather than straight away
|
||||
// since by the time the next tick happens this style could have been reinserted
|
||||
this.classRemovalQueue = new Set();
|
||||
|
||||
this.rulesToClass = rulesToClass;
|
||||
}
|
||||
|
||||
tracker: Tracker;
|
||||
sheet: StyleSheet;
|
||||
usedClasses: Map<string, number>;
|
||||
garbageTimer: ?TimeoutID;
|
||||
classRemovalQueue: Set<string>;
|
||||
rulesToClass: RulesToClass;
|
||||
|
||||
hasQueuedCollection(): boolean {
|
||||
return Boolean(this.garbageTimer);
|
||||
}
|
||||
|
||||
getReferenceCount(key: string): number {
|
||||
return this.usedClasses.get(key) || 0;
|
||||
}
|
||||
|
||||
// component has been mounted so make sure it's being depended on
|
||||
registerClassUse(name: string) {
|
||||
const count = this.usedClasses.get(name) || 0;
|
||||
this.usedClasses.set(name, count + 1);
|
||||
if (this.classRemovalQueue.has(name)) {
|
||||
this.classRemovalQueue.delete(name);
|
||||
|
||||
if (this.classRemovalQueue.size === 0) {
|
||||
this.haltGarbage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// component has been unmounted so remove its dependencies
|
||||
deregisterClassUse(name: string) {
|
||||
let count = this.usedClasses.get(name);
|
||||
if (count == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
count--;
|
||||
this.usedClasses.set(name, count);
|
||||
|
||||
if (count === 0) {
|
||||
this.classRemovalQueue.add(name);
|
||||
this.scheduleGarbage();
|
||||
}
|
||||
}
|
||||
|
||||
scheduleGarbage() {
|
||||
if (this.garbageTimer != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.garbageTimer = setTimeout(() => {
|
||||
this.collectGarbage();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
haltGarbage() {
|
||||
if (this.garbageTimer) {
|
||||
clearTimeout(this.garbageTimer);
|
||||
this.garbageTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
getCollectionQueue(): Array<string> {
|
||||
return Array.from(this.classRemovalQueue);
|
||||
}
|
||||
|
||||
collectGarbage() {
|
||||
this.haltGarbage();
|
||||
for (const name of this.classRemovalQueue) {
|
||||
const trackerInfo = this.tracker.get(name);
|
||||
invariant(trackerInfo != null, 'trying to remove unknown class');
|
||||
|
||||
const {rules} = trackerInfo;
|
||||
this.rulesToClass.delete(rules);
|
||||
|
||||
this.sheet.delete(name);
|
||||
this.tracker.delete(name);
|
||||
this.usedClasses.delete(name);
|
||||
}
|
||||
this.classRemovalQueue.clear();
|
||||
}
|
||||
|
||||
flush() {
|
||||
this.haltGarbage();
|
||||
this.classRemovalQueue.clear();
|
||||
this.usedClasses.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
export default function hash(str: string): string {
|
||||
const m = 0x5bd1e995;
|
||||
const r = 24;
|
||||
let h = str.length;
|
||||
let length = str.length;
|
||||
let currentIndex = 0;
|
||||
|
||||
while (length >= 4) {
|
||||
let k = UInt32(str, currentIndex);
|
||||
|
||||
k = Umul32(k, m);
|
||||
k ^= k >>> r;
|
||||
k = Umul32(k, m);
|
||||
|
||||
h = Umul32(h, m);
|
||||
h ^= k;
|
||||
|
||||
currentIndex += 4;
|
||||
length -= 4;
|
||||
}
|
||||
|
||||
switch (length) {
|
||||
case 3:
|
||||
h ^= UInt16(str, currentIndex);
|
||||
h ^= str.charCodeAt(currentIndex + 2) << 16;
|
||||
h = Umul32(h, m);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
h ^= UInt16(str, currentIndex);
|
||||
h = Umul32(h, m);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
h ^= str.charCodeAt(currentIndex);
|
||||
h = Umul32(h, m);
|
||||
break;
|
||||
}
|
||||
|
||||
h ^= h >>> 13;
|
||||
h = Umul32(h, m);
|
||||
h ^= h >>> 15;
|
||||
|
||||
return (h >>> 0).toString(36);
|
||||
}
|
||||
|
||||
function UInt32(str: string, pos: number): number {
|
||||
return (
|
||||
str.charCodeAt(pos++) +
|
||||
(str.charCodeAt(pos++) << 8) +
|
||||
(str.charCodeAt(pos++) << 16) +
|
||||
(str.charCodeAt(pos) << 24)
|
||||
);
|
||||
}
|
||||
|
||||
function UInt16(str: string, pos: number): number {
|
||||
return str.charCodeAt(pos++) + (str.charCodeAt(pos++) << 8);
|
||||
}
|
||||
|
||||
function Umul32(n: number, m: number): number {
|
||||
n |= 0;
|
||||
m |= 0;
|
||||
const nlo = n & 0xffff;
|
||||
const nhi = n >>> 16;
|
||||
const res = (nlo * m + (((nhi * m) & 0xffff) << 16)) | 0;
|
||||
return res;
|
||||
}
|
||||
@@ -5,436 +5,5 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {BaseRules, KeyframeRules, RawRules} from './rules.js';
|
||||
import {buildKeyframeRules, buildRules, normaliseRules} from './rules.js';
|
||||
import assignDeep from '../../utils/assignDeep.js';
|
||||
import * as performance from '../../utils/performance.js';
|
||||
import {GarbageCollector} from './gc.js';
|
||||
import {StyleSheet} from './sheet.js';
|
||||
import hash from './hash.js';
|
||||
|
||||
const React = require('react');
|
||||
|
||||
export type Tracker = Map<
|
||||
string,
|
||||
{
|
||||
displayName: ?string,
|
||||
namespace: string,
|
||||
rules: BaseRules,
|
||||
selector: string,
|
||||
style: Object,
|
||||
},
|
||||
>;
|
||||
|
||||
export type RulesToClass = WeakMap<BaseRules, string>;
|
||||
|
||||
// map of inserted classes and metadata about them
|
||||
export const tracker: Tracker = new Map();
|
||||
|
||||
// map of rules to their class
|
||||
const rulesToClass: RulesToClass = new WeakMap();
|
||||
|
||||
export const sheet = new StyleSheet(process.env.NODE_ENV === 'production');
|
||||
export const gc = new GarbageCollector(sheet, tracker, rulesToClass);
|
||||
|
||||
function addRules(
|
||||
displayName: string,
|
||||
rules: BaseRules,
|
||||
namespace,
|
||||
props: Object,
|
||||
context: Object,
|
||||
) {
|
||||
// if these rules have been cached to a className then retrieve it
|
||||
const cachedClass = rulesToClass.get(rules);
|
||||
if (cachedClass != null) {
|
||||
return cachedClass;
|
||||
}
|
||||
|
||||
//
|
||||
const declarations = [];
|
||||
const style = buildRules(rules, props, context);
|
||||
|
||||
// generate css declarations based on the style object
|
||||
for (const key in style) {
|
||||
const val = style[key];
|
||||
declarations.push(` ${key}: ${val};`);
|
||||
}
|
||||
const css = declarations.join('\n');
|
||||
|
||||
// build the class name with the display name of the styled component and a unique id based on the css and namespace
|
||||
const className = displayName + '__' + hash(namespace + css);
|
||||
|
||||
// this is the first time we've found this className
|
||||
if (!tracker.has(className)) {
|
||||
// build up the correct selector, explode on commas to allow multiple selectors
|
||||
const selector = namespace
|
||||
.split(', ')
|
||||
.map(part => {
|
||||
if (part[0] === '&') {
|
||||
return '.' + className + part.slice(1);
|
||||
} else {
|
||||
return '.' + className + ' ' + part;
|
||||
}
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
// insert the new style text
|
||||
tracker.set(className, {displayName, namespace, rules, selector, style});
|
||||
sheet.insert(className, `${selector} {\n${css}\n}`);
|
||||
|
||||
// if there's no dynamic rules then cache this
|
||||
if (hasDynamicRules(rules) === false) {
|
||||
rulesToClass.set(rules, className);
|
||||
}
|
||||
}
|
||||
|
||||
return className;
|
||||
}
|
||||
|
||||
// remove all styhles
|
||||
export function flush() {
|
||||
gc.flush();
|
||||
tracker.clear();
|
||||
sheet.flush();
|
||||
}
|
||||
|
||||
export type TagName = string | Function;
|
||||
|
||||
type StyledComponentState = {|
|
||||
extraClassNames: Array<string>,
|
||||
classNames: Array<string>,
|
||||
lastBuiltRules: ?Object,
|
||||
lastBuiltRulesIsDynamic: boolean,
|
||||
|};
|
||||
|
||||
export class StylableComponent<
|
||||
Props = void,
|
||||
State = void,
|
||||
> extends React.Component<Props, State> {
|
||||
static extends(
|
||||
rules: RawRules,
|
||||
opts?: StyledComponentOpts,
|
||||
): StyledComponent<any> {
|
||||
return createStyledComponent(this, rules, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class StylablePureComponent<
|
||||
Props = void,
|
||||
State = void,
|
||||
> extends React.PureComponent<Props, State> {
|
||||
static extends(
|
||||
rules: RawRules,
|
||||
opts?: StyledComponentOpts,
|
||||
): StyledComponent<any> {
|
||||
return createStyledComponent(this, rules, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class StyledComponentBase<Props> extends React.PureComponent<
|
||||
Props,
|
||||
StyledComponentState,
|
||||
> {
|
||||
constructor(props: Props, context: Object): void {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
classNames: [],
|
||||
extraClassNames: [],
|
||||
lastBuiltRulesIsDynamic: false,
|
||||
lastBuiltRules: null,
|
||||
};
|
||||
}
|
||||
|
||||
static defaultProps: ?$Shape<Props>;
|
||||
|
||||
static STYLED_CONFIG: {|
|
||||
tagName: TagName,
|
||||
ignoreAttributes: ?Array<string>,
|
||||
builtRules: any,
|
||||
|};
|
||||
|
||||
static extends(
|
||||
rules: RawRules,
|
||||
opts?: StyledComponentOpts,
|
||||
): StyledComponent<any> {
|
||||
return createStyledComponent(this, rules, opts);
|
||||
}
|
||||
|
||||
componentWillMount(): void {
|
||||
this.generateClassnames(this.props, null);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props): void {
|
||||
this.generateClassnames(nextProps, this.props);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
for (const name of this.state.classNames) {
|
||||
gc.deregisterClassUse(name);
|
||||
}
|
||||
}
|
||||
|
||||
generateClassnames(props: Props, prevProps: ?Props): void {
|
||||
throw new Error('unimplemented');
|
||||
}
|
||||
}
|
||||
|
||||
function hasDynamicRules(rules: Object): boolean {
|
||||
for (const key in rules) {
|
||||
const val = rules[key];
|
||||
|
||||
if (typeof val === 'function') {
|
||||
return true;
|
||||
} else if (typeof val === 'object' && hasDynamicRules(val)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasEquivProps(props: Object, nextProps: Object): boolean {
|
||||
// check if the props are equivalent
|
||||
for (const key in props) {
|
||||
// ignore `children` since we do that check later
|
||||
if (key === 'children') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check strict equality of prop value
|
||||
if (nextProps[key] !== props[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check if nextProps has any values that props doesn't
|
||||
for (const key in nextProps) {
|
||||
if (!(key in props)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check if the boolean equality of children is equivalent
|
||||
if (Boolean(props.children) !== Boolean(nextProps.children)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export type StyledComponent<Props> = Class<StyledComponentBase<Props>>;
|
||||
|
||||
type StyledComponentOpts = {
|
||||
displayName?: string,
|
||||
contextTypes?: Object,
|
||||
ignoreAttributes?: Array<string>,
|
||||
};
|
||||
|
||||
function createStyledComponent(
|
||||
tagName: TagName,
|
||||
rules: RawRules,
|
||||
opts?: StyledComponentOpts = {},
|
||||
): StyledComponent<any> {
|
||||
let {contextTypes = {}, ignoreAttributes} = opts;
|
||||
|
||||
// build up rules
|
||||
let builtRules = normaliseRules(rules);
|
||||
|
||||
// if inheriting from another styled component then take all of it's properties
|
||||
if (typeof tagName === 'function' && tagName.STYLED_CONFIG) {
|
||||
// inherit context types
|
||||
if (tagName.contextTypes) {
|
||||
contextTypes = {...contextTypes, ...tagName.contextTypes};
|
||||
}
|
||||
|
||||
const parentConfig = tagName.STYLED_CONFIG;
|
||||
|
||||
// inherit tagname
|
||||
tagName = parentConfig.tagName;
|
||||
|
||||
// inherit ignoreAttributes
|
||||
if (parentConfig.ignoreAttributes) {
|
||||
if (ignoreAttributes) {
|
||||
ignoreAttributes = ignoreAttributes.concat(
|
||||
parentConfig.ignoreAttributes,
|
||||
);
|
||||
} else {
|
||||
ignoreAttributes = parentConfig.ignoreAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
// inherit rules
|
||||
builtRules = assignDeep({}, parentConfig.builtRules, builtRules);
|
||||
}
|
||||
|
||||
const displayName: string =
|
||||
opts.displayName == null ? 'StyledComponent' : opts.displayName;
|
||||
const isDOM = typeof tagName === 'string';
|
||||
|
||||
class Constructor<Props: Object> extends StyledComponentBase<Props> {
|
||||
generateClassnames(props: Props, prevProps: ?Props) {
|
||||
// if this is a secondary render then check if the props are essentially equivalent
|
||||
// NOTE: hasEquivProps is not a standard shallow equality test
|
||||
if (prevProps != null && hasEquivProps(props, prevProps)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const debugId = performance.mark();
|
||||
const extraClassNames = [];
|
||||
|
||||
let myBuiltRules = builtRules;
|
||||
|
||||
// if passed any classes from another styled component, ignore that class and merge in their
|
||||
// resolved styles
|
||||
if (props.className) {
|
||||
const propClassNames = props.className.trim().split(/[\s]+/g);
|
||||
for (const className of propClassNames) {
|
||||
const classInfo = tracker.get(className);
|
||||
if (classInfo) {
|
||||
const {namespace, style} = classInfo;
|
||||
myBuiltRules = assignDeep({}, myBuiltRules, {[namespace]: style});
|
||||
} else {
|
||||
extraClassNames.push(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we had the exact same rules as last time and they weren't dynamic then we can bail out here
|
||||
if (
|
||||
myBuiltRules !== this.state.lastBuiltRules ||
|
||||
this.state.lastBuiltRulesIsDynamic !== false
|
||||
) {
|
||||
const prevClasses = this.state.classNames;
|
||||
const classNames = [];
|
||||
|
||||
// add rules
|
||||
for (const namespace in myBuiltRules) {
|
||||
const className = addRules(
|
||||
displayName,
|
||||
myBuiltRules[namespace],
|
||||
namespace,
|
||||
props,
|
||||
this.context,
|
||||
);
|
||||
classNames.push(className);
|
||||
|
||||
// if this is the first mount render or we didn't previously have this class then add it as new
|
||||
if (prevProps == null || !prevClasses.includes(className)) {
|
||||
gc.registerClassUse(className);
|
||||
}
|
||||
}
|
||||
|
||||
// check what classNames have been removed if this is a secondary render
|
||||
if (prevProps != null) {
|
||||
for (const className of prevClasses) {
|
||||
// if this previous class isn't in the current classes then deregister it
|
||||
if (!classNames.includes(className)) {
|
||||
gc.deregisterClassUse(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
classNames,
|
||||
lastBuiltRules: myBuiltRules,
|
||||
lastBuiltRulesIsDynamic: hasDynamicRules(myBuiltRules),
|
||||
extraClassNames,
|
||||
});
|
||||
}
|
||||
|
||||
performance.measure(
|
||||
debugId,
|
||||
`🚀 ${this.constructor.name} [style calculate]`,
|
||||
);
|
||||
}
|
||||
render() {
|
||||
const {children, innerRef, ...props} = this.props;
|
||||
|
||||
if (ignoreAttributes) {
|
||||
for (const key of ignoreAttributes) {
|
||||
delete props[key];
|
||||
}
|
||||
}
|
||||
// build class names
|
||||
const className = this.state.classNames
|
||||
.concat(this.state.extraClassNames)
|
||||
.join(' ');
|
||||
if (props.is) {
|
||||
props.class = className;
|
||||
} else {
|
||||
props.className = className;
|
||||
}
|
||||
//
|
||||
if (innerRef) {
|
||||
if (isDOM) {
|
||||
// dom ref
|
||||
props.ref = innerRef;
|
||||
} else {
|
||||
// probably another styled component so pass it down
|
||||
props.innerRef = innerRef;
|
||||
}
|
||||
}
|
||||
return React.createElement(tagName, props, children);
|
||||
}
|
||||
}
|
||||
Constructor.STYLED_CONFIG = {
|
||||
builtRules,
|
||||
ignoreAttributes,
|
||||
tagName,
|
||||
};
|
||||
|
||||
Constructor.contextTypes = {
|
||||
...contextTypes,
|
||||
};
|
||||
|
||||
Object.defineProperty(Constructor, 'name', {
|
||||
value: displayName,
|
||||
});
|
||||
|
||||
return Constructor;
|
||||
}
|
||||
export function buildKeyframes(spec: KeyframeRules) {
|
||||
let css = [];
|
||||
|
||||
const builtRules = buildKeyframeRules(spec);
|
||||
for (const key in builtRules) {
|
||||
const declarations = [];
|
||||
const rules = builtRules[key];
|
||||
|
||||
for (const key in rules) {
|
||||
declarations.push(` ${key}: ${String(rules[key])};`);
|
||||
}
|
||||
css.push(` ${key} {`);
|
||||
css = css.concat(declarations);
|
||||
css.push(' }');
|
||||
}
|
||||
css = css.join('\n');
|
||||
return css;
|
||||
}
|
||||
function createKeyframes(spec: KeyframeRules): string {
|
||||
const body = buildKeyframes(spec);
|
||||
const className = `animation-${hash(body)}`;
|
||||
|
||||
const css = `@keyframes ${className} {\n${body}\n}`;
|
||||
sheet.insert(className, css);
|
||||
return className;
|
||||
}
|
||||
type StyledComponentFactory = (
|
||||
rules: RawRules,
|
||||
opts?: StyledComponentOpts,
|
||||
) => StyledComponent<any>;
|
||||
|
||||
function createStyledComponentFactory(tagName: string): StyledComponentFactory {
|
||||
return (rules: RawRules, opts?: StyledComponentOpts) => {
|
||||
return createStyledComponent(tagName, rules, opts);
|
||||
};
|
||||
}
|
||||
export default {
|
||||
image: createStyledComponentFactory('img'),
|
||||
view: createStyledComponentFactory('div'),
|
||||
text: createStyledComponentFactory('span'),
|
||||
textInput: createStyledComponentFactory('input'),
|
||||
customHTMLTag: createStyledComponent,
|
||||
keyframes: createKeyframes,
|
||||
StylableComponent,
|
||||
StylablePureComponent,
|
||||
};
|
||||
import styled from 'react-emotion';
|
||||
export default styled;
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {CSSPropertySet, CSSPropertyValue} from './types.js';
|
||||
|
||||
const dashify = require('dashify');
|
||||
|
||||
export type NormalisedRules = {
|
||||
[namespace: string]: BaseRules,
|
||||
};
|
||||
|
||||
export type BaseRules = {
|
||||
[key: string]: CSSPropertyValue<string | number>,
|
||||
};
|
||||
|
||||
export type PlainRules = {
|
||||
[key: string]: string,
|
||||
};
|
||||
|
||||
export type NormalisedKeyframeRules = {
|
||||
[key: string]: PlainRules,
|
||||
};
|
||||
|
||||
export type KeyframeRules = {
|
||||
[key: string]: CSSPropertySet,
|
||||
};
|
||||
|
||||
export type RawRules = {
|
||||
...CSSPropertySet,
|
||||
[key: string]: CSSPropertySet,
|
||||
};
|
||||
|
||||
const unitlessNumberProperties = new Set([
|
||||
'animation-iteration-count',
|
||||
'border-image-outset',
|
||||
'border-image-slice',
|
||||
'border-image-width',
|
||||
'column-count',
|
||||
'flex',
|
||||
'flex-grow',
|
||||
'flex-positive',
|
||||
'flex-shrink',
|
||||
'flex-order',
|
||||
'grid-row',
|
||||
'grid-column',
|
||||
'font-weight',
|
||||
'line-clamp',
|
||||
'line-height',
|
||||
'opacity',
|
||||
'order',
|
||||
'orphans',
|
||||
'tab-size',
|
||||
'widows',
|
||||
'z-index',
|
||||
'zoom',
|
||||
'fill-opacity',
|
||||
'flood-opacity',
|
||||
'stop-opacity',
|
||||
'stroke-dasharray',
|
||||
'stroke-dashoffset',
|
||||
'stroke-miterlimit',
|
||||
'stroke-opacity',
|
||||
'stroke-width',
|
||||
]);
|
||||
|
||||
// put top level styles into an '&' object
|
||||
function expandRules(rules: RawRules): NormalisedRules {
|
||||
const expandedRules = {};
|
||||
const rootRules = {};
|
||||
|
||||
for (const key in rules) {
|
||||
const val = rules[key];
|
||||
|
||||
if (typeof val === 'object') {
|
||||
expandedRules[key] = val;
|
||||
} else {
|
||||
rootRules[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(rootRules).length) {
|
||||
expandedRules['&'] = rootRules;
|
||||
}
|
||||
|
||||
return expandedRules;
|
||||
}
|
||||
|
||||
function shouldAppendPixel(key: string, val: mixed): boolean {
|
||||
return (
|
||||
typeof val === 'number' && !unitlessNumberProperties.has(key) && !isNaN(val)
|
||||
);
|
||||
}
|
||||
|
||||
export function normaliseRules(rules: RawRules): NormalisedRules {
|
||||
const expandedRules = expandRules(rules);
|
||||
|
||||
const builtRules = {};
|
||||
|
||||
for (const key in expandedRules) {
|
||||
const rules = expandedRules[key];
|
||||
const myRules = {};
|
||||
|
||||
for (const key in rules) {
|
||||
const val = rules[key];
|
||||
|
||||
let dashedKey = dashify(key);
|
||||
if (/[A-Z]/.test(key[0])) {
|
||||
dashedKey = `-${dashedKey}`;
|
||||
}
|
||||
|
||||
myRules[dashedKey] = val;
|
||||
}
|
||||
|
||||
if (Object.keys(myRules).length) {
|
||||
builtRules[key] = myRules;
|
||||
}
|
||||
}
|
||||
|
||||
return builtRules;
|
||||
}
|
||||
|
||||
export function buildKeyframeRules(
|
||||
rules: KeyframeRules,
|
||||
): NormalisedKeyframeRules {
|
||||
const spec = {};
|
||||
|
||||
for (const selector in rules) {
|
||||
const newRules = {};
|
||||
|
||||
const rules2 = rules[selector];
|
||||
if (!rules2 || typeof rules2 !== 'object') {
|
||||
throw new Error('Keyframe spec must only have objects');
|
||||
}
|
||||
|
||||
for (const key in rules2) {
|
||||
let val = rules2[key];
|
||||
|
||||
if (shouldAppendPixel(key, val)) {
|
||||
val += 'px';
|
||||
} else if (typeof val === 'number') {
|
||||
val = String(val);
|
||||
}
|
||||
|
||||
if (typeof val !== 'string') {
|
||||
throw new Error('Keyframe objects must only have strings values');
|
||||
}
|
||||
|
||||
newRules[key] = val;
|
||||
}
|
||||
|
||||
spec[selector] = newRules;
|
||||
}
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
export function buildRules(
|
||||
rules: BaseRules,
|
||||
props: NormalisedRules,
|
||||
context: Object,
|
||||
): PlainRules {
|
||||
const style = {};
|
||||
for (const key in rules) {
|
||||
let val = rules[key];
|
||||
if (typeof val === 'function') {
|
||||
val = val(props, context);
|
||||
}
|
||||
if (val != null && shouldAppendPixel(key, val)) {
|
||||
val += 'px';
|
||||
}
|
||||
if (val != null) {
|
||||
style[key] = String(val);
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
function makeStyleTag(): HTMLStyleElement {
|
||||
const tag = document.createElement('style');
|
||||
tag.type = 'text/css';
|
||||
tag.appendChild(document.createTextNode(''));
|
||||
|
||||
const {head} = document;
|
||||
invariant(head, 'expected head');
|
||||
head.appendChild(tag);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
export class StyleSheet {
|
||||
constructor(isSpeedy?: boolean) {
|
||||
this.injected = false;
|
||||
this.isSpeedy = Boolean(isSpeedy);
|
||||
|
||||
this.flush();
|
||||
this.inject();
|
||||
}
|
||||
|
||||
ruleIndexes: Array<string>;
|
||||
injected: boolean;
|
||||
isSpeedy: boolean;
|
||||
tag: HTMLStyleElement;
|
||||
|
||||
getRuleCount(): number {
|
||||
return this.ruleIndexes.length;
|
||||
}
|
||||
|
||||
flush() {
|
||||
this.ruleIndexes = [];
|
||||
if (this.tag) {
|
||||
this.tag.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
inject() {
|
||||
if (this.injected) {
|
||||
throw new Error('already injected stylesheet!');
|
||||
}
|
||||
|
||||
this.tag = makeStyleTag();
|
||||
this.injected = true;
|
||||
}
|
||||
|
||||
delete(key: string) {
|
||||
const index = this.ruleIndexes.indexOf(key);
|
||||
if (index < 0) {
|
||||
// TODO maybe error
|
||||
return;
|
||||
}
|
||||
|
||||
this.ruleIndexes.splice(index, 1);
|
||||
|
||||
const tag = this.tag;
|
||||
if (this.isSpeedy) {
|
||||
const sheet = tag.sheet;
|
||||
invariant(sheet, 'expected sheet');
|
||||
|
||||
// $FlowFixMe: sheet is actually CSSStylesheet
|
||||
sheet.deleteRule(index);
|
||||
} else {
|
||||
tag.removeChild(tag.childNodes[index + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
insert(key: string, rule: string) {
|
||||
const tag = this.tag;
|
||||
|
||||
if (this.isSpeedy) {
|
||||
const sheet = tag.sheet;
|
||||
invariant(sheet, 'expected sheet');
|
||||
|
||||
// $FlowFixMe: sheet is actually CSSStylesheet
|
||||
sheet.insertRule(rule, sheet.cssRules.length);
|
||||
} else {
|
||||
tag.appendChild(document.createTextNode(rule));
|
||||
}
|
||||
|
||||
this.ruleIndexes.push(key);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user