Merge pull request #77 from unix/image

feat(image): add browser style
This commit is contained in:
witt
2020-04-09 13:40:31 +08:00
committed by GitHub
8 changed files with 299 additions and 5 deletions

View File

@@ -39,7 +39,7 @@ const Display: React.FC<React.PropsWithChildren<DisplayProps>> = React.memo(({
margin: 0 auto;
border-radius: 4px;
overflow: hidden;
width: ${width ? width : 'fit-content'};
width: ${width ? width : 'max-content'};
box-shadow: ${shadow ? theme.expressiveness.shadowLarge : 'none'};
max-width: 100%;
}

View File

@@ -0,0 +1,13 @@
import React from 'react'
const ImageBrowserHttpsIcon = () => {
return (
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
<path d="M5 10.2H19V21H5V10.2Z" />
<path fillRule="evenodd" clipRule="evenodd"
d="M12 12C13.933 12 15.5 10.3882 15.5 8.4C15.5 6.41177 13.933 4.8 12 4.8C10.067 4.8 8.5 6.41177 8.5 8.4C8.5 10.3882 10.067 12 12 12ZM12 13.8C14.8995 13.8 17.25 11.3823 17.25 8.4C17.25 5.41766 14.8995 3 12 3C9.10051 3 6.75 5.41766 6.75 8.4C6.75 11.3823 9.10051 13.8 12 13.8Z" />
</svg>
)
}
export default ImageBrowserHttpsIcon

View File

@@ -0,0 +1,189 @@
import React, { useMemo } from 'react'
import Link from '../link'
import useTheme from '../styles/use-theme'
import withDefaults from '../utils/with-defaults'
import ImageBrowserHttpsIcon from './image-browser-https-icon'
import { getBrowserColors, BrowserColors } from './styles'
interface Props {
title?: string
url?: string
showFullLink?: boolean
invert?: boolean
className?: string
}
const defaultProps = {
className: '',
showFullLink: false,
invert: false
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type ImageBrowserProps = Props & typeof defaultProps & NativeAttrs
const getHostFromUrl = (url: string) => {
try {
return (new URL(url)).host
} catch (e) {
return url
}
}
const getTitle = (
title: string,
colors: BrowserColors,
) => (
<div className="title">
{title}
<style jsx>{`
.title {
color: ${colors.titleColor};
font-size: .75rem;
}
`}</style>
</div>
)
const getAddressInput = (
url: string,
showFullLink: boolean,
colors: BrowserColors,
) => (
<div className="address-input">
<span className="https"><ImageBrowserHttpsIcon /></span>
<Link pure href={url} title={url} target="_blank">
{showFullLink ? url : getHostFromUrl(url)}
</Link>
<style jsx>{`
.address-input {
height: 1.75rem;
max-width: 55%;
min-width: 40%;
background-color: ${colors.inputBgColor};
color: inherit;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10px;
overflow: hidden;
position: relative;
}
.address-input :global(*) {
font-size: .75rem;
color: inherit;
}
.address-input :global(a) {
max-width: 90%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
display: inline-block;
color: inherit;
}
.https {
width: 12px;
height: 12px;
margin-right: 5px;
user-select: none;
margin-top: -1px;
color: inherit;
}
`}</style>
</div>
)
const ImageBrowser: React.FC<React.PropsWithChildren<ImageBrowserProps>> = ({
url, title, children, showFullLink, invert, className, ...props
}) => {
const theme = useTheme()
const colors = useMemo(
() => getBrowserColors(invert, theme.palette),
[invert, theme.palette]
)
const input = useMemo(() => {
if (url) return getAddressInput(url, showFullLink, colors)
if (title) return getTitle(title, colors)
return null
}, [url, showFullLink, title, colors])
return (
<div className={`bowser ${className}`} {...props}>
<header>
<div className="traffic">
<span className="close" />
<span className="mini" />
<span className="full" />
</div>
{input}
</header>
{children}
<style jsx>{`
.bowser {
background-color: transparent;
box-shadow: ${theme.expressiveness.shadowLarge};
width: max-content;
margin: 0 auto;
border-radius: ${theme.layout.radius};
overflow: hidden;
}
.bowser :global(.image) {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
header {
height: 2.5rem;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
color: ${colors.color};
background-color: ${colors.barBgColor};
border-bottom: 1px solid ${colors.borderColor};
}
.traffic {
width: auto;
position: absolute;
left: ${theme.layout.gapHalf};
top: 50%;
transform: translateY(-50%);
bottom: 0;
height: 100%;
display: flex;
align-items: center;
user-select: none;
}
.traffic span {
border-radius: 50%;
width: .75rem;
height: .75rem;
display: inline-block;
margin-right: .5rem;
}
.close {
background-color: #ff5f56;
}
.mini {
background-color: #ffbd2e;
}
.full {
background-color: #27c93f;
}
`}</style>
</div>
)
}
export default withDefaults(ImageBrowser, defaultProps)

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from 'react'
import withDefaults from '../utils/with-defaults'
import useTheme from '../styles/use-theme'
import ImageSkeleton from './image.skeleton'
import ImageBrowser from './image-browser'
interface Props {
src: string
@@ -83,4 +83,12 @@ const Image: React.FC<ImageProps> = React.memo(({
)
})
export default withDefaults(Image, defaultProps)
type ImageComponent<P = {}> = React.FC<P> & {
Browser: typeof ImageBrowser
}
type ComponentProps = Partial<typeof defaultProps> & Omit<Props, keyof typeof defaultProps> & NativeAttrs
(Image as ImageComponent<ComponentProps>).defaultProps = defaultProps
export default Image as ImageComponent<ComponentProps>

View File

@@ -1,5 +1,6 @@
import Image from './image'
import { ImageProps } from './image'
import ImageBrowser from './image-browser'
Image.Browser = ImageBrowser
export type Props = ImageProps
export default Image

View File

@@ -0,0 +1,25 @@
import { ZeitUIThemesPalette } from 'components/styles/themes'
export type BrowserColors = {
color: string
barBgColor: string
inputBgColor: string
borderColor: string
titleColor: string
}
export const getBrowserColors = (invert: boolean, palette: ZeitUIThemesPalette): BrowserColors => {
return invert ? {
color: palette.background,
barBgColor: palette.foreground,
inputBgColor: palette.accents_8,
borderColor: palette.accents_7,
titleColor: palette.accents_2,
} : {
color: palette.foreground,
barBgColor: palette.background,
inputBgColor: palette.accents_1,
borderColor: palette.border,
titleColor: palette.accents_5,
}
}

View File

@@ -26,6 +26,25 @@ Display image content.
src="http://www.deelay.me/2000/https://react.zeit-ui.co/images/custom-domains.png" />
`} />
<Playground
title="Browser"
desc="Add a browser style warpper to the image."
scope={{ Image, Display, Code }}
code={`
<Image.Browser url="https://react.zeit-ui.co/en-us/guide/introduction" >
<Image width="540" height="246" src="https://user-images.githubusercontent.com/11304944/76085431-fd036480-5fec-11ea-8412-9e581425344a.png" />
</Image.Browser>
`} />
<Playground
title="Browser Invert"
scope={{ Image, Display, Code }}
code={`
<Image.Browser url="https://react.zeit-ui.co/en-us/guide/introduction" invert>
<Image width="540" height="246" src="https://user-images.githubusercontent.com/11304944/76085431-fd036480-5fec-11ea-8412-9e581425344a.png" />
</Image.Browser>
`} />
<Playground
title="Compose"
desc="Show `Image` in `Display` component."
@@ -49,6 +68,16 @@ Display image content.
| **scale** | scale value | `string` | - | `100%` |
| ... | native props | `ImgHTMLAttributes` | `'alt', 'className', ...` | - |
<Attributes.Title>Image.Browser.Props</Attributes.Title>
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| **title** | show text title (when "url" is unset) | `string` | - | - |
| **url** | show url on browser address input | `string` | - | - |
| **showFullLink** | show full url | `boolean` | - | `false` |
| **invert** | invert colors | `boolean` | - | `false` |
| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
</Attributes>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>

View File

@@ -25,6 +25,25 @@ export const meta = {
src="http://www.deelay.me/2000/https://react.zeit-ui.co/images/custom-domains.png" />
`} />
<Playground
title="浏览器风格"
desc="为图片增加浏览器风格的外装饰。"
scope={{ Image, Display, Code }}
code={`
<Image.Browser url="https://react.zeit-ui.co/en-us/guide/introduction" >
<Image width="540" height="246" src="https://user-images.githubusercontent.com/11304944/76085431-fd036480-5fec-11ea-8412-9e581425344a.png" />
</Image.Browser>
`} />
<Playground
title="反转的浏览器风格"
scope={{ Image, Display, Code }}
code={`
<Image.Browser url="https://react.zeit-ui.co/en-us/guide/introduction" invert>
<Image width="540" height="246" src="https://user-images.githubusercontent.com/11304944/76085431-fd036480-5fec-11ea-8412-9e581425344a.png" />
</Image.Browser>
`} />
<Playground
title="组合"
desc="与 `Display` 组件组合使用。"
@@ -48,6 +67,16 @@ export const meta = {
| **scale** | 缩放比例 | `string` | - | `100%` |
| ... | 原生属性 | `ImgHTMLAttributes` | `'alt', 'className', ...` | - |
<Attributes.Title>Image.Browser.Props</Attributes.Title>
| 属性 | 描述 | 类型 | 推荐值 | 默认
| ---------- | ---------- | ---- | -------------- | ------ |
| **title** | 显示标题 (当 "url" 未设置时生效) | `string` | - | - |
| **url** | 在浏览器地址栏显示链接 | `string` | - | - |
| **showFullLink** | 显示完整的链接而非域名 | `boolean` | - | `false` |
| **invert** | 反转所有颜色 | `boolean` | - | `false` |
| ... | 原生属性 | `HTMLAttributes` | `'id', 'className', ...` | - |
</Attributes>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>