Skip to content

Commit 4ae6678

Browse files
committed
feat: add draft contextView component
1 parent d30b163 commit 4ae6678

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed

src/components/contextview/index.tsx

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as React from 'react';
2+
import * as ReactDOM from 'react-dom';
3+
import { prefixClaName, classNames } from 'mo/common/className';
4+
import { getRelativePosition, select, HTMLElementType, IPosition } from 'mo/common/dom';
5+
import './style.scss';
6+
7+
export interface IContextViewProps {
8+
render?: () => React.ReactNode
9+
}
10+
11+
export interface IContextView {
12+
contextView: HTMLElementType;
13+
show(anchorPos: IPosition, render?: () => React.ReactNode): void;
14+
hide(): void;
15+
}
16+
17+
const contextViewClass = prefixClaName('context-view');
18+
const contentClass = '.context-view-content';
19+
20+
export function useContextView(props?: IContextViewProps): IContextView {
21+
const claName = classNames(contextViewClass, 'fade-in');
22+
let contextView: HTMLElementType = select('.' + contextViewClass); // Singleton contextView dom
23+
24+
const show = (anchorPos: IPosition, render?: () => React.ReactNode) => {
25+
const content = select(contentClass);
26+
const renderContent = render || props?.render;
27+
if (!renderContent) throw Error('ContextView show Error: the render parameter not allowed be null!');
28+
ReactDOM.render(<>
29+
{ renderContent() }
30+
</>, content, () => {
31+
// Notice: if want to get the computed offsetHeight of contextView,
32+
// must display contextView ahead.
33+
contextView!.style.display = 'block';
34+
const position = getRelativePosition(contextView!, anchorPos);
35+
contextView!.style.cssText = `
36+
top: ${position.y}px;
37+
left: ${position.x}px;
38+
`;
39+
});
40+
};
41+
42+
const hide = () => {
43+
if (contextView) {
44+
contextView.style.display = 'none';
45+
ReactDOM.unmountComponentAtNode(select(contentClass)!);
46+
}
47+
};
48+
49+
const onMaskClick = (e: React.MouseEvent) => {
50+
e.preventDefault();
51+
e.stopPropagation();
52+
hide();
53+
};
54+
55+
if (!contextView) {
56+
contextView = document.createElement('div');
57+
contextView.className = claName;
58+
contextView.style.display = 'none';
59+
document.body.appendChild(contextView);
60+
61+
ReactDOM.render(<>
62+
<div className="context-view-block" onClick={onMaskClick} onContextMenu={onMaskClick}></div>
63+
<div className="context-view-content"></div>
64+
</>, contextView);
65+
}
66+
67+
return { contextView, show, hide };
68+
};

src/components/contextview/style.scss

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@import 'mo/style/const';
2+
$name: 'context-view';
3+
4+
#{prefix($name)} {
5+
border: 0;
6+
outline: 0;
7+
position: absolute;
8+
width: initial;
9+
z-index: 2500;
10+
11+
.#{$name}-block {
12+
bottom: 0;
13+
height: 100%;
14+
left: 0;
15+
opacity: 0;
16+
position: fixed;
17+
right: 0;
18+
top: 0;
19+
width: 100%;
20+
z-index: -1;
21+
}
22+
}
+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
2+
import * as React from 'react';
3+
import { storiesOf } from '@storybook/react';
4+
import { withKnobs } from '@storybook/addon-knobs';
5+
import { propsTable } from '../common/propsTable';
6+
7+
import { useContextView } from 'mo/components/contextview';
8+
9+
const stories = storiesOf('ContextView', module);
10+
stories.addDecorator(withKnobs);
11+
12+
const propDefinitions = [{
13+
property: 'render',
14+
propType: '() => React.ReactNode',
15+
required: false,
16+
description: 'Default render content',
17+
defaultValue: null,
18+
}];
19+
20+
stories.add('Basic Usage', () => {
21+
const contextView = useContextView();
22+
23+
const show = (event: React.MouseEvent): void => {
24+
const x = event.clientX;
25+
const y = event.clientY;
26+
console.log('x, y:', x, y);
27+
contextView.show({
28+
x: x,
29+
y: y,
30+
}, () => {
31+
return (
32+
<div>
33+
<h1>Hello World: </h1>
34+
<p>x: {x}</p>
35+
<p>y: {y}</p>
36+
</div>
37+
);
38+
});
39+
};
40+
41+
const styled: React.CSSProperties = {
42+
position: 'relative',
43+
width: 200,
44+
height: 200,
45+
top: 0,
46+
left: 0,
47+
margin: 'auto',
48+
background: '#dddddd',
49+
};
50+
51+
return (
52+
<div>
53+
<h2>简述</h2>
54+
<p>
55+
ContextView 组件主要是提供了一个可根据指定锚点位置、渲染内容的视图容器。
56+
</p>
57+
<h2>使用示例</h2>
58+
<div id="topLeft"
59+
onClick={show}
60+
style={styled}>
61+
Click me!
62+
</div>
63+
</div>
64+
);
65+
}, {
66+
info: {
67+
inline: true,
68+
TableComponent: () => propsTable({ propDefinitions }),
69+
// propTablesExclude: [],
70+
text: `
71+
代码示例:
72+
~~~js
73+
import { useContextView } from 'mo/components/contextview';
74+
75+
const contextView = useContextView();
76+
77+
const mouseMove = (event: React.MouseEvent): void => {
78+
contextView.show({
79+
x: event.clientX,
80+
y: event.clientY,
81+
}, () => {
82+
return (
83+
<h1>Hello World</h1>
84+
);
85+
});
86+
};
87+
88+
return (
89+
<div>
90+
<div id="topLeft"
91+
onMouseMove={mouseMove}
92+
style={
93+
{
94+
position: 'absolute',
95+
width: 200,
96+
height: 200,
97+
top: 0,
98+
left: 0,
99+
right: 0,
100+
bottom: 0,
101+
background: '#dddddd',
102+
}
103+
}>
104+
Hover me!
105+
</div>
106+
</div>
107+
);
108+
~~
109+
`,
110+
},
111+
});

0 commit comments

Comments
 (0)