Skip to content
Snippets Groups Projects
Commit 963252c5 authored by Jure's avatar Jure
Browse files

feat: improve shared components

parent 9d2de6b4
No related branches found
No related tags found
No related merge requests found
......@@ -59,7 +59,8 @@ const label = (status, published) => {
new: 'Unsubmitted',
rejected: 'Rejected',
submitted: 'Submitted',
revise: 'Revising',
revise: 'Revise',
revising: 'Revising',
invited: 'Invited', // reviewer status
completed: 'Completed', // reviewer status
}
......
/* eslint-disable class-methods-use-this */
/* eslint-disable handle-callback-err */
/* eslint-disable react/sort-comp */
import React from 'react'
class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true }
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error(error, errorInfo)
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>
}
return this.props.children
}
}
export { ErrorBoundary }
......@@ -49,6 +49,20 @@ export const SectionRow = styled.div`
padding: ${grid(2)} ${grid(3)};
`
export const ClickableSectionRow = styled(SectionRow)`
color: ${th('colorText')};
:last-of-type {
border-radius: 0 0 ${th('borderRadius')} ${th('borderRadius')};
}
&:hover {
cursor: pointer;
background-color: ${th('colorBackgroundHue')};
svg {
stroke: ${th('colorPrimary')};
}
}
`
export const SectionRowGrid = styled(SectionRow)`
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
......@@ -81,5 +95,6 @@ export { Page, Heading }
export const HeadingWithAction = styled.div`
display: grid;
grid-template-columns: 1fr auto;
grid-gap: ${grid(2)};
align-items: center;
`
......@@ -13,7 +13,7 @@ const IconWrapper = styled.div`
position: relative;
border-radius: 6px;
padding: ${props => (props.noPadding || props.inline ? '0' : '8px 12px')};
top: ${props => props.top || 0};
svg {
stroke: ${props => props.color || props.theme.colorText};
width: calc(${props => props.size} * ${th('gridUnit')});
......@@ -28,6 +28,7 @@ export const Icon = ({
size = 3,
noPadding,
inline,
top,
...props
}) => {
const name = _.upperFirst(_.camelCase(children))
......@@ -40,6 +41,7 @@ export const Icon = ({
noPadding={noPadding}
role="img"
size={size}
top={top}
>
{icons[name]({})}
</IconWrapper>
......
/* eslint-disable no-nested-ternary */
import React, { useContext } from 'react'
import ReactSelect from 'react-select'
import { ThemeContext } from 'styled-components'
......@@ -10,10 +11,19 @@ const styles = th => ({
control: (provided, state) => ({
...provided,
border: state.isFocused
? `1px solid ${th.colorPrimary}`
: `1px solid ${th.colorBorder}`,
boxShadow: state.isFocused ? `0 0 0 1px ${th.colorPrimary}` : 'none',
border: !state.selectProps.standalone
? state.isFocused
? `1px solid ${th.colorPrimary}`
: `1px solid ${th.colorBorder}`
: 'none',
boxShadow: !state.selectProps.standalone
? state.isFocused
? `0 0 0 1px ${th.colorPrimary}`
: 'none'
: state.isFocused
? `0 0 0 1px ${th.colorPrimary}`
: th.boxShadow,
borderRadius: th.borderRadius,
'&:hover': {
boxShadow: `0 0 0 1px ${th.colorPrimary}`,
......
import React, { useState, useEffect } from 'react'
import styled from 'styled-components'
import { th, override } from '@pubsweet/ui-toolkit'
const Tab = styled.div`
padding: ${th('gridUnit')} 1em;
font-size: ${th('fontSizeBaseSmall')};
font-weight: 500;
background-color: ${({ active }) =>
active ? th('colorBackground') : th('colorFurniture')};
border-radius: ${th('borderRadius')} ${th('borderRadius')} 0 0;
border-bottom: 2px solid
${({ active }) => (active ? th('colorPrimary') : th('colorFurniture'))};
color: ${({ active }) => (active ? th('colorPrimary') : th('colorText'))};
cursor: pointer;
${override('ui.Tab')};
`
const TabsContainer = styled.div`
display: flex;
`
const TabContainer = styled.div.attrs(props => ({
'data-test-id': props['data-test-id'] || 'tab-container',
}))``
const Content = styled.div``
const Tabs = ({ sections, onChange, defaultActiveKey = null }) => {
const [activeKey, setActiveKey] = useState(defaultActiveKey)
useEffect(() => {
setActiveKey(defaultActiveKey)
}, [defaultActiveKey])
const setActiveKeyAndCallOnChange = activeKey => {
setActiveKey(activeKey)
if (typeof onChange === 'function') {
onChange(activeKey)
}
}
const currentContent = (
sections.find(section => section.key === activeKey) || {}
).content
return (
<>
<TabsContainer>
{sections.map(({ key, label }) => (
<TabContainer
key={key}
onClick={() => setActiveKeyAndCallOnChange(key)}
>
<Tab active={activeKey === key}>{label || key}</Tab>
</TabContainer>
))}
</TabsContainer>
{activeKey && <Content>{currentContent}</Content>}
</>
)
}
export { Tabs }
import React, { useState, useEffect } from 'react'
import styled from 'styled-components'
import { grid } from '@pubsweet/ui-toolkit'
import { Select } from './Select'
const Container = styled.div`
margin-top: ${props => grid(props.top)};
`
export const VersionSwitcher = ({ versions = [], children, top = 2 }) => {
// One can pass in versions as prop or as children
let normalizedVersions
let mode
if (versions.length) {
normalizedVersions = versions
mode = 'props'
} else if (children) {
normalizedVersions = children
mode = 'children'
}
const defaultVersion = normalizedVersions[0] && normalizedVersions[0].key
const [selectedVersionKey, selectVersionKey] = useState(defaultVersion)
useEffect(() => {
normalizedVersions = versions.length ? versions : children
selectVersionKey(normalizedVersions[0] && normalizedVersions[0].key)
}, [])
if (!normalizedVersions) {
return null
}
const selectedVersion = normalizedVersions.find(
v => v.key === selectedVersionKey,
)
return (
<>
<Select
onChange={option => {
selectVersionKey(option.value)
}}
options={normalizedVersions.map(d => ({
value: d.key,
label: mode === 'props' ? d.label : d.props.label,
}))}
placeholder="Select version..."
standalone
value={selectedVersionKey}
/>
<Container top={top}>
{mode === 'props' ? selectedVersion.content : selectedVersion}
</Container>
</>
)
}
......@@ -10,3 +10,6 @@ export * from './Badge'
export * from './Select'
export * from './Dropzone'
export * from './FilesUpload'
export * from './VersionSwitcher'
export * from './Tabs'
export * from './ErrorBoundary'
......@@ -9,8 +9,9 @@ import EditorElements from './EditorElements'
const Layout = styled.div`
background-color: ${th('colorBackground')};
border-radius: ${th('borderRadius')};
max-width: 90rem;
border-radius: 0 ${th('borderRadius')} ${th('borderRadius')}
${th('borderRadius')};
// max-width: 90rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
display: grid;
......
import moment from 'moment'
// TODO: memoize
const manuscriptVersions = manuscript => {
const versions = []
if (manuscript.manuscriptVersions?.[0]) {
// TODO: The manuscript versions generally come ordered by
// created descending, but we could sort them again if need be
versions.push(...manuscript.manuscriptVersions)
versions.push(manuscript)
} else {
versions.push(manuscript)
}
return versions.map((manuscript, index) => ({
label:
index === 0
? `Current version (${versions.length})`
: `${moment(manuscript.created).format(
'YYYY-MM-DD',
)} (${versions.length - index})`,
manuscript,
}))
}
export default manuscriptVersions
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment