diff --git a/scss/components/_popup.scss b/scss/components/_popup.scss new file mode 100644 index 000000000..a70b6dea0 --- /dev/null +++ b/scss/components/_popup.scss @@ -0,0 +1,123 @@ +@keyframes PopUpIn { + from { + height: 0; + } +} + +@keyframes PopUpOut { + to { + height: 0; + } +} + +@keyframes PopUpOpacityIn { + from { + opacity: 0; + } +} + +@keyframes PopUpOpacityOut { + to { + opacity: 0; + } +} +.#{$ns}PopUp { + width: 100%; + position: fixed; + background: var(--PopOver-bg); + left: 0; + bottom: 0; + z-index: $zindex-popover; + padding: 0; + margin: 0; + font-weight: var(--fontWeightNormal); + letter-spacing: normal; + line-height: var(--lineHeightBase); + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: var(--fontSizeBase); + box-shadow: var(--boxShadow); + border: var(--borderWidth) solid var(--borderColor); + border-radius: var(--borderRadius); + overflow: hidden; + &.in, + &.out { + animation-duration: var(--animation-duration); + animation-fill-mode: both; + } + + &.in { + animation-name: PopUpIn; + .#{$ns}PopUp-overlay{ + animation-name: PopUpOpacityIn; + } + } + + &.out { + animation-name: PopUpOut; + .#{$ns}PopUp-overlay{ + animation-name: PopUpOpacityOut; + } + } + + &-inner{ + position: relative; + overflow: hidden; + height: 100%; + box-sizing: border-box; + background: $white; + padding-top: px2rem(36px); + } + + &-closeWrap{ + width: 100%; + position: absolute !important; + left: 0; + top: 0; + } + + &-close{ + width: px2rem(34px) !important; + height: px2rem(34px) !important; + padding: px2rem(12px); + cursor: pointer; + } + + &-content{ + overflow-y: auto; + height: 100%; + display: flex; + } + + & > * { + position: relative; + z-index: 2; + } + + &-overlay { + position: fixed !important; + top: 0; + left: 0; + right: 0; + z-index: 1; + bottom: 0; + background: rgba(0, 0, 0, 0.3);; + opacity: 1; + animation-duration: var(--animation-duration); + animation-fill-mode: both; + } + + &--leftBottomLeftTop { + margin-top: px2rem(4px); + } + &--leftTopLeftBottom { + margin-top: px2rem(-4px); + } +} diff --git a/scss/components/form/_color.scss b/scss/components/form/_color.scss index 6502fedc5..7e30965d3 100644 --- a/scss/components/form/_color.scss +++ b/scss/components/form/_color.scss @@ -10,6 +10,10 @@ color: var(--ColorPicker-color); border-radius: var(--borderRadius); + &-popup{ + height: 80vh; + } + &:not(.is-disabled) { cursor: pointer; diff --git a/scss/components/form/_date-range.scss b/scss/components/form/_date-range.scss index 50d208738..45e250c9b 100644 --- a/scss/components/form/_date-range.scss +++ b/scss/components/form/_date-range.scss @@ -127,6 +127,10 @@ } } +.#{$ns}DateRangePicker-popup { + height: 90vh; +} + @include media-breakpoint-up(sm) { .#{$ns}DateRangePicker-wrap { white-space: nowrap; diff --git a/scss/components/form/_date.scss b/scss/components/form/_date.scss index 0a0f7953c..7363020a6 100644 --- a/scss/components/form/_date.scss +++ b/scss/components/form/_date.scss @@ -122,6 +122,9 @@ } } +.#{$ns}DatePicker-popup { + height: 80vh; +} // override third-party styles .rdt { user-select: none; diff --git a/scss/components/form/_tree-select.scss b/scss/components/form/_tree-select.scss index 3b38b719e..b1bfc70e8 100644 --- a/scss/components/form/_tree-select.scss +++ b/scss/components/form/_tree-select.scss @@ -15,6 +15,10 @@ box-shadow: var(--Form-input-boxShadow); background: var(--Form-input-onFocused-bg); } + + &-popup { + height: 80vh; + } } .#{$ns}TreeSelect-popover { diff --git a/scss/themes/_common.scss b/scss/themes/_common.scss index dedc88333..7a5e1a8ba 100644 --- a/scss/themes/_common.scss +++ b/scss/themes/_common.scss @@ -18,6 +18,7 @@ @import '../components/tooltip'; @import '../components/tpl'; @import '../components/popover'; +@import '../components/popup'; @import '../components/picker-columns'; @import '../components/toast'; @import '../components/alert'; diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index b3794f87c..360bcff83 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -11,8 +11,9 @@ import {Icon} from './icons'; import Overlay from './Overlay'; import {uncontrollable} from 'uncontrollable'; import PopOver from './PopOver'; +import PopUp from './PopUp'; import {ClassNamesFn, themeable, ThemeProps} from '../theme'; -import {autobind, isObject} from '../utils/helper'; +import {autobind, isMobile, isObject} from '../utils/helper'; import {localeable, LocaleProps} from '../locale'; export type PresetColor = {color: string; title: string} | string; @@ -32,6 +33,7 @@ export interface ColorProps extends LocaleProps, ThemeProps { presetColors?: PresetColor[]; resetValue?: string; allowCustomColor?: boolean; + useMobileUI?: boolean; } export interface ColorControlState { @@ -218,7 +220,8 @@ export class ColorControl extends React.PureComponent< placement, classnames: cx, presetColors, - allowCustomColor + allowCustomColor, + useMobileUI } = this.props; const __ = this.props.translate; @@ -270,7 +273,7 @@ export class ColorControl extends React.PureComponent< - {isOpened ? ( + {!(useMobileUI && isMobile()) && isOpened ? ( findDOMNode(this)} @@ -317,6 +320,45 @@ export class ColorControl extends React.PureComponent< ) : null} + { + useMobileUI && isMobile() && ( + + {allowCustomColor ? ( + + ) : ( + typeof item === 'string' || isObject(item) + ) + .map(item => + typeof item === 'string' + ? item + : isObject(item) + ? item?.color + : item + ) as string[]) + : undefined + } + onChangeComplete={this.handleChange} + /> + )} + + ) + } ); } diff --git a/src/components/DatePicker.tsx b/src/components/DatePicker.tsx index bc4e3bcd4..7bc282a77 100644 --- a/src/components/DatePicker.tsx +++ b/src/components/DatePicker.tsx @@ -9,13 +9,14 @@ import moment from 'moment'; import 'moment/locale/zh-cn'; import {Icon} from './icons'; import PopOver from './PopOver'; +import PopUp from './PopUp'; import Overlay from './Overlay'; import {ClassNamesFn, themeable, ThemeProps} from '../theme'; import {PlainObject} from '../types'; import Calendar from './calendar/Calendar'; import 'react-datetime/css/react-datetime.css'; -import {localeable, LocaleProps, TranslateFn} from '../locale'; -import {ucFirst} from '../utils/helper'; +import { localeable, LocaleProps, TranslateFn } from '../locale'; +import {isMobile, ucFirst} from '../utils/helper'; const availableShortcuts: {[propName: string]: any} = { now: { @@ -559,6 +560,7 @@ export class DatePicker extends React.Component { borderMode, embed, minDate, + useMobileUI, schedules, largeMode, scheduleClassNames, @@ -662,7 +664,7 @@ export class DatePicker extends React.Component { - {isOpened ? ( + {!(useMobileUI && isMobile()) && isOpened ? ( { ) : null} + { + useMobileUI && isMobile() ? ( + + {this.renderShortCuts(shortcuts)} + + + + ) : null + } ); } diff --git a/src/components/DateRangePicker.tsx b/src/components/DateRangePicker.tsx index cb59f0daf..51fb91c18 100644 --- a/src/components/DateRangePicker.tsx +++ b/src/components/DateRangePicker.tsx @@ -13,9 +13,10 @@ import Overlay from './Overlay'; import {ShortCuts, ShortCutDateRange} from './DatePicker'; import Calendar from './calendar/Calendar'; import PopOver from './PopOver'; +import PopUp from './PopUp'; import {ClassNamesFn, themeable, ThemeProps} from '../theme'; import {PlainObject} from '../types'; -import {noop, ucFirst} from '../utils/helper'; +import {isMobile, noop, ucFirst} from '../utils/helper'; import {LocaleProps, localeable} from '../locale'; export interface DateRangePickerProps extends ThemeProps, LocaleProps { @@ -47,6 +48,7 @@ export interface DateRangePickerProps extends ThemeProps, LocaleProps { embed?: boolean; viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters'; borderMode?: 'full' | 'half' | 'none'; + useMobileUI?: boolean; } export interface DateRangePickerState { @@ -760,7 +762,8 @@ export class DateRangePicker extends React.Component< disabled, embed, overlayPlacement, - borderMode + borderMode, + useMobileUI } = this.props; const {isOpened, isFocused} = this.state; @@ -836,7 +839,7 @@ export class DateRangePicker extends React.Component< - {isOpened ? ( + {!(useMobileUI && isMobile()) && isOpened ? ( this.dom.current} onHide={this.close} @@ -856,6 +859,17 @@ export class DateRangePicker extends React.Component< ) : null} + { + useMobileUI && isMobile() && ( + + {this.renderCalendar()} + + ) + } ); } diff --git a/src/components/MonthRangePicker.tsx b/src/components/MonthRangePicker.tsx index 277832c94..42a523d3e 100644 --- a/src/components/MonthRangePicker.tsx +++ b/src/components/MonthRangePicker.tsx @@ -12,14 +12,15 @@ import {Icon} from './icons'; import Overlay from './Overlay'; import Calendar from './calendar/Calendar'; import PopOver from './PopOver'; +import PopUp from './PopUp'; import {themeable, ThemeProps} from '../theme'; import {PlainObject} from '../types'; -import {noop} from '../utils/helper'; +import {isMobile, noop} from '../utils/helper'; import {LocaleProps, localeable} from '../locale'; import {DateRangePicker} from './DateRangePicker'; import capitalize from 'lodash/capitalize'; import {ShortCuts, ShortCutDateRange} from './DatePicker'; -import {availableRanges} from './DateRangePicker'; +import { availableRanges } from './DateRangePicker'; export interface MonthRangePickerProps extends ThemeProps, LocaleProps { className?: string; @@ -47,6 +48,7 @@ export interface MonthRangePickerProps extends ThemeProps, LocaleProps { resetValue?: any; popOverContainer?: any; embed?: boolean; + useMobileUI?: boolean; } export interface MonthRangePickerState { @@ -528,7 +530,8 @@ export class MonthRangePicker extends React.Component< clearable, disabled, embed, - overlayPlacement + overlayPlacement, + useMobileUI } = this.props; const {isOpened, isFocused} = this.state; @@ -603,7 +606,7 @@ export class MonthRangePicker extends React.Component< - {isOpened ? ( + {!(useMobileUI && isMobile()) && isOpened ? ( this.dom.current} onHide={this.close} @@ -623,6 +626,17 @@ export class MonthRangePicker extends React.Component< ) : null} + { + useMobileUI && isMobile() && ( + + {this.renderCalendar()} + + ) + } ); } diff --git a/src/components/PopUp.tsx b/src/components/PopUp.tsx new file mode 100644 index 000000000..78ca819f7 --- /dev/null +++ b/src/components/PopUp.tsx @@ -0,0 +1,133 @@ +/** + * @file PopUp + * @description + * @author fex + */ + +import React from 'react'; +import {ClassNamesFn, themeable} from '../theme'; +import Transition, { + ENTERED, + EXITING, + EXITED, + ENTERING +} from 'react-transition-group/Transition'; +import Portal from 'react-overlays/Portal'; +import { Icon } from './icons'; + + +export interface PopUpPorps { + className?: string; + style?: { + [styleName: string]: string; + }; + overlay?: boolean; + onHide?: () => void; + classPrefix: string; + classnames: ClassNamesFn; + [propName: string]: any; + isShow?: boolean; + container?: any; + hideClose?: boolean; + placement?: 'left' | 'center' | 'right'; +} + +const fadeStyles: { + [propName: string]: string; +} = { + [ENTERED]: '', + [EXITING]: 'out', + [EXITED]: '', + [ENTERING]: 'in' +}; +export class PopUp extends React.PureComponent { + static defaultProps = { + className: '', + overlay: true, + isShow: false, + container: document.body, + hideClose: false, + }; + + componentDidMount() { + + } + handleClick(e: React.MouseEvent) { + e.stopPropagation(); + } + + render() { + const { + style, + children, + overlay, + onHide, + classPrefix: ns, + classnames: cx, + className, + isShow, + container, + hideClose, + placement='center', + ...rest + } = this.props; + + const outerStyle: any = { + ...style, + }; + delete outerStyle.top; + return ( + + + {(status: string) => { + return ( +
+ {overlay && ( +
+ )} +
+ { + !hideClose && ( +
+ +
+ ) + } +
+ {children} +
+
+
+ ) + }} + + + ) + } +} + +export default themeable(PopUp); diff --git a/src/renderers/Form/InputColor.tsx b/src/renderers/Form/InputColor.tsx index 1b2070a90..94e6ab781 100644 --- a/src/renderers/Form/InputColor.tsx +++ b/src/renderers/Form/InputColor.tsx @@ -68,12 +68,11 @@ export default class ColorControl extends React.PureComponent< }; render() { - const {className, classPrefix: ns, value, ...rest} = this.props; - + const {className, classPrefix: ns, value, env, ...rest} = this.props; return (
...
}> - +
); diff --git a/src/renderers/Form/InputDate.tsx b/src/renderers/Form/InputDate.tsx index 6cd4e50c9..1464f41fc 100644 --- a/src/renderers/Form/InputDate.tsx +++ b/src/renderers/Form/InputDate.tsx @@ -424,6 +424,7 @@ export default class DateControl extends React.PureComponent< format, timeFormat, valueFormat, + env, largeMode, render, ...rest @@ -441,6 +442,7 @@ export default class DateControl extends React.PureComponent< format={valueFormat || format} {...this.state} classnames={cx} + useMobileUI={env.useMobileUI} schedules={this.state.schedules} largeMode={largeMode} onScheduleClick={this.onScheduleClick.bind(this)} diff --git a/src/renderers/Form/InputDateRange.tsx b/src/renderers/Form/InputDateRange.tsx index 91fc1e4c3..146ae4ba2 100644 --- a/src/renderers/Form/InputDateRange.tsx +++ b/src/renderers/Form/InputDateRange.tsx @@ -172,6 +172,7 @@ export default class DateRangeControl extends React.Component { maxDuration, data, format, + env, ...rest } = this.props; @@ -186,6 +187,7 @@ export default class DateRangeControl extends React.Component { maxDate={maxDate ? filterDate(maxDate, data, format) : undefined} minDuration={minDuration ? parseDuration(minDuration) : undefined} maxDuration={maxDuration ? parseDuration(maxDuration) : undefined} + useMobileUI={env.useMobileUI} /> ); diff --git a/src/renderers/Form/InputMonthRange.tsx b/src/renderers/Form/InputMonthRange.tsx index 52156a993..bc2d89c3a 100644 --- a/src/renderers/Form/InputMonthRange.tsx +++ b/src/renderers/Form/InputMonthRange.tsx @@ -170,6 +170,7 @@ export default class MonthRangeControl extends React.Component maxDuration, data, format, + env, ...rest } = this.props; @@ -184,6 +185,7 @@ export default class MonthRangeControl extends React.Component maxDate={maxDate ? filterDate(maxDate, data, format) : undefined} minDuration={minDuration ? parseDuration(minDuration) : undefined} maxDuration={maxDuration ? parseDuration(maxDuration) : undefined} + useMobileUI={env.useMobileUI} /> ); diff --git a/src/renderers/Form/InputQuarterRange.tsx b/src/renderers/Form/InputQuarterRange.tsx index dd82bc4ea..f9c7bd022 100644 --- a/src/renderers/Form/InputQuarterRange.tsx +++ b/src/renderers/Form/InputQuarterRange.tsx @@ -26,6 +26,7 @@ export default class QuarterRangeControl extends InputDateRange { maxDuration, data, format, + env, ...rest } = this.props; @@ -44,6 +45,7 @@ export default class QuarterRangeControl extends InputDateRange { maxDate={maxDate ? filterDate(maxDate, data, format) : undefined} minDuration={minDuration ? parseDuration(minDuration) : undefined} maxDuration={maxDuration ? parseDuration(maxDuration) : undefined} + useMobileUI={env.useMobileUI} /> ); diff --git a/src/renderers/Form/InputYearRange.tsx b/src/renderers/Form/InputYearRange.tsx index 6d4729440..ed633a231 100644 --- a/src/renderers/Form/InputYearRange.tsx +++ b/src/renderers/Form/InputYearRange.tsx @@ -26,6 +26,7 @@ export default class YearRangeControl extends InputDateRange { maxDuration, data, format, + env, ...rest } = this.props; @@ -44,6 +45,7 @@ export default class YearRangeControl extends InputDateRange { maxDate={maxDate ? filterDate(maxDate, data, format) : undefined} minDuration={minDuration ? parseDuration(minDuration) : undefined} maxDuration={maxDuration ? parseDuration(maxDuration) : undefined} + useMobileUI={env.useMobileUI} /> ); diff --git a/src/renderers/Form/TreeSelect.tsx b/src/renderers/Form/TreeSelect.tsx index 2fb28948c..226217e0a 100644 --- a/src/renderers/Form/TreeSelect.tsx +++ b/src/renderers/Form/TreeSelect.tsx @@ -1,6 +1,7 @@ import React from 'react'; import Overlay from '../../components/Overlay'; import PopOver from '../../components/PopOver'; +import PopUp from '../../components/PopUp'; import { OptionsControl, @@ -17,7 +18,7 @@ import {Api} from '../../types'; import {isEffectiveApi} from '../../utils/api'; import Spinner from '../../components/Spinner'; import ResultBox from '../../components/ResultBox'; -import {autobind, getTreeAncestors} from '../../utils/helper'; +import {autobind, getTreeAncestors, isMobile} from '../../utils/helper'; import {findDOMNode} from 'react-dom'; import {normalizeOptions} from '../../components/Select'; @@ -90,6 +91,7 @@ export interface TreeSelectProps extends OptionsControlProps { hideNodePathLabel?: boolean; enableNodePath?: boolean; pathSeparator?: string; + useMobileUI?: boolean; } export interface TreeSelectState { @@ -502,67 +504,51 @@ export default class TreeSelectControl extends React.Component< : options; return ( - this.container.current)} - target={() => this.target} - show - > - - - - + ); } @@ -581,9 +567,13 @@ export default class TreeSelectControl extends React.Component< autoComplete, selectedOptions, placeholder, + popOverContainer, + env, translate: __ } = this.props; + const { isOpened } = this.state; + const { useMobileUI } = env; return (
{loading ? : undefined} - {this.state.isOpened ? this.renderOuter() : null} + { !(useMobileUI && isMobile()) && isOpened ? ( + this.container.current)} + target={() => this.target} + show + > + + {this.renderOuter()} + + + ) : null} + { + useMobileUI && isMobile() && ( + + {this.renderOuter()} + + ) + }
); }