From 8e327e69815ab7a5ffc4cfaa48584e3da6e933fd Mon Sep 17 00:00:00 2001 From: liaoxuezhi Date: Wed, 18 Dec 2019 18:57:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=8E=86=E6=8E=A7?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scss/_variables.scss | 305 ++++----- scss/components/form/_date-range.scss | 20 +- scss/components/form/_date.scss | 133 +++- scss/themes/cxd.scss | 2 + src/components/DatePicker.tsx | 628 +----------------- src/components/DateRangePicker.tsx | 92 ++- src/components/calendar/Calendar.tsx | 81 +++ src/components/calendar/CalendarContainer.tsx | 15 + src/components/calendar/DaysView.tsx | 248 +++++++ src/components/calendar/MonthsView.tsx | 61 ++ src/components/calendar/YearsView.tsx | 59 ++ src/renderers/Form/Date.tsx | 2 +- 12 files changed, 788 insertions(+), 858 deletions(-) create mode 100644 src/components/calendar/Calendar.tsx create mode 100644 src/components/calendar/CalendarContainer.tsx create mode 100644 src/components/calendar/DaysView.tsx create mode 100644 src/components/calendar/MonthsView.tsx create mode 100644 src/components/calendar/YearsView.tsx diff --git a/scss/_variables.scss b/scss/_variables.scss index 082adfa08..eaccdf11c 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -35,17 +35,31 @@ $dark: $gray800 !default; $remFactor: 16px !default; // 字体相关 -$fontFamilySansSerif: -apple-system, BlinkMacSystemFont, 'SF Pro SC', - 'SF Pro Text', 'Helvetica Neue', Helvetica, 'PingFang SC', 'Segoe UI', Roboto, - 'Hiragino Sans GB', 'Arial', 'microsoft yahei ui', 'Microsoft YaHei', SimSun, - sans-serif !default; -$fontFamilyMonospace: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', - 'Courier New', monospace !default; +$fontFamilySansSerif: -apple-system, +BlinkMacSystemFont, +'SF Pro SC', +'SF Pro Text', +'Helvetica Neue', +Helvetica, +'PingFang SC', +'Segoe UI', +Roboto, +'Hiragino Sans GB', +'Arial', +'microsoft yahei ui', +'Microsoft YaHei', +SimSun, +sans-serif !default; +$fontFamilyMonospace: SFMono-Regular, +Menlo, +Monaco, +Consolas, +'Liberation Mono', +'Courier New', +monospace !default; $fontFamilyBase: $fontFamilySansSerif !default; -$fontSizeBase: px2rem( - 14px -) !default; // Assumes the browser default, typically `16px` +$fontSizeBase: px2rem(14px) !default; // Assumes the browser default, typically `16px` $fontSizeMd: px2rem(16px) !default; $fontSizeLg: px2rem(20px) !default; $fontSizeXl: px2rem(24px) !default; @@ -85,13 +99,11 @@ $boxShadow: 0 0.5rem 1rem rgba($black, 0.15) !default; $boxShadowLg: 0 1rem 3rem rgba($black, 0.175) !default; // 窗口适配 -$breakpoints: ( - xs: 0, +$breakpoints: (xs: 0, sm: 576px, md: 768px, lg: 992px, - xl: 1200px -) !default; + xl: 1200px) !default; // 段落间距 $paragraph-marginBottom: 1rem !default; @@ -147,14 +159,10 @@ $Layout-aside--md-width: px2rem(250px) !default; $Layout-aside--lg-width: px2rem(300px) !default; $Layout-aside--folded-width: px2rem(60px) !default; $Layout-aside-bg: $dark !default; -$Layout-aside-onAcitve-bg: saturate( - darken($Layout-aside-bg, 5%), - 2.5% -) !default; -$Layout-aside-subList-bg: saturate( - darken($Layout-aside-bg, 10%), - 2.5% -) !default; +$Layout-aside-onAcitve-bg: saturate(darken($Layout-aside-bg, 5%), + 2.5%) !default; +$Layout-aside-subList-bg: saturate(darken($Layout-aside-bg, 10%), + 2.5%) !default; $Layout-aside-onHover-bg: saturate(darken($Layout-aside-bg, 3%), 2.5%) !default; $Layout-aside-color: desaturate(lighten($Layout-aside-bg, 40%), 10%) !default; @@ -175,16 +183,14 @@ $Layout-asideLink-arrowColor: $Layout-asideLink-color !default; $Layout-asideLink-onActive-arrowColor: $Layout-asideLink-onActive-color !default; $Layout-asideLabel-color: darken($Layout-aside-color, 10%) !default; $Layout-brand-bg: $dark !default; -$Layout-brandBar-color: desaturate( - lighten($Layout-brand-bg, 40%), - 10% -) !default; +$Layout-brandBar-color: desaturate(lighten($Layout-brand-bg, 40%), + 10%) !default; $Layout-brand-color: lighten($Layout-brandBar-color, 25%) !default; $Layout-header-height: px2rem(50px) !default; $Layout-headerBar-borderBottom: none !default; $Layout-header-bg: $white !default; $Layout-header-boxShadow: 0 px2rem(2px) px2rem(2px) rgba(0, 0, 0, 0.05), - 0 1px 0 rgba(0, 0, 0, 0.05) !default; +0 1px 0 rgba(0, 0, 0, 0.05) !default; $Layout-nav-height: px2rem(40px) !default; $Layout-nav-lgHeight: px2rem(50px) !default; $Layout-nav--folded-height: px2rem(50px) !default; @@ -217,9 +223,7 @@ $Modal-header-bg: darken($Modal-bg, 2.5%) !default; $Modal-title-lineHeight: $lineHeightBase !default; $Modal-title-fontSize: $fontSizeBase !default; $Modal-title-color: $text--loud-color !default; -$Modal-header-paddingY: ( - $Modal-header-height - $Modal-title-lineHeight * $Modal-title-fontSize - ) / 2 !default; +$Modal-header-paddingY: ($Modal-header-height - $Modal-title-lineHeight * $Modal-title-fontSize) / 2 !default; $Modal-header-paddingX: $gap-md !default; $Modal-close-width: px2rem(12px) !default; $Modal-close-color: $text--muted-color !default; @@ -227,10 +231,8 @@ $Model-close-onHover-color: $text-color !default; $Modal-body-paddingX: $gap-md !default; $Modal-body-paddingY: $gap-md !default; $Modal-body--noHeader-paddingTop: $gap-base; -$Modal-body-borderTop: $Modal-content-borderWidth solid - lighten($Modal-content-borderColor, 5%) !default; -$Modal-body-borderBottom: $Modal-content-borderWidth solid - lighten($Modal-content-borderColor, 5%) !default; +$Modal-body-borderTop: $Modal-content-borderWidth solid lighten($Modal-content-borderColor, 5%) !default; +$Modal-body-borderBottom: $Modal-content-borderWidth solid lighten($Modal-content-borderColor, 5%) !default; $Modal-footer-padding: $gap-sm !default; $Modal-footer-marginY: 0 !default; $Modal-footer-marginX: 0 !default; @@ -353,10 +355,8 @@ $Alert-marginBottom: $gap-md !default; $Alert--danger-color: #a94442 !default; $Alert--danger-bg: #f2dede !default; -$Alert--danger-borderColor: darken( - adjust-hue($Alert--danger-bg, -10), - 5% -) !default; +$Alert--danger-borderColor: darken(adjust-hue($Alert--danger-bg, -10), + 5%) !default; $Alert--info-color: #31708f !default; $Alert--info-bg: #d9edf7 !default; @@ -364,17 +364,13 @@ $Alert--info-borderColor: darken(adjust-hue($Alert--info-bg, -10), 5%) !default; $Alert--success-color: #3c763d !default; $Alert--success-bg: #dff0d8 !default; -$Alert--success-borderColor: darken( - adjust-hue($Alert--success-bg, -10), - 5% -) !default; +$Alert--success-borderColor: darken(adjust-hue($Alert--success-bg, -10), + 5%) !default; $Alert--warning-color: #8a6d3b !default; $Alert--warning-bg: #fcf8e3 !default; -$Alert--warning-borderColor: darken( - adjust-hue($Alert--warning-bg, -10), - 5% -) !default; +$Alert--warning-borderColor: darken(adjust-hue($Alert--warning-bg, -10), + 5%) !default; // spinner $Spinner-overlay-bg: rgba(255, 255, 255, 0.4) !default; @@ -462,8 +458,7 @@ $Table-tree-borderColor: $Table-borderColor !default; $TableCell-height: px2rem(40px) !default; $TableCell-paddingX: $gap-sm !default; $TableCell--edge-paddingX: $gap-md !default; -$TableCell-paddingY: ($TableCell-height - $Table-fontSize * $Table-lineHeight) / - 2; +$TableCell-paddingY: ($TableCell-height - $Table-fontSize * $Table-lineHeight) / 2; $Table-placeholder-height: px2rem(100px) !default; // $Table-checkCell-width: px2rem(50px) !default; @@ -637,15 +632,10 @@ $Form-input-placeholderColor: $text--muted-color !default; $Form-input-lineHeight: 20/14 !default; $Form-input-fontSize: $Form-fontSize !default; $Form-input-boxShadow: none !default; -$Form-input-paddingY: ( - $Form-input-height - $Form-input-lineHeight * $Form-input-fontSize - - px2rem(2px) - )/2 !default; +$Form-input-paddingY: ($Form-input-height - $Form-input-lineHeight * $Form-input-fontSize - px2rem(2px))/2 !default; $Form-input-paddingX: px2rem(12px) !default; $Form-input-marginBottom: px2rem(6px) !default; -$Form-label-paddingTop: ( - $Form-input-height - $Form-input-lineHeight * $Form-input-fontSize - )/2 !default; +$Form-label-paddingTop: ($Form-input-height - $Form-input-lineHeight * $Form-input-fontSize)/2 !default; $Form-input-addOnBg: #edf1f2 !default; $Form-input-addOnColor: $text-color !default; @@ -687,14 +677,10 @@ $Form-select-onFocused-borderColor: $Form-input-onFocused-borderColor !default; $Form-select-onError-borderColor: $Form-input-onError-borderColor !default; $Form-selectOption-height: $Form-input-height !default; $Form-selectValue-color: $info !default; -$Form-selectValue-bg: saturate( - lighten($Form-selectValue-color, 40%), - 2.5% -) !default; -$Form-selectValue-borderColor: saturate( - lighten($Form-selectValue-color, 30%), - 2.5% -) !default; +$Form-selectValue-bg: saturate(lighten($Form-selectValue-color, 40%), + 2.5%) !default; +$Form-selectValue-borderColor: saturate(lighten($Form-selectValue-color, 30%), + 2.5%) !default; $Form-selectValue-fontSize: $fontSizeSm !default; $Form-select-caret-vender: 'FontAwesome' !default; $Form-select-caret-icon: '\f0d7' !default; @@ -723,10 +709,7 @@ $InputGroup-addOn-bg: $Form-input-addOnBg !default; $InputGroup-addOn-borderWidth: $Form-input-borderWidth !default; $InputGroup-addOn-borderColor: $Form-input-borderColor !default; $InputGroup-addOn-borderRadius: $Form-input-borderRadius !default; -$InputGroup-paddingY: ( - $InputGroup-height - $Form-input-lineHeight * $Form-input-fontSize - - px2rem(2px) - )/2 !default; +$InputGroup-paddingY: ($InputGroup-height - $Form-input-lineHeight * $Form-input-fontSize - px2rem(2px))/2 !default; $InputGroup-paddingX: px2rem(10px) !default; $InputGroup-addOn-onFocused-borderColor: $Form-input-onFocused-borderColor !default; $InputGroup-select-borderWidth: $Form-select-borderWidth !default; @@ -752,10 +735,7 @@ $Button-height: $Form-input-height !default; $Button-mimWidth: auto !default; $Button-lineHeight: $Form-input-lineHeight !default; $Button-paddingX: px2rem(12px) !default; -$Button-paddingY: ( - $Button-height - $Button-borderWidth * 2 - $Button-lineHeight * - $Button-fontSize - )/2 !default; +$Button-paddingY: ($Button-height - $Button-borderWidth * 2 - $Button-lineHeight * $Button-fontSize)/2 !default; $Button--iconOnly-minWidthRate: 4 / 3 !default; @@ -763,40 +743,28 @@ $Button--xs-fontSize: $fontSizeXs !default; $Button--xs-height: px2rem(22px) !default; $Button--xs-lineHeight: 18 / 11 !default; $Button--xs-paddingX: px2rem(5px) !default; -$Button--xs-paddingY: ( - $Button--xs-height - $Button-borderWidth * 2 - $Button--xs-lineHeight * - $Button--xs-fontSize - )/2 !default; +$Button--xs-paddingY: ($Button--xs-height - $Button-borderWidth * 2 - $Button--xs-lineHeight * $Button--xs-fontSize)/2 !default; $Button--sm-fontSize: $fontSizeSm !default; $Button--sm-height: px2rem(30px) !default; $Button--sm-lineHeight: 18 / 12 !default; $Button--sm-paddingX: px2rem(8px) !default; -$Button--sm-paddingY: ( - $Button--sm-height - $Button-borderWidth * 2 - $Button--sm-lineHeight * - $Button--sm-fontSize - )/2 !default; +$Button--sm-paddingY: ($Button--sm-height - $Button-borderWidth * 2 - $Button--sm-lineHeight * $Button--sm-fontSize)/2 !default; $Button--md-fontSize: $Button-fontSize !default; $Button--md-height: $Button-height !default; $Button--md-lineHeight: $Button-lineHeight !default; $Button--md-paddingX: $Button-paddingX !default; -$Button--md-paddingY: ( - $Button--md-height - $Button-borderWidth * 2 - $Button--md-lineHeight * - $Button--md-fontSize - )/2 !default; +$Button--md-paddingY: ($Button--md-height - $Button-borderWidth * 2 - $Button--md-lineHeight * $Button--md-fontSize)/2 !default; $Button--lg-fontSize: $fontSizeLg !default; $Button--lg-height: px2rem(46px) !default; $Button--lg-lineHeight: 24 / 20 !default; $Button--lg-paddingX: px2rem(16px) !default; -$Button--lg-paddingY: ( - $Button--lg-height - $Button-borderWidth * 2 - $Button--lg-lineHeight * - $Button--lg-fontSize - )/2 !default; +$Button--lg-paddingY: ($Button--lg-height - $Button-borderWidth * 2 - $Button--lg-lineHeight * $Button--lg-fontSize)/2 !default; $Button-boxShadow: inset 0 1px 0 rgba($white, 0.15), - 0 1px 1px rgba($black, 0.075) !default; +0 1px 1px rgba($black, 0.075) !default; $Button-onFocus-boxShadow: none !default; $Button-onActive-boxShadow: inset 0 3px 5px rgba($black, 0.125) !default; $Button-onDisabled-opacity: 0.65 !default; @@ -808,8 +776,10 @@ $Button-borderRadius: $borderRadius !default; $Button--lg-borderRadius: $borderRadius !default; $Button--sm-borderRadius: $borderRadius !default; -$Button-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, - border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !default; +$Button-transition: color 0.15s ease-in-out, +background-color 0.15s ease-in-out, +border-color 0.15s ease-in-out, +box-shadow 0.15s ease-in-out !default; $Button--primary-bg: $primary !default; $Button--primary-border: $Button--primary-bg !default; @@ -818,26 +788,20 @@ $Button--primary-onHover-bg: darken($Button--primary-bg, 7.5%) !default; $Button--primary-onHover-border: darken($Button--primary-border, 10%) !default; $Button--primary-onHover-color: $Button--primary-color !default; $Button--primary-onActive-bg: darken($Button--primary-bg, 10%) !default; -$Button--primary-onActive-border: darken( - $Button--primary-border, - 12.5% -) !default; +$Button--primary-onActive-border: darken($Button--primary-border, + 12.5%) !default; $Button--primary-onActive-color: $Button--primary-color !default; $Button--secondary-bg: $secondary !default; $Button--secondary-border: $Button--secondary-bg !default; $Button--secondary-color: $white !default; $Button--secondary-onHover-bg: darken($Button--secondary-bg, 7.5%) !default; -$Button--secondary-onHover-border: darken( - $Button--secondary-border, - 10% -) !default; +$Button--secondary-onHover-border: darken($Button--secondary-border, + 10%) !default; $Button--secondary-onHover-color: $Button--secondary-color !default; $Button--secondary-onActive-bg: darken($Button--secondary-bg, 10%) !default; -$Button--secondary-onActive-border: darken( - $Button--secondary-border, - 12.5% -) !default; +$Button--secondary-onActive-border: darken($Button--secondary-border, + 12.5%) !default; $Button--secondary-onActive-color: $Button--secondary-color !default; $Button--success-bg: $success !default; @@ -847,10 +811,8 @@ $Button--success-onHover-bg: darken($Button--success-bg, 7.5%) !default; $Button--success-onHover-border: darken($Button--success-border, 10%) !default; $Button--success-onHover-color: $Button--success-color !default; $Button--success-onActive-bg: darken($Button--success-bg, 10%) !default; -$Button--success-onActive-border: darken( - $Button--success-border, - 12.5% -) !default; +$Button--success-onActive-border: darken($Button--success-border, + 12.5%) !default; $Button--success-onActive-color: $Button--success-color !default; $Button--info-bg: $info !default; @@ -870,10 +832,8 @@ $Button--warning-onHover-bg: darken($Button--warning-bg, 7.5%) !default; $Button--warning-onHover-border: darken($Button--warning-border, 10%) !default; $Button--warning-onHover-color: $Button--warning-color !default; $Button--warning-onActive-bg: darken($Button--warning-bg, 10%) !default; -$Button--warning-onActive-border: darken( - $Button--warning-border, - 12.5% -) !default; +$Button--warning-onActive-border: darken($Button--warning-border, + 12.5%) !default; $Button--warning-onActive-color: $Button--warning-color !default; $Button--danger-bg: $danger !default; @@ -913,10 +873,8 @@ $Button--default-onHover-bg: darken($Button--default-bg, 7.5%) !default; $Button--default-onHover-border: darken($Button--default-border, 10%) !default; $Button--default-onHover-color: $Button--default-color !default; $Button--default-onActive-bg: darken($Button--default-bg, 10%) !default; -$Button--default-onActive-border: darken( - $Button--default-border, - 12.5% -) !default; +$Button--default-onActive-border: darken($Button--default-border, + 12.5%) !default; $Button--default-onActive-color: $Button--default-color !default; $Button--link-color: $text-color !default; @@ -932,9 +890,7 @@ $DropDown-menu-height: px2rem(34px) !default; $DropDown-menu-minWidth: px2rem(160px) !default; $DropDown-menu-paddingY: $gap-xs !default; $DropDown-menu-paddingX: 0 !default; -$DropDown-menuItem-paddingY: ( - $DropDown-menu-height - $fontSizeBase * $lineHeightBase - ) / 2 !default; +$DropDown-menuItem-paddingY: ($DropDown-menu-height - $fontSizeBase * $lineHeightBase) / 2 !default; $DropDown-menuItem-paddingX: $gap-sm !default; $DropDown-menuItem-onHover-color: inherit !default; $DropDown-menuItem-onHover-bg: $Button--default-onHover-bg !default; @@ -989,9 +945,7 @@ $ColorPicker-height: $Form-input-height !default; $ColorPicker-lineHeight: $Form-input-lineHeight !default; $ColorPicker-fontSize: $Form-input-fontSize !default; $ColorPicker-paddingX: px2rem(12px) !default; -$ColorPicker-paddingY: ( - $ColorPicker-height - $ColorPicker-lineHeight * $ColorPicker-fontSize - )/2 - $ColorPicker-borderWidth !default; +$ColorPicker-paddingY: ($ColorPicker-height - $ColorPicker-lineHeight * $ColorPicker-fontSize)/2 - $ColorPicker-borderWidth !default; $ColorPicker-placeholderColor: $Form-input-placeholderColor !default; $ColorPicker-onFocused-borderColor: $Form-input-onFocused-borderColor !default; $DatePicker-onHover-borderColor: $Form-input-borderColor !default; @@ -1007,9 +961,7 @@ $DatePicker-height: $Form-input-height !default; $DatePicker-lineHeight: $Form-input-lineHeight !default; $DatePicker-fontSize: $Form-input-fontSize !default; $DatePicker-paddingX: px2rem(12px) !default; -$DatePicker-paddingY: ( - $DatePicker-height - $DatePicker-lineHeight * $DatePicker-fontSize - )/2 - $DatePicker-borderWidth !default; +$DatePicker-paddingY: ($DatePicker-height - $DatePicker-lineHeight * $DatePicker-fontSize)/2 - $DatePicker-borderWidth !default; $DatePicker-placeholderColor: $Form-input-placeholderColor !default; $DatePicker-iconColor: $icon-color !default; $DatePicker-onHover-iconColor: $icon-onHover-color !default; @@ -1035,18 +987,13 @@ $Calendar-input-borderRadius: $borderRadius !default; $Calendar-input-height: px2rem(30px) !default; $Calendar-input-lineHeight: $lineHeightBase; $Calendar-input-paddingX: px2rem(10px) !default; -$Calendar-input-paddingY: ( - $Calendar-input-height - $Calendar-input-lineHeight * - $Calendar-input-fontSize - ) / 2; +$Calendar-input-paddingY: ($Calendar-input-height - $Calendar-input-lineHeight * $Calendar-input-fontSize) / 2; $Calendar-btn-fontSize: $fontSizeSm !default; $Calendar-btn-lineHeight: $lineHeightBase !default; $Calendar-btn-height: px2rem(30px) !default; $Calendar-btn-paddingX: px2rem(10px) !default; -$Calendar-btn-paddingY: ( - $Calendar-btn-height - $Calendar-btn-lineHeight * $Calendar-btn-fontSize - )/2 !default; +$Calendar-btn-paddingY: ($Calendar-btn-height - $Calendar-btn-lineHeight * $Calendar-btn-fontSize)/2 !default; $Calendar-btn-bg: $info !default; $Calendar-btn-border: $Calendar-btn-bg !default; @@ -1064,16 +1011,12 @@ $Calendar-btnCancel-border: $Calendar-btnCancel-bg !default; $Calendar-btnCancel-borderRadius: $Button-borderRadius !default; $Calendar-btnCancel-color: $text-color !default; $Calendar-btnCancel-onHover-bg: darken($Calendar-btnCancel-bg, 7.5%) !default; -$Calendar-btnCancel-onHover-border: darken( - $Calendar-btnCancel-border, - 10% -) !default; +$Calendar-btnCancel-onHover-border: darken($Calendar-btnCancel-border, + 10%) !default; $Calendar-btnCancel-onHover-color: $Calendar-btnCancel-color !default; $Calendar-btnCancel-onActive-bg: darken($Calendar-btnCancel-bg, 10%) !default; -$Calendar-btnCancel-onActive-border: darken( - $Calendar-btnCancel-border, - 12.5% -) !default; +$Calendar-btnCancel-onActive-border: darken($Calendar-btnCancel-border, + 12.5%) !default; $Calendar-btnCancel-onActive-color: $Calendar-btnCancel-color !default; $Calendar-color: $text-color !default; @@ -1081,8 +1024,18 @@ $Calendar-wLabel-color: #999 !default; $Calendar-cell-bg: $white !default; $Calendar-cell-onHover-bg: darken($Calendar-cell-bg, 7%) !default; $Calendar-cell-onActive-bg: $info !default; +$Calendar-cell-onBetween-bg: rgba($info, 0.1) !default; $Calendar-cell-onDisabled-bg: $light !default; +$Calendar-shortcuts-bg: transparent; +$Calendar-shortcuts-height: px2rem(30px); + +$Calendar-shortcut-color: $info !default; +$Calendar-shortcut-decoration: none !default; +$Calendar-shortcut-onHover-color: darken($Calendar-shortcut-color, + 15%) !default; +$Calendar-shortcut-onHover-decoration: none !default; + // List Control $ListControl-fontSize: $Form-fontSize !default; $ListControl-gutterWidth: px2rem(10px) !default; @@ -1096,18 +1049,14 @@ $ListControl-item-paddingX: px2rem(12px) !default; $ListControl-item-paddingY: px2rem(6px) !default; $ListControl-item-color: $text-color !default; -$ListControl-item-onHover-borderColor: darken( - $ListControl-item-borderColor, - 10% -) !default; +$ListControl-item-onHover-borderColor: darken($ListControl-item-borderColor, + 10%) !default; $ListControl-item-onHover-bg: darken($ListControl-item-bg, 7.5%) !default; $ListControl-item-onHover-color: $ListControl-item-color !default; $ListControl-item-onActive-bg: $primary !default; -$ListControl-item-onActive-borderColor: darken( - $ListControl-item-onActive-bg, - 10% -) !default; +$ListControl-item-onActive-borderColor: darken($ListControl-item-onActive-bg, + 10%) !default; $ListControl-item-onActive-color: $white !default; $ListControl-item-onActive-before-bg: $white !default; $ListControl-item-onActive-after-borderColor: $primary !default; @@ -1140,10 +1089,7 @@ $Combo-addBtn-borderRadius: $Button-borderRadius; $Combo-addBtn-height: px2rem(26px) !default; $Combo-addBtn-lineHeight: $Button--sm-lineHeight !default; $Combo-addBtn-paddingX: $Button--sm-paddingX !default; -$Combo-addBtn-paddingY: ( - $Combo-addBtn-height - $Button-borderWidth * 2 - $Combo-addBtn-lineHeight * - $Combo-addBtn-fontSize - )/2 !default; +$Combo-addBtn-paddingY: ($Combo-addBtn-height - $Button-borderWidth * 2 - $Combo-addBtn-lineHeight * $Combo-addBtn-fontSize)/2 !default; $Combo--vertical-item-gap: px2rem(5px); $Combo--vertical-item-borderColor: $borderColor !default; @@ -1158,10 +1104,8 @@ $Combo--vertical-itemToolbar-bg: $info !default; $Combo--vertical-itemToolbar-color: darken($white, 5%) !default; $Combo--vertical-itemToolbar-onHover-color: $white !default; $Combo--vertical-itemToolbar-borderWidth: $borderWidth !default; -$Combo--vertical-itemToolbar-borderColor: darken( - $Combo--vertical-itemToolbar-bg, - 5% -) !default; +$Combo--vertical-itemToolbar-borderColor: darken($Combo--vertical-itemToolbar-bg, + 5%) !default; $Combo--vertical-itemToolbar-borderRadius: px2rem(3px) !default; $Combo--vertical-itemToolbar-transion: all 0.25s ease-in-out !default; $Combo--vertical-itemToolbar-positionTop: -$Combo--vertical-itemToolbar-height !default; @@ -1177,10 +1121,8 @@ $SubForm--addBtn-onHover-bg: darken($SubForm--addBtn-bg, 7.5%) !default; $SubForm--addBtn-onHover-border: darken($SubForm--addBtn-border, 10%) !default; $SubForm--addBtn-onHover-color: $SubForm--addBtn-color !default; $SubForm--addBtn-onActive-bg: darken($SubForm--addBtn-bg, 10%) !default; -$SubForm--addBtn-onActive-border: darken( - $SubForm--addBtn-border, - 12.5% -) !default; +$SubForm--addBtn-onActive-border: darken($SubForm--addBtn-border, + 12.5%) !default; $SubForm--addBtn-onActive-color: $SubForm--addBtn-color !default; $SubForm--addBtn-fontSize: $Button--sm-fontSize !default; @@ -1188,10 +1130,7 @@ $SubForm--addBtn-borderRadius: $Button-borderRadius; $SubForm--addBtn-height: $Button--sm-height !default; $SubForm--addBtn-lineHeight: $Button--sm-lineHeight !default; $SubForm--addBtn-paddingX: $Button--sm-paddingX !default; -$SubForm--addBtn-paddingY: ( - $SubForm--addBtn-height - $Button-borderWidth * 2 - - $SubForm--addBtn-lineHeight * $SubForm--addBtn-fontSize - )/2 !default; +$SubForm--addBtn-paddingY: ($SubForm--addBtn-height - $Button-borderWidth * 2 - $SubForm--addBtn-lineHeight * $SubForm--addBtn-fontSize)/2 !default; // InputRange $InputRange-fontFamily: $fontFamilyBase !default; @@ -1204,17 +1143,15 @@ $InputRange-onDisabled-color: #cccccc !default; $InputRange-slider-bg: $InputRange-primaryColor !default; $InputRange-slider-border: px2rem(1px) solid $InputRange-primaryColor !default; $InputRange-slider-onFocus-borderRadius: $borderRadiusMd !default; -$InputRange-slider-onFocus-boxShadow: 0 0 0 - $InputRange-slider-onFocus-borderRadius - transparentize($InputRange-slider-bg, 0.8) !default; +$InputRange-slider-onFocus-boxShadow: 0 0 0 $InputRange-slider-onFocus-borderRadius transparentize($InputRange-slider-bg, 0.8) !default; $InputRange-slider-height: px2rem(24px) !default; $InputRange-slider-width: px2rem(18px) !default; -$InputRange-slider-transition: transform 0.3s ease-out, box-shadow 0.3s ease-out !default; +$InputRange-slider-transition: transform 0.3s ease-out, +box-shadow 0.3s ease-out !default; $InputRange-sliderContainer-transition: left 0.3s ease-out !default; $InputRange-slider-onActive-transform: scale(1.3) !default; $InputRange-slider-onDisabled-bg: $InputRange-onDisabled-color !default; -$InputRange-slider-onDisabled-border: px2rem(1px) solid - $InputRange-onDisabled-color !default; +$InputRange-slider-onDisabled-border: px2rem(1px) solid $InputRange-onDisabled-color !default; // input-range-label $InputRange-label-color: $InputRange-neutralColor !default; @@ -1226,7 +1163,8 @@ $InputRange-label--value-positionTop: px2rem(-40px) !default; // input-range-track $InputRange-track-bg: $InputRange-neutralLightColor !default; $InputRange-track-height: px2rem(12px) !default; -$InputRange-track-transition: left 0.3s ease-out, width 0.3s ease-out !default; +$InputRange-track-transition: left 0.3s ease-out, +width 0.3s ease-out !default; $InputRange-track-onActive-bg: $InputRange-primaryColor !default; $InputRange-track-onDisabled-bg: $InputRange-neutralLightColor !default; @@ -1235,16 +1173,12 @@ $ImageControl-addBtn-bg: $Button--default-bg !default; $ImageControl-addBtn-border: $Button--default-border !default; $ImageControl-addBtn-color: $Button--default-color !default; $ImageControl-addBtn-onHover-bg: darken($ImageControl-addBtn-bg, 7.5%) !default; -$ImageControl-addBtn-onHover-border: darken( - $ImageControl-addBtn-border, - 10% -) !default; +$ImageControl-addBtn-onHover-border: darken($ImageControl-addBtn-border, + 10%) !default; $ImageControl-addBtn-onHover-color: $Button--default-color !default; $ImageControl-addBtn-onActive-bg: darken($ImageControl-addBtn-bg, 10%) !default; -$ImageControl-addBtn-onActive-border: darken( - $ImageControl-addBtn-border, - 12.5% -) !default; +$ImageControl-addBtn-onActive-border: darken($ImageControl-addBtn-border, + 12.5%) !default; $ImageControl-addBtn-onActive-color: $ImageControl-addBtn-color !default; $ImageControl-addBtn-onDisabled-bg: $Form-input-onDisabled-bg !default; $ImageControl-addBtn-onDisabled-border: $Form-input-onDisabled-borderColor !default; @@ -1257,16 +1191,12 @@ $TagControl-sugBtn-bg: $Button--default-bg !default; $TagControl-sugBtn-border: $Button--default-border !default; $TagControl-sugBtn-color: $Button--default-color !default; $TagControl-sugBtn-onHover-bg: darken($TagControl-sugBtn-bg, 7.5%) !default; -$TagControl-sugBtn-onHover-border: darken( - $TagControl-sugBtn-border, - 10% -) !default; +$TagControl-sugBtn-onHover-border: darken($TagControl-sugBtn-border, + 10%) !default; $TagControl-sugBtn-onHover-color: $Button--default-color !default; $TagControl-sugBtn-onActive-bg: darken($TagControl-sugBtn-bg, 10%) !default; -$TagControl-sugBtn-onActive-border: darken( - $TagControl-sugBtn-border, - 12.5% -) !default; +$TagControl-sugBtn-onActive-border: darken($TagControl-sugBtn-border, + 12.5%) !default; $TagControl-sugBtn-onActive-color: $TagControl-sugBtn-color !default; $TagControl-sugBtn-borderWidth: $Button-borderWidth !default; @@ -1275,10 +1205,7 @@ $TagControl-sugBtn-borderRadius: $Button-borderRadius !default; $TagControl-sugBtn-height: $Button--sm-height !default; $TagControl-sugBtn-lineHeight: $Button--sm-lineHeight !default; $TagControl-sugBtn-paddingX: $Button--sm-paddingX !default; -$TagControl-sugBtn-paddingY: ( - $TagControl-sugBtn-height - $Button-borderWidth * 2 - - $TagControl-sugBtn-lineHeight * $TagControl-sugBtn-fontSize - )/2 !default; +$TagControl-sugBtn-paddingY: ($TagControl-sugBtn-height - $Button-borderWidth * 2 - $TagControl-sugBtn-lineHeight * $TagControl-sugBtn-fontSize)/2 !default; // Wizard $Wizard-steps-bg: $gray100 !default; @@ -1452,4 +1379,4 @@ $Picker-iconColor: $icon-color !default; $Picker-onHover-iconColor: $icon-onHover-color !default; $Picker-btn-vendor: 'FontAwesome' !default; $Picker-btn-fontSize: $Form-fontSize !default; -$Picker-btn-icon: '\f2d2' !default; +$Picker-btn-icon: '\f2d2' !default; \ No newline at end of file diff --git a/scss/components/form/_date-range.scss b/scss/components/form/_date-range.scss index 0d0f6e7a7..63c02689d 100644 --- a/scss/components/form/_date-range.scss +++ b/scss/components/form/_date-range.scss @@ -33,7 +33,7 @@ &.is-disabled { background: $gray200; - > &-input { + >&-input { color: $text--muted-color; } } @@ -71,12 +71,13 @@ .#{$ns}DateRangePicker-wrap { width: auto; - padding: $gap-md; + padding: $gap-sm; } .#{$ns}DateRangePicker-start, .#{$ns}DateRangePicker-end { display: inline-block; + vertical-align: top; .rdtPicker { padding: 0; @@ -109,10 +110,19 @@ margin-top: $gap-sm; } -.#{$ns}DateRangeControl:not(.is-inline) > .#{$ns}DateRangePicker { +.#{$ns}DateRangeControl:not(.is-inline)>.#{$ns}DateRangePicker { display: flex; } +.#{$ns}DateRangePicker-popover { + margin: px2rem(2px) 0 0; + + &.#{$ns}PopOver--leftTopLeftBottom, + &.#{$ns}PopOver--rightTopRightBottom { + margin: px2rem(-2px) 0 0; + } +} + @include media-breakpoint-up(sm) { .#{$ns}DateRangePicker-wrap { white-space: nowrap; @@ -120,6 +130,6 @@ .#{$ns}DateRangePicker-end { margin-top: 0; - margin-left: 20px; + margin-left: $gap-sm; } -} +} \ No newline at end of file diff --git a/scss/components/form/_date.scss b/scss/components/form/_date.scss index d863414ad..7c2e841c6 100644 --- a/scss/components/form/_date.scss +++ b/scss/components/form/_date.scss @@ -33,7 +33,7 @@ &.is-disabled { background: $gray200; - > &-input { + >&-input { color: $text--muted-color; } } @@ -78,26 +78,44 @@ } } -.#{$ns}DateControl:not(.is-inline) > .#{$ns}DatePicker { +.#{$ns}DateControl:not(.is-inline)>.#{$ns}DatePicker { display: flex; } .#{$ns}DatePicker-shortcuts { - margin: $gap-sm $gap-md $gap-sm $gap-md; - padding: 0; + margin: 0; + background: $Calendar-shortcuts-bg; + padding: ($Calendar-shortcuts-height - $Calendar-fontSize * $lineHeightBase) / 2 $gap-sm; list-style: none; - max-width: 206px; - & + .rdt .rdtPicker { + + &+.rdt .rdtPicker { padding-top: 0; } } .#{$ns}DatePicker-shortcut { display: inline-block; - margin-right: $gap-sm; + margin-right: $gap-md; a { + font-size: $Calendar-fontSize; cursor: pointer; + color: $Calendar-shortcut-color; + text-decoration: $Calendar-shortcut-decoration; + + &:hover { + color: $Calendar-shortcut-onHover-color; + text-decoration: $Calendar-shortcut-onHover-decoration; + } + } +} + +.#{$ns}DatePicker-popover { + margin: px2rem(2px) 0 0; + + &.#{$ns}PopOver--leftTopLeftBottom, + &.#{$ns}PopOver--rightTopRightBottom { + margin: px2rem(-2px) 0 0; } } @@ -109,7 +127,7 @@ .rdtPicker { margin-top: 0; - padding: $gap-md; + padding: $gap-sm; background: transparent; border: none; @@ -130,6 +148,10 @@ } } + td.rdtBetween { + background: $Calendar-cell-onBetween-bg; + } + td.rdtToday:before { border-bottom-color: $Calendar-cell-onActive-bg; } @@ -143,6 +165,8 @@ background: $Calendar-cell-onActive-bg; } + + td.rdtDisabled, td.rdtDisabled:hover { background-color: $Calendar-cell-onDisabled-bg; @@ -151,8 +175,8 @@ thead tr:first-child th { cursor: default; - text-align: left; font-weight: normal; + border-bottom: none; } thead tr:first-child th:hover { @@ -237,8 +261,7 @@ font-size: $fontSizeSm; } - @include button-variant( - $Calendar-btn-bg, + @include button-variant($Calendar-btn-bg, $Calendar-btn-border, $Calendar-btn-color, $Calendar-btn-onHover-bg, @@ -246,18 +269,16 @@ $Calendar-btn-onHover-color, $Calendar-btn-onActive-bg, $Calendar-btn-onActive-border, - $Calendar-btn-onActive-color - ); + $Calendar-btn-onActive-color); border-radius: $Calendar-btn-borderRadius; - & + .rdtBtn { + &+.rdtBtn { margin-left: $gap-xs; } &Cancel { - @include button-variant( - $Calendar-btnCancel-bg, + @include button-variant($Calendar-btnCancel-bg, $Calendar-btnCancel-border, $Calendar-btnCancel-color, $Calendar-btnCancel-onHover-bg, @@ -265,8 +286,7 @@ $Calendar-btnCancel-onHover-color, $Calendar-btnCancel-onActive-bg, $Calendar-btnCancel-onActive-border, - $Calendar-btnCancel-onActive-color - ); + $Calendar-btnCancel-onActive-color); } } @@ -290,26 +310,79 @@ content: $DatePicker-nextBtn-icon; } +.rdtPrev, +.rdtNext { + cursor: pointer !important; + width: px2rem(20px); + padding: 0; + color: #999; + font-size: px2rem(20px); + text-decoration: none; + font-family: auto; + font-weight: normal; + + &:hover { + text-decoration: none; + color: #000; + } +} + +.rdtSwitch { + text-align: center; + color: #000; + cursor: pointer; + font-weight: normal; + + &:hover { + color: $link-onHover-color; + text-decoration: none; + } +} + .rdtHeader { display: table; table-layout: fixed; border-collapse: separate; width: 100%; + // padding: 0 10px; - .rdtBtn { + >* { display: table-cell; - width: px2rem(30px); - padding: 0; + vertical-align: middle; + text-align: center; } - .rdtSelect { - display: table-cell; - width: 100%; - border-left: 1px solid $DatePicker-header-select-borderColor; - border-right: 1px solid $DatePicker-header-select-borderColor; - - .#{$ns}Select { - display: flex; - } + .rdtSwitch+.rdtSwitch { + margin-left: $gap-xs; } } + +td.rdtMonth, +td.rdtYear { + width: px2rem(50px); + height: px2rem(40px); + + >span { + height: px2rem(24px); + display: block; + } + + &:hover, + &.rdtActive { + background: transparent !important; + + >span { + background: $info; + color: $white; + } + } + + &.rdtDisabled { + background: transparent !important; + + >span { + background: #edf1f2; + color: #999; + } + } +} \ No newline at end of file diff --git a/scss/themes/cxd.scss b/scss/themes/cxd.scss index 928221972..1270d5da2 100644 --- a/scss/themes/cxd.scss +++ b/scss/themes/cxd.scss @@ -188,6 +188,8 @@ $DatePicker-nextBtn-fontSize: px2rem(14px); $DatePicker-nextBtn-icon: '\e63b'; $Calendar-input-borderRadius: 0; +$Calendar-shortcuts-bg: #f5f5f5; +$Calendar-shortcuts-height: px2rem(30px); // button $Button-mimWidth: px2rem(68px); diff --git a/src/components/DatePicker.tsx b/src/components/DatePicker.tsx index ea33d18c6..f4cbbf3f3 100644 --- a/src/components/DatePicker.tsx +++ b/src/components/DatePicker.tsx @@ -8,602 +8,12 @@ import React from 'react'; import cx from 'classnames'; import moment from 'moment'; import 'moment/locale/zh-cn'; - -// hack 进去,让 days view 用 CustomDaysView 代替 -import CalendarContainer from 'react-datetime/src/CalendarContainer'; -import ReactDatePicker from 'react-datetime'; -import Select from './Select'; import {Icon} from './icons'; import PopOver from './PopOver'; import Overlay from './Overlay'; -import {classPrefix, classnames} from '../themes/default'; import {ClassNamesFn, themeable} from '../theme'; -import {findDOMNode} from 'react-dom'; -import find from 'lodash/find'; import {PlainObject} from '../types'; - -class HackedCalendarContainer extends CalendarContainer { - render() { - if (this.props.view === 'days') { - return ; - } - - return super.render(); - } -} - -// hack 后,view 中可以调用 setDateTimeState -class BaseDatePicker extends ReactDatePicker { - __hacked: boolean; - - render() { - if (!this.__hacked) { - this.__hacked = true; - const origin = (this as any).getComponentProps; - const setState = this.setState.bind(this); - (this as any).getComponentProps = function() { - const props = origin.apply(this); - props.setDateTimeState = setState; - [ - 'onChange', - 'onClose', - 'requiredConfirm', - 'classPrefix', - 'prevIcon', - 'nextIcon', - 'isEndDate' - ].forEach(key => (props[key] = (this.props as any)[key])); - - return props; - }; - } - - // TODO: Make a function or clean up this code, - // logic right now is really hard to follow - let className = - 'rdt' + - (this.props.className - ? Array.isArray(this.props.className) - ? ' ' + this.props.className.join(' ') - : ' ' + this.props.className - : ''), - children: Array = []; - - if (this.props.input) { - var finalInputProps = { - type: 'text', - className: 'form-control', - onClick: this.openCalendar, - onFocus: this.openCalendar, - onChange: this.onInputChange, - onKeyDown: this.onInputKey, - value: this.state.inputValue, - ...this.props.inputProps - }; - - if (this.props.renderInput) { - children = [ -
- {this.props.renderInput( - finalInputProps, - this.openCalendar, - this.closeCalendar - )} -
- ]; - } else { - children = []; - } - } else { - className += ' rdtStatic'; - } - - if (this.state.open) className += ' rdtOpen'; - - return ( -
- {children.concat( -
- -
- )} -
- ); - } -} - -interface CustomDaysViewProps { - classPrefix?: string; - prevIcon?: string; - nextIcon?: string; - viewDate: moment.Moment; - selectedDate: moment.Moment; - timeFormat: string; - requiredConfirm?: boolean; - isEndDate?: boolean; - renderDay?: Function; - onClose?: () => void; - onChange: (value: moment.Moment) => void; - setDateTimeState: (state: any) => void; - setTime: (type: string, amount: number) => void; - subtractTime: ( - amount: number, - type: string, - toSelected?: moment.Moment - ) => () => void; - addTime: ( - amount: number, - type: string, - toSelected?: moment.Moment - ) => () => void; - isValidDate?: ( - currentDate: moment.Moment, - selected?: moment.Moment - ) => boolean; - showView: (view: string) => () => void; - updateSelectedDate: (event: React.MouseEvent, close?: boolean) => void; - handleClickOutside: () => void; -} - -class CustomDaysView extends React.Component { - static defaultProps = { - classPrefix: 'a-' - }; - - constructor(props: CustomDaysViewProps) { - super(props); - this.handleClickOutside = this.handleClickOutside.bind(this); - this.handleYearChange = this.handleYearChange.bind(this); - this.handleMonthChange = this.handleMonthChange.bind(this); - this.handleDayChange = this.handleDayChange.bind(this); - this.confirm = this.confirm.bind(this); - this.cancel = this.cancel.bind(this); - } - - getDaysOfWeek(locale: moment.Locale) { - const days: Array = locale.weekdaysMin(); - const first = locale.firstDayOfWeek(); - const dow: Array = []; - let i = 0; - - days.forEach(function(day) { - dow[(7 + i++ - first) % 7] = day; - }); - - return dow; - } - - alwaysValidDate() { - return true; - } - - handleDayChange(event: React.MouseEvent) { - // need confirm - if (this.props.requiredConfirm) { - const viewDate = this.props.viewDate.clone(); - const currentDate = this.props.selectedDate || viewDate; - - const target = event.target as HTMLElement; - let modifier = 0; - - if (~target.className.indexOf('rdtNew')) { - modifier = 1; - } - if (~target.className.indexOf('rdtOld')) { - modifier = -1; - } - - viewDate - .month(viewDate.month() + modifier) - .date(parseInt(target.getAttribute('data-value') as string, 10)) - .hours(currentDate.hours()) - .minutes(currentDate.minutes()) - .seconds(currentDate.seconds()) - .milliseconds(currentDate.milliseconds()); - - this.props.setDateTimeState({ - viewDate, - selectedDate: viewDate.clone() - }); - return; - } - - this.props.updateSelectedDate(event, true); - } - - handleMonthChange(option: any) { - // const div = document.createElement('div'); - // div.innerHTML = ``; - - // const fakeEvent = { - // target: div.firstChild - // }; - - // this.props.updateSelectedDate(fakeEvent as any); - - const viewDate = this.props.viewDate; - this.props.setDateTimeState({ - viewDate: viewDate - .clone() - .month(option.value) - .startOf('month') - }); - } - - handleYearChange(option: any) { - // const div = document.createElement('div'); - // div.innerHTML = ``; - - // const fakeEvent = { - // target: div.firstChild - // }; - - // this.props.updateSelectedDate(fakeEvent as any); - - const viewDate = this.props.viewDate; - const newDate = viewDate.clone().year(option.value); - this.props.setDateTimeState({ - viewDate: newDate[newDate.isBefore(viewDate) ? 'endOf' : 'startOf']( - 'year' - ) - }); - } - - setTime( - type: 'hours' | 'minutes' | 'seconds' | 'milliseconds', - value: number - ) { - const date = (this.props.selectedDate || this.props.viewDate).clone(); - date[type](value); - - this.props.setDateTimeState({ - viewDate: date.clone(), - selectedDate: date.clone() - }); - - if (!this.props.requiredConfirm) { - this.props.onChange(date); - } - } - - confirm() { - const date = this.props.viewDate.clone(); - - this.props.setDateTimeState({ - selectedDate: date - }); - this.props.onChange(date); - this.props.onClose && this.props.onClose(); - } - - cancel() { - this.props.onClose && this.props.onClose(); - } - - handleClickOutside() { - this.props.handleClickOutside(); - } - - renderYearsSelect() { - const classPrefix = this.props.classPrefix; - const date = this.props.viewDate; - const years: Array = []; - const isValid = this.props.isValidDate || this.alwaysValidDate; - const irrelevantMonth = 0; - const irrelevantDate = 1; - let year = date.year(); - let count = 0; - - years.push(year); - while (count < 20) { - year++; - - let currentYear = date.clone().set({ - year: year, - month: irrelevantMonth, - date: irrelevantDate - }); - const noOfDaysInYear = parseInt( - currentYear.endOf('year').format('DDD'), - 10 - ); - const daysInYear = Array.from( - { - length: noOfDaysInYear - }, - (e, i) => i + 1 - ); - const validDay = daysInYear.find(d => - isValid(currentYear.clone().dayOfYear(d)) - ); - - if (!validDay) { - break; - } - - years.push(year); - count++; - } - - count = 0; - year = date.year(); - while (count < 20) { - year--; - - let currentYear = date.clone().set({ - year: year, - month: irrelevantMonth, - date: irrelevantDate - }); - const noOfDaysInYear = parseInt( - currentYear.endOf('year').format('DDD'), - 10 - ); - const daysInYear = Array.from( - { - length: noOfDaysInYear - }, - (e, i) => i + 1 - ); - const validDay = daysInYear.find(d => - isValid(currentYear.clone().dayOfYear(d)) - ); - - if (!validDay) { - break; - } - - years.unshift(year); - count++; - } - - return ( - ({ - label: `${day + 1}`, - value: day - }))} - onChange={this.handleMonthChange} - clearable={false} - searchable={false} - /> - ); - } - - renderDay(props: any, currentDate: moment.Moment) { - return {currentDate.date()}; - } - - renderTimes() { - const {timeFormat, selectedDate, viewDate, isEndDate} = this.props; - - const date = selectedDate || (isEndDate ? viewDate.endOf('day') : viewDate); - const inputs: Array = []; - - timeFormat.split(':').forEach((format, i) => { - const type = /h/i.test(format) - ? 'hours' - : /m/i.test(format) - ? 'minutes' - : 'seconds'; - const min = 0; - const max = type === 'hours' ? 23 : 59; - - inputs.push( - - this.setTime( - type, - Math.max( - min, - Math.min( - parseInt(e.currentTarget.value.replace(/\D/g, ''), 10) || 0, - max - ) - ) - ) - } - /> - ); - - inputs.push(:); - }); - - inputs.length && inputs.pop(); - - return
{inputs}
; - } - - renderFooter() { - if (!this.props.timeFormat && !this.props.requiredConfirm) { - return null; - } - - return ( - - - - {this.props.timeFormat ? this.renderTimes() : null} - {this.props.requiredConfirm ? ( - - ) : null} - - - - ); - } - - renderDays() { - const date = this.props.viewDate; - const selected = this.props.selectedDate && this.props.selectedDate.clone(); - const prevMonth = date.clone().subtract(1, 'months'); - const currentYear = date.year(); - const currentMonth = date.month(); - const weeks = []; - let days = []; - const renderer = this.props.renderDay || this.renderDay; - const isValid = this.props.isValidDate || this.alwaysValidDate; - let classes, isDisabled, dayProps: any, currentDate; - - // Go to the last week of the previous month - prevMonth.date(prevMonth.daysInMonth()).startOf('week'); - var lastDay = prevMonth.clone().add(42, 'd'); - - while (prevMonth.isBefore(lastDay)) { - classes = 'rdtDay'; - currentDate = prevMonth.clone(); - - if ( - (prevMonth.year() === currentYear && - prevMonth.month() < currentMonth) || - prevMonth.year() < currentYear - ) - classes += ' rdtOld'; - else if ( - (prevMonth.year() === currentYear && - prevMonth.month() > currentMonth) || - prevMonth.year() > currentYear - ) - classes += ' rdtNew'; - - if (selected && prevMonth.isSame(selected, 'day')) - classes += ' rdtActive'; - - if (prevMonth.isSame(moment(), 'day')) classes += ' rdtToday'; - - isDisabled = !isValid(currentDate, selected); - if (isDisabled) classes += ' rdtDisabled'; - - dayProps = { - 'key': prevMonth.format('M_D'), - 'data-value': prevMonth.date(), - 'className': classes - }; - - if (!isDisabled) dayProps.onClick = this.handleDayChange; - - days.push(renderer(dayProps, currentDate, selected)); - - if (days.length === 7) { - weeks.push({days}); - days = []; - } - - prevMonth.add(1, 'd'); - } - - return weeks; - } - - render() { - const footer = this.renderFooter(); - const date = this.props.viewDate; - const locale = date.localeData(); - - const tableChildren = [ - - - -
- - - -
{this.renderYearsSelect()}
-
{this.renderMonthsSelect()}
- - - -
- - - - {this.getDaysOfWeek(locale).map((day, index) => ( - - {day} - - ))} - - , - - {this.renderDays()} - ]; - - footer && tableChildren.push(footer); - - return ( -
- {tableChildren}
-
- ); - } -} +import Calendar from './calendar/Calendar'; const availableShortcuts: {[propName: string]: any} = { today: { @@ -768,20 +178,24 @@ const advancedShortcuts = [ } ]; +export type ShortCutDate = { + label: string; + date: moment.Moment; +}; + +export type ShortCutDateRange = { + label: string; + startDate?: moment.Moment; + endDate?: moment.Moment; +}; + export type ShortCuts = | { label: string; value: string; } - | { - label: string; - date: moment.Moment; - } - | { - label: string; - startDate?: moment.Moment; - endDate?: moment.Moment; - }; + | ShortCutDate + | ShortCutDateRange; export interface DateProps { viewMode: 'years' | 'months' | 'days' | 'time'; @@ -1016,10 +430,10 @@ export class DatePicker extends React.Component { if (typeof item === 'string') { shortcut = this.getAvailableShortcuts(item); shortcut.key = item; - } else if (item.date) { + } else if ((item as ShortCutDate).date) { shortcut = { ...item, - date: () => item.date + date: () => (item as ShortCutDate).date }; } return ( @@ -1043,7 +457,6 @@ export class DatePicker extends React.Component { value, placeholder, disabled, - format, inputFormat, dateFormat, timeFormat, @@ -1093,7 +506,6 @@ export class DatePicker extends React.Component { {isOpened ? ( { > {this.renderShortCuts(shortcuts)} - { } export default themeable(DatePicker); - -export {BaseDatePicker}; diff --git a/src/components/DateRangePicker.tsx b/src/components/DateRangePicker.tsx index ee0afdf00..0b2ef0403 100644 --- a/src/components/DateRangePicker.tsx +++ b/src/components/DateRangePicker.tsx @@ -10,10 +10,12 @@ import {findDOMNode} from 'react-dom'; import cx from 'classnames'; import {Icon} from './icons'; import Overlay from './Overlay'; -import {BaseDatePicker, ShortCuts} from './DatePicker'; +import {ShortCuts, ShortCutDateRange} from './DatePicker'; +import Calendar from './calendar/Calendar'; import PopOver from './PopOver'; import {ClassNamesFn, themeable} from '../theme'; import {PlainObject} from '../types'; +import {noop} from '../utils/helper'; export interface DateRangePickerProps { className?: string; @@ -34,6 +36,7 @@ export interface DateRangePickerProps { onChange: (value: any) => void; data?: any; disabled?: boolean; + closeOnSelect?: boolean; [propName: string]: any; } @@ -78,20 +81,20 @@ const availableRanges: {[propName: string]: any} = { '7daysago': { label: '最近7天', startDate: (now: moment.Moment) => { - return now.add(-7, 'days'); + return now.add(-7, 'days').startOf('day'); }, endDate: (now: moment.Moment) => { - return now; + return now.add(-1, 'days').endOf('day'); } }, '90daysago': { label: '最近90天', startDate: (now: moment.Moment) => { - return now.add(-90, 'days'); + return now.add(-90, 'days').startOf('day'); }, endDate: (now: moment.Moment) => { - return now; + return now.add(-1, 'days').endOf('day'); } }, @@ -168,7 +171,8 @@ export class DateRangePicker extends React.Component< delimiter: ',', ranges: 'yesterday,7daysago,prevweek,thismonth,prevmonth,prevquarter', iconClassName: 'fa fa-calendar', - resetValue: '' + resetValue: '', + closeOnSelect: false }; innerDom: any; @@ -217,6 +221,7 @@ export class DateRangePicker extends React.Component< } dom: React.RefObject; + nextMonth = moment().add(1, 'months'); constructor(props: DateRangePickerProps) { super(props); @@ -235,6 +240,7 @@ export class DateRangePicker extends React.Component< this.handleClick = this.handleClick.bind(this); this.handleKeyPress = this.handleKeyPress.bind(this); this.handlePopOverClick = this.handlePopOverClick.bind(this); + this.renderDay = this.renderDay.bind(this); const {format, joinValues, delimiter, value} = this.props; this.state = { @@ -338,28 +344,53 @@ export class DateRangePicker extends React.Component< this.close(); } - handleStartChange(newValue: any) { + handleStartChange(newValue: moment.Moment) { + if ( + this.state.startDate && + !this.state.endDate && + newValue.isAfter(this.state.startDate) + ) { + return this.setState({ + endDate: newValue.clone() + }); + } + this.setState({ startDate: newValue.clone() }); } - handleEndChange(newValue: any) { + handleEndChange(newValue: moment.Moment) { newValue = !this.state.endDate && !this.props.timeFormat ? newValue.endOf('day') : newValue; + + if ( + this.state.endDate && + !this.state.startDate && + newValue.isBefore(this.state.endDate) + ) { + return this.setState({ + startDate: newValue.clone() + }); + } + this.setState({ endDate: newValue.clone() }); } selectRannge(range: PlainObject) { + const {closeOnSelect} = this.props; const now = moment(); - this.setState({ - startDate: range.startDate(now.clone()), - endDate: range.endDate(now.clone()) - }); + this.setState( + { + startDate: range.startDate(now.clone()), + endDate: range.endDate(now.clone()) + }, + closeOnSelect ? this.close : noop + ); } renderRanges(ranges: string | Array | undefined) { @@ -383,11 +414,14 @@ export class DateRangePicker extends React.Component< if (typeof item === 'string') { range = availableRanges[item]; range.key = item; - } else if (item.startDate && item.endDate) { + } else if ( + (item as ShortCutDateRange).startDate && + (item as ShortCutDateRange).endDate + ) { range = { ...item, - startDate: () => item.startDate, - endDate: () => item.endDate + startDate: () => (item as ShortCutDateRange).startDate, + endDate: () => (item as ShortCutDateRange).endDate }; } return ( @@ -454,6 +488,20 @@ export class DateRangePicker extends React.Component< return true; } + renderDay(props: any, currentDate: moment.Moment) { + let {startDate, endDate} = this.state; + + if ( + startDate && + endDate && + currentDate.isBetween(startDate, endDate, 'day', '[]') + ) { + props.className += ' rdtBetween'; + } + + return {currentDate.date()}; + } + render() { const { className, @@ -529,7 +577,6 @@ export class DateRangePicker extends React.Component< {isOpened ? ( this.dom.current} onHide={this.close} container={popOverContainer || (() => findDOMNode(this))} @@ -546,9 +593,7 @@ export class DateRangePicker extends React.Component<
{this.renderRanges(ranges)} - -
diff --git a/src/components/calendar/Calendar.tsx b/src/components/calendar/Calendar.tsx new file mode 100644 index 000000000..06c4118ca --- /dev/null +++ b/src/components/calendar/Calendar.tsx @@ -0,0 +1,81 @@ +/** + * @file 基于 react-datetime 改造。 + */ +import ReactDatePicker from 'react-datetime'; +import React from 'react'; +import CustomCalendarContainer from './CalendarContainer'; +import cx from 'classnames'; +import moment from 'moment'; + +interface BaseDatePickerProps extends ReactDatePicker.DatetimepickerProps { + onViewModeChange?: (type: string) => void; + requiredConfirm?: boolean; + onClose?: () => void; + isEndDate?: boolean; + renderDay?: ( + props: any, + currentDate: moment.Moment, + selectedDate: moment.Moment + ) => JSX.Element; +} + +class BaseDatePicker extends ReactDatePicker { + state: any; + props: BaseDatePickerProps; + setState: (state: any) => void; + getComponentProps = ((origin: Function) => { + return () => { + const props = origin.call(this); + props.setDateTimeState = this.setState.bind(this); + + [ + 'onChange', + 'onClose', + 'requiredConfirm', + 'classPrefix', + 'prevIcon', + 'nextIcon', + 'isEndDate' + ].forEach(key => (props[key] = (this.props as any)[key])); + + return props; + }; + })((this as any).getComponentProps); + + setDate = (type: 'month' | 'year') => { + const nextViews = { + month: 'days', + year: 'days' + }; + + return (e: any) => { + this.setState({ + viewDate: this.state.viewDate + .clone() + [type]( + parseInt(e.target.closest('td').getAttribute('data-value'), 10) + ) + .startOf(type), + currentView: nextViews[type] + }); + this.props.onViewModeChange!(nextViews[type]); + }; + }; + + render() { + const Component = CustomCalendarContainer as any; + return ( +
+
+ +
+
+ ); + } +} + +const Calendar: any = BaseDatePicker; +export default Calendar as React.ComponentType; diff --git a/src/components/calendar/CalendarContainer.tsx b/src/components/calendar/CalendarContainer.tsx new file mode 100644 index 000000000..7299a1448 --- /dev/null +++ b/src/components/calendar/CalendarContainer.tsx @@ -0,0 +1,15 @@ +// @ts-ignore +import CalendarContainer from 'react-datetime/src/CalendarContainer'; + +import CustomDaysView from './DaysView'; +import CustomYearsView from './YearsView'; +import CustomMonthsView from './MonthsView'; + +export default class CustomCalendarContainer extends CalendarContainer { + viewComponents: any = { + ...(this as any).viewComponents, + days: CustomDaysView, + years: CustomYearsView, + months: CustomMonthsView + }; +} diff --git a/src/components/calendar/DaysView.tsx b/src/components/calendar/DaysView.tsx new file mode 100644 index 000000000..d97085c2b --- /dev/null +++ b/src/components/calendar/DaysView.tsx @@ -0,0 +1,248 @@ +import moment from 'moment'; +// @ts-ignore +import DaysView from 'react-datetime/src/DaysView'; +import React from 'react'; + +interface CustomDaysViewProps { + classPrefix?: string; + prevIcon?: string; + nextIcon?: string; + viewDate: moment.Moment; + selectedDate: moment.Moment; + timeFormat: string; + requiredConfirm?: boolean; + isEndDate?: boolean; + renderDay?: Function; + onClose?: () => void; + onChange: (value: moment.Moment) => void; + setDateTimeState: (state: any) => void; + setTime: (type: string, amount: number) => void; + subtractTime: ( + amount: number, + type: string, + toSelected?: moment.Moment + ) => () => void; + addTime: ( + amount: number, + type: string, + toSelected?: moment.Moment + ) => () => void; + isValidDate?: ( + currentDate: moment.Moment, + selected?: moment.Moment + ) => boolean; + showView: (view: string) => () => void; + updateSelectedDate: (event: React.MouseEvent, close?: boolean) => void; + handleClickOutside: () => void; +} + +export default class CustomDaysView extends DaysView { + props: CustomDaysViewProps; + getDaysOfWeek: (locale: any) => any; + renderDays: () => JSX.Element; + + updateSelectedDate = (event: React.MouseEvent) => { + // need confirm + if (this.props.requiredConfirm) { + const viewDate = this.props.viewDate.clone(); + const currentDate = this.props.selectedDate || viewDate; + + const target = event.target as HTMLElement; + let modifier = 0; + + if (~target.className.indexOf('rdtNew')) { + modifier = 1; + } + if (~target.className.indexOf('rdtOld')) { + modifier = -1; + } + + viewDate + .month(viewDate.month() + modifier) + .date(parseInt(target.getAttribute('data-value') as string, 10)) + .hours(currentDate.hours()) + .minutes(currentDate.minutes()) + .seconds(currentDate.seconds()) + .milliseconds(currentDate.milliseconds()); + + this.props.setDateTimeState({ + viewDate, + selectedDate: viewDate.clone() + }); + return; + } + + this.props.updateSelectedDate(event, true); + }; + + setTime = ( + type: 'hours' | 'minutes' | 'seconds' | 'milliseconds', + value: number + ) => { + const date = (this.props.selectedDate || this.props.viewDate).clone(); + date[type](value); + + this.props.setDateTimeState({ + viewDate: date.clone(), + selectedDate: date.clone() + }); + + if (!this.props.requiredConfirm) { + this.props.onChange(date); + } + }; + + confirm = () => { + const date = this.props.viewDate.clone(); + + this.props.setDateTimeState({ + selectedDate: date + }); + this.props.onChange(date); + this.props.onClose && this.props.onClose(); + }; + + cancel = () => { + this.props.onClose && this.props.onClose(); + }; + + renderDay = (props: any, currentDate: moment.Moment) => { + return {currentDate.date()}; + }; + + renderTimes = () => { + const {timeFormat, selectedDate, viewDate, isEndDate} = this.props; + + const date = selectedDate || (isEndDate ? viewDate.endOf('day') : viewDate); + const inputs: Array = []; + + timeFormat.split(':').forEach((format, i) => { + const type = /h/i.test(format) + ? 'hours' + : /m/i.test(format) + ? 'minutes' + : 'seconds'; + const min = 0; + const max = type === 'hours' ? 23 : 59; + + inputs.push( + + this.setTime( + type, + Math.max( + min, + Math.min( + parseInt(e.currentTarget.value.replace(/\D/g, ''), 10) || 0, + max + ) + ) + ) + } + /> + ); + + inputs.push(:); + }); + + inputs.length && inputs.pop(); + + return
{inputs}
; + }; + + renderFooter = () => { + if (!this.props.timeFormat && !this.props.requiredConfirm) { + return null; + } + + return ( + + + + {this.props.timeFormat ? this.renderTimes() : null} + {this.props.requiredConfirm ? ( + + ) : null} + + + + ); + }; + + render() { + const footer = this.renderFooter(); + const date = this.props.viewDate; + const locale = date.localeData(); + + const tableChildren = [ + + + + + + + + {this.getDaysOfWeek(locale).map((day: number, index: number) => ( + + {day} + + ))} + + , + + {this.renderDays()} + ]; + + footer && tableChildren.push(footer); + + return ( +
+ {tableChildren}
+
+ ); + } +} diff --git a/src/components/calendar/MonthsView.tsx b/src/components/calendar/MonthsView.tsx new file mode 100644 index 000000000..999572c0f --- /dev/null +++ b/src/components/calendar/MonthsView.tsx @@ -0,0 +1,61 @@ +// @ts-ignore +import MonthsView from 'react-datetime/src/MonthsView'; +import moment from 'moment'; +import React from 'react'; + +export default class CustomMonthsView extends MonthsView { + props: { + viewDate: moment.Moment; + subtractTime: ( + amount: number, + type: string, + toSelected?: moment.Moment + ) => () => void; + addTime: ( + amount: number, + type: string, + toSelected?: moment.Moment + ) => () => void; + }; + renderMonths: () => JSX.Element; + renderMonth = (props: any, month: number) => { + var localMoment = this.props.viewDate; + var monthStr = localMoment + .localeData() + .monthsShort(localMoment.month(month)); + var strLength = 3; + // Because some months are up to 5 characters long, we want to + // use a fixed string length for consistency + var monthStrFixedLength = monthStr.substring(0, strLength); + return ( + + {monthStrFixedLength} + + ); + }; + render() { + return ( +
+ + + + + + + + +
+ « + {this.props.viewDate.year()}年 + » +
+ + {this.renderMonths()} +
+
+ ); + } +} diff --git a/src/components/calendar/YearsView.tsx b/src/components/calendar/YearsView.tsx new file mode 100644 index 000000000..18f96b571 --- /dev/null +++ b/src/components/calendar/YearsView.tsx @@ -0,0 +1,59 @@ +// @ts-ignore +import YearsView from 'react-datetime/src/YearsView'; +import moment from 'moment'; +import React from 'react'; + +export default class CustomYearsView extends YearsView { + props: { + viewDate: moment.Moment; + subtractTime: ( + amount: number, + type: string, + toSelected?: moment.Moment + ) => () => void; + addTime: ( + amount: number, + type: string, + toSelected?: moment.Moment + ) => () => void; + showView: (view: string) => () => void; + }; + renderYears: (year: number) => JSX.Element; + renderYear = (props: any, year: number) => { + return ( + + {year} + + ); + }; + render() { + let year = this.props.viewDate.year(); + year = year - (year % 10); + + return ( +
+ + + + + + + + +
+ « + + {year}年-{year + 9}年 + + » +
+ + {this.renderYears(year)} +
+
+ ); + } +} diff --git a/src/renderers/Form/Date.tsx b/src/renderers/Form/Date.tsx index 371d225bc..320c483f1 100644 --- a/src/renderers/Form/Date.tsx +++ b/src/renderers/Form/Date.tsx @@ -13,7 +13,7 @@ export interface DateProps extends FormControlProps { format?: string; timeConstrainst?: object; closeOnSelect?: boolean; - disabled?: boolean; + disabled: boolean; iconClassName?: string; }