diff --git a/scss/_mixins.scss b/scss/_mixins.scss index bcc12029..da6f8713 100644 --- a/scss/_mixins.scss +++ b/scss/_mixins.scss @@ -171,13 +171,18 @@ } } -@mixin hover-focus { - &:hover, +@mixin focus { &:focus { @content; } } +@mixin hover-focus { + &:hover:focus { + @content; + } +} + @mixin hover-active { &:hover:active { @content; @@ -226,10 +231,24 @@ border-color: $border; box-shadow: $Button-boxShadow; + @include hover { + color: $hover-color; + background-color: $hover-background; + border-color: $hover-border; + } + + @include focus { + color: $color; + background-color: $background; + border-color: $border; + box-shadow: $Button-boxShadow; + } + @include hover-focus { color: $hover-color; background-color: $hover-background; border-color: $hover-border; + box-shadow: $Button-boxShadow; } &.is-disabled, diff --git a/scss/components/form/_date.scss b/scss/components/form/_date.scss index 7c2e841c..bbc77a64 100644 --- a/scss/components/form/_date.scss +++ b/scss/components/form/_date.scss @@ -87,6 +87,7 @@ background: $Calendar-shortcuts-bg; padding: ($Calendar-shortcuts-height - $Calendar-fontSize * $lineHeightBase) / 2 $gap-sm; list-style: none; + width: px2rem(250px); &+.rdt .rdtPicker { padding-top: 0; @@ -95,7 +96,7 @@ .#{$ns}DatePicker-shortcut { display: inline-block; - margin-right: $gap-md; + margin-right: $gap-sm; a { font-size: $Calendar-fontSize; diff --git a/src/components/DatePicker.tsx b/src/components/DatePicker.tsx index d50c0852..ba6a6456 100644 --- a/src/components/DatePicker.tsx +++ b/src/components/DatePicker.tsx @@ -17,6 +17,12 @@ import Calendar from './calendar/Calendar'; import 'react-datetime/css/react-datetime.css'; const availableShortcuts: {[propName: string]: any} = { + now: { + label: '现在', + date: (now: moment.Moment) => { + return now; + } + }, today: { label: '今天', date: (now: moment.Moment) => { @@ -89,6 +95,28 @@ const availableShortcuts: {[propName: string]: any} = { }; const advancedShortcuts = [ + { + regexp: /^(\d+)hoursago$/, + resolve: (_: string, hours: string) => { + return { + label: `${hours}小时前`, + date: (now: moment.Moment) => { + return now.subtract(hours, 'hours'); + } + }; + } + }, + { + regexp: /^(\d+)hourslater$/, + resolve: (_: string, hours: string) => { + return { + label: `${hours}小时后`, + date: (now: moment.Moment) => { + return now.add(hours, 'hours'); + } + }; + } + }, { regexp: /^(\d+)daysago$/, resolve: (_: string, days: string) => { diff --git a/src/utils/tpl-builtin.ts b/src/utils/tpl-builtin.ts index d85bfd9e..1f296508 100644 --- a/src/utils/tpl-builtin.ts +++ b/src/utils/tpl-builtin.ts @@ -101,17 +101,17 @@ export const filterDate = ( const from = m[1] ? filterDate(m[1], data, format, utc) : mm( - /(minute|min|hour|second)s?/.test(m[4]) - ? [ - date.getFullYear(), - date.getMonth(), - date.getDate(), - date.getHours(), - date.getMinutes(), - date.getSeconds() - ] - : [date.getFullYear(), date.getMonth(), date.getDate()] - ); + /(minute|min|hour|second)s?/.test(m[4]) + ? [ + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + date.getMinutes(), + date.getSeconds() + ] + : [date.getFullYear(), date.getMonth(), date.getDate()] + ); return m[2] === '-' ? from.subtract(step, timeUnitMap[m[4]] as moment.DurationInputArg2) @@ -348,7 +348,14 @@ export const filters: { * @param data 数据域 */ function getStrOrVariable(arg: string, data: any) { - return /^('|")(.*)\1$/.test(arg) ? RegExp.$2 : resolveVariable(arg, data); + + return /^('|")(.*)\1$/.test(arg) + ? RegExp.$2 + : /^-?\d+$/.test(arg) + ? parseInt(arg, 10) + : /^(-?\d+)\.\d+?$/.test(arg) + ? parseFloat(arg) + : resolveVariable(arg, data); } function getConditionValue( @@ -485,44 +492,44 @@ export const resolveVariableAndFilter = ( return ret == null && !~originalKey.indexOf('default') ? '' : paths.reduce((input, filter) => { - let params = filter - .replace( - /([^\\])\\([\:\\])/g, - (_, affix, content) => - `${affix}__${content === ':' ? 'colon' : 'slash'}__` + let params = filter + .replace( + /([^\\])\\([\:\\])/g, + (_, affix, content) => + `${affix}__${content === ':' ? 'colon' : 'slash'}__` + ) + .split(':') + .map(item => + item.replace(/__(slash|colon)__/g, (_, type) => + type === 'colon' ? ':' : '\\' ) - .split(':') - .map(item => - item.replace(/__(slash|colon)__/g, (_, type) => - type === 'colon' ? ':' : '\\' - ) - ); - let key = params.shift() as string; + ); + let key = params.shift() as string; - if ( - ~[ - 'isTrue', - 'isFalse', - 'isMatch', - 'isEquals', - 'notMatch', - 'notEquals' - ].indexOf(key) - ) { - if (prevConInputChanged) { - return input; - } else { - const result = filters[key].call(data, input, ...params); - prevConInputChanged = result !== input; - return result; - } + if ( + ~[ + 'isTrue', + 'isFalse', + 'isMatch', + 'isEquals', + 'notMatch', + 'notEquals' + ].indexOf(key) + ) { + if (prevConInputChanged) { + return input; } else { - // 后面再遇到非类三元filter就重置了吧,不影响再后面的其他三元filter - prevConInputChanged = false; + const result = filters[key].call(data, input, ...params); + prevConInputChanged = result !== input; + return result; } + } else { + // 后面再遇到非类三元filter就重置了吧,不影响再后面的其他三元filter + prevConInputChanged = false; + } - return (filters[key] || filters.raw).call(data, input, ...params); - }, ret); + return (filters[key] || filters.raw).call(data, input, ...params); + }, ret); }; export const tokenize = ( @@ -559,8 +566,8 @@ function resolveMapping( return typeof value === 'string' && isPureVariable(value) ? resolveVariableAndFilter(value, data, defaultFilter) : typeof value === 'string' && ~value.indexOf('$') - ? tokenize(value, data, defaultFilter) - : value; + ? tokenize(value, data, defaultFilter) + : value; } export function dataMapping(to: any, from: PlainObject): any { @@ -584,13 +591,13 @@ export function dataMapping(to: any, from: PlainObject): any { } else if (key === '&') { const v = isPlainObject(value) && - (keys = Object.keys(value)) && - keys.length === 1 && - from[keys[0].substring(1)] && - Array.isArray(from[keys[0].substring(1)]) + (keys = Object.keys(value)) && + keys.length === 1 && + from[keys[0].substring(1)] && + Array.isArray(from[keys[0].substring(1)]) ? from[keys[0].substring(1)].map((raw: object) => - dataMapping(value[keys[0]], createObject(from, raw)) - ) + dataMapping(value[keys[0]], createObject(from, raw)) + ) : resolveMapping(value, from); if (Array.isArray(v) || typeof v === 'string') { diff --git a/src/utils/validations.ts b/src/utils/validations.ts index a1b7fd9a..11d68efb 100644 --- a/src/utils/validations.ts +++ b/src/utils/validations.ts @@ -4,7 +4,7 @@ const isEmpty = (value: any) => value === ''; const makeRegexp = (reg: string | RegExp) => { if (reg instanceof RegExp) { return reg; - } else if (/\/(.+)\/([gimuy]*)/.test(reg)) { + } else if (/^\/(.+)\/([gimuy]*)$/.test(reg)) { return new RegExp(RegExp.$1, RegExp.$2 || ''); } else if (typeof reg === 'string') { return new RegExp(reg);