mirror of
https://github.com/zhigang1992/react.git
synced 2026-03-26 22:42:51 +08:00
@@ -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%;
|
||||
}
|
||||
|
||||
13
components/image/image-browser-https-icon.tsx
Normal file
13
components/image/image-browser-https-icon.tsx
Normal 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
|
||||
189
components/image/image-browser.tsx
Normal file
189
components/image/image-browser.tsx
Normal 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)
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
25
components/image/styles.ts
Normal file
25
components/image/styles.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user