Skip to content

Commit

Permalink
Better modal (#2554)
Browse files Browse the repository at this point in the history
- With examples using `pattern`s, allow building the URL from its component parts, including the query string.
- Provide a button to copy the link, with an animation.

To enable this for other badges, convert them to use a `pattern`: #1961.
  • Loading branch information
paulmelnikow authored Jan 11, 2019
1 parent da12f00 commit 6c2b040
Show file tree
Hide file tree
Showing 16 changed files with 1,288 additions and 79 deletions.
9 changes: 9 additions & 0 deletions frontend/components/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import React from 'react'
import PropTypes from 'prop-types'
import styled, { css } from 'styled-components'

const noAutocorrect = Object.freeze({
autoComplete: 'off',
autoCorrect: 'off',
autoCapitalize: 'off',
spellCheck: 'false',
})

const nonBreakingSpace = '\u00a0'

const BaseFont = styled.div`
Expand Down Expand Up @@ -94,11 +101,13 @@ const VerticalSpace = styled.hr`
`

export {
noAutocorrect,
nonBreakingSpace,
BaseFont,
H2,
H3,
Badge,
StyledInput,
InlineInput,
BlockInput,
VerticalSpace,
Expand Down
10 changes: 8 additions & 2 deletions frontend/components/main.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import styled, { createGlobalStyle } from 'styled-components'
import groupBy from 'lodash.groupby'
import {
categories,
Expand All @@ -21,6 +21,12 @@ import { CategoryHeadings, CategoryNav } from './category-headings'
import BadgeExamples from './badge-examples'
import { BaseFont } from './common'

const GlobalStyle = createGlobalStyle`
* {
box-sizing: border-box;
}
`

const AppContainer = styled(BaseFont)`
text-align: center;
`
Expand Down Expand Up @@ -147,13 +153,13 @@ export default class Main extends React.Component {

return (
<AppContainer id="app">
<GlobalStyle />
<Meta />
<Header />
<MarkupModal
example={selectedExample}
onRequestClose={this.dismissMarkupModal}
baseUrl={baseUrl}
key={selectedExample}
/>
<section>
<SuggestionAndSearch
Expand Down
48 changes: 48 additions & 0 deletions frontend/components/markup-modal/builder-common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

const BuilderOuterContainer = styled.div`
margin-top: 10px;
margin-bottom: 10px;
`

// The inner container is inline-block so that its width matches its columns.
const BuilderInnerContainer = styled.div`
display: inline-block;
padding: 11px 14px 10px;
border-radius: 4px;
background: #eef;
`

const BuilderContainer = ({ children }) => (
<BuilderOuterContainer>
<BuilderInnerContainer>{children}</BuilderInnerContainer>
</BuilderOuterContainer>
)
BuilderContainer.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
}

const labelFont = `
font-family: system-ui;
font-size: 11px;
text-transform: lowercase;
`

const BuilderLabel = styled.label`
${labelFont}
`

const BuilderCaption = styled.span`
${labelFont}
color: #999;
`

export { BuilderContainer, BuilderLabel, BuilderCaption }
74 changes: 74 additions & 0 deletions frontend/components/markup-modal/copied-content-indicator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react'
import PropTypes from 'prop-types'
import posed from 'react-pose'
import styled from 'styled-components'

const ContentAnchor = styled.span`
position: relative;
display: inline-block;
`

// 100vw allows providing styled content which is wider than its container.
const ContentContainer = styled.span`
width: 100vw;
position: absolute;
left: 50%;
transform: translateX(-50%);
will-change: opacity, top;
pointer-events: none;
`

const PosedContentContainer = posed(ContentContainer)({
hidden: { opacity: 0, transition: { duration: 100 } },
effectStart: { top: '-10px', opacity: 1.0, transition: { duration: 0 } },
effectEnd: { top: '-75px', opacity: 0.5 },
})

// When `trigger()` is called, render copied content that floats up, then
// disappears.
export default class CopiedContentIndicator extends React.Component {
state = {
pose: 'hidden',
}

trigger() {
this.setState({ pose: 'effectStart' })
}

handlePoseComplete = () => {
const { pose } = this.state
if (pose === 'effectStart') {
this.setState({ pose: 'effectEnd' })
} else {
this.setState({ pose: 'hidden' })
}
}

render() {
const { pose } = this.state
return (
<ContentAnchor>
<PosedContentContainer
pose={pose}
onPoseComplete={this.handlePoseComplete}
>
{this.props.copiedContent}
</PosedContentContainer>
{this.props.children}
</ContentAnchor>
)
}
}
CopiedContentIndicator.propTypes = {
copiedContent: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ import React from 'react'
import PropTypes from 'prop-types'
import Modal from 'react-modal'
import styled from 'styled-components'
import { badgeUrlFromPath, badgeUrlFromPattern } from '../../lib/make-badge-url'
import generateAllMarkup from '../lib/generate-image-markup'
import { advertisedStyles } from '../../supported-features.json'
import { Snippet } from './snippet'
import { BaseFont, H3, Badge, BlockInput } from './common'
import {
badgeUrlFromPath,
badgeUrlFromPattern,
} from '../../../lib/make-badge-url'
import { advertisedStyles } from '../../../supported-features.json'
import generateAllMarkup from '../../lib/generate-image-markup'
import { Snippet } from '../snippet'
import { BaseFont, H3, Badge, BlockInput } from '../common'
import MarkupModalContent from './markup-modal-content'

const ContentContainer = styled(BaseFont)`
text-align: center;
Expand Down Expand Up @@ -176,6 +180,8 @@ export default class MarkupModal extends React.Component {
const { onRequestClose, example: { title } = {} } = this.props
const { link, badgeUrl, exampleUrl, style } = this.state

const hasModernExample = isOpen && this.props.example.example.pattern

const common = {
autoComplete: 'off',
autoCorrect: 'off',
Expand All @@ -190,63 +196,69 @@ export default class MarkupModal extends React.Component {
contentLabel="Example Modal"
ariaHideApp={false}
>
<ContentContainer>
<form action="">
<H3>{title}</H3>
{isOpen && this.renderLivePreview()}
<p>
<label>
Link&nbsp;
<BlockInput
type="url"
value={link}
onChange={event => {
this.setState({ link: event.target.value })
}}
{...common}
/>
</label>
</p>
<p>
<label>
Path&nbsp;
<BlockInput
type="url"
value={badgeUrl}
onChange={event => {
this.setState({ badgeUrl: event.target.value })
}}
{...common}
/>
</label>
</p>
{exampleUrl && (
{hasModernExample ? (
<ContentContainer>
<MarkupModalContent {...this.props} />
</ContentContainer>
) : (
<ContentContainer>
<form action="">
<H3>{title}</H3>
{isOpen && this.renderLivePreview()}
<p>
<label>
Link&nbsp;
<BlockInput
type="url"
value={link}
onChange={event => {
this.setState({ link: event.target.value })
}}
{...common}
/>
</label>
</p>
<p>
<label>
Path&nbsp;
<BlockInput
type="url"
value={badgeUrl}
onChange={event => {
this.setState({ badgeUrl: event.target.value })
}}
{...common}
/>
</label>
</p>
{exampleUrl && (
<p>
Example&nbsp;
<Snippet fontSize="10pt" snippet={exampleUrl} />
</p>
)}
<p>
Example&nbsp;
<Snippet fontSize="10pt" snippet={exampleUrl} />
<label>
Style&nbsp;
<select
value={style}
onChange={event => {
this.setState({ style: event.target.value })
}}
>
{advertisedStyles.map(style => (
<option key={style} value={style}>
{style}
</option>
))}
</select>
</label>
</p>
)}
<p>
<label>
Style&nbsp;
<select
value={style}
onChange={event => {
this.setState({ style: event.target.value })
}}
>
{advertisedStyles.map(style => (
<option key={style} value={style}>
{style}
</option>
))}
</select>
</label>
</p>
{isOpen && this.renderMarkup()}
{isOpen && this.renderDocumentation()}
</form>
</ContentContainer>
{isOpen && this.renderMarkup()}
{isOpen && this.renderDocumentation()}
</form>
</ContentContainer>
)}
</Modal>
)
}
Expand Down
Loading

0 comments on commit 6c2b040

Please sign in to comment.