Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support custom arrow icon in vertical and inline mode. #182

Merged
merged 20 commits into from
Aug 22, 2018
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,18 @@ ReactDOM.render(<Menu>
<th>see <a href="./src/placements.jsx">placements.jsx</a></th>
<td>Describes how the popup menus should be positioned</td>
</tr>
<tr>
<td>itemIcon</td>
<td>(props: MenuItemProps) => ReactNode</td>
<th></th>
<td>specific the menu item icon.</td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specify icon for menu item.

</tr>
<tr>
<td>expandIcon</td>
<td>(props: SubMenuProps) => ReactNode</td>
<th></th>
<td>specific the menu item icon.</td>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SubMenu

Copy link
Contributor

@picodoth picodoth Aug 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specify expanded icon for menu item submenu title

</tr>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arrowIcon?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

itemIcon?

</tbody>
</table>

Expand Down Expand Up @@ -237,6 +249,12 @@ ReactDOM.render(<Menu>
<th></th>
<td></td>
</tr>
<tr>
<td>itemIcon</td>
<td>(props: MenuItemProps) => ReactNode</td>
<th></th>
<td>specific the menu item icon.</td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specify icon for menu item.

</tr>
</tbody>
</table>

Expand Down Expand Up @@ -313,6 +331,18 @@ ReactDOM.render(<Menu>
<th></th>
<td>The offset of the popup submenu, in an x, y coordinate array. e.g.: `[0,15]`</td>
</tr>
<tr>
<td>expandIcon</td>
<td>(props: SubMenuProps) => ReactNode</td>
<th></th>
<td>specific the menu item icon.</td>
</tr>
<tr>
<td>itemIcon</td>
<td>(props: SubMenuProps & { isSubMenu: boolean }) => ReactNode</td>
<th></th>
<td>specific the menu item icon.</td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上

</tr>
</tbody>
</table>

Expand Down
Empty file added examples/custom-icon.html
Empty file.
216 changes: 216 additions & 0 deletions examples/custom-icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/* eslint no-console:0 */
import * as React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import Menu, { SubMenu, Item as MenuItem, Divider } from 'rc-menu';
import 'rc-menu/assets/index.less';
import animate from 'css-animation';

const getSvgIcon = (style = {}, text) => {
if (text) {
return (
<i style={style}>
{text}
</i>
);
}
const path = 'M869 487.8L491.2 159.9c-2.9-2.5-6.6-3.9-10.5-3.9h' +
'-88.5c-7.4 0-10.8 9.2-5.2 14l350.2 304H152c-4.4 0-8 3.6-8 8v' +
'60c0 4.4 3.6 8 8 8h585.1L386.9 854c-5.6 4.9-2.2 14 5.2 14h91' +
'.5c1.9 0 3.8-0.7 5.2-2L869 536.2c14.7-12.8 14.7-35.6 0-48.4z';
return (
<i style={style}>
<svg
viewBox="0 0 1024 1024"
width="1em"
height="1em"
fill="currentColor"
style={{ verticalAlign: '-.125em' }}
>
<path d={path} />
</svg>
</i>
);
};

function itemIcon(props) {
return getSvgIcon({
position: 'absolute',
right: '1rem',
color: props.isSelected ? 'pink' : 'inherit',
});
}

function expandIcon(props) {
return getSvgIcon({
position: 'absolute',
right: '1rem',
color: 'lightblue',
transform: `rotate(${props.isOpen ? 90 : 0}deg)`,
});
}

const animation = {
enter(node, done) {
let height;
return animate(node, 'rc-menu-collapse', {
start() {
height = node.offsetHeight;
node.style.height = 0;
},
active() {
node.style.height = `${height}px`;
},
end() {
node.style.height = '';
done();
},
});
},

appear() {
return this.enter.apply(this, arguments);
},

leave(node, done) {
return animate(node, 'rc-menu-collapse', {
start() {
node.style.height = `${node.offsetHeight}px`;
},
active() {
node.style.height = 0;
},
end() {
node.style.height = '';
done();
},
});
},
};

class Demo extends React.Component {

static childContextTypes = {
expandIcon: PropTypes.func,
itemIcon: PropTypes.func,
}

state = {
useContext: false,
};

getChildContext() {
if (!this.state.useContext) {
return {};
}
return {
expandIcon: (props) => getSvgIcon({
position: 'absolute',
right: '1rem',
color: 'lightblue',
transform: `rotate(${props.isOpen ? 90 : 0}deg)`,
}, '>>'),
itemIcon: (props) => getSvgIcon({
position: 'absolute',
right: '1rem',
color: props.isSelected ? 'pink' : 'inherit',
}, '--'),
};
}

onOpenChange = (value) => {
console.log('onOpenChange', value);
}

handleClick = (info) => {
console.log(`clicked ${info.key}`);
console.log(info);
}

toggleContext = () => {
this.setState({
useContext: !this.state.useContext,
});
}

renderNestSubMenu = (props = {}) => {
return (
<SubMenu title={<span>offset sub menu 2</span>} key="4" popupOffset={[10, 15]} {...props}>
<MenuItem key="4-1">inner inner</MenuItem>
<Divider />
<SubMenu
key="4-2"
title={<span>sub menu 3</span>}
>
<SubMenu title="sub 4-2-0" key="4-2-0">
<MenuItem key="4-2-0-1">inner inner</MenuItem>
<MenuItem key="4-2-0-2">inner inner2</MenuItem>
</SubMenu>
<MenuItem key="4-2-1">inn</MenuItem>
<SubMenu title={<span>sub menu 4</span>} key="4-2-2">
<MenuItem key="4-2-2-1">inner inner</MenuItem>
<MenuItem key="4-2-2-2">inner inner2</MenuItem>
</SubMenu>
<SubMenu title="sub 4-2-3" key="4-2-3">
<MenuItem key="4-2-3-1">inner inner</MenuItem>
<MenuItem key="4-2-3-2">inner inner2</MenuItem>
</SubMenu>
</SubMenu>
</SubMenu>
);
}

renderCommonMenu = (props = {}) => {
return (
<Menu onClick={this.handleClick} onOpenChange={this.onOpenChange} {...props}>
<SubMenu title={<span>sub menu</span>} key="1">
<MenuItem key="1-1">0-1</MenuItem>
<MenuItem key="1-2">0-2</MenuItem>
</SubMenu>
{this.renderNestSubMenu()}
<MenuItem key="2">1</MenuItem>
<MenuItem key="3">outer</MenuItem>
<MenuItem disabled>disabled</MenuItem>
<MenuItem key="5">outer3</MenuItem>
</Menu>
);
}

render() {
const verticalMenu = this.renderCommonMenu({
mode: 'vertical',
openAnimation: 'zoom',
itemIcon,
expandIcon,
});

const inlineMenu = this.renderCommonMenu({
mode: 'inline',
defaultOpenKeys: ['1'],
openAnimation: animation,
itemIcon,
expandIcon,
});

return (
<div style={{ margin: 20 }}>
<h2>Antd menu - custom icon</h2>
<div>
<input
type="checkbox"
checked={this.state.useContext}
onChange={this.toggleContext}
/> use context
</div>
<div>
<h3>vertical</h3>
<div style={{ margin: 20, width: 200 }}>{verticalMenu}</div>
<h3>inline</h3>
<div style={{ margin: 20, width: 400 }}>{inlineMenu}</div>
</div>
</div>
);
}
}

ReactDOM.render(<Demo />, document.getElementById('__react-content'));
13 changes: 13 additions & 0 deletions src/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@ class Menu extends React.Component {
activeKey: PropTypes.string,
prefixCls: PropTypes.string,
builtinPlacements: PropTypes.object,
itemIcon: PropTypes.func,
expandIcon: PropTypes.func,
};

static contextTypes = {
itemIcon: PropTypes.func,
expandIcon: PropTypes.func,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

context 应该去掉?

Copy link
Contributor Author

@HeskeyBaozi HeskeyBaozi Aug 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

师兄你是对的。
用到的两处地方DropdownSelect都可以通过传itemIconexpandIcon<MenuItem />即可。

我一开始的想法就是透过原本的rc-dropdown和rc-select传入到里层的rc-menu,才用的context。现在发现没有必要。
这样之后rc-select可能还会有个pr。


static defaultProps = {
selectable: true,
onClick: noop,
Expand Down Expand Up @@ -195,6 +202,10 @@ class Menu extends React.Component {

render() {
let { ...props } = this.props;
const {
itemIcon: itemIconFromCtx,
expandIcon: expandIconFromCtx,
} = this.context;
props.className += ` ${props.prefixCls}-root`;
props = {
...props,
Expand All @@ -204,6 +215,8 @@ class Menu extends React.Component {
onSelect: this.onSelect,
openTransitionName: this.getOpenTransitionName(),
parentMenu: this,
itemIcon: itemIconFromCtx || props.itemIcon,
expandIcon: expandIconFromCtx || props.expandIcon,
};
return (
<Provider store={this.store}>
Expand Down
5 changes: 5 additions & 0 deletions src/MenuItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class MenuItem extends React.Component {
multiple: PropTypes.bool,
isSelected: PropTypes.bool,
manualRef: PropTypes.func,
itemIcon: PropTypes.func,
};

static defaultProps = {
Expand Down Expand Up @@ -182,6 +183,9 @@ export class MenuItem extends React.Component {
style.paddingLeft = props.inlineIndent * props.level;
}
menuAllProps.forEach(key => delete props[key]);

const icon = typeof this.props.itemIcon === 'function' ?
React.createElement(this.props.itemIcon, this.props) : null;
return (
<li
{...props}
Expand All @@ -190,6 +194,7 @@ export class MenuItem extends React.Component {
style={style}
>
{props.children}
{icon}
</li>
);
}
Expand Down
18 changes: 16 additions & 2 deletions src/SubMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export class SubMenu extends React.Component {
store: PropTypes.object,
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']),
manualRef: PropTypes.func,
expandIcon: PropTypes.func,
itemIcon: PropTypes.func,
};

static defaultProps = {
Expand Down Expand Up @@ -366,6 +368,8 @@ export class SubMenu extends React.Component {
prefixCls: props.rootPrefixCls,
id: this._menuId,
manualRef: this.saveMenuInstance,
itemIcon: props.itemIcon,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

多余的?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SubMenu也类似Menu一样,需要ItemIcon传给children中的MenuItem。
若去掉则整个Menu只有第一层有图标,SubMenu中的MenuItem都没有图标。

expandIcon: props.expandIcon,
};

const haveRendered = this.haveRendered;
Expand Down Expand Up @@ -461,6 +465,13 @@ export class SubMenu extends React.Component {
};
}

// expand custom icon should NOT be displayed in menu with horizontal mode.
let icon = null;
if (props.mode !== 'horizontal') {
icon = typeof this.props.expandIcon === 'function' ?
React.createElement(this.props.expandIcon, { ...this.props, isSubMenu: true }) : null;
}

const title = (
<div
ref={this.saveSubMenuTitle}
Expand All @@ -474,7 +485,7 @@ export class SubMenu extends React.Component {
title={typeof props.title === 'string' ? props.title : undefined}
>
{props.title}
<i className={`${prefixCls}-arrow`} />
{icon || <i className={`${prefixCls}-arrow`} />}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 icon 应该只给 MenuItem 用吧

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

也包括SubMenu

</div>
);
const children = this.renderChildren(props.children);
Expand Down Expand Up @@ -529,7 +540,10 @@ export class SubMenu extends React.Component {
}
}

const connected = connect(({ openKeys, activeKey, selectedKeys }, { eventKey, subMenuKey }) => ({
const connected = connect((
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

无关的提交

{ openKeys, activeKey, selectedKeys },
{ eventKey, subMenuKey }
) => ({
isOpen: openKeys.indexOf(eventKey) > -1,
active: activeKey[subMenuKey] === eventKey,
selectedKeys,
Expand Down
Loading