Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
京东前端
nutui-react
提交
9bef2157
N
nutui-react
项目概览
京东前端
/
nutui-react
通知
0
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
N
nutui-react
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
9bef2157
编写于
8月 23, 2022
作者:
Y
yangxiaolu1993
提交者:
GitHub
8月 23, 2022
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat:Popover功能补齐与单元测试 (#219)
上级
d57f997c
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
624 addition
and
345 deletion
+624
-345
src/packages/popover/__tests__/popover.spec.tsx
src/packages/popover/__tests__/popover.spec.tsx
+90
-0
src/packages/popover/demo.tsx
src/packages/popover/demo.tsx
+180
-142
src/packages/popover/doc.md
src/packages/popover/doc.md
+24
-18
src/packages/popover/popover.scss
src/packages/popover/popover.scss
+205
-83
src/packages/popover/popover.tsx
src/packages/popover/popover.tsx
+125
-102
未找到文件。
src/packages/popover/__tests__/popover.spec.tsx
0 → 100644
浏览文件 @
9bef2157
import
React
from
'
react
'
import
{
render
,
waitFor
,
fireEvent
}
from
'
@testing-library/react
'
import
'
@testing-library/jest-dom
'
import
Popover
from
'
../index
'
import
Button
from
'
@/packages/button
'
const
itemList
=
[
{
name
:
'
选项一
'
,
},
{
name
:
'
选项二
'
,
},
{
name
:
'
选项三
'
,
},
]
const
itemListDisabled
=
[
{
name
:
'
选项一
'
,
disabled
:
true
,
},
{
name
:
'
选项二
'
,
disabled
:
true
,
},
{
name
:
'
选项三
'
,
},
]
const
iconItemList
=
[
{
name
:
'
选项一
'
,
icon
:
'
my2
'
,
},
{
name
:
'
选项二
'
,
icon
:
'
cart2
'
,
},
{
name
:
'
选项三
'
,
icon
:
'
location2
'
,
},
]
test
(
'
render popover content
'
,
async
()
=>
{
const
{
container
}
=
render
(
<
Popover
visible
list
=
{
itemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
明朗风格
</
Button
>
</
Popover
>
)
const
content
=
container
.
querySelectorAll
(
'
.popover-content
'
)[
0
]
expect
(
content
.
className
).
toContain
(
'
popover-content-show popover-content popover-content--bottom
'
)
})
test
(
'
should emit onchoose event when clicking the action
'
,
async
()
=>
{
const
choose
=
jest
.
fn
()
const
{
container
}
=
render
(
<
Popover
visible
list
=
{
itemList
}
onChoose
=
{
choose
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
明朗风格
</
Button
>
</
Popover
>
)
const
contentItem
=
container
.
querySelectorAll
(
'
.popover-menu-item
'
)[
0
]
fireEvent
.
click
(
contentItem
)
await
waitFor
(()
=>
expect
(
choose
.
mock
.
calls
[
0
][
0
].
name
).
toEqual
(
'
选项一
'
))
await
waitFor
(()
=>
expect
(
choose
.
mock
.
calls
[
0
][
1
]).
toBe
(
0
))
})
test
(
'
should not emit select event when the action is disabled
'
,
async
()
=>
{
const
choose
=
jest
.
fn
()
const
{
container
}
=
render
(
<
Popover
visible
list
=
{
itemListDisabled
}
onChoose
=
{
choose
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
明朗风格
</
Button
>
</
Popover
>
)
const
contentItem
=
container
.
querySelectorAll
(
'
.popover-menu-item
'
)[
0
]
fireEvent
.
click
(
contentItem
)
await
waitFor
(()
=>
expect
(
choose
).
not
.
toBeCalled
())
})
src/packages/popover/demo.tsx
浏览文件 @
9bef2157
import
React
,
{
useState
}
from
'
react
'
import
React
,
{
use
Ref
,
use
State
}
from
'
react
'
import
{
Popover
}
from
'
./popover
'
import
Button
from
'
@/packages/button
'
import
Icon
from
'
@/packages/icon
'
interface
List
{
name
:
string
icon
?:
string
disabled
?:
boolean
}
const
BadgeDemo
=
()
=>
{
const
selfContentStyle
=
{
width
:
'
195px
'
,
...
...
@@ -23,14 +29,7 @@ const BadgeDemo = () => {
fontSize
:
'
10px
'
,
textAlign
:
'
center
'
,
}
as
any
const
hTwo
=
{
marginTop
:
'
30px
'
,
marginBottom
:
'
10px
'
,
fontSize
:
'
14px
'
,
color
:
'
#909ca4
'
,
padding
:
'
0 10px
'
,
fontWeight
:
400
,
}
as
any
const
itemList
=
[
{
name
:
'
选项一
'
,
...
...
@@ -99,144 +98,183 @@ const BadgeDemo = () => {
const
[
darkTheme
,
setDarkTheme
]
=
useState
(
false
)
const
[
showIcon
,
setShowIcon
]
=
useState
(
false
)
const
[
disableAction
,
setDisableAction
]
=
useState
(
false
)
const
[
topLocation
,
setTopLocation
]
=
useState
(
false
)
const
[
rightLocation
,
setRightLocation
]
=
useState
(
false
)
const
[
leftLocation
,
setLeftLocation
]
=
useState
(
false
)
const
[
customized
,
setCustomized
]
=
useState
(
false
)
const
customLocation
=
useRef
([
{
bottom
:
false
},
{
top
:
false
},
{
left
:
false
},
{
right
:
false
},
{
'
top-start
'
:
false
},
{
'
top-end
'
:
false
},
{
'
bottom-start
'
:
false
},
{
'
bottom-end
'
:
false
},
{
'
left-start
'
:
false
},
{
'
left-end
'
:
false
},
{
'
right-start
'
:
false
},
{
'
right-end
'
:
false
},
])
const
[
customLocationName
,
setCustomLocationName
]
=
useState
(
'
top
'
)
const
[
customLocationShow
,
setCustomLocationShow
]
=
useState
(
false
)
const
chooseHandle
=
(
item
:
List
,
index
:
number
)
=>
{
console
.
log
(
'
选择
'
)
}
const
styles
=
`
.customButtonBox {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
.brickBox {
display: flex;
justify-content: center;
margin: 80px 0;
}
.brick {
width: 60px;
height: 60px;
background: #1989fa;
border-radius: 10px;
}
.popover-content {
width: 100px;
}
.customContent .popover-content{
width: 200px;
}
`
return
(
<>
<
style
>
{
styles
}
</
style
>
<
div
className
=
"demo"
>
<
div
>
<
h6
style
=
{
hTwo
}
>
基础用法
</
h6
>
<
Popover
visible
=
{
lightTheme
}
onClick
=
{
()
=>
{
lightTheme
?
setLightTheme
(
false
)
:
setLightTheme
(
true
)
}
}
list
=
{
itemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
明朗风格
</
Button
>
</
Popover
>
<
Popover
visible
=
{
darkTheme
}
theme
=
"dark"
onClick
=
{
()
=>
{
darkTheme
?
setDarkTheme
(
false
)
:
setDarkTheme
(
true
)
}
}
list
=
{
itemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
暗黑风格
</
Button
>
</
Popover
>
</
div
>
<
div
>
<
h6
style
=
{
hTwo
}
>
选项配置
</
h6
>
<
Popover
visible
=
{
showIcon
}
theme
=
"dark"
onClick
=
{
()
=>
{
showIcon
?
setShowIcon
(
false
)
:
setShowIcon
(
true
)
}
}
list
=
{
iconItemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
展示图标
</
Button
>
</
Popover
>
<
Popover
visible
=
{
disableAction
}
onClick
=
{
()
=>
{
disableAction
?
setDisableAction
(
false
)
:
setDisableAction
(
true
)
}
}
list
=
{
itemListDisabled
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
禁用选项
</
Button
>
</
Popover
>
</
div
>
<
div
>
<
h6
style
=
{
hTwo
}
>
自定义内容
</
h6
>
<
Popover
visible
=
{
customized
}
onClick
=
{
()
=>
{
customized
?
setCustomized
(
false
)
:
setCustomized
(
true
)
}
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
自定义内容
</
Button
>
{
customized
?
(
<
div
className
=
"self-content"
style
=
{
selfContentStyle
}
>
{
selfContent
.
map
((
item
:
any
)
=>
{
return
(
<
div
className
=
"self-content-item"
style
=
{
selfContentItem
}
key
=
{
item
.
name
}
>
<
Icon
name
=
{
item
.
name
}
size
=
"15"
/>
<
div
className
=
"self-content-desc"
style
=
{
selfContentDesc
}
>
{
item
.
desc
}
</
div
>
<
h2
>
基础用法
</
h2
>
<
Popover
visible
=
{
lightTheme
}
onClick
=
{
()
=>
{
lightTheme
?
setLightTheme
(
false
)
:
setLightTheme
(
true
)
}
}
list
=
{
itemList
}
style
=
{
{
marginRight
:
'
30px
'
}
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
明朗风格
</
Button
>
</
Popover
>
<
Popover
visible
=
{
darkTheme
}
theme
=
"dark"
onClick
=
{
()
=>
{
darkTheme
?
setDarkTheme
(
false
)
:
setDarkTheme
(
true
)
}
}
list
=
{
itemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
暗黑风格
</
Button
>
</
Popover
>
<
h2
>
选项配置
</
h2
>
<
Popover
visible
=
{
showIcon
}
theme
=
"dark"
onClick
=
{
()
=>
{
showIcon
?
setShowIcon
(
false
)
:
setShowIcon
(
true
)
}
}
list
=
{
iconItemList
}
style
=
{
{
marginRight
:
'
30px
'
}
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
展示图标
</
Button
>
</
Popover
>
<
Popover
visible
=
{
disableAction
}
onClick
=
{
()
=>
{
disableAction
?
setDisableAction
(
false
)
:
setDisableAction
(
true
)
}
}
list
=
{
itemListDisabled
}
onChoose
=
{
chooseHandle
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
禁用选项
</
Button
>
</
Popover
>
<
h2
>
自定义内容
</
h2
>
<
Popover
visible
=
{
customized
}
onClick
=
{
()
=>
{
customized
?
setCustomized
(
false
)
:
setCustomized
(
true
)
}
}
location
=
"bottom-start"
className
=
"customContent"
>
<
Button
type
=
"primary"
shape
=
"square"
>
自定义内容
</
Button
>
{
customized
?
(
<
div
className
=
"self-content"
style
=
{
selfContentStyle
}
>
{
selfContent
.
map
((
item
:
any
)
=>
{
return
(
<
div
className
=
"self-content-item"
style
=
{
selfContentItem
}
key
=
{
item
.
name
}
>
<
Icon
name
=
{
item
.
name
}
size
=
"15"
/>
<
div
className
=
"self-content-desc"
style
=
{
selfContentDesc
}
>
{
item
.
desc
}
</
div
>
)
})
}
</
div
>
)
:
(
''
)
}
</
Popover
>
</
div
>
<
div
>
<
h6
style
=
{
hTwo
}
>
位置自定义
</
h6
>
<
Popover
visible
=
{
topLocation
}
location
=
"top"
theme
=
"dark"
onClick
=
{
()
=>
{
topLocation
?
setTopLocation
(
false
)
:
setTopLocation
(
true
)
}
}
list
=
{
iconItemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
向上弹出
</
Button
>
</
Popover
>
{
/* eslint-disable-next-line jsx-a11y/heading-has-content */
}
<
h2
style
=
{
hTwo
}
/>
<
Popover
visible
=
{
rightLocation
}
location
=
"right"
theme
=
"dark"
onClick
=
{
()
=>
{
rightLocation
?
setRightLocation
(
false
)
:
setRightLocation
(
true
)
}
}
list
=
{
iconItemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
向右弹出
</
Button
>
</
Popover
>
<
Popover
visible
=
{
leftLocation
}
location
=
"left"
theme
=
"dark"
onClick
=
{
()
=>
{
leftLocation
?
setLeftLocation
(
false
)
:
setLeftLocation
(
true
)
}
}
list
=
{
iconItemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
向左弹出
</
Button
>
</
Popover
>
</
div
>
)
})
}
</
div
>
)
:
(
''
)
}
</
Popover
>
<
h2
className
=
"demoClass"
>
位置自定义
</
h2
>
<
Popover
visible
=
{
customLocationShow
}
location
=
{
customLocationName
}
onClick
=
{
()
=>
{
setCustomLocationShow
(
false
)
}
}
list
=
{
iconItemList
}
onChoose
=
{
chooseHandle
}
className
=
"brickBox"
>
<
div
className
=
"brick"
/>
</
Popover
>
<
div
className
=
"customButtonBox"
>
{
customLocation
.
current
.
map
((
location
,
i
)
=>
{
const
k
=
Object
.
keys
(
location
)[
0
]
as
any
const
v
=
Object
.
values
(
location
)[
0
]
return
(
<
Button
key
=
{
i
}
type
=
"primary"
shape
=
"square"
style
=
{
{
width
:
'
160px
'
,
marginBottom
:
'
8px
'
}
}
onClick
=
{
()
=>
{
setCustomLocationName
(
k
)
setCustomLocationShow
(
!
customLocationShow
)
}
}
>
{
k
}
弹出
</
Button
>
)
})
}
</
div
>
</
div
>
</>
...
...
src/packages/popover/doc.md
浏览文件 @
9bef2157
...
...
@@ -161,6 +161,26 @@ export default App;
:::
### 位置自定义
通过 location 属性来控制气泡的弹出位置。可选值
```
top # 顶部中间位置
left # 左侧中间位置
right # 右侧中间位置
bottom # 底部中间位置
```
自
`v1.2.3`
起新增
```
top-start # 顶部左侧位置
top-end # 顶部右侧位置
left-start # 左侧上方位置
left-end # 左侧下方位置
right-start # 右侧上方位置
right-end # 右侧下方位置
bottom-start # 底部左侧位置
bottom-end # 底部右侧位置
```
:::demo
```
tsx
import
React
,
{
useState
,
useRef
}
from
"
react
"
;
...
...
@@ -186,22 +206,6 @@ const App = () => {
list
=
{
iconItemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
向上弹出
</
Button
>
</
Popover
>
<
Popover
visible
=
{
rightLocation
}
location
=
"right"
theme
=
"dark"
onClick
=
{
()
=>
{
rightLocation
?
setRightLocation
(
false
)
:
setRightLocation
(
true
)}
}
list
=
{
iconItemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
向右弹出
</
Button
>
</
Popover
>
<
Popover
visible
=
{
leftLocation
}
location
=
"left"
theme
=
"dark"
onClick
=
{
()
=>
{
leftLocation
?
setLeftLocation
(
false
)
:
setLeftLocation
(
true
)}
}
list
=
{
iconItemList
}
>
<
Button
type
=
"primary"
shape
=
"square"
>
向左弹出
</
Button
>
</
Popover
>
</>
)
}
...
...
@@ -219,7 +223,8 @@ export default App;
| list | 选项列表 | List[] | [] |
| visible | 是否展示气泡弹出层 | boolean | false |
| theme | 主题风格,可选值为 dark | string |
`light`
|
| location | 弹出位置,可选值为 top,left,right | string |
`bottom`
|
| location | 弹出位置 | string |
`bottom`
|
| offset
`v1.2.3`
| 出现位置的偏移量 | number | 20 |
### List 数据结构
...
...
@@ -235,7 +240,8 @@ List 属性是一个由对象构成的数组,数组中的每个对象配置一
| 名称 | 说明 |
|---------|--------------|
| onClick | 打开(关闭)菜单时触发 |
| onClick | 点击菜单时触发 |
| onChoose | 点击选项时触发 |
src/packages/popover/popover.scss
浏览文件 @
9bef2157
@import
'../icon/icon.scss'
;
.popBox
{
// background: skyblue;
// // background: #fff;
// padding: 20px;
.titBox
{
// margin-bottom: 20px;
}
}
.nut-popover--dark
,
.nut-popover
{
position
:
relative
;
display
:
inline-block
;
margin-right
:
20px
;
>
div
{
position
:
relative
;
// z-index: 9999;
}
.more-background
{
background
:
$popover-white-background-color
;
opacity
:
0
;
...
...
@@ -21,10 +17,163 @@
z-index
:
10
;
left
:
0
;
}
.popoverContent--left
,
.popoverContent--right
,
.popoverContent--top
,
.popoverContent
{
.popover-arrow
{
position
:
absolute
;
width
:
0
;
height
:
0
;
border
:
8px
solid
transparent
;
}
// top
.popover-arrow-top
{
bottom
:
0
;
border-top-color
:
$popover-white-background-color
;
border-bottom-width
:
0
;
margin-bottom
:
-8px
;
}
.popover-content--top
{
left
:
50%
;
transform
:
translateX
(
-50%
);
.popover-arrow--top
{
left
:
50%
;
transform
:
translateX
(
-50%
);
}
}
.popover-content--top-end
{
.popover-arrow--top-end
{
right
:
16px
;
transform
:
translateX
(
0%
);
}
right
:
0
;
}
.popover-content--top-start
{
left
:
0
;
.popover-arrow--top-start
{
left
:
16px
;
transform
:
translateX
(
0%
);
}
}
// bottom
.popover-content--bottom
{
left
:
50%
;
transform
:
translateX
(
-50%
);
}
.popover-content--bottom-end
{
right
:
0
;
}
.popover-content--bottom-start
{
left
:
0
;
}
// left
.popover-content--left
{
top
:
50%
;
transform
:
translateY
(
-50%
);
}
.popover-content--left-end
{
bottom
:
0
;
}
.popover-content--left-start
{
top
:
0
;
}
// right
.popover-content--right
{
top
:
50%
;
transform
:
translateY
(
-50%
);
}
.popover-content--right-end
{
bottom
:
0
;
}
.popover-content--right-start
{
top
:
0
;
}
// arrow bottom
.popover-arrow-bottom
{
top
:
0px
;
border-bottom-color
:
$popover-white-background-color
;
border-top-width
:
0
;
margin-top
:
-8px
;
}
.popover-arrow--bottom
{
left
:
50%
;
transform
:
translateX
(
-50%
);
}
.popover-arrow--bottom-start
{
left
:
16px
;
transform
:
translateX
(
0%
);
}
.popover-arrow--bottom-end
{
right
:
16px
;
transform
:
translateX
(
0%
);
}
// arrow left
.popover-arrow-left
{
right
:
0px
;
border-left-color
:
$popover-white-background-color
;
border-right-width
:
0
;
margin-right
:
-8px
;
}
.popover-arrow--left
{
top
:
50%
;
transform
:
translateY
(
-50%
);
}
.popover-arrow--left-start
{
top
:
16px
;
transform
:
translateY
(
0%
);
}
.popover-arrow--left-end
{
bottom
:
16px
;
transform
:
translateY
(
0%
);
}
// arrow right
.popover-arrow-right
{
left
:
0px
;
border-right-color
:
$popover-white-background-color
;
border-left-width
:
0
;
margin-left
:
-8px
;
}
.popover-arrow--right
{
top
:
50%
;
transform
:
translateY
(
-50%
);
}
.popover-arrow--right-start
{
top
:
16px
;
transform
:
translateY
(
0%
);
}
.popover-arrow--right-end
{
bottom
:
16px
;
transform
:
translateY
(
0%
);
}
.popover-content
{
z-index
:
12
;
background
:
$popover-white-background-color
;
border-radius
:
5px
;
...
...
@@ -34,63 +183,37 @@
font-weight
:
normal
;
color
:
$popover-primary-text-color
;
position
:
absolute
;
.popoverArrow
{
position
:
absolute
;
width
:
0
;
height
:
0
;
border-left
:
8px
solid
transparent
;
border-right
:
8px
solid
transparent
;
border-top
:
10px
solid
transparent
;
border-bottom
:
10px
solid
$popover-white-background-color
;
box-shadow
:
0
2px
12px
#323233
1f
;
opacity
:
0
;
transition
:
opacity
0
.1s
;
&
.popover-content-show
{
opacity
:
1
;
}
.title-item
{
.popover-menu-item
{
display
:
flex
;
align-items
:
center
;
padding-bottom
:
8px
;
margin
:
8px
;
border-bottom
:
1px
solid
$popover-border-bottom-color
;
&
:first-child
{
margin-top
:
15px
;
}
&
:last-child
{
margin-bottom
:
2px
;
border-bottom
:
none
;
}
.title-name
{
.popover-menu-item-name
{
margin-right
:
12px
;
margin-left
:
8px
;
width
:
100%
;
}
}
}
.popoverContent--top
{
.popoverArrow--top
{
position
:
absolute
;
top
:
auto
;
border-left
:
8px
solid
transparent
;
border-right
:
8px
solid
transparent
;
border-top
:
10px
solid
$popover-white-background-color
;
border-bottom
:
10px
solid
transparent
;
}
}
.popoverContent--left
{
.popoverArrow--left
{
position
:
absolute
;
border-left
:
10px
solid
$popover-white-background-color
;
border-right
:
10px
solid
transparent
;
border-top
:
8px
solid
transparent
;
border-bottom
:
8px
solid
transparent
;
}
}
.popoverContent--right
{
.popoverArrow--right
{
position
:
absolute
;
border-left
:
10px
solid
transparent
;
border-right
:
10px
solid
$popover-white-background-color
;
border-top
:
8px
solid
transparent
;
border-bottom
:
8px
solid
transparent
;
}
}
.disabled
{
color
:
$popover-disable-color
;
...
...
@@ -101,42 +224,41 @@
.nut-popover--dark
{
background
:
$popover-dark-background-color
;
color
:
$popover-white-background-color
;
.popoverContent--left
,
.popoverContent--right
,
.popoverContent--top
,
.popoverContent
{
background
:
$popover-dark-background-color
;
color
:
$popover-white-background-color
;
.popoverArrow
{
border-bottom
:
10px
solid
$popover-dark-background-color
;
.popover-content
{
background
:
$popover-dark-background-color
!
important
;
color
:
$popover-white-background-color
!
important
;
}
.popover-content--bottom
,
.popover-content--bottom-start
,
.popover-content--bottom-end
{
.popover-arrow
{
border-bottom-color
:
$popover-dark-background-color
;
}
}
.popoverContent--top
{
.popoverArrow--top
{
position
:
absolute
;
top
:
auto
;
border-left
:
8px
solid
transparent
;
border-right
:
8px
solid
transparent
;
border-top
:
10px
solid
$popover-dark-background-color
;
border-bottom
:
10px
solid
transparent
;
.popover-content--top
,
.popover-content--top-start
,
.popover-content--top-end
{
.popover-arrow
{
border-top-color
:
$popover-dark-background-color
;
}
}
.popoverContent--left
{
.popoverArrow--left
{
position
:
absolute
;
border-left
:
10px
solid
$popover-dark-background-color
;
border-right
:
10px
solid
transparent
;
border-top
:
8px
solid
transparent
;
border-bottom
:
8px
solid
transparent
;
.popover-content--left
,
.popover-content--left-start
,
.popover-content--left-end
{
.popover-arrow
{
border-left-color
:
$popover-dark-background-color
;
}
}
.popoverContent--right
{
.popoverArrow--right
{
position
:
absolute
;
border-left
:
10px
solid
transparent
;
border-right
:
10px
solid
$popover-dark-background-color
;
border-top
:
8px
solid
transparent
;
border-bottom
:
8px
solid
transparent
;
.popover-content--right
,
.popover-content--right-start
,
.popover-content--right-end
{
.popover-arrow
{
border-right-color
:
$popover-dark-background-color
;
}
}
}
src/packages/popover/popover.tsx
浏览文件 @
9bef2157
...
...
@@ -5,73 +5,90 @@ import React, {
useRef
,
useState
,
}
from
'
react
'
import
ReactDOM
from
'
react-dom
'
import
Trigger
from
'
./Trigger
'
import
Icon
from
'
@/packages/icon
'
import
Overlay
from
'
@/packages/overlay
'
export
type
PopoverTheme
=
'
light
'
|
'
dark
'
export
type
PopoverLocation
=
|
'
bottom
'
|
'
top
'
|
'
left
'
|
'
right
'
|
'
top-start
'
|
'
top-end
'
|
'
bottom-start
'
|
'
bottom-end
'
|
'
left-start
'
|
'
left-end
'
|
'
right-start
'
|
'
right-end
'
export
interface
List
{
name
:
string
icon
?:
string
disabled
?:
boolean
}
export
interface
PopoverProps
{
list
:
Array
<
any
>
theme
:
string
location
:
string
list
:
List
[]
theme
:
PopoverTheme
location
:
PopoverLocation
|
string
visible
:
boolean
offset
:
string
|
number
className
:
string
style
?:
CSSProperties
onClick
:
(
e
:
MouseEvent
)
=>
void
children
?:
React
.
ReactNode
onClick
:
(
e
:
React
.
MouseEvent
)
=>
void
onChoose
:
(
item
:
List
,
index
:
number
)
=>
void
}
export
function
findDOMNode
<
T
=
HTMLElement
>
(
node
:
React
.
ReactInstance
|
HTMLElement
):
T
{
if
(
node
instanceof
HTMLElement
)
{
return
node
as
unknown
as
T
}
// eslint-disable-next-line react/no-find-dom-node
return
ReactDOM
.
findDOMNode
(
node
)
as
unknown
as
T
}
const
getEleAttr
=
(
ele
:
HTMLElement
|
Element
)
=>
{
if
(
ele
&&
ele
.
getBoundingClientRect
)
{
return
ele
.
getBoundingClientRect
()
}
return
null
}
export
type
PopoverType
=
|
'
default
'
|
'
primary
'
|
'
success
'
|
'
warning
'
|
'
danger
'
const
defaultProps
=
{
list
:
[],
theme
:
'
light
'
,
location
:
'
bottom
'
,
visible
:
false
,
offset
:
20
,
className
:
''
,
onClick
:
(
e
:
MouseEvent
)
=>
{},
onClick
:
(
e
:
React
.
MouseEvent
)
=>
{},
onChoose
:
(
item
,
index
)
=>
{},
}
as
PopoverProps
export
const
Popover
:
FunctionComponent
<
Partial
<
PopoverProps
>>
=
(
props
)
=>
{
export
const
Popover
:
FunctionComponent
<
Partial
<
PopoverProps
>
&
React
.
HTMLAttributes
<
HTMLDivElement
>
>
=
(
props
)
=>
{
const
{
children
,
list
,
theme
,
location
,
visible
,
offset
,
className
,
style
,
onClick
,
onChoose
,
...
reset
}
=
{
...
defaultProps
,
...
props
,
}
const
goodItem
=
useRef
(
null
)
// eslint-disable-next-line react/no-find-dom-node
const
aa
=
goodItem
.
current
&&
findDOMNode
(
goodItem
.
current
)
setTimeout
(()
=>
{
if
(
aa
)
{
setElWidth
((
getEleAttr
(
aa
)
as
any
).
width
)
setElHeight
((
getEleAttr
(
aa
)
as
any
).
height
)
if
(
goodItem
.
current
&&
getEleAttr
(
goodItem
.
current
)
)
{
setElWidth
((
getEleAttr
(
goodItem
.
current
)
as
any
).
width
)
setElHeight
((
getEleAttr
(
goodItem
.
current
)
as
any
).
height
)
}
})
const
[
classes
,
setClasses
]
=
useState
(
''
)
const
[
elWidth
,
setElWidth
]
=
useState
(
0
)
const
[
elHeight
,
setElHeight
]
=
useState
(
0
)
...
...
@@ -83,101 +100,107 @@ export const Popover: FunctionComponent<Partial<PopoverProps>> = (props) => {
setPopoverArrow
(
popoverArrowSelf
())
},
[
list
,
theme
])
const
getStyle
=
()
=>
{
const
offNumer
=
Number
(
offset
)
?
Number
(
offset
)
:
0
const
style
:
CSSProperties
=
{}
if
(
location
===
'
top
'
)
{
style
.
bottom
=
elHeight
+
20
style
.
left
=
0
}
else
if
(
location
===
'
right
'
)
{
style
.
top
=
0
style
.
right
=
-
elWidth
-
20
}
else
if
(
location
===
'
left
'
)
{
style
.
top
=
0
style
.
left
=
-
elWidth
-
20
}
else
{
style
.
top
=
elHeight
+
20
style
.
left
=
0
}
style
.
top
+=
'
px
'
style
.
left
+=
'
px
'
style
.
bottom
+=
'
px
'
style
.
right
+=
'
px
'
return
style
}
const
getArrowStyle
=
()
=>
{
const
style
:
CSSProperties
=
{}
if
(
location
===
'
top
'
)
{
style
.
bottom
=
-
20
style
.
left
=
elWidth
/
2
}
else
if
(
location
===
'
right
'
)
{
style
.
top
=
20
style
.
left
=
-
20
}
else
if
(
location
===
'
left
'
)
{
style
.
top
=
20
style
.
right
=
-
20
if
(
location
.
includes
(
'
top
'
))
{
style
.
bottom
=
`
${
elHeight
+
offNumer
}
px`
}
else
if
(
location
.
includes
(
'
right
'
))
{
style
.
left
=
`
${
elWidth
+
offNumer
}
px`
}
else
if
(
location
.
includes
(
'
left
'
))
{
style
.
right
=
`
${
elWidth
+
offNumer
}
px`
}
else
{
style
.
left
=
elWidth
/
2
style
.
top
=
-
20
style
.
top
=
`
${
elHeight
+
offNumer
}
px`
}
style
.
top
+=
'
px
'
style
.
left
+=
'
px
'
style
.
bottom
+=
'
px
'
style
.
right
+=
'
px
'
return
style
}
const
classesSelf
=
()
=>
{
const
prefixCls
=
'
nut-popover
'
return
`
${
prefixCls
}
${
theme
?
`
${
prefixCls
}
--
${
theme
}
`
:
''
}
`
return
`
${
prefixCls
}
${
theme
?
`
${
prefixCls
}
--
${
theme
}
`
:
''
}
`
}
const
popoverContentSelf
=
()
=>
{
const
prefixCls
=
'
popoverContent
'
return
`
${
prefixCls
}
${
location
?
`
${
prefixCls
}
--
${
location
}
`
:
''
}
`
const
prefixCls
=
'
popover-content
'
return
`
${
prefixCls
}
-show
${
prefixCls
}
${
location
?
`
${
prefixCls
}
--
${
location
}
`
:
''
}
`
}
const
filter
=
()
=>
{
const
ms
=
[
'
top
'
,
'
bottom
'
,
'
left
'
,
'
right
'
]
return
ms
.
filter
((
m
)
=>
location
.
includes
(
m
))[
0
]
}
const
popoverArrowSelf
=
()
=>
{
const
prefixCls
=
'
popoverArrow
'
return
`
${
prefixCls
}
${
location
?
`
${
prefixCls
}
--
${
location
}
`
:
''
}
`
const
prefixCls
=
'
popover-arrow
'
return
`
${
prefixCls
}
${
prefixCls
}
-
${
filter
()}
${
location
?
`
${
prefixCls
}
--
${
location
}
`
:
''
}
`
}
// const showPopup = props.visible
const
handleClick
=
(
e
:
any
)
=>
{
const
handleClick
=
(
e
:
React
.
MouseEvent
)
=>
{
if
(
props
.
onClick
)
{
props
.
onClick
(
e
)
}
}
const
handleChoose
=
(
item
:
List
,
index
:
number
)
=>
{
if
(
!
item
.
disabled
)
{
onChoose
(
item
,
index
)
}
}
return
(
<
div
className
=
{
`
${
classes
}
${
className
}
`
}
style
=
{
{
...
style
}
}
{
...
reset
}
>
<
Trigger
forwardedRef
=
{
goodItem
}
>
<
div
onClick
=
{
(
e
)
=>
handleClick
(
e
)
}
>
{
Array
.
isArray
(
children
)
?
children
[
0
]
:
children
}
{
visible
?
(
<
div
className
=
{
`
${
popoverContent
}
`
}
style
=
{
getStyle
()
}
>
<
div
className
=
{
`
${
popoverArrow
}
`
}
style
=
{
getArrowStyle
()
}
>
{
'
'
}
<>
<
div
className
=
{
`
${
classes
}
${
className
}
`
}
style
=
{
{
...
style
}
}
{
...
reset
}
>
<
Trigger
forwardedRef
=
{
goodItem
}
>
<
div
onClick
=
{
(
e
)
=>
handleClick
(
e
)
}
>
{
Array
.
isArray
(
children
)
?
children
[
0
]
:
children
}
{
visible
?
(
<
div
className
=
{
`
${
popoverContent
}
`
}
style
=
{
getStyle
()
}
>
<
div
className
=
{
`
${
popoverArrow
}
`
}
/>
{
Array
.
isArray
(
children
)
?
children
[
1
]
:
''
}
{
list
.
map
((
item
:
List
,
i
:
number
)
=>
{
return
(
<
div
key
=
{
item
.
name
}
className
=
{
`popover-menu-item
${
item
.
disabled
?
'
disabled
'
:
''
}
`
}
onClick
=
{
()
=>
{
handleChoose
(
item
,
i
)
}
}
>
{
item
.
icon
?
(
<
Icon
className
=
"popover-menu-item-img"
name
=
{
item
.
icon
}
/>
)
:
(
''
)
}
<
div
className
=
"popover-menu-item-name"
>
{
item
.
name
}
</
div
>
</
div
>
)
})
}
</
div
>
{
Array
.
isArray
(
children
)
?
children
[
1
]
:
''
}
{
list
.
map
((
item
)
=>
{
return
(
<
div
key
=
{
item
.
name
}
className
=
{
`title-item
${
item
.
disabled
?
'
disabled
'
:
''
}
`
}
>
{
item
.
icon
?
(
<
Icon
className
=
"item-img"
name
=
{
item
.
icon
}
/>
)
:
(
''
)
}
<
div
className
=
"title-name"
>
{
item
.
name
}
</
div
>
</
div
>
)
})
}
</
div
>
)
:
null
}
</
div
>
</
Trigger
>
</
div
>
)
:
null
}
</
div
>
</
Trigger
>
</
div
>
{
visible
?
(
<
Overlay
visible
=
{
visible
}
onClick
=
{
(
e
)
=>
handleClick
(
e
)
}
style
=
{
{
background
:
'
transparent
'
}
}
/>
)
:
(
''
)
}
</>
)
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录