@@ -20,7 +20,7 @@ export const Separator = styled.div`
|
||||
margin-left: 12px;
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background: ${props => props.theme.slateLight};
|
||||
background: ${props => props.theme.divider};
|
||||
`;
|
||||
|
||||
const Actions = styled(Flex)`
|
||||
@@ -29,7 +29,7 @@ const Actions = styled(Flex)`
|
||||
right: 0;
|
||||
left: 0;
|
||||
border-radius: 3px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
background: ${props => props.theme.background};
|
||||
padding: 12px;
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class Alert extends React.Component<Props> {
|
||||
|
||||
const Container = styled(Flex)`
|
||||
height: $headerHeight;
|
||||
color: #ffffff;
|
||||
color: ${props => props.theme.white};
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ const CircleImg = styled.img`
|
||||
width: ${props => props.size}px;
|
||||
height: ${props => props.size}px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid ${props => props.theme.white};
|
||||
border: 2px solid ${props => props.theme.background};
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ const RealButton = styled.button`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: ${props => props.theme.blackLight};
|
||||
color: ${props => props.theme.white};
|
||||
background: ${props => props.theme.buttonBackground};
|
||||
color: ${props => props.theme.buttonText};
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 2px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
@@ -28,26 +28,26 @@ const RealButton = styled.button`
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: ${props => darken(0.05, props.theme.blackLight)};
|
||||
background: ${props => darken(0.05, props.theme.buttonBackground)};
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
color: ${props => lighten(0.2, props.theme.blackLight)};
|
||||
color: ${props => lighten(0.2, props.theme.buttonText)};
|
||||
}
|
||||
|
||||
${props =>
|
||||
props.neutral &&
|
||||
`
|
||||
background: ${props.theme.white};
|
||||
color: ${props.theme.text};
|
||||
background: ${props.theme.buttonNeutralBackground};
|
||||
color: ${props.theme.buttonNeutralText};
|
||||
box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 2px;
|
||||
border: 1px solid ${props.theme.slateLight};
|
||||
border: 1px solid ${darken(0.1, props.theme.buttonNeutralBackground)};
|
||||
|
||||
&:hover {
|
||||
background: ${darken(0.05, props.theme.white)};
|
||||
border: 1px solid ${darken(0.05, props.theme.slateLight)};
|
||||
background: ${darken(0.05, props.theme.buttonNeutralBackground)};
|
||||
border: 1px solid ${darken(0.15, props.theme.buttonNeutralBackground)};
|
||||
}
|
||||
`} ${props =>
|
||||
props.danger &&
|
||||
|
||||
@@ -4,6 +4,7 @@ import { observer } from 'mobx-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Document from 'models/Document';
|
||||
import styled, { withTheme } from 'styled-components';
|
||||
import { darken } from 'polished';
|
||||
import Flex from 'shared/components/Flex';
|
||||
import Highlight from 'components/Highlight';
|
||||
import { StarredIcon } from 'outline-icons';
|
||||
@@ -20,7 +21,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const StyledStar = withTheme(styled(({ solid, theme, ...props }) => (
|
||||
<StarredIcon color={solid ? theme.black : theme.text} {...props} />
|
||||
<StarredIcon color={theme.text} {...props} />
|
||||
))`
|
||||
flex-shrink: 0;
|
||||
opacity: ${props => (props.solid ? '1 !important' : 0)};
|
||||
@@ -59,8 +60,8 @@ const DocumentLink = styled(Link)`
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: ${props => props.theme.smokeLight};
|
||||
border: 2px solid ${props => props.theme.smoke};
|
||||
background: ${props => props.theme.listItemHoverBackground};
|
||||
border: 2px solid ${props => props.theme.listItemHoverBorder};
|
||||
outline: none;
|
||||
|
||||
${StyledStar}, ${StyledDocumentMenu} {
|
||||
@@ -73,7 +74,7 @@ const DocumentLink = styled(Link)`
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 2px solid ${props => props.theme.slateDark};
|
||||
border: 2px solid ${props => darken(0.5, props.theme.listItemHoverBorder)};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -102,7 +103,7 @@ const Title = styled(Highlight)`
|
||||
|
||||
const ResultContext = styled(Highlight)`
|
||||
display: block;
|
||||
color: ${props => props.theme.slateDark};
|
||||
color: ${props => props.theme.textTertiary};
|
||||
font-size: 14px;
|
||||
margin-top: -0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
|
||||
@@ -7,13 +7,13 @@ import Flex from 'shared/components/Flex';
|
||||
import Time from 'shared/components/Time';
|
||||
|
||||
const Container = styled(Flex)`
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.textTertiary};
|
||||
font-size: 13px;
|
||||
`;
|
||||
|
||||
const Modified = styled.span`
|
||||
color: ${props =>
|
||||
props.highlight ? props.theme.slateDark : props.theme.slate};
|
||||
props.highlight ? props.theme.text : props.theme.textTertiary};
|
||||
font-weight: ${props => (props.highlight ? '600' : '400')};
|
||||
`;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import * as React from 'react';
|
||||
import invariant from 'invariant';
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react';
|
||||
import styled from 'styled-components';
|
||||
import { PortalWithState } from 'react-portal';
|
||||
import styled from 'styled-components';
|
||||
import Flex from 'shared/components/Flex';
|
||||
import { fadeAndScaleIn } from 'shared/styles/animations';
|
||||
|
||||
@@ -89,14 +89,13 @@ const Menu = styled.div`
|
||||
right: ${({ right }) => right}px;
|
||||
top: ${({ top }) => top}px;
|
||||
z-index: 1000;
|
||||
border: ${props => props.theme.slateLight};
|
||||
background: ${props => props.theme.white};
|
||||
|
||||
background: ${props => props.theme.menuBackground};
|
||||
border-radius: 2px;
|
||||
padding: 0.5em 0;
|
||||
min-width: 180px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 8px rgba(0, 0, 0, 0.08),
|
||||
0 2px 4px rgba(0, 0, 0, 0.08);
|
||||
box-shadow: ${props => props.theme.menuShadow};
|
||||
|
||||
@media print {
|
||||
display: none;
|
||||
|
||||
@@ -23,13 +23,13 @@ const MenuItem = styled.a`
|
||||
height: 32px;
|
||||
|
||||
color: ${props =>
|
||||
props.disabled ? props.theme.slate : props.theme.slateDark};
|
||||
props.disabled ? props.theme.textTertiary : props.theme.textSecondary};
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
font-size: 15px;
|
||||
cursor: default;
|
||||
|
||||
svg {
|
||||
svg:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as React from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react';
|
||||
import { withTheme } from 'styled-components';
|
||||
import RichMarkdownEditor from 'rich-markdown-editor';
|
||||
import { uploadFile } from 'utils/uploadFile';
|
||||
import isInternalUrl from 'utils/isInternalUrl';
|
||||
@@ -89,7 +90,7 @@ class Editor extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
// $FlowIssue - https://github.com/facebook/flow/issues/6103
|
||||
export default React.forwardRef((props, ref) => (
|
||||
<Editor {...props} forwardedRef={ref} />
|
||||
));
|
||||
export default withTheme(
|
||||
// $FlowIssue - https://github.com/facebook/flow/issues/6103
|
||||
React.forwardRef((props, ref) => <Editor {...props} forwardedRef={ref} />)
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import styled from 'styled-components';
|
||||
|
||||
const HelpText = styled.p`
|
||||
margin-top: 0;
|
||||
color: ${props => props.theme.slateDark};
|
||||
color: ${props => props.theme.textSecondary};
|
||||
font-size: ${props => (props.small ? '13px' : 'inherit')};
|
||||
`;
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ function Highlight({
|
||||
|
||||
const Mark = styled.mark`
|
||||
background: ${props => props.theme.yellow};
|
||||
border-radius: 2px;
|
||||
padding: 0 4px;
|
||||
`;
|
||||
|
||||
export default Highlight;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { observable } from 'mobx';
|
||||
import styled from 'styled-components';
|
||||
import Flex from 'shared/components/Flex';
|
||||
|
||||
@@ -9,10 +11,11 @@ const RealTextarea = styled.textarea`
|
||||
padding: 8px 12px;
|
||||
outline: none;
|
||||
background: none;
|
||||
color: ${props => props.theme.text};
|
||||
|
||||
&:disabled,
|
||||
&::placeholder {
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.placeholder};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -22,10 +25,11 @@ const RealInput = styled.input`
|
||||
padding: 8px 12px;
|
||||
outline: none;
|
||||
background: none;
|
||||
color: ${props => props.theme.text};
|
||||
|
||||
&:disabled,
|
||||
&::placeholder {
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.placeholder};
|
||||
}
|
||||
|
||||
&::-webkit-search-cancel-button {
|
||||
@@ -46,13 +50,14 @@ export const Outline = styled(Flex)`
|
||||
color: inherit;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: ${props => (props.hasError ? 'red' : props.theme.slateLight)};
|
||||
border-color: ${props =>
|
||||
props.hasError
|
||||
? 'red'
|
||||
: props.focused
|
||||
? props.theme.inputBorderFocused
|
||||
: props.theme.inputBorder};
|
||||
border-radius: 4px;
|
||||
font-weight: normal;
|
||||
|
||||
&:focus {
|
||||
border-color: ${props => props.theme.slate};
|
||||
}
|
||||
`;
|
||||
|
||||
export const LabelText = styled.div`
|
||||
@@ -68,26 +73,39 @@ export type Props = {
|
||||
short?: boolean,
|
||||
};
|
||||
|
||||
export default function Input({
|
||||
type = 'text',
|
||||
label,
|
||||
className,
|
||||
short,
|
||||
...rest
|
||||
}: Props) {
|
||||
const InputComponent = type === 'textarea' ? RealTextarea : RealInput;
|
||||
@observer
|
||||
class Input extends React.Component<Props> {
|
||||
@observable focused: boolean = false;
|
||||
|
||||
return (
|
||||
<Wrapper className={className} short={short}>
|
||||
<label>
|
||||
{label && <LabelText>{label}</LabelText>}
|
||||
<Outline>
|
||||
<InputComponent
|
||||
type={type === 'textarea' ? undefined : type}
|
||||
{...rest}
|
||||
/>
|
||||
</Outline>
|
||||
</label>
|
||||
</Wrapper>
|
||||
);
|
||||
handleBlur = () => {
|
||||
this.focused = false;
|
||||
};
|
||||
|
||||
handleFocus = () => {
|
||||
this.focused = true;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { type = 'text', label, className, short, ...rest } = this.props;
|
||||
|
||||
const InputComponent = type === 'textarea' ? RealTextarea : RealInput;
|
||||
|
||||
return (
|
||||
<Wrapper className={className} short={short}>
|
||||
<label>
|
||||
{label && <LabelText>{label}</LabelText>}
|
||||
<Outline focused={this.focused}>
|
||||
<InputComponent
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.handleFocus}
|
||||
type={type === 'textarea' ? undefined : type}
|
||||
{...rest}
|
||||
/>
|
||||
</Outline>
|
||||
</label>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Input;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import * as React from 'react';
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react';
|
||||
import styled from 'styled-components';
|
||||
import styled, { withTheme } from 'styled-components';
|
||||
import Input, { LabelText, Outline } from 'components/Input';
|
||||
|
||||
type Props = {
|
||||
@@ -15,11 +15,20 @@ type Props = {
|
||||
@observer
|
||||
class InputRich extends React.Component<Props> {
|
||||
@observable editorComponent: *;
|
||||
@observable focused: boolean = false;
|
||||
|
||||
componentDidMount() {
|
||||
this.loadEditor();
|
||||
}
|
||||
|
||||
handleBlur = () => {
|
||||
this.focused = false;
|
||||
};
|
||||
|
||||
handleFocus = () => {
|
||||
this.focused = true;
|
||||
};
|
||||
|
||||
loadEditor = async () => {
|
||||
const EditorImport = await import('./Editor');
|
||||
this.editorComponent = EditorImport.default;
|
||||
@@ -33,8 +42,16 @@ class InputRich extends React.Component<Props> {
|
||||
<React.Fragment>
|
||||
<LabelText>{label}</LabelText>
|
||||
{Editor ? (
|
||||
<StyledOutline maxHeight={maxHeight} minHeight={minHeight}>
|
||||
<Editor {...rest} />
|
||||
<StyledOutline
|
||||
maxHeight={maxHeight}
|
||||
minHeight={minHeight}
|
||||
focused={this.focused}
|
||||
>
|
||||
<Editor
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.handleFocus}
|
||||
{...rest}
|
||||
/>
|
||||
</StyledOutline>
|
||||
) : (
|
||||
<Input
|
||||
@@ -60,4 +77,4 @@ const StyledOutline = styled(Outline)`
|
||||
}
|
||||
`;
|
||||
|
||||
export default InputRich;
|
||||
export default withTheme(InputRich);
|
||||
|
||||
@@ -7,7 +7,7 @@ const Key = styled.kbd`
|
||||
font: 11px 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier,
|
||||
monospace;
|
||||
line-height: 10px;
|
||||
color: ${props => props.theme.text};
|
||||
color: ${props => props.theme.almostBlack};
|
||||
vertical-align: middle;
|
||||
background-color: ${props => props.theme.smokeLight};
|
||||
border: solid 1px ${props => props.theme.slateLight};
|
||||
|
||||
@@ -21,7 +21,7 @@ export const Label = styled(Flex)`
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: #9fa6ab;
|
||||
color: ${props => props.theme.textTertiary};
|
||||
letter-spacing: 0.04em;
|
||||
`;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import * as React from 'react';
|
||||
import { Switch, Route, Redirect } from 'react-router-dom';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import styled from 'styled-components';
|
||||
import styled, { withTheme } from 'styled-components';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import { observable } from 'mobx';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
@@ -35,6 +35,7 @@ type Props = {
|
||||
auth: AuthStore,
|
||||
ui: UiStore,
|
||||
notifications?: React.Node,
|
||||
theme: Object,
|
||||
};
|
||||
|
||||
@observer
|
||||
@@ -42,12 +43,23 @@ class Layout extends React.Component<Props> {
|
||||
scrollable: ?HTMLDivElement;
|
||||
@observable redirectTo: ?string;
|
||||
|
||||
componentWillMount() {
|
||||
this.updateBackground();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.updateBackground();
|
||||
|
||||
if (this.redirectTo) {
|
||||
this.redirectTo = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
updateBackground() {
|
||||
// ensure the wider page color always matches the theme
|
||||
window.document.body.style.background = this.props.theme.background;
|
||||
}
|
||||
|
||||
@keydown(['/', 't', 'meta+k'])
|
||||
goToSearch(ev) {
|
||||
ev.preventDefault();
|
||||
@@ -124,6 +136,7 @@ class Layout extends React.Component<Props> {
|
||||
}
|
||||
|
||||
const Container = styled(Flex)`
|
||||
background: ${props => props.theme.background};
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
min-height: 100%;
|
||||
@@ -142,4 +155,4 @@ const Content = styled(Flex)`
|
||||
`};
|
||||
`;
|
||||
|
||||
export default inject('auth', 'ui', 'documents')(Layout);
|
||||
export default inject('auth', 'ui', 'documents')(withTheme(Layout));
|
||||
|
||||
@@ -29,7 +29,7 @@ const Wrapper = styled.li`
|
||||
display: flex;
|
||||
padding: ${props => (props.compact ? '8px' : '12px')} 0;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid ${props => props.theme.smokeDark};
|
||||
border-bottom: 1px solid ${props => props.theme.divider};
|
||||
`;
|
||||
|
||||
const Image = styled(Flex)`
|
||||
|
||||
@@ -25,7 +25,7 @@ const Redacted = styled(Flex)`
|
||||
width: ${props => (props.header ? props.width / 2 : props.width)}%;
|
||||
height: ${props => (props.height ? props.height : props.header ? 24 : 18)}px;
|
||||
margin-bottom: 6px;
|
||||
background-color: ${props => props.theme.smokeDark};
|
||||
background-color: ${props => props.theme.divider};
|
||||
animation: ${pulsate} 1.3s infinite;
|
||||
|
||||
&:last-child {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { observer } from 'mobx-react';
|
||||
import styled, { createGlobalStyle } from 'styled-components';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import ReactModal from 'react-modal';
|
||||
import { transparentize } from 'polished';
|
||||
import { CloseIcon } from 'outline-icons';
|
||||
import { fadeAndScaleIn } from 'shared/styles/animations';
|
||||
import Flex from 'shared/components/Flex';
|
||||
@@ -17,6 +18,8 @@ type Props = {
|
||||
|
||||
const GlobalStyles = createGlobalStyle`
|
||||
.ReactModal__Overlay {
|
||||
background-color: ${props =>
|
||||
transparentize(0.25, props.theme.background)} !important;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@@ -76,7 +79,7 @@ const StyledModal = styled(ReactModal)`
|
||||
align-items: flex-start;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
background: white;
|
||||
background: ${props => props.theme.background};
|
||||
padding: 13vh 2rem 2rem;
|
||||
outline: none;
|
||||
`;
|
||||
@@ -93,7 +96,7 @@ const Close = styled.a`
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
opacity: 0.5;
|
||||
color: ${props => props.theme.text};
|
||||
color: ${props => props.theme.textSecondary};
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
|
||||
@@ -6,10 +6,12 @@ import styled from 'styled-components';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { CloseIcon, MenuIcon } from 'outline-icons';
|
||||
import Fade from 'components/Fade';
|
||||
import Flex from 'shared/components/Flex';
|
||||
|
||||
import UiStore from 'stores/UiStore';
|
||||
|
||||
let firstRender = true;
|
||||
|
||||
type Props = {
|
||||
children: React.Node,
|
||||
location: Location,
|
||||
@@ -30,8 +32,7 @@ class Sidebar extends React.Component<Props> {
|
||||
|
||||
render() {
|
||||
const { children, ui } = this.props;
|
||||
|
||||
return (
|
||||
const content = (
|
||||
<Container
|
||||
editMode={ui.editMode}
|
||||
mobileSidebarVisible={ui.mobileSidebarVisible}
|
||||
@@ -46,6 +47,14 @@ class Sidebar extends React.Component<Props> {
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
|
||||
// Fade in the sidebar on first render after page load
|
||||
if (firstRender) {
|
||||
firstRender = false;
|
||||
return <Fade>{content}</Fade>;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +64,7 @@ const Container = styled(Flex)`
|
||||
bottom: 0;
|
||||
left: ${props => (props.editMode ? `-${props.theme.sidebarWidth}` : 0)};
|
||||
width: 100%;
|
||||
background: ${props => props.theme.smoke};
|
||||
background: ${props => props.theme.sidebarBackground};
|
||||
transition: left 100ms ease-out;
|
||||
margin-left: ${props => (props.mobileSidebarVisible ? 0 : '-100%')};
|
||||
z-index: 2;
|
||||
@@ -68,7 +77,7 @@ const Container = styled(Flex)`
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
background: ${props => props.theme.smoke};
|
||||
background: ${props => props.theme.sidebarBackground};
|
||||
position: absolute;
|
||||
top: -50vh;
|
||||
left: 0;
|
||||
|
||||
@@ -6,7 +6,7 @@ const Header = styled(Flex)`
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
color: ${props => props.theme.slateDark};
|
||||
color: ${props => props.theme.sidebarText};
|
||||
letter-spacing: 0.04em;
|
||||
margin: 4px 16px;
|
||||
`;
|
||||
|
||||
@@ -46,7 +46,7 @@ const Subheading = styled.div`
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
color: ${props => props.theme.slateDark};
|
||||
color: ${props => props.theme.sidebarText};
|
||||
`;
|
||||
|
||||
const TeamName = styled.div`
|
||||
|
||||
@@ -34,7 +34,7 @@ class SidebarLink extends React.Component<Props> {
|
||||
|
||||
activeStyle = {
|
||||
color: this.props.theme.text,
|
||||
background: 'rgba(0, 0, 0, 0.05)',
|
||||
background: this.props.theme.sidebarItemBackground,
|
||||
fontWeight: 600,
|
||||
...this.style,
|
||||
};
|
||||
@@ -115,7 +115,7 @@ const StyledNavLink = styled(NavLink)`
|
||||
text-overflow: ellipsis;
|
||||
padding: 4px 16px;
|
||||
border-radius: 4px;
|
||||
color: ${props => props.theme.slateDark};
|
||||
color: ${props => props.theme.sidebarText};
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -128,7 +128,7 @@ const Action = styled.span`
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.textTertiary};
|
||||
|
||||
svg {
|
||||
opacity: 0.75;
|
||||
|
||||
@@ -5,9 +5,9 @@ const Subheading = styled.h3`
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.textTertiary};
|
||||
letter-spacing: 0.04em;
|
||||
border-bottom: 1px solid ${props => props.theme.slateLight};
|
||||
border-bottom: 1px solid ${props => props.theme.textTertiary};
|
||||
padding-bottom: 8px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
@@ -8,21 +8,21 @@ const NavItem = styled(NavLink)`
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.textTertiary};
|
||||
letter-spacing: 0.04em;
|
||||
margin-right: 24px;
|
||||
padding-bottom: 8px;
|
||||
|
||||
&:hover {
|
||||
color: ${props => props.theme.slateDark};
|
||||
color: ${props => props.theme.text};
|
||||
}
|
||||
`;
|
||||
|
||||
function Tab(props: *) {
|
||||
const activeStyle = {
|
||||
paddingBottom: '5px',
|
||||
borderBottom: `3px solid ${props.theme.slateLight}`,
|
||||
color: props.theme.slate,
|
||||
borderBottom: `3px solid ${props.theme.textTertiary}`,
|
||||
color: props.theme.textTertiary,
|
||||
};
|
||||
|
||||
return <NavItem {...props} activeStyle={activeStyle} />;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Tabs = styled.nav`
|
||||
border-bottom: 1px solid ${props => props.theme.slateLight};
|
||||
border-bottom: 1px solid ${props => props.theme.textTertiary};
|
||||
margin-top: 22px;
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
|
||||
25
app/components/Theme.js
Normal file
25
app/components/Theme.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { dark, light } from 'shared/styles/theme';
|
||||
import GlobalStyles from 'shared/styles/globals';
|
||||
import UiStore from 'stores/UiStore';
|
||||
|
||||
type Props = {
|
||||
ui: UiStore,
|
||||
children: React.Node,
|
||||
};
|
||||
|
||||
function Theme({ children, ui }: Props) {
|
||||
return (
|
||||
<ThemeProvider theme={ui.theme === 'dark' ? dark : light} key={ui.theme}>
|
||||
<React.Fragment>
|
||||
<GlobalStyles />
|
||||
{children}
|
||||
</React.Fragment>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default inject('ui')(observer(Theme));
|
||||
@@ -4,7 +4,194 @@ import { TooltipTrigger } from 'pui-react-tooltip';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
const GlobalStyles = createGlobalStyle`
|
||||
.tooltip:hover .tooltip-container:not(.tooltip-container-hidden){visibility:visible;opacity:1}.tooltip-container{visibility:hidden;-webkit-transition:opacity ease-out 0.2s;transition:opacity ease-out 0.2s;z-index:10;position:absolute;bottom:100%;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);margin:0 0 8px 0;text-align:left}.tooltip-container.tooltip-container-visible{visibility:visible}.tooltip-container.tooltip-hoverable:after{content:"";position:absolute;width:calc(100% + 16px);height:calc(100% + 16px);top:50%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.tooltip-container .tooltip-content{white-space:nowrap;padding:4px 8px;font-size:12px;line-height:16px;font-weight:400;letter-spacing:0;text-transform:none;background-color:#243641;color:#fff;border-radius:2px;border:1px solid #243641;box-shadow:0px 2px 2px 0px rgba(36, 54, 65, .1),0px 0px 2px 0px rgba(36, 54, 65, .1)}.tooltip-container .tooltip-content:before{content:"";z-index:1;position:absolute;bottom:-4px;left:50%;-webkit-transform:translateX(-50%) rotateZ(45deg);transform:translateX(-50%) rotateZ(45deg);background-color:#243641;border-bottom:1px solid #243641;border-right:1px solid #243641;width:8px;height:8px}.tooltip-container .tooltip-content:after{content:"";box-sizing:content-box;z-index:-1;position:absolute;bottom:-4px;left:50%;-webkit-transform:translateX(-50%) rotateZ(45deg);transform:translateX(-50%) rotateZ(45deg);background-color:#243641;box-shadow:0px 2px 2px 0px rgba(36, 54, 65, .1),0px 0px 2px 0px rgba(36, 54, 65, .1);width:8px;height:8px}.tooltip{position:relative;display:inline-block}.tooltip.tooltip-light .tooltip-content{background-color:#fff;color:#243641;border:1px solid #DFE5E8}.tooltip.tooltip-light .tooltip-content:before{background-color:#fff;border-bottom:1px solid #DFE5E8;border-right:1px solid #DFE5E8}.tooltip.tooltip-light .tooltip-content:after{background-color:#fff}.tooltip.tooltip-bottom .tooltip-container{top:100%;bottom:auto;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);margin:8px 0 0 0}.tooltip.tooltip-bottom .tooltip-container .tooltip-content:before{bottom:auto;top:-4px;border-top:1px solid #243641;border-right:none;border-bottom:none;border-left:1px solid #243641}.tooltip.tooltip-bottom .tooltip-container .tooltip-content:after{bottom:auto;top:-4px}.tooltip.tooltip-bottom.tooltip-light .tooltip-content:before{border-top:1px solid #DFE5E8;border-left:1px solid #DFE5E8}.tooltip.tooltip-right .tooltip-container{top:50%;bottom:auto;left:100%;-webkit-transform:translatey(-50%);transform:translatey(-50%);margin:0 0 0 8px}.tooltip.tooltip-right .tooltip-container .tooltip-content:before{bottom:auto;left:-4px;top:50%;-webkit-transform:translatey(-50%) rotateZ(45deg);transform:translatey(-50%) rotateZ(45deg);border-top:none;border-right:none;border-bottom:1px solid #243641;border-left:1px solid #243641}.tooltip.tooltip-right .tooltip-container .tooltip-content:after{bottom:auto;left:-4px;top:50%;-webkit-transform:translatey(-50%) rotateZ(45deg);transform:translatey(-50%) rotateZ(45deg)}.tooltip.tooltip-right.tooltip-light .tooltip-content:before{border-bottom:1px solid #DFE5E8;border-left:1px solid #DFE5E8}.tooltip.tooltip-left .tooltip-container{top:50%;bottom:auto;right:100%;left:auto;-webkit-transform:translatey(-50%);transform:translatey(-50%);margin:0 8px 0 0}.tooltip.tooltip-left .tooltip-container .tooltip-content:before{bottom:auto;right:-4px;left:auto;top:50%;-webkit-transform:translatey(-50%) rotateZ(45deg);transform:translatey(-50%) rotateZ(45deg);border-top:1px solid #243641;border-right:1px solid #243641;border-bottom:none;border-left:none}.tooltip.tooltip-left .tooltip-container .tooltip-content:after{bottom:auto;right:-4px;left:auto;top:50%;-webkit-transform:translatey(-50%) rotateZ(45deg);transform:translatey(-50%) rotateZ(45deg)}.tooltip.tooltip-left.tooltip-light .tooltip-content:before{border-top:1px solid #DFE5E8;border-right:1px solid #DFE5E8}.tooltip-sm.tooltip-container{width:120px}.tooltip-sm.tooltip-container .tooltip-content{white-space:normal}.tooltip-md.tooltip-container{width:240px}.tooltip-md.tooltip-container .tooltip-content{white-space:normal}.tooltip-lg.tooltip-container{width:360px}.tooltip-lg.tooltip-container .tooltip-content{white-space:normal}.tether-element{z-index:99}.overlay-trigger{color:#1B78B3;-webkit-transition:all 300ms ease-out;transition:all 300ms ease-out;-webkit-transition-property:background-color, color, opacity;transition-property:background-color, color, opacity}.overlay-trigger:hover,.overlay-trigger:focus{color:#1f8ace;cursor:pointer;outline:none;text-decoration:none}.overlay-trigger:active,.overlay-trigger.active{color:#176698}
|
||||
.tooltip:hover .tooltip-container:not(.tooltip-container-hidden){
|
||||
visibility:visible;
|
||||
opacity:1
|
||||
}
|
||||
.tooltip-container{
|
||||
visibility:hidden;
|
||||
-webkit-transition:opacity ease-out 0.2s;
|
||||
transition:opacity ease-out 0.2s;
|
||||
z-index:10;
|
||||
position:absolute;
|
||||
bottom:100%;
|
||||
left:50%;
|
||||
-webkit-transform:translateX(-50%);
|
||||
transform:translateX(-50%);
|
||||
margin:0 0 8px 0;
|
||||
text-align:left
|
||||
}
|
||||
.tooltip-container.tooltip-container-visible{
|
||||
visibility:visible
|
||||
}
|
||||
.tooltip-container.tooltip-hoverable:after{
|
||||
content:"";
|
||||
position:absolute;
|
||||
width:calc(100% + 16px);
|
||||
height:calc(100% + 16px);
|
||||
top:50%;
|
||||
left:50%;
|
||||
-webkit-transform:translateX(-50%) translateY(-50%);
|
||||
transform:translateX(-50%) translateY(-50%)
|
||||
}
|
||||
.tooltip-container .tooltip-content{
|
||||
white-space:nowrap;
|
||||
padding:4px 8px;
|
||||
font-size:12px;
|
||||
line-height:16px;
|
||||
font-weight:400;
|
||||
letter-spacing:0;
|
||||
text-transform:none;
|
||||
background-color:${props => props.theme.tooltipBackground};
|
||||
color: ${props => props.theme.tooltipText};
|
||||
border-radius:2px;
|
||||
border:1px solid ${props => props.theme.tooltipBackground};
|
||||
box-shadow:0px 2px 2px 0px rgba(36, 54, 65, .1),0px 0px 2px 0px rgba(36, 54, 65, .1)
|
||||
}
|
||||
.tooltip-container .tooltip-content:before{
|
||||
content:"";
|
||||
z-index:1;
|
||||
position:absolute;
|
||||
bottom:-4px;
|
||||
left:50%;
|
||||
-webkit-transform:translateX(-50%) rotateZ(45deg);
|
||||
transform:translateX(-50%) rotateZ(45deg);
|
||||
background-color:${props => props.theme.tooltipBackground};
|
||||
border-bottom:1px solid ${props => props.theme.tooltipBackground};
|
||||
border-right:1px solid ${props => props.theme.tooltipBackground};
|
||||
width:8px;
|
||||
height:8px
|
||||
}
|
||||
.tooltip-container .tooltip-content:after{
|
||||
content:"";
|
||||
box-sizing:content-box;
|
||||
z-index:-1;
|
||||
position:absolute;
|
||||
bottom:-4px;
|
||||
left:50%;
|
||||
-webkit-transform:translateX(-50%) rotateZ(45deg);
|
||||
transform:translateX(-50%) rotateZ(45deg);
|
||||
background-color:${props => props.theme.tooltipBackground};
|
||||
box-shadow:0px 2px 2px 0px rgba(36, 54, 65, .1),0px 0px 2px 0px rgba(36, 54, 65, .1);
|
||||
width:8px;
|
||||
height:8px
|
||||
}
|
||||
.tooltip{
|
||||
position:relative;
|
||||
display:inline-block
|
||||
}
|
||||
.tooltip.tooltip-bottom .tooltip-container{
|
||||
top:100%;
|
||||
bottom:auto;
|
||||
left:50%;
|
||||
-webkit-transform:translateX(-50%);
|
||||
transform:translateX(-50%);
|
||||
margin:8px 0 0 0
|
||||
}
|
||||
.tooltip.tooltip-bottom .tooltip-container .tooltip-content:before{
|
||||
bottom:auto;
|
||||
top:-4px;
|
||||
border-top:1px solid ${props => props.theme.tooltipBackground};
|
||||
border-right:none;
|
||||
border-bottom:none;
|
||||
border-left:1px solid ${props => props.theme.tooltipBackground}
|
||||
}
|
||||
.tooltip.tooltip-bottom .tooltip-container .tooltip-content:after{
|
||||
bottom:auto;
|
||||
top:-4px
|
||||
}
|
||||
.tooltip.tooltip-right .tooltip-container{
|
||||
top:50%;
|
||||
bottom:auto;
|
||||
left:100%;
|
||||
-webkit-transform:translatey(-50%);
|
||||
transform:translatey(-50%);
|
||||
margin:0 0 0 8px
|
||||
}
|
||||
.tooltip.tooltip-right .tooltip-container .tooltip-content:before{
|
||||
bottom:auto;
|
||||
left:-4px;
|
||||
top:50%;
|
||||
-webkit-transform:translatey(-50%) rotateZ(45deg);
|
||||
transform:translatey(-50%) rotateZ(45deg);
|
||||
border-top:none;
|
||||
border-right:none;
|
||||
border-bottom:1px solid ${props => props.theme.tooltipBackground};
|
||||
border-left:1px solid ${props => props.theme.tooltipBackground}
|
||||
}
|
||||
.tooltip.tooltip-right .tooltip-container .tooltip-content:after{
|
||||
bottom:auto;
|
||||
left:-4px;
|
||||
top:50%;
|
||||
-webkit-transform:translatey(-50%) rotateZ(45deg);
|
||||
transform:translatey(-50%) rotateZ(45deg)
|
||||
}
|
||||
.tooltip.tooltip-left .tooltip-container{
|
||||
top:50%;
|
||||
bottom:auto;
|
||||
right:100%;
|
||||
left:auto;
|
||||
-webkit-transform:translatey(-50%);
|
||||
transform:translatey(-50%);
|
||||
margin:0 8px 0 0
|
||||
}
|
||||
.tooltip.tooltip-left .tooltip-container .tooltip-content:before{
|
||||
bottom:auto;
|
||||
right:-4px;
|
||||
left:auto;
|
||||
top:50%;
|
||||
-webkit-transform:translatey(-50%) rotateZ(45deg);
|
||||
transform:translatey(-50%) rotateZ(45deg);
|
||||
border-top:1px solid ${props => props.theme.tooltipBackground};
|
||||
border-right:1px solid ${props => props.theme.tooltipBackground};
|
||||
border-bottom:none;
|
||||
border-left:none
|
||||
}
|
||||
.tooltip.tooltip-left .tooltip-container .tooltip-content:after{
|
||||
bottom:auto;
|
||||
right:-4px;
|
||||
left:auto;
|
||||
top:50%;
|
||||
-webkit-transform:translatey(-50%) rotateZ(45deg);
|
||||
transform:translatey(-50%) rotateZ(45deg)
|
||||
}
|
||||
.tooltip-sm.tooltip-container{
|
||||
width:120px
|
||||
}
|
||||
.tooltip-sm.tooltip-container .tooltip-content{
|
||||
white-space:normal
|
||||
}
|
||||
.tooltip-md.tooltip-container{
|
||||
width:240px
|
||||
}
|
||||
.tooltip-md.tooltip-container .tooltip-content{
|
||||
white-space:normal
|
||||
}
|
||||
.tooltip-lg.tooltip-container{
|
||||
width:360px
|
||||
}
|
||||
.tooltip-lg.tooltip-container .tooltip-content{
|
||||
white-space:normal
|
||||
}
|
||||
.tether-element{
|
||||
z-index:99
|
||||
}
|
||||
.overlay-trigger{
|
||||
color:#1B78B3;
|
||||
-webkit-transition:all 300ms ease-out;
|
||||
transition:all 300ms ease-out;
|
||||
-webkit-transition-property:background-color, color, opacity;
|
||||
transition-property:background-color, color, opacity
|
||||
}
|
||||
.overlay-trigger:hover,.overlay-trigger:focus{
|
||||
color:#1f8ace;
|
||||
cursor:pointer;
|
||||
outline:none;
|
||||
text-decoration:none
|
||||
}
|
||||
.overlay-trigger:active,.overlay-trigger.active{
|
||||
color:#176698
|
||||
}
|
||||
`;
|
||||
|
||||
const Tooltip = function(props: *) {
|
||||
|
||||
@@ -70,7 +70,7 @@ const Rounded = styled.div`
|
||||
|
||||
const Iframe = styled.iframe`
|
||||
border: 1px solid;
|
||||
border-color: #ddd #ddd #ccc;
|
||||
border-color: ${props => props.theme.embedBorder};
|
||||
border-radius: 3px;
|
||||
`;
|
||||
|
||||
|
||||
17
app/index.js
17
app/index.js
@@ -2,17 +2,15 @@
|
||||
import * as React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Provider } from 'mobx-react';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
import stores from 'stores';
|
||||
import theme from 'shared/styles/theme';
|
||||
import GlobalStyles from 'shared/styles/globals';
|
||||
import 'shared/styles/prism.css';
|
||||
|
||||
import ErrorBoundary from 'components/ErrorBoundary';
|
||||
import ScrollToTop from 'components/ScrollToTop';
|
||||
import Toasts from 'components/Toasts';
|
||||
import Theme from 'components/Theme';
|
||||
import Routes from './routes';
|
||||
|
||||
let DevTools;
|
||||
@@ -25,10 +23,9 @@ const element = document.getElementById('root');
|
||||
if (element) {
|
||||
render(
|
||||
<React.Fragment>
|
||||
<GlobalStyles />
|
||||
<ThemeProvider theme={theme}>
|
||||
<ErrorBoundary>
|
||||
<Provider {...stores}>
|
||||
<ErrorBoundary>
|
||||
<Provider {...stores}>
|
||||
<Theme>
|
||||
<Router>
|
||||
<React.Fragment>
|
||||
<ScrollToTop>
|
||||
@@ -37,9 +34,9 @@ if (element) {
|
||||
<Toasts />
|
||||
</React.Fragment>
|
||||
</Router>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
</ThemeProvider>
|
||||
</Theme>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
{DevTools && <DevTools position={{ bottom: 0, right: 0 }} />}
|
||||
</React.Fragment>,
|
||||
element
|
||||
|
||||
@@ -3,8 +3,11 @@ import * as React from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { observable } from 'mobx';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { MoonIcon } from 'outline-icons';
|
||||
import styled, { withTheme } from 'styled-components';
|
||||
import UiStore from 'stores/UiStore';
|
||||
import AuthStore from 'stores/AuthStore';
|
||||
import Flex from 'shared/components/Flex';
|
||||
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
import {
|
||||
developers,
|
||||
@@ -18,6 +21,7 @@ type Props = {
|
||||
label: React.Node,
|
||||
ui: UiStore,
|
||||
auth: AuthStore,
|
||||
theme: Object,
|
||||
};
|
||||
|
||||
@observer
|
||||
@@ -42,6 +46,8 @@ class AccountMenu extends React.Component<Props> {
|
||||
|
||||
render() {
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
|
||||
const { ui, theme } = this.props;
|
||||
const isLightTheme = ui.theme === 'light';
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
@@ -72,9 +78,22 @@ class AccountMenu extends React.Component<Props> {
|
||||
</DropdownMenuItem>
|
||||
<hr />
|
||||
<DropdownMenuItem onClick={this.handleLogout}>Logout</DropdownMenuItem>
|
||||
<hr />
|
||||
<DropdownMenuItem onClick={ui.toggleDarkMode}>
|
||||
<NightMode justify="space-between">
|
||||
Night Mode{' '}
|
||||
<MoonIcon
|
||||
color={isLightTheme ? theme.textSecondary : theme.primary}
|
||||
/>
|
||||
</NightMode>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('ui', 'auth')(AccountMenu);
|
||||
const NightMode = styled(Flex)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export default inject('ui', 'auth')(withTheme(AccountMenu));
|
||||
|
||||
@@ -4,7 +4,7 @@ import { observable } from 'mobx';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { Redirect, Link, Switch, Route } from 'react-router-dom';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import styled, { withTheme } from 'styled-components';
|
||||
import {
|
||||
CollectionIcon,
|
||||
PrivateCollectionIcon,
|
||||
@@ -43,6 +43,7 @@ type Props = {
|
||||
documents: DocumentsStore,
|
||||
collections: CollectionsStore,
|
||||
match: Object,
|
||||
theme: Object,
|
||||
};
|
||||
|
||||
@observer
|
||||
@@ -115,7 +116,7 @@ class CollectionScene extends React.Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { documents } = this.props;
|
||||
const { documents, theme } = this.props;
|
||||
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
|
||||
if (!this.isFetching && !this.collection) return <Search notFound />;
|
||||
@@ -139,7 +140,7 @@ class CollectionScene extends React.Component<Props> {
|
||||
</HelpText>
|
||||
<Wrapper>
|
||||
<Link to={newDocumentUrl(collection)}>
|
||||
<Button icon={<NewDocumentIcon color="#FFF" />}>
|
||||
<Button icon={<NewDocumentIcon color={theme.buttonText} />}>
|
||||
Create a document
|
||||
</Button>
|
||||
</Link>
|
||||
@@ -184,6 +185,7 @@ class CollectionScene extends React.Component<Props> {
|
||||
id={collection.id}
|
||||
key={collection.description}
|
||||
defaultValue={collection.description}
|
||||
theme={theme}
|
||||
readOnly
|
||||
/>
|
||||
)}
|
||||
@@ -289,4 +291,6 @@ const Wrapper = styled(Flex)`
|
||||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
export default inject('collections', 'documents', 'ui')(CollectionScene);
|
||||
export default inject('collections', 'documents', 'ui')(
|
||||
withTheme(CollectionScene)
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Redirect } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import { NewDocumentIcon } from 'outline-icons';
|
||||
import { transparentize } from 'polished';
|
||||
import Document from 'models/Document';
|
||||
import AuthStore from 'stores/AuthStore';
|
||||
import { documentEditUrl } from 'utils/routeHelpers';
|
||||
@@ -227,9 +228,9 @@ const Actions = styled(Flex)`
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
background: ${props => transparentize(0.1, props.theme.background)};
|
||||
border-bottom: 1px solid
|
||||
${props => (props.isCompact ? props.theme.smoke : 'transparent')};
|
||||
${props => (props.isCompact ? props.theme.background : 'transparent')};
|
||||
padding: 12px;
|
||||
transition: all 100ms ease-out;
|
||||
transform: translate3d(0, 0, 0);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { lighten } from 'polished';
|
||||
import styled, { withTheme } from 'styled-components';
|
||||
import { SearchIcon } from 'outline-icons';
|
||||
import Flex from 'shared/components/Flex';
|
||||
@@ -26,8 +25,8 @@ class SearchField extends React.Component<Props> {
|
||||
<Field align="center">
|
||||
<StyledIcon
|
||||
type="Search"
|
||||
size={48}
|
||||
color={lighten(0.1, this.props.theme.slate)}
|
||||
size={46}
|
||||
color={this.props.theme.textTertiary}
|
||||
onClick={this.focusInput}
|
||||
/>
|
||||
<StyledInput
|
||||
@@ -56,24 +55,25 @@ const StyledInput = styled.input`
|
||||
font-weight: 400;
|
||||
outline: none;
|
||||
border: 0;
|
||||
background: ${props => props.theme.smoke};
|
||||
background: ${props => props.theme.sidebarBackground};
|
||||
border-radius: 4px;
|
||||
|
||||
color: ${props => props.theme.text};
|
||||
|
||||
::-webkit-search-cancel-button {
|
||||
-webkit-appearance: searchfield-cancel-button;
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.placeholder};
|
||||
}
|
||||
:-moz-placeholder {
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.placeholder};
|
||||
}
|
||||
::-moz-placeholder {
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.placeholder};
|
||||
}
|
||||
:-ms-input-placeholder {
|
||||
color: ${props => props.theme.slate};
|
||||
color: ${props => props.theme.placeholder};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ class Profile extends React.Component<Props> {
|
||||
}
|
||||
|
||||
const DangerZone = styled.div`
|
||||
background: #fff;
|
||||
background: ${props => props.theme.background};
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
`;
|
||||
|
||||
@@ -8,6 +8,7 @@ import AuthStore from 'stores/AuthStore';
|
||||
import ShareListItem from './components/ShareListItem';
|
||||
import List from 'components/List';
|
||||
import CenteredContent from 'components/CenteredContent';
|
||||
import Subheading from 'components/Subheading';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import HelpText from 'components/HelpText';
|
||||
|
||||
@@ -46,6 +47,7 @@ class Shares extends React.Component<Props> {
|
||||
sharing in <Link to="/settings/security">security settings</Link>.
|
||||
</HelpText>
|
||||
)}
|
||||
<Subheading>Shared Documents</Subheading>
|
||||
<List>
|
||||
{shares.orderedData.map(share => (
|
||||
<ShareListItem key={share.id} share={share} />
|
||||
|
||||
@@ -7,6 +7,8 @@ import Collection from 'models/Collection';
|
||||
import type { Toast } from '../types';
|
||||
|
||||
class UiStore {
|
||||
@observable
|
||||
theme: 'light' | 'dark' = window.localStorage.getItem('theme') || 'light';
|
||||
@observable activeModalName: ?string;
|
||||
@observable activeModalProps: ?Object;
|
||||
@observable activeDocumentId: ?string;
|
||||
@@ -16,6 +18,12 @@ class UiStore {
|
||||
@observable mobileSidebarVisible: boolean = false;
|
||||
@observable toasts: Map<string, Toast> = new Map();
|
||||
|
||||
@action
|
||||
toggleDarkMode = () => {
|
||||
this.theme = this.theme === 'dark' ? 'light' : 'dark';
|
||||
window.localStorage.setItem('theme', this.theme);
|
||||
};
|
||||
|
||||
@action
|
||||
setActiveModal = (name: string, props: ?Object): void => {
|
||||
this.activeModalName = name;
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"mobx-react": "^5.4.2",
|
||||
"natural-sort": "^1.0.0",
|
||||
"nodemailer": "^4.4.0",
|
||||
"outline-icons": "^1.6.0",
|
||||
"outline-icons": "^1.7.0",
|
||||
"oy-vey": "^0.10.0",
|
||||
"pg": "^6.1.5",
|
||||
"pg-hstore": "2.3.2",
|
||||
|
||||
@@ -20,21 +20,15 @@
|
||||
flex: 1;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#sidebar-placeholder {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 280px;
|
||||
background: #F4F7FA;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
<div id="sidebar-placeholder"></div>
|
||||
</div>
|
||||
<div id="root"></div>
|
||||
<script>
|
||||
if (window.localStorage.getItem("theme") === "dark") {
|
||||
window.document.querySelector('#root').style.background = "#111319";
|
||||
}
|
||||
</script>
|
||||
<script src="/static/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -23,21 +23,15 @@
|
||||
flex: 1;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#sidebar-placeholder {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 280px;
|
||||
background: #F4F7FA;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
<div id="sidebar-placeholder"></div>
|
||||
</div>
|
||||
<div id="root"></div>
|
||||
<script>
|
||||
if (window.localStorage.getItem("theme") === "dark") {
|
||||
window.document.querySelector('#root').style.background = "#111319";
|
||||
}
|
||||
</script>
|
||||
<script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-3.min.js" data-apikey="<%= BUGSNAG_KEY %>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
ThemeProvider,
|
||||
} from 'styled-components';
|
||||
import Layout from '../pages/components/Layout';
|
||||
import theme from '../../shared/styles/theme';
|
||||
import { light } from '../../shared/styles/theme';
|
||||
|
||||
const sheet = new ServerStyleSheet();
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function renderpage(ctx: Object, children: React.Node) {
|
||||
|
||||
const html = ReactDOMServer.renderToString(
|
||||
<StyleSheetManager sheet={sheet.instance}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<ThemeProvider theme={light}>
|
||||
<Layout sessions={sessions} loggedIn={loggedIn}>
|
||||
{children}
|
||||
</Layout>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// @flow
|
||||
import styled from 'styled-components';
|
||||
import { darken } from 'polished';
|
||||
|
||||
const TeamLogo = styled.img`
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 4px;
|
||||
background: ${props => props.theme.white};
|
||||
border: 1px solid ${props => props.theme.slateLight};
|
||||
border: 1px solid ${props => darken(0.1, props.theme.sidebarBackground)};
|
||||
`;
|
||||
|
||||
export default TeamLogo;
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
// @flow
|
||||
import theme from './theme';
|
||||
|
||||
export default `
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url('/fonts/LatoLatin-Semibold.woff') format('woff');
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: ${theme.text};
|
||||
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
a {
|
||||
color: ${theme.blue};
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Lato',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;
|
||||
font-weight: 500;
|
||||
line-height: 1.25;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
color: ${theme.text};
|
||||
}
|
||||
h1 { font-size: 2.25em; }
|
||||
h2 { font-size: 1.5em; }
|
||||
h3 { font-size: 1.25em; }
|
||||
h4 { font-size: 1em; }
|
||||
h5 { font-size: 0.875em; }
|
||||
h6 { font-size: 0.75em; }
|
||||
|
||||
p,
|
||||
dl,
|
||||
ol,
|
||||
ul,
|
||||
pre,
|
||||
blockquote {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 0;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
`;
|
||||
@@ -1,9 +1,86 @@
|
||||
// @flow
|
||||
import styledNormalize from 'styled-normalize';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import base from './base';
|
||||
|
||||
export default createGlobalStyle`
|
||||
${styledNormalize}
|
||||
${base}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url('/fonts/LatoLatin-Semibold.woff') format('woff');
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: ${props => props.theme.text};
|
||||
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
a {
|
||||
color: ${props => props.theme.primary};
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Lato',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;
|
||||
font-weight: 500;
|
||||
line-height: 1.25;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
color: ${props => props.theme.text};
|
||||
}
|
||||
h1 { font-size: 2.25em; }
|
||||
h2 { font-size: 1.5em; }
|
||||
h3 { font-size: 1.25em; }
|
||||
h4 { font-size: 1em; }
|
||||
h5 { font-size: 0.875em; }
|
||||
h6 { font-size: 0.75em; }
|
||||
|
||||
p,
|
||||
dl,
|
||||
ol,
|
||||
ul,
|
||||
pre,
|
||||
blockquote {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 0;
|
||||
border-top: 1px solid ${props => props.theme.divider};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
// @flow
|
||||
const theme = {
|
||||
fontFamily:
|
||||
"-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif",
|
||||
monospaceFontFamily: `Menlo, Consolas, 'Liberation Mono', monospace;`,
|
||||
fontWeight: 400,
|
||||
import { darken, lighten } from 'polished';
|
||||
|
||||
text: '#171B35',
|
||||
link: '#1AB6FF',
|
||||
primary: '#1AB6FF',
|
||||
placeholder: '#b1becc',
|
||||
danger: '#D0021B',
|
||||
warning: '#f08a24',
|
||||
success: '#2f3336',
|
||||
info: '#a0d3e8',
|
||||
const colors = {
|
||||
almostBlack: '#111319',
|
||||
lightBlack: '#2F3336',
|
||||
almostWhite: '#E6E6E6',
|
||||
|
||||
slate: '#9BA6B2',
|
||||
slateLight: '#DAE1E9',
|
||||
@@ -22,19 +14,131 @@ const theme = {
|
||||
smokeLight: '#F9FBFC',
|
||||
smokeDark: '#E8EBED',
|
||||
|
||||
white: '#FFFFFF',
|
||||
blue: '#1AB6FF',
|
||||
black: '#000000',
|
||||
blackLight: '#2f3336',
|
||||
white: '#FFF',
|
||||
white10: 'rgba(255, 255, 255, 0.1)',
|
||||
black: '#000',
|
||||
black05: 'rgba(0, 0, 0, 0.05)',
|
||||
black10: 'rgba(0, 0, 0, 0.1)',
|
||||
black50: 'rgba(0, 0, 0, 0.50)',
|
||||
primary: '#1AB6FF',
|
||||
yellow: '#FBCA04',
|
||||
|
||||
danger: '#D0021B',
|
||||
warning: '#f08a24',
|
||||
success: '#2f3336',
|
||||
info: '#a0d3e8',
|
||||
};
|
||||
|
||||
const spacing = {
|
||||
padding: '1.5vw 1.875vw',
|
||||
vpadding: '1.5vw',
|
||||
hpadding: '1.875vw',
|
||||
sidebarWidth: '280px',
|
||||
sidebarMinWidth: '250px',
|
||||
sidebarMaxWidth: '350px',
|
||||
|
||||
contentHeaderBackground: 'hsl(180, 58%, 85%)',
|
||||
};
|
||||
|
||||
export default theme;
|
||||
export const base = {
|
||||
...colors,
|
||||
...spacing,
|
||||
fontFamily:
|
||||
"-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif",
|
||||
fontWeight: 400,
|
||||
link: colors.primary,
|
||||
};
|
||||
|
||||
export const light = {
|
||||
...base,
|
||||
background: colors.white,
|
||||
|
||||
text: colors.almostBlack,
|
||||
textSecondary: colors.slateDark,
|
||||
textTertiary: colors.slate,
|
||||
placeholder: '#B1BECC',
|
||||
|
||||
sidebarBackground: 'rgb(244, 247, 250)',
|
||||
sidebarItemBackground: colors.black05,
|
||||
sidebarText: 'rgb(78, 92, 110)',
|
||||
|
||||
menuBackground: colors.white,
|
||||
menuShadow:
|
||||
'0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 8px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.08)',
|
||||
divider: colors.slateLight,
|
||||
inputBorder: colors.slateLight,
|
||||
inputBorderFocused: colors.slate,
|
||||
|
||||
listItemHoverBackground: colors.smoke,
|
||||
listItemHoverBorder: colors.smokeDark,
|
||||
|
||||
toolbarBackground: colors.lightBlack,
|
||||
toolbarInput: colors.white10,
|
||||
toolbarItem: colors.white,
|
||||
|
||||
buttonBackground: colors.lightBlack,
|
||||
buttonText: colors.white,
|
||||
buttonNeutralBackground: colors.white,
|
||||
buttonNeutralText: colors.almostBlack,
|
||||
|
||||
tooltipBackground: colors.almostBlack,
|
||||
tooltipText: colors.white,
|
||||
|
||||
blockToolbarBackground: colors.smoke,
|
||||
blockToolbarTrigger: colors.slate,
|
||||
blockToolbarTriggerIcon: colors.white,
|
||||
blockToolbarItem: colors.almostBlack,
|
||||
|
||||
quote: colors.slateLight,
|
||||
codeBackground: colors.smoke,
|
||||
codeBorder: colors.smokeDark,
|
||||
embedBorder: '#DDD #DDD #CCC',
|
||||
horizontalRule: colors.smokeDark,
|
||||
};
|
||||
|
||||
export const dark = {
|
||||
...base,
|
||||
background: colors.almostBlack,
|
||||
|
||||
text: colors.almostWhite,
|
||||
textSecondary: lighten(0.2, colors.slate),
|
||||
textTertiary: colors.slate,
|
||||
placeholder: darken(0.5, '#B1BECC'),
|
||||
|
||||
sidebarBackground: colors.black50,
|
||||
sidebarItemBackground: colors.black50,
|
||||
sidebarText: colors.slate,
|
||||
|
||||
menuBackground: lighten(0.015, colors.almostBlack),
|
||||
menuShadow:
|
||||
'0 0 0 1px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.08)',
|
||||
divider: colors.slate,
|
||||
inputBorder: colors.slateDark,
|
||||
inputBorderFocused: colors.slate,
|
||||
|
||||
listItemHoverBackground: colors.black10,
|
||||
listItemHoverBorder: colors.black50,
|
||||
|
||||
toolbarBackground: colors.white,
|
||||
toolbarInput: colors.black10,
|
||||
toolbarItem: colors.lightBlack,
|
||||
|
||||
buttonBackground: colors.white,
|
||||
buttonText: colors.lightBlack,
|
||||
buttonNeutralBackground: colors.almostBlack,
|
||||
buttonNeutralText: colors.white,
|
||||
|
||||
tooltipBackground: colors.white,
|
||||
tooltipText: colors.lightBlack,
|
||||
|
||||
blockToolbarBackground: colors.white,
|
||||
blockToolbarTrigger: colors.almostWhite,
|
||||
blockToolbarTriggerIcon: colors.almostBlack,
|
||||
blockToolbarItem: colors.lightBlack,
|
||||
|
||||
quote: colors.almostWhite,
|
||||
codeBackground: colors.almostBlack,
|
||||
codeBorder: colors.black50,
|
||||
embedBorder: colors.black50,
|
||||
horizontalRule: colors.almostWhite,
|
||||
};
|
||||
|
||||
export default light;
|
||||
|
||||
@@ -7038,6 +7038,10 @@ outline-icons@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.6.0.tgz#6c7897d354e6bd77ca5498cd3a989b8cb9482574"
|
||||
|
||||
outline-icons@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.7.0.tgz#093f2f18c80bf5577bc31a6ff41460f2feb76fb7"
|
||||
|
||||
oy-vey@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/oy-vey/-/oy-vey-0.10.0.tgz#16160f837f0ea3d0340adfc2377ba93d1ed9ce76"
|
||||
|
||||
Reference in New Issue
Block a user