diff --git a/components/dropdown/dropdown.jsx b/components/dropdown/dropdown.jsx index 5149f67ea..526d74f96 100644 --- a/components/dropdown/dropdown.jsx +++ b/components/dropdown/dropdown.jsx @@ -38,11 +38,14 @@ const Dropdown = { class: `${prefixCls}-trigger`, disabled, }) - const overlay = $slots.overlay && $slots.overlay[0] - // menu cannot be selectable in dropdown defaultly + const overlay = this.overlay || $slots.overlay && $slots.overlay[0] + // menu cannot be selectable in dropdown defaultly, but multiple type can be selectable const overlayProps = overlay && getPropsData(overlay) - const selectable = (overlayProps.selectable !== undefined && overlayProps.selectable !== false) || false - const fixedModeOverlay = cloneElement(overlay, { + let selectable = false + if (overlayProps) { + selectable = !!overlayProps.selectable || overlayProps.multiple + } + const fixedModeOverlay = overlay && cloneElement(overlay, { props: { mode: 'vertical', selectable, diff --git a/components/vc-menu/assets/index.less b/components/vc-menu/assets/index.less new file mode 100644 index 000000000..1da996cf5 --- /dev/null +++ b/components/vc-menu/assets/index.less @@ -0,0 +1,298 @@ +@menuPrefixCls: rc-menu; + +@font-face { + font-family: 'FontAwesome'; + src: url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?v=4.2.0'); + src: url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} + +.@{menuPrefixCls} { + outline: none; + margin-bottom: 0; + padding-left: 0; // Override default ul/ol + list-style: none; + border: 1px solid #d9d9d9; + box-shadow: 0 0 4px #d9d9d9; + border-radius: 3px; + color: #666; + + &-hidden { + display: none; + } + + &-collapse { + overflow: hidden; + &-active { + transition: height .3s ease-out; + } + } + + &-item-group-list { + margin: 0; + padding: 0; + } + + &-item-group-title { + color: #999; + line-height: 1.5; + padding: 8px 10px; + border-bottom: 1px solid #dedede; + } + + &-item-active, + &-submenu-active > &-submenu-title { + background-color: #eaf8fe; + } + + &-item-selected { + background-color: #eaf8fe; + // fix chrome render bug + transform: translateZ(0); + } + + &-submenu-selected { + background-color: #eaf8fe; + } + + & > li&-submenu { + padding: 0; + } + + &-horizontal&-sub, + &-vertical&-sub, + &-vertical-left&-sub, + &-vertical-right&-sub { + min-width: 160px; + margin-top: 0; + } + + &-item, &-submenu-title { + margin: 0; + position: relative; + display: block; + padding: 7px 7px 7px 16px; + white-space: nowrap; + + // Disabled state sets text to gray and nukes hover/tab effects + &.@{menuPrefixCls}-item-disabled, &.@{menuPrefixCls}-submenu-disabled { + color: #777 !important; + } + } + & > &-item-divider { + height: 1px; + margin: 1px 0; + overflow: hidden; + padding: 0; + line-height: 0; + background-color: #e5e5e5; + } + + &-submenu { + &-popup { + position: absolute; + } + > .@{menuPrefixCls} { + background-color: #fff; + } + } + + .@{menuPrefixCls}-submenu-title, .@{menuPrefixCls}-item { + .anticon { + width: 14px; + height: 14px; + margin-right: 8px; + top: -1px; + } + } + + &-horizontal { + background-color: #F3F5F7; + border: none; + border-bottom: 1px solid transparent; + border-bottom: 1px solid #d9d9d9; + box-shadow: none; + + & > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title { + padding: 15px 20px; + } + + & > .@{menuPrefixCls}-submenu, & > .@{menuPrefixCls}-item { + float: left; + border-bottom: 2px solid transparent; + + &-active { + border-bottom: 2px solid #2db7f5; + background-color: #F3F5F7; + color: #2baee9; + } + } + + &:after { + content: "\20"; + display: block; + height: 0; + clear: both; + } + } + + &-vertical, + &-vertical-left, + &-vertical-right, + &-inline { + padding: 12px 0; + & > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title { + padding: 12px 8px 12px 24px; + } + .@{menuPrefixCls}-submenu-arrow { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + vertical-align: baseline; + text-align: center; + text-transform: none; + text-rendering: auto; + position: absolute; + right: 16px; + line-height: 1.5em; + &:before { + content: "\f0da"; + } + } + } + &-inline { + .@{menuPrefixCls}-submenu-arrow { + transform: rotate(90deg); + transition: transform .3s; + } + & .@{menuPrefixCls}-submenu-open > .@{menuPrefixCls}-submenu-title { + .@{menuPrefixCls}-submenu-arrow { + transform: rotate(-90deg); + } + } + } + + &-vertical&-sub, + &-vertical-left&-sub, + &-vertical-right&-sub { + padding: 0; + } + + &-sub&-inline { + padding: 0; + border: none; + border-radius: 0; + box-shadow: none; + + & > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title { + padding-top: 8px; + padding-bottom: 8px; + padding-right: 0; + } + } + + .effect() { + animation-duration: .3s; + animation-fill-mode: both; + transform-origin: 0 0; + } + + &-open { + + &-slide-up-enter, &-slide-up-appear { + .effect(); + opacity: 0; + animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); + animation-play-state: paused; + } + + &-slide-up-leave { + .effect(); + opacity: 1; + animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); + animation-play-state: paused; + } + + &-slide-up-enter&-slide-up-enter-active, &-slide-up-appear&-slide-up-appear-active { + animation-name: rcMenuOpenSlideUpIn; + animation-play-state: running; + } + + &-slide-up-leave&-slide-up-leave-active { + animation-name: rcMenuOpenSlideUpOut; + animation-play-state: running; + } + + @keyframes rcMenuOpenSlideUpIn { + 0% { + opacity: 0; + transform-origin: 0% 0%; + transform: scaleY(0); + } + 100% { + opacity: 1; + transform-origin: 0% 0%; + transform: scaleY(1); + } + } + @keyframes rcMenuOpenSlideUpOut { + 0% { + opacity: 1; + transform-origin: 0% 0%; + transform: scaleY(1); + } + 100% { + opacity: 0; + transform-origin: 0% 0%; + transform: scaleY(0); + } + } + + &-zoom-enter, &-zoom-appear { + opacity: 0; + .effect(); + animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); + animation-play-state: paused; + } + + &-zoom-leave { + .effect(); + animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); + animation-play-state: paused; + } + + &-zoom-enter&-zoom-enter-active, &-zoom-appear&-zoom-appear-active { + animation-name: rcMenuOpenZoomIn; + animation-play-state: running; + } + + &-zoom-leave&-zoom-leave-active { + animation-name: rcMenuOpenZoomOut; + animation-play-state: running; + } + + @keyframes rcMenuOpenZoomIn { + 0% { + opacity: 0; + transform: scale(0, 0); + } + 100% { + opacity: 1; + transform: scale(1, 1); + } + } + @keyframes rcMenuOpenZoomOut { + 0% { + + transform: scale(1, 1); + } + 100% { + opacity: 0; + transform: scale(0, 0); + } + } + } + +} + diff --git a/components/vc-table/demo/colspan-rowspan.js b/components/vc-table/demo/colspan-rowspan.js new file mode 100644 index 000000000..eb5bcf82b --- /dev/null +++ b/components/vc-table/demo/colspan-rowspan.js @@ -0,0 +1,108 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import Table from '../index' +import '../assets/index.less' + +const columns = [ + { title: '手机号', dataIndex: 'a', colSpan: 2, width: 100, key: 'a', render (h, o, row, index) { + const obj = { + children: o, + props: {}, + } + // 设置第一行为链接 + if (index === 0) { + obj.children = {o} + } + // 第5行合并两列 + if (index === 4) { + obj.props.colSpan = 2 + } + + if (index === 5) { + obj.props.colSpan = 6 + } + return obj + } }, + { title: '电话', dataIndex: 'b', colSpan: 0, width: 100, key: 'b', render (h, o, row, index) { + const obj = { + children: o, + props: {}, + } + // 列合并掉的表格设置colSpan=0,不会去渲染 + if (index === 4 || index === 5) { + obj.props.colSpan = 0 + } + return obj + } }, + { title: 'Name', dataIndex: 'c', width: 100, key: 'c', render (h, o, row, index) { + const obj = { + children: o, + props: {}, + } + + if (index === 5) { + obj.props.colSpan = 0 + } + return obj + } }, + { title: 'Address', dataIndex: 'd', width: 200, key: 'd', render (h, o, row, index) { + const obj = { + children: o, + props: {}, + } + if (index === 0) { + obj.props.rowSpan = 2 + } + if (index === 1 || index === 5) { + obj.props.rowSpan = 0 + } + + return obj + } }, + { title: 'Gender', dataIndex: 'e', width: 200, key: 'e', render (h, o, row, index) { + const obj = { + children: o, + props: {}, + } + if (index === 5) { + obj.props.colSpan = 0 + } + return obj + } }, + { + title: 'Operations', dataIndex: '', key: 'f', + render (h, o, row, index) { + if (index === 5) { + return { + props: { + colSpan: 0, + }, + } + } + return Operations + }, + }, +] + +const data = [ + { a: '13812340987', b: '0571-12345678', c: '张三', d: '文一西路', e: 'Male', key: '1' }, + { a: '13812340986', b: '0571-98787658', c: '张夫人', d: '文一西路', e: 'Female', key: '2' }, + { a: '13812988888', b: '0571-099877', c: '李四', d: '文二西路', e: 'Male', key: '3' }, + { a: '1381200008888', b: '0571-099877', c: '王五', d: '文二西路', e: 'Male', key: '4' }, + { a: '0571-88888110', c: '李警官', d: '武林门', e: 'Male', key: '5' }, + { a: '资料统计完毕于xxxx年xxx月xxx日', key: '6' }, +] + +export default { + render () { + return ( +
+

colSpan & rowSpan

+ + + ) + }, +} diff --git a/components/vc-table/demo/column-resize.js b/components/vc-table/demo/column-resize.js new file mode 100644 index 000000000..b5a95d37a --- /dev/null +++ b/components/vc-table/demo/column-resize.js @@ -0,0 +1,76 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import Table from '../index' +import '../assets/index.less' +import BaseMixin from '../../_util/BaseMixin' + +const ResizeableTitle = (h, props, children) => { + console.log(props) + const { onResize, width, ...restProps } = props + + if (!width) { + return + } + return ( + + ) +} + +export default { + mixins: [BaseMixin], + data () { + return { + columns: [ + { title: 'title1', dataIndex: 'a', key: 'a', width: 100 }, + { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title3', dataIndex: 'c', key: 'c', width: 200 }, + { + title: 'Operations', dataIndex: '', key: 'd', render () { + return Operations + }, + }, + ], + data: [ + { a: '123', key: '1' }, + { a: 'cdd', b: 'edd', key: '2' }, + { a: '1333', c: 'eee', d: 2, key: '3' }, + ], + components: { + header: { + cell: ResizeableTitle, + }, + }, + } + }, + methods: { + handleResize (index) { + return (e, { size }) => { + this.setState(({ columns }) => { + const nextColumns = [...columns] + nextColumns[index] = { + ...nextColumns[index], + width: size.width, + } + return { columns: nextColumns } + }) + } + }, + }, + + render () { + const columns = this.columns.map((col, index) => ({ + ...col, + onHeaderCell: (column) => ({ + width: column.width, + onResize: this.handleResize(index), + }), + })) + + return ( +
+

Integrate with react-resizable

+
{children}{children}
+ + ) + }, +} + diff --git a/components/vc-table/demo/dropdown.js b/components/vc-table/demo/dropdown.js new file mode 100644 index 000000000..f92c6950b --- /dev/null +++ b/components/vc-table/demo/dropdown.js @@ -0,0 +1,109 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import Table from '../index' +import '../assets/index.less' +import Menu from '../../menu' +const Item = Menu.Item +const Divider = Menu.Divider +import DropDown from '../../dropdown' + +const data = [] +for (let i = 0; i < 10; i++) { + data.push({ + key: i, + a: `a${i}`, + b: `b${i}`, + c: `c${i}`, + }) +} + +export default { + data () { + this.filters = [] + return { + visible: false, + } + }, + methods: { + handleVisibleChange (visible) { + this.visible = visible + }, + + handleSelect (selected) { + this.filters.push(selected) + }, + + handleDeselect (key) { + const index = this.filters.indexOf(key) + if (index !== -1) { + this.filters.splice(index, 1) + } + }, + + confirmFilter () { + console.log(this.filters.join(',')) + this.visible = false + }, + }, + + render () { + const menu = ( + + one + two + three + + + + + + ) + + const columns = [ + { + title: ( +
+ title1 + + + filter + +
+ ), key: 'a', dataIndex: 'a', width: 100, + }, + { title: 'title2', key: 'b', dataIndex: 'b', width: 100 }, + { title: 'title3', key: 'c', dataIndex: 'c', width: 200 }, + ] + + return ( +
+

use dropdown

+
record.key} + /> + + + ) + }, +} + diff --git a/components/vc-table/demo/expandedRowRender.js b/components/vc-table/demo/expandedRowRender.js new file mode 100644 index 000000000..06b52e422 --- /dev/null +++ b/components/vc-table/demo/expandedRowRender.js @@ -0,0 +1,110 @@ +/* eslint-disable no-console,func-names,react/no-multi-comp */ +import Table from '../index' +import '../assets/index.less' + +const tableData = [ + { key: 0, a: '123' }, + { key: 1, a: 'cdd', b: 'edd' }, + { key: 2, a: '1333', c: 'eee', d: 2 }, +] + +export default { + data () { + return { + data: tableData, + expandedRowKeys: [], + expandIconAsCell: true, + expandRowByClick: false, + columns: [ + { title: 'title 1', dataIndex: 'a', key: 'a', width: 100 }, + { title: 'title 2', dataIndex: 'b', key: 'b', width: 100 }, + { title: 'title 3', dataIndex: 'c', key: 'c', width: 200 }, + { title: 'Operation', dataIndex: '', key: 'x', render: this.renderAction }, + ], + } + }, + methods: { + onExpand (expanded, record) { + console.log('onExpand', expanded, record) + }, + + onExpandedRowsChange (rows) { + this.setState({ + expandedRowKeys: rows, + }) + }, + + onExpandIconAsCellChange (e) { + this.setState({ + expandIconAsCell: e.target.checked, + }) + }, + + onExpandRowByClickChange (e) { + this.setState({ + expandRowByClick: e.target.checked, + }) + }, + + toggleButton () { + if (this.expandedRowKeys.length) { + const closeAll = () => { this.expandedRowKeys = [] } + return + } + const openAll = () => { this.expandedRowKeys = [0, 1, 2] } + return + }, + + remove (index) { + const data = this.data + data.splice(index, 1) + this.setState({ data }) + }, + + expandedRowRender (record) { + // console.log(record); + return

extra: {record.a}

+ }, + + renderAction (o, row, index) { + return this.remove(index)}>Delete + }, + }, + + render () { + const { expandIconAsCell, expandRowByClick, expandedRowKeys, data } = this + return ( +
+

expandedRowRender

+
+ {this.toggleButton()} + + + expandIconAsCell + + + expandRowByClick +
+ + + + ) + }, +} diff --git a/components/vc-table/src/TableHeaderRow.jsx b/components/vc-table/src/TableHeaderRow.jsx index e87532b51..bfc0f538f 100644 --- a/components/vc-table/src/TableHeaderRow.jsx +++ b/components/vc-table/src/TableHeaderRow.jsx @@ -13,7 +13,7 @@ const TableHeaderRow = { height: PropTypes.any, }, name: 'TableHeaderRow', - render () { + render (h) { const { row, index, height, components, $listeners = {}} = this const onHeaderRow = $listeners.headerRow const HeaderRow = components.header.row @@ -40,6 +40,9 @@ const TableHeaderRow = { ...customProps, key: column.key || column.dataIndex || i, }) + if (typeof HeaderCell === 'function') { + return HeaderCell(h, headerCellProps, children) + } return (