mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 02:58:05 +08:00
暂存表单修改
This commit is contained in:
parent
317c1ad14d
commit
48a3329bd6
@ -678,7 +678,7 @@ order: 31
|
||||
}
|
||||
],
|
||||
searchable: true,
|
||||
multiple: false,
|
||||
multiple: true,
|
||||
joinValues: true,
|
||||
clearable: true
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ order: 68
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "tabs",
|
||||
"swipeable": true,
|
||||
"tabs": [
|
||||
{
|
||||
"title": "Tab 1",
|
||||
@ -23,6 +24,54 @@ order: 68
|
||||
{
|
||||
"title": "Tab 2",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 3",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 4",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 5",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 6",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 7",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 8",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 9",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 10",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 11",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 12",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 13",
|
||||
"tab": "Content 2"
|
||||
},
|
||||
{
|
||||
"title": "Tab 14",
|
||||
"tab": "Content 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -789,6 +838,7 @@ order: 68
|
||||
| sidePosition | `left` / `right` | `left` | `sidebar` 模式下,标签栏位置 |
|
||||
| collapseOnExceed | `number` | | 当 tabs 超出多少个时开始折叠 |
|
||||
| collapseBtnLabel | `string` | `more` | 用来设置折叠按钮的文字 |
|
||||
| swipeable | `boolean` | false | 是否开启手势滑动切换(移动端生效) |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -1,24 +1,18 @@
|
||||
export default {
|
||||
type: 'page',
|
||||
title: '表单页面',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
mode: 'horizontal',
|
||||
api: '/api/mock2/form/saveForm',
|
||||
body: [
|
||||
{
|
||||
label: 'Name',
|
||||
type: 'input-text',
|
||||
name: 'name'
|
||||
},
|
||||
{
|
||||
label: 'Email',
|
||||
type: 'input-email',
|
||||
placeholder: '请输入邮箱地址',
|
||||
name: 'email'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
body: {
|
||||
type: 'form',
|
||||
body: [
|
||||
{
|
||||
label: '多选',
|
||||
type: 'select',
|
||||
name: 'select2',
|
||||
searchable: true,
|
||||
checkAll: true,
|
||||
multiple: true,
|
||||
clearable: true,
|
||||
source: '/api/mock2/form/getOptions'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -1676,6 +1676,8 @@
|
||||
--InputRange-track-onActive-bg: var(--colors-brand-5);
|
||||
--InputRange-handle-height: var(--sizes-size-9);
|
||||
--InputRange-handle-width: var(--sizes-size-9);
|
||||
--InputRange-handle-mobile-height: var(--sizes-base-11);
|
||||
--InputRange-handle-mobile-width: var(--sizes-base-11);
|
||||
--InputRange-handle-bg: var(--colors-neutral-fill-11);
|
||||
--InputRange-handle-top-border-color: var(--colors-brand-5);
|
||||
--InputRange-handle-top-border-width: 0.0625rem;
|
||||
@ -1730,6 +1732,7 @@
|
||||
);
|
||||
--InputRange-label-position-bottom: calc(100% + 8px);
|
||||
--InputRange-input-width: var(--sizes-base-40);
|
||||
--InputRange-input-mobile-width: var(--sizes-base-20);
|
||||
--InputRange-input-marginTop: var(--sizes-size-0);
|
||||
--InputRange-input-marginBottom: var(--sizes-size-0);
|
||||
--InputRange-input-marginLeft: var(--sizes-size-5);
|
||||
@ -2821,6 +2824,7 @@
|
||||
--select-tree-active-bg-color: var(--colors-brand-10);
|
||||
|
||||
--Form-select-bg: var(--select-base-default-bg-color);
|
||||
--Form-select-mobile-icon-check-color: var(--colors-brand-5);
|
||||
--Form-select-height: var(--Form-select-outer-top);
|
||||
--Form-select-borderColor: var(--select-base-default-top-border-color)
|
||||
var(--select-base-default-right-border-color)
|
||||
|
@ -527,6 +527,10 @@ $Table-strip-bg: transparent;
|
||||
--ListMenu-item-bg: var(--colors-neutral-fill-11);
|
||||
--ListMenu-item-color: var(--colors-neutral-text-2);
|
||||
--ListMenu-item-height: var(--sizes-base-15);
|
||||
--ListMenu-item-mobile-margin: #{px2rem(5px)};
|
||||
--ListMenu-item-mobile-width: #{px2rem(90px)};
|
||||
--ListMenu-item-mobile-bg: #f5f5f5;
|
||||
--ListMenu-item-mobile-active-bg: #e7f1ff;
|
||||
|
||||
--Log-bg: #222;
|
||||
--Log-padding: var(--gap-sm) 0;
|
||||
@ -768,6 +772,11 @@ $Table-strip-bg: transparent;
|
||||
--UserSelect--border-color: #f7f7f9;
|
||||
--UserSelect--content-bg: #f5f7f8;
|
||||
--UserSelect--bread-color: #5e626a;
|
||||
|
||||
--Cascader-border-color: #f7f7f9;
|
||||
--Cascader-border-active-bg-color: #f7f7f9;
|
||||
--Cascader-option-disable-color: #b8babf;
|
||||
|
||||
// tag
|
||||
--Tag-content-fontSize: var(--fontSizeSm);
|
||||
--Tag-height: #{px2rem(24px)};
|
||||
|
@ -123,8 +123,15 @@
|
||||
border-width: 0;
|
||||
|
||||
&--time {
|
||||
height: fit-content;
|
||||
height: px2rem(360px);
|
||||
max-height: 90vh;
|
||||
.#{$ns}PopUp-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&--years {
|
||||
height: px2rem(360px);
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,6 +253,7 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: px2rem(4px);
|
||||
padding: 0 px2rem(10px);
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,11 +290,11 @@
|
||||
line-height: px2rem(48px);
|
||||
}
|
||||
.#{$ns}DateRangePicker-rangers {
|
||||
position: absolute;
|
||||
padding-left: revert;
|
||||
white-space: nowrap;
|
||||
.#{$ns}DateRangePicker-ranger {
|
||||
margin: 0 px2rem(25px);
|
||||
}
|
||||
line-height: inherit;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.#{$ns}DatePicker-shortcuts {
|
||||
width: auto;
|
||||
|
@ -15,6 +15,9 @@
|
||||
height: px2rem(260px);
|
||||
overflow-y: auto;
|
||||
display: inline-block;
|
||||
padding-left: px2rem(10px);
|
||||
border: 1px solid var(--Cascader-border-color);
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
@ -52,13 +55,18 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: px2rem(6px) 0;
|
||||
padding: px2rem(6px) 0 px2rem(6px) px2rem(10px);
|
||||
font-size: var(--fontSizeMd);
|
||||
line-height: var(--Cascader-option-lineHeight);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--Cascader-border-active-bg-color);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
span {
|
||||
.#{$ns}Cascader-option--text {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
@ -68,11 +76,26 @@
|
||||
}
|
||||
}
|
||||
&--text {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
user-select: none;
|
||||
|
||||
&.disabled {
|
||||
color: var(--Cascader-option-disable-color);
|
||||
}
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
padding-right: px2rem(6px);
|
||||
> svg {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
&-selectedNum {
|
||||
min-width: px2rem(16px);
|
||||
height: px2rem(16px);
|
||||
|
@ -24,7 +24,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Collapse:not(:last-child) {
|
||||
.#{$ns}Collapse:not(:last-child):not(.is-mobile) {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@ -40,4 +40,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
&.icon-position-right {
|
||||
.#{$ns}Collapse-header {
|
||||
.#{$ns}Collapse-arrow-wrap {
|
||||
margin-right: px2rem(-18px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,17 @@
|
||||
background: var(--Collapse-header-onHover-bg);
|
||||
color: var(--collapse-default-header-hover-color);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
background: none;
|
||||
border-radius: 0 !important;
|
||||
position: relative;
|
||||
padding-left: 0;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Collapse-arrow {
|
||||
@ -76,7 +87,7 @@
|
||||
// display: inline-block;
|
||||
// }
|
||||
|
||||
&.is-active > &-header > &-arrow-wrap > &-arrow {
|
||||
&.is-active > .#{$ns}Collapse-header > .#{$ns}Collapse-arrow-wrap > &-arrow {
|
||||
transform: rotate(var(--collapse-icon-rotate));
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
@ -133,6 +144,28 @@
|
||||
line-height: var(--collapse-default-content-lineHeight);
|
||||
background: var(--collapse-default-bg-color);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.#{$ns}Collapse-icon-tranform,
|
||||
.#{$ns}Collapse-arrow-wrap {
|
||||
margin-right: px2rem(-18px);
|
||||
float: right;
|
||||
margin-top: px2rem(2px);
|
||||
}
|
||||
|
||||
.#{$ns}Collapse-content {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FieldSet Form + Collapse
|
||||
|
@ -64,9 +64,15 @@
|
||||
height: #{px2rem(250px)};
|
||||
margin-top: #{px2rem(10px)};
|
||||
|
||||
& > div {
|
||||
&:not(.is-mobile) > div {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
width: 100%;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&-settings.only-variable {
|
||||
@ -389,6 +395,16 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&-popup {
|
||||
height: 80vh;
|
||||
|
||||
&-inner {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 px2rem(16px);
|
||||
}
|
||||
}
|
||||
|
||||
&-input {
|
||||
flex: 1;
|
||||
margin-right: #{px2rem(10px)};
|
||||
|
@ -72,4 +72,28 @@
|
||||
&.is-active &-caret {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
border-bottom: var(--Form-input-borderWidth) solid
|
||||
var(--Form-input-borderColor);
|
||||
|
||||
&.is-error,
|
||||
.is-error > & {
|
||||
border-bottom-color: var(--Form-input-onError-borderColor);
|
||||
}
|
||||
|
||||
&.is-focused {
|
||||
border-bottom-color: var(--Form-input-onFocused-borderColor);
|
||||
}
|
||||
|
||||
&.is-error.is-focused {
|
||||
border-bottom-color: var(--Form-input-onError-borderColor);
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
border-bottom-color: var(--Form-input-onDisabled-borderColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,15 @@
|
||||
|
||||
color: var(--Form-input-color);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
& > span {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
border-bottom: var(--Form-input-borderWidth) solid
|
||||
var(--Form-input-borderColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-value {
|
||||
@ -121,4 +130,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
.#{$ns}Number {
|
||||
border-radius: 0;
|
||||
border-bottom: var(--Number-borderWidth) solid
|
||||
var(--Form-input-borderColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,42 @@
|
||||
)
|
||||
var(--Form-select-paddingX);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.#{$ns}ListMenu-item {
|
||||
width: calc((100vw - var(--ListMenu-item-mobile-margin) * 7) / 3);
|
||||
display: inline-flex;
|
||||
background: var(--ListMenu-item-mobile-bg);
|
||||
margin: var(--ListMenu-item-mobile-margin);
|
||||
|
||||
&.is-active {
|
||||
background: var(--ListMenu-item-mobile-active-bg);
|
||||
}
|
||||
|
||||
.#{$ns}ListMenu-itemLabel {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}ListMenu-add-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
margin: px2rem(20px) 0;
|
||||
text-align: center;
|
||||
|
||||
input {
|
||||
&::-webkit-input-placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}PopOver > .#{$ns}ListMenu {
|
||||
|
@ -220,7 +220,7 @@
|
||||
calc(var(--Panel-marginBottom) / 2);
|
||||
|
||||
.#{$ns}Panel-body {
|
||||
padding: 0 var(--gap-md) var(--gap-md);
|
||||
// padding: 0 var(--gap-md) var(--gap-md);
|
||||
}
|
||||
|
||||
> .#{$ns}Panel-heading {
|
||||
|
@ -132,6 +132,12 @@
|
||||
background: var(--SearchBox-enhonce-disabled-color);
|
||||
color: var(--SearchBox-enhonce-disabled-search-color);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
border-bottom: var(--borderWidth) solid var(--borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}SearchBox-history {
|
||||
|
@ -657,6 +657,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
.#{$ns}Table-table > tbody > tr {
|
||||
&:hover,
|
||||
&.is-hovered {
|
||||
background: var(--Table-bg);
|
||||
border-color: var(--Table-borderColor);
|
||||
color: var(--Table-color);
|
||||
|
||||
& + tr {
|
||||
border-color: var(--Table-borderColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&Cell-sortBtn,
|
||||
&Cell-searchBtn,
|
||||
&Cell-filterBtn {
|
||||
|
@ -112,6 +112,18 @@
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
|
||||
&.is-mobile {
|
||||
overflow-x: auto;
|
||||
-ms-overflow-style: none;
|
||||
overflow: -moz-scrollbars-none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Tabs-links-drag {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
|
@ -11,7 +11,7 @@
|
||||
border-radius: var(--borderRadius);
|
||||
|
||||
&-popup {
|
||||
height: px2rem(400px);
|
||||
height: px2rem(460px);
|
||||
}
|
||||
|
||||
&:not(.is-disabled) {
|
||||
@ -122,3 +122,26 @@
|
||||
border-radius: var(--borderRadius) !important;
|
||||
box-shadow: var(--ColorPicker-boxShadow) !important;
|
||||
}
|
||||
|
||||
.#{$ns}ColorPicker-popup {
|
||||
.sketch-picker {
|
||||
width: 80% !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
input {
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.flexbox-fix {
|
||||
&:last-child {
|
||||
> div {
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
margin: 0px 16px 10px 0px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,6 +320,34 @@
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
.#{$ns}Form-item {
|
||||
padding: px2rem(10px) 0 0 0;
|
||||
|
||||
.#{$ns}Form-rowInner {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Combo-delBtn {
|
||||
height: px2rem(20px);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile:not(.#{$ns}Combo--ver) {
|
||||
.#{$ns}TextareaControl > textarea,
|
||||
.#{$ns}Form-control > .#{$ns}TextControl-input,
|
||||
.#{$ns}TextControl.is-focused > .#{$ns}TextControl-input {
|
||||
border-bottom: var(--Form-input-borderWidth) solid var(--borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile:is(.#{$ns}Combo--hor) {
|
||||
.#{$ns}Form-control {
|
||||
padding-top: px2rem(10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}ComboTabs > .#{$ns}Tabs-links {
|
||||
|
@ -185,10 +185,22 @@
|
||||
width: auto;
|
||||
// padding-bottom: var(--gap-sm);
|
||||
padding: 0;
|
||||
|
||||
&.is-mobile {
|
||||
flex: 1;
|
||||
|
||||
.#{$ns}DateRangePicker-end {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}DateRangePicker-picker-wrap {
|
||||
display: flex;
|
||||
|
||||
&.is-vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}DateRangePicker-start,
|
||||
@ -274,6 +286,10 @@
|
||||
border-color: var(--DatePicker-borderColor);
|
||||
background: var(--DatePicker-bg);
|
||||
border-radius: var(--DatePicker-borderRadius);
|
||||
|
||||
&.is-mobile {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
// 移动端输入框样式
|
||||
|
@ -456,7 +456,7 @@
|
||||
color: var(--inputTime-active-color);
|
||||
background: var(--inputTime-active-bg-color);
|
||||
}
|
||||
&:hover {
|
||||
&:not(.is-mobile):hover {
|
||||
color: var(--inputTime-hover-color);
|
||||
background: var(--inputTime-hover-bg-color);
|
||||
}
|
||||
@ -466,6 +466,7 @@
|
||||
|
||||
.#{$ns}TimeContentWrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.#{$ns}TimeRangeHeaderWrapper {
|
||||
@ -473,6 +474,7 @@
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid var(--Calendar-input-borderColor);
|
||||
margin: 0 px2rem(4px);
|
||||
}
|
||||
|
||||
.#{$ns}TimeFooterWrapper {
|
||||
|
@ -25,6 +25,10 @@
|
||||
border-left: 0 !important;
|
||||
}
|
||||
|
||||
> legend.#{$ns}Collapse-header.is-mobile {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.collapse {
|
||||
position: relative;
|
||||
}
|
||||
@ -188,4 +192,13 @@ fieldset.#{$ns}Collapse {
|
||||
top: calc(var(--fieldSet-size-lg-fontSize) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Collapse-header.is-mobile {
|
||||
padding-left: var(--gap-xs) !important;
|
||||
padding-right: px2rem(18px);
|
||||
|
||||
&:hover {
|
||||
background: var(--white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -489,15 +489,21 @@
|
||||
/* 移动端样式调整 */
|
||||
@include media-breakpoint-down(sm) {
|
||||
.#{$ns}Form {
|
||||
// &::before {
|
||||
// position: absolute;
|
||||
// box-sizing: border-box;
|
||||
// content: ' ';
|
||||
// pointer-events: none;
|
||||
// right: 0;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// border-bottom: var(--Form-input-borderWidth) solid var(--borderColor);
|
||||
// }
|
||||
}
|
||||
|
||||
.#{$ns}Combo-form {
|
||||
&::before {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
content: ' ';
|
||||
pointer-events: none;
|
||||
right: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-bottom: var(--Form-input-borderWidth) solid var(--borderColor);
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -612,6 +618,7 @@
|
||||
flex: 1;
|
||||
flex-wrap: wrap;
|
||||
font-size: var(--fontSizeLg);
|
||||
overflow-x: auto;
|
||||
|
||||
&.is-disabled > .#{$ns}TextControl-input {
|
||||
background: transparent;
|
||||
@ -686,6 +693,7 @@
|
||||
border: none;
|
||||
padding: 0 var(--Form-input-paddingX) 0 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
@ -716,9 +724,9 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.#{$ns}Divider {
|
||||
display: none;
|
||||
}
|
||||
// .#{$ns}Divider {
|
||||
// display: none;
|
||||
// }
|
||||
|
||||
.#{$ns}Tabs-pane {
|
||||
padding: 0;
|
||||
@ -727,4 +735,25 @@
|
||||
.#{$ns}Form-item .#{$ns}Form-groupColumn > .#{$ns}Form-item {
|
||||
padding-bottom: var(--Form-input-paddingX);
|
||||
}
|
||||
|
||||
.#{$ns}Form-groupColumn {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
content: ' ';
|
||||
pointer-events: none;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-bottom: var(--Form-input-borderWidth) solid var(--borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Form-groupColumn:nth-last-of-type(1) {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +151,12 @@
|
||||
.#{$ns}Form-static {
|
||||
margin-right: var(--gap-xs);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
.#{$ns}Form-control {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}InputGroup:not(.is-inline) {
|
||||
|
@ -39,6 +39,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&-placeholder {
|
||||
color: var(--colors-neutral-text-6);
|
||||
user-select: none;
|
||||
@ -72,6 +76,27 @@
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-popup {
|
||||
height: px2rem(460px);
|
||||
|
||||
&-inner {
|
||||
flex: 1;
|
||||
padding: 0 px2rem(16px);
|
||||
|
||||
.#{$ns}MapPicker-search {
|
||||
padding-top: 0;
|
||||
margin-bottom: px2rem(16px);
|
||||
|
||||
.#{$ns}TextControl-input {
|
||||
border-radius: 0;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}LocationControl {
|
||||
|
@ -1,4 +1,6 @@
|
||||
.#{$ns}MatrixControl {
|
||||
max-width: 100%;
|
||||
|
||||
&-error {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
@ -71,6 +71,14 @@
|
||||
box-shadow: var(--inputNumber-base-active-shadow);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
border: none;
|
||||
|
||||
&-focused {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
border-width: var(--inputNumber-base-disabled-top-border-width)
|
||||
var(--inputNumber-base-disabled-right-border-width)
|
||||
|
@ -206,3 +206,15 @@
|
||||
min-width: px2rem(150px);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}PickerControl.is-mobile {
|
||||
width: 100%;
|
||||
|
||||
.#{$ns}Form-item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.#{$ns}Picker-input {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
padding: 0;
|
||||
|
||||
.#{$ns}InputRange-input {
|
||||
width: var(--InputRange-input-mobile-width);
|
||||
// margin-left: 0;
|
||||
|
||||
.#{$ns}Number-handler-wrap {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 0 !important;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-clear {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -97,6 +115,11 @@
|
||||
width: var(--InputRange-handle-width);
|
||||
height: var(--InputRange-handle-height);
|
||||
|
||||
&.is-mobile {
|
||||
width: var(--InputRange-handle-mobile-width);
|
||||
height: var(--InputRange-handle-mobile-height);
|
||||
}
|
||||
|
||||
&-icon,
|
||||
&-drage {
|
||||
width: 100%;
|
||||
@ -304,4 +327,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
.#{$ns}InputRange-marks {
|
||||
div {
|
||||
top: px2rem(4px);
|
||||
|
||||
&:nth-child(2n) {
|
||||
top: px2rem(-34px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -436,6 +436,38 @@
|
||||
// display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
position: relative;
|
||||
|
||||
& > a {
|
||||
margin-right: calc(
|
||||
20px + var(--select-base-default-option-paddingRight)
|
||||
);
|
||||
}
|
||||
|
||||
.#{$ns}Select-option-item-check {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
border-bottom: px2rem(1px) solid var(--borderColor);
|
||||
}
|
||||
|
||||
.#{$ns}Select-option-mcheck {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: var(--select-base-default-option-paddingRight);
|
||||
flex: none;
|
||||
width: px2rem(16px);
|
||||
color: var(--Form-select-mobile-icon-check-color);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.#{$ns}Select-option-item-check {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-noResult {
|
||||
|
@ -237,6 +237,16 @@
|
||||
color: var(--transfer-search-placeholder-font-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
.#{$ns}InputBox {
|
||||
border: none;
|
||||
border-bottom: var(--transfer-search-bottom-border-width)
|
||||
var(--transfer-search-bottom-border-style)
|
||||
var(--transfer-search-bottom-border-color);
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-mid {
|
||||
@ -363,6 +373,25 @@
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Transfer-result {
|
||||
.#{$ns}Transfer-title {
|
||||
height: px2rem(40px);
|
||||
line-height: px2rem(40px);
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
.#{$ns}Transfer-search {
|
||||
.#{$ns}InputBox {
|
||||
border: none;
|
||||
border-bottom: var(--transfer-search-bottom-border-width)
|
||||
var(--transfer-search-bottom-border-style)
|
||||
var(--transfer-search-bottom-border-color);
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TabsTransfer {
|
||||
@ -463,6 +492,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TabsTransfer-search {
|
||||
&.is-mobile {
|
||||
.#{$ns}InputBox {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--TabsTransfer-border-color);
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -528,7 +567,9 @@
|
||||
|
||||
&.is-mobile {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
& > .#{$ns}Transfer-selection {
|
||||
flex-grow: 1;
|
||||
max-height: var(--Transfer-selection-maxHeight);
|
||||
|
@ -147,9 +147,10 @@
|
||||
|
||||
&:hover {
|
||||
.#{$ns}Tree {
|
||||
&-itemLabel-item {
|
||||
&-itemLabel-item:not(.is-mobile) {
|
||||
background-color: var(--Tree-item-onHover-bg-pure);
|
||||
}
|
||||
|
||||
&-item-icons {
|
||||
visibility: visible;
|
||||
}
|
||||
@ -255,6 +256,15 @@
|
||||
background: var(--Form-input-onFocused-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
> input {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
border-bottom: var(--Form-input-borderWidth) solid
|
||||
var(--Form-input-borderColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-addTopBtn {
|
||||
@ -398,3 +408,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}PopUp {
|
||||
.#{$ns}Tree {
|
||||
flex: 1;
|
||||
|
||||
.#{$ns}Tree-itemLabel {
|
||||
&:hover {
|
||||
.#{$ns}Tree {
|
||||
&-itemLabel-item {
|
||||
background-color: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,14 @@
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #f9f9f9;
|
||||
|
||||
&.is-mobile-year {
|
||||
width: px2rem(184px);
|
||||
}
|
||||
|
||||
&.is-mobile-embed {
|
||||
width: px2rem(240px);
|
||||
}
|
||||
}
|
||||
|
||||
.rdtPickerNotDays {
|
||||
|
@ -165,15 +165,17 @@ export class BaiduMapPicker extends React.Component<
|
||||
if (poiLength) {
|
||||
for (let i = 0; i < poiLength; i++) {
|
||||
const poi = result.getPoi(i);
|
||||
sugs.push(
|
||||
[
|
||||
poi.province,
|
||||
poi.city,
|
||||
poi.district,
|
||||
poi.street,
|
||||
poi.business
|
||||
].join(' ')
|
||||
);
|
||||
if (poi) {
|
||||
sugs.push(
|
||||
[
|
||||
poi.province,
|
||||
poi.city,
|
||||
poi.district,
|
||||
poi.street,
|
||||
poi.business
|
||||
].join(' ')
|
||||
);
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
sugs
|
||||
@ -288,21 +290,23 @@ export class BaiduMapPicker extends React.Component<
|
||||
if (this.props.coordinatesType == 'gcj02') {
|
||||
this.covertPoint(point, COORDINATES_BD09, COORDINATES_GCJ02).then(
|
||||
(convertedPoint: any) => {
|
||||
(typeof this.props?.onChange === 'function') && this.props.onChange({
|
||||
address: loc.address.trim() || loc.title,
|
||||
lat: convertedPoint.lat,
|
||||
lng: convertedPoint.lng,
|
||||
city: loc.city
|
||||
});
|
||||
typeof this.props?.onChange === 'function' &&
|
||||
this.props.onChange({
|
||||
address: loc.address.trim() || loc.title,
|
||||
lat: convertedPoint.lat,
|
||||
lng: convertedPoint.lng,
|
||||
city: loc.city
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
(typeof this.props?.onChange === 'function') && this.props?.onChange({
|
||||
address: loc.address.trim() || loc.title,
|
||||
lat: loc.lat,
|
||||
lng: loc.lng,
|
||||
city: loc.city
|
||||
});
|
||||
typeof this.props?.onChange === 'function' &&
|
||||
this.props?.onChange({
|
||||
address: loc.address.trim() || loc.title,
|
||||
lat: loc.lat,
|
||||
lng: loc.lng,
|
||||
city: loc.city
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,9 @@ import {themeable, ThemeProps} from 'amis-core';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import {autobind} from 'amis-core';
|
||||
import Button from './Button';
|
||||
import {ShortCuts, ShortCutDateRange} from './DatePicker';
|
||||
import {advancedRanges, availableRanges} from './DateRangePicker';
|
||||
import {noop} from 'amis-core';
|
||||
|
||||
export interface CalendarMobileProps extends ThemeProps, LocaleProps {
|
||||
className?: string;
|
||||
@ -49,6 +52,7 @@ export interface CalendarMobileProps extends ThemeProps, LocaleProps {
|
||||
};
|
||||
};
|
||||
defaultDate?: moment.Moment;
|
||||
ranges?: string | Array<ShortCuts>;
|
||||
}
|
||||
|
||||
export interface CalendarMobileState {
|
||||
@ -281,6 +285,97 @@ export class CalendarMobile extends React.Component<
|
||||
return dow;
|
||||
}
|
||||
|
||||
@autobind
|
||||
renderRanges(ranges: string | Array<ShortCuts> | undefined) {
|
||||
if (!ranges) {
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
classPrefix: ns,
|
||||
embed,
|
||||
minDate,
|
||||
maxDate,
|
||||
confirm,
|
||||
onChange
|
||||
} = this.props;
|
||||
let rangeArr: Array<string | ShortCuts>;
|
||||
if (typeof ranges === 'string') {
|
||||
rangeArr = ranges.split(',');
|
||||
} else {
|
||||
rangeArr = ranges;
|
||||
}
|
||||
const __ = this.props.translate;
|
||||
|
||||
return (
|
||||
<ul className={`${ns}DateRangePicker-rangers`}>
|
||||
{rangeArr.map(item => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
let range: any = {};
|
||||
if (typeof item === 'string') {
|
||||
if (availableRanges[item]) {
|
||||
range = availableRanges[item];
|
||||
range.key = item;
|
||||
} else {
|
||||
// 通过正则尝试匹配
|
||||
for (let i = 0, len = advancedRanges.length; i < len; i++) {
|
||||
let value = advancedRanges[i];
|
||||
const m = value.regexp.exec(item);
|
||||
if (m) {
|
||||
range = value.resolve.apply(item, [__, ...m]);
|
||||
range.key = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
(item as ShortCutDateRange).startDate &&
|
||||
(item as ShortCutDateRange).endDate
|
||||
) {
|
||||
range = {
|
||||
...item,
|
||||
startDate: () => (item as ShortCutDateRange).startDate,
|
||||
endDate: () => (item as ShortCutDateRange).endDate
|
||||
};
|
||||
}
|
||||
if (Object.keys(range).length) {
|
||||
return (
|
||||
<li
|
||||
className={`${ns}DateRangePicker-ranger`}
|
||||
onClick={() => {
|
||||
const now = moment();
|
||||
const startDate =
|
||||
minDate && minDate.isValid()
|
||||
? moment.max(range.startDate(now.clone()), minDate)
|
||||
: range.startDate(now.clone());
|
||||
const endDate =
|
||||
maxDate && maxDate.isValid()
|
||||
? moment.min(maxDate, range.endDate(now.clone()))
|
||||
: range.endDate(now.clone());
|
||||
|
||||
this.setState({
|
||||
startDate,
|
||||
endDate
|
||||
});
|
||||
!embed && onChange && onChange({startDate, endDate}, noop);
|
||||
// 内嵌模式
|
||||
embed &&
|
||||
onChange &&
|
||||
onChange({startDate, endDate}, () => confirm && confirm());
|
||||
}}
|
||||
key={range.key || range.label}
|
||||
>
|
||||
<a>{__(range.label)}</a>
|
||||
</li>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleCalendarClick(isDisabled: boolean) {
|
||||
if (isDisabled) {
|
||||
@ -697,7 +792,8 @@ export class CalendarMobile extends React.Component<
|
||||
footerExtra,
|
||||
timeFormat,
|
||||
showViewMode,
|
||||
isDatePicker
|
||||
isDatePicker,
|
||||
ranges
|
||||
} = this.props;
|
||||
const __ = this.props.translate;
|
||||
|
||||
@ -755,7 +851,7 @@ export class CalendarMobile extends React.Component<
|
||||
{timeFormat && startDate && this.renderMobileTimePicker()}
|
||||
<div className={cx('CalendarMobile-footer-toolbar')}>
|
||||
<div className={cx('CalendarMobile-footer-ranges')}>
|
||||
{footerExtra}
|
||||
{this.renderRanges(ranges)}
|
||||
</div>
|
||||
{confirm && !embed && (
|
||||
<Button
|
||||
|
@ -12,12 +12,15 @@ import compact from 'lodash/compact';
|
||||
import find from 'lodash/find';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import Button from './Button';
|
||||
import Checkbox from './Checkbox';
|
||||
import {flattenTree, findTree, getTreeDepth} from 'amis-core';
|
||||
import type {OptionsControlProps} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
|
||||
export type CascaderOption = {
|
||||
text?: string;
|
||||
value?: string | number;
|
||||
valueField?: string;
|
||||
color?: string;
|
||||
disabled?: boolean;
|
||||
children?: Options;
|
||||
@ -50,12 +53,12 @@ export type CascaderTab = {
|
||||
|
||||
export interface CascaderState {
|
||||
selectedOptions: Options;
|
||||
activeTab: number;
|
||||
tabs: Array<{
|
||||
options: Options;
|
||||
}>;
|
||||
// 用于在只选择子节点模式的时候禁用按钮
|
||||
disableConfirm: boolean;
|
||||
activePaths: CascaderOption[];
|
||||
}
|
||||
|
||||
export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
@ -68,8 +71,8 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
constructor(props: CascaderProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
activePaths: [],
|
||||
selectedOptions: this.props.selectedOptions || [],
|
||||
activeTab: 0,
|
||||
tabs: [
|
||||
{
|
||||
options: this.props.options.slice() || []
|
||||
@ -109,19 +112,9 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
option.children.forEach((option: Option) => (option.disabled = true));
|
||||
}
|
||||
}
|
||||
return multiple && !onlyLeaf
|
||||
? {
|
||||
options: [
|
||||
{
|
||||
...option,
|
||||
isCheckAll: true
|
||||
},
|
||||
...(option.children ? option.children : [])
|
||||
]
|
||||
}
|
||||
: {
|
||||
options: option.children ? option.children : []
|
||||
};
|
||||
return {
|
||||
options: option.children ? option.children : []
|
||||
};
|
||||
});
|
||||
this.setState({
|
||||
selectedOptions,
|
||||
@ -129,15 +122,6 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleTabSelect(index: number) {
|
||||
const tabs = this.state.tabs.slice(0, index + 1);
|
||||
this.setState({
|
||||
activeTab: index,
|
||||
tabs
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
getOptionParent(option: Option) {
|
||||
const {options, valueField = 'value'} = this.props;
|
||||
@ -202,6 +186,23 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
return loop(selectedOptions);
|
||||
}
|
||||
|
||||
// 判断配置onlyChildren属性时节点选中情况
|
||||
@autobind
|
||||
getOnlyChildrenSelect(option: Option, selectedOptions?: Option[]) {
|
||||
const {onlyChildren} = this.props;
|
||||
selectedOptions = selectedOptions || this.state.selectedOptions;
|
||||
|
||||
return (
|
||||
onlyChildren &&
|
||||
option.children
|
||||
?.filter(option => !option.children?.length)
|
||||
.every(
|
||||
option =>
|
||||
!option.children?.length && selectedOptions?.includes(option)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@autobind
|
||||
getSelectedChildNum(option: Option): number {
|
||||
let count = 0;
|
||||
@ -267,9 +268,10 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
let index = selectedOptions.findIndex(
|
||||
(item: Option) => item[valueField] === option[valueField]
|
||||
);
|
||||
let isSelect = this.getOnlyChildrenSelect(option, selectedOptions);
|
||||
if (index !== -1) {
|
||||
selectedOptions.splice(index, 1);
|
||||
} else {
|
||||
} else if (!isSelect) {
|
||||
if (!(onlyChildren && option.children?.length)) {
|
||||
selectedOptions.push(option);
|
||||
}
|
||||
@ -279,7 +281,7 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
return;
|
||||
}
|
||||
option.children.forEach((item: Option) => {
|
||||
if (index !== -1) {
|
||||
if (index !== -1 || isSelect) {
|
||||
// 删除选中节点及其子节点
|
||||
selectedOptions = selectedOptions.filter(
|
||||
(sItem: Option) => sItem[valueField] !== item[valueField]
|
||||
@ -313,22 +315,27 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
valueField = 'value',
|
||||
cascade,
|
||||
onlyLeaf,
|
||||
onlyChildren
|
||||
onlyChildren,
|
||||
withChildren
|
||||
} = this.props;
|
||||
|
||||
let tabs = this.state.tabs.slice();
|
||||
let {activeTab} = this.state;
|
||||
let selectedOptions = this.state.selectedOptions;
|
||||
const isDisable = option.disabled;
|
||||
if (!isDisable) {
|
||||
if (multiple) {
|
||||
// 父子级分离
|
||||
if (cascade) {
|
||||
if (
|
||||
option.isCheckAll ||
|
||||
!option.children ||
|
||||
!option.children.length
|
||||
) {
|
||||
let index = selectedOptions.findIndex(
|
||||
(item: Option) => item[valueField] === option[valueField]
|
||||
);
|
||||
if (index !== -1) {
|
||||
selectedOptions.splice(index, 1);
|
||||
} else {
|
||||
selectedOptions.push(option);
|
||||
}
|
||||
} else {
|
||||
if (withChildren || onlyChildren) {
|
||||
selectedOptions = this.dealChildrenSelect(option, selectedOptions);
|
||||
} else {
|
||||
let index = selectedOptions.findIndex(
|
||||
(item: Option) => item[valueField] === option[valueField]
|
||||
);
|
||||
@ -338,29 +345,58 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
selectedOptions.push(option);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
option.isCheckAll ||
|
||||
!option.children ||
|
||||
!option.children.length
|
||||
) {
|
||||
selectedOptions = this.dealChildrenSelect(option, selectedOptions);
|
||||
if (!onlyChildren) {
|
||||
selectedOptions = this.dealParentSelect(option, selectedOptions);
|
||||
}
|
||||
}
|
||||
selectedOptions = this.dealParentSelect(option, selectedOptions);
|
||||
}
|
||||
} else {
|
||||
// 单选
|
||||
selectedOptions = [option];
|
||||
if (onlyLeaf) {
|
||||
if (!option.children?.length) {
|
||||
selectedOptions = [option];
|
||||
}
|
||||
} else {
|
||||
selectedOptions = [option];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dealOptionDisable(selectedOptions);
|
||||
|
||||
let disableConfirm = false;
|
||||
if (onlyLeaf && selectedOptions.length && selectedOptions[0].children) {
|
||||
disableConfirm = true;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedOptions,
|
||||
disableConfirm
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleExpand(option: Option, tabIndex: number) {
|
||||
const activePaths = this.state.activePaths.slice();
|
||||
|
||||
if (option.children?.length) {
|
||||
activePaths[tabIndex] = option;
|
||||
} else {
|
||||
activePaths.splice(tabIndex);
|
||||
}
|
||||
|
||||
let tabs = this.state.tabs.slice();
|
||||
if (tabs.length > tabIndex + 1) {
|
||||
tabs = tabs.slice(0, tabIndex + 1);
|
||||
}
|
||||
|
||||
if (option?.children) {
|
||||
const nextTab = {
|
||||
options: option.children
|
||||
};
|
||||
if (tabs[tabIndex + 1]) {
|
||||
tabs[tabIndex + 1] = nextTab;
|
||||
} else {
|
||||
tabs.push(nextTab);
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const tabWidth = this.tabRef.current?.offsetWidth || 1;
|
||||
const parentTree = this.getParentTree(option, [option]);
|
||||
@ -370,60 +406,9 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
}
|
||||
});
|
||||
|
||||
if (option?.children && !option.isCheckAll) {
|
||||
const nextTab =
|
||||
multiple && !onlyLeaf
|
||||
? {
|
||||
options: [
|
||||
{
|
||||
...option,
|
||||
isCheckAll: true
|
||||
},
|
||||
...option.children
|
||||
]
|
||||
}
|
||||
: {
|
||||
options: option.children
|
||||
};
|
||||
|
||||
if (tabs[tabIndex + 1]) {
|
||||
tabs[tabIndex + 1] = nextTab;
|
||||
} else {
|
||||
tabs.push(nextTab);
|
||||
}
|
||||
activeTab += 1;
|
||||
}
|
||||
let disableConfirm = false;
|
||||
if (onlyLeaf && selectedOptions.length && selectedOptions[0].children) {
|
||||
disableConfirm = true;
|
||||
}
|
||||
this.setState({
|
||||
tabs,
|
||||
activeTab,
|
||||
selectedOptions,
|
||||
disableConfirm
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
onNextClick(option: CascaderOption, tabIndex: number) {
|
||||
let {activeTab} = this.state;
|
||||
let tabs = this.state.tabs.slice();
|
||||
if (option.c)
|
||||
if (option?.children) {
|
||||
const nextTab = {
|
||||
options: option.children
|
||||
};
|
||||
if (tabs[tabIndex + 1]) {
|
||||
tabs[tabIndex + 1] = nextTab;
|
||||
} else {
|
||||
tabs.push(nextTab);
|
||||
}
|
||||
activeTab += 1;
|
||||
}
|
||||
this.setState({
|
||||
tabs,
|
||||
activeTab
|
||||
activePaths,
|
||||
tabs
|
||||
});
|
||||
}
|
||||
|
||||
@ -462,15 +447,7 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
|
||||
@autobind
|
||||
confirm() {
|
||||
const {
|
||||
onChange,
|
||||
joinValues,
|
||||
delimiter,
|
||||
extractValue,
|
||||
valueField,
|
||||
onClose,
|
||||
onlyLeaf
|
||||
} = this.props;
|
||||
const {onChange, onClose, onlyLeaf} = this.props;
|
||||
const selectedOptions = this.getSelectedOptions();
|
||||
if (onlyLeaf && selectedOptions.length && selectedOptions[0].children) {
|
||||
return;
|
||||
@ -490,15 +467,15 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
@autobind
|
||||
renderOption(option: CascaderOption, tabIndex: number) {
|
||||
const {
|
||||
onlyLeaf,
|
||||
activeColor,
|
||||
optionRender,
|
||||
labelField,
|
||||
valueField = 'value',
|
||||
classnames: cx,
|
||||
cascade,
|
||||
multiple
|
||||
multiple,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
const {selectedOptions} = this.state;
|
||||
const {selectedOptions, activePaths} = this.state;
|
||||
const selectedValueArr = selectedOptions.map(item => item[valueField]);
|
||||
|
||||
let selfChecked = selectedValueArr.includes(option[valueField]);
|
||||
@ -508,21 +485,51 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
) : (
|
||||
<span>{option[labelField]}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'Cascader-option',
|
||||
{
|
||||
selected: selfChecked,
|
||||
disabled: option.disabled
|
||||
'selected': selfChecked,
|
||||
'disabled': option.disabled,
|
||||
'is-active': activePaths.includes(option)
|
||||
},
|
||||
option.className
|
||||
)}
|
||||
style={{color}}
|
||||
onClick={() => this.onSelect(option, tabIndex)}
|
||||
key={tabIndex + '-' + option[valueField]}
|
||||
onClick={() => {
|
||||
!multiple && this.onSelect(option, tabIndex);
|
||||
this.handleExpand(option, tabIndex);
|
||||
}}
|
||||
>
|
||||
<span className={cx('Cascader-option--text')}>{Text}</span>
|
||||
{multiple ? (
|
||||
<Checkbox
|
||||
disabled={option.disabled || (onlyLeaf && option.children?.length)}
|
||||
checked={
|
||||
selectedOptions.includes(option) ||
|
||||
this.getOnlyChildrenSelect(option)
|
||||
}
|
||||
onChange={() => this.onSelect(option, tabIndex)}
|
||||
>
|
||||
<span className={cx('Cascader-option--text')}>{Text}</span>
|
||||
</Checkbox>
|
||||
) : (
|
||||
<span
|
||||
className={cx('Cascader-option--text', {
|
||||
disabled: onlyLeaf && option.children?.length
|
||||
})}
|
||||
>
|
||||
{Text}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{option.children?.length ? (
|
||||
<span className={cx('Cascader-option-arrow')}>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</span>
|
||||
) : null}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@ -551,7 +558,7 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
const {options} = tab;
|
||||
return (
|
||||
<div
|
||||
className={cx(`Cascader-tab`)}
|
||||
className={cx(`Cascader-tab depth-${tabIndex}`)}
|
||||
ref={this.tabRef}
|
||||
key={tabIndex}
|
||||
>
|
||||
@ -563,7 +570,10 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
? Array(getTreeDepth(options) - tabs.length)
|
||||
.fill(1)
|
||||
.map((item: number, index: number) => (
|
||||
<div className={cx(`Cascader-tab`)} key={index}></div>
|
||||
<div
|
||||
className={cx(`Cascader-tab depth-${index + 1}`)}
|
||||
key={index}
|
||||
></div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
|
@ -14,6 +14,7 @@ import Transition, {
|
||||
import {autobind} from 'amis-core';
|
||||
import {isClickOnInput} from 'amis-core';
|
||||
import {TranslateFn} from 'amis-core';
|
||||
import {isMobile} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
|
||||
const collapseStyles: {
|
||||
@ -54,6 +55,7 @@ export interface CollapseProps {
|
||||
propsUpdate?: boolean;
|
||||
partial?: boolean;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface CollapseState {
|
||||
@ -170,19 +172,25 @@ export class Collapse extends React.Component<CollapseProps, CollapseState> {
|
||||
showArrow,
|
||||
expandIcon,
|
||||
disabled,
|
||||
children
|
||||
children,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const finalHeader = this.state.collapsed
|
||||
? header
|
||||
: collapseHeader || header;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
let dom = [
|
||||
finalHeader ? (
|
||||
<HeadingComponent
|
||||
key="header"
|
||||
onClick={this.toggleCollapsed}
|
||||
className={cx(`Collapse-header`, headingClassName)}
|
||||
className={cx(
|
||||
`Collapse-header`,
|
||||
{'is-mobile': mobileUI},
|
||||
headingClassName
|
||||
)}
|
||||
>
|
||||
{showArrow && collapsable ? (
|
||||
expandIcon ? (
|
||||
@ -249,6 +257,7 @@ export class Collapse extends React.Component<CollapseProps, CollapseState> {
|
||||
className={cx(
|
||||
`Collapse`,
|
||||
{
|
||||
'is-mobile': mobileUI,
|
||||
'is-active': !this.state.collapsed,
|
||||
[`Collapse--${size}`]: size,
|
||||
'Collapse--disabled': disabled,
|
||||
|
@ -13,6 +13,7 @@ export interface CollapseItem {
|
||||
|
||||
import {ClassNamesFn, themeable, autobind} from 'amis-core';
|
||||
import type {SchemaNode} from 'amis-core';
|
||||
import {isMobile} from 'amis-core';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
export interface CollapseGroupProps {
|
||||
@ -26,6 +27,7 @@ export interface CollapseGroupProps {
|
||||
classnames: ClassNamesFn;
|
||||
classPrefix: string;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface CollapseGroupState {
|
||||
@ -136,8 +138,10 @@ class CollapseGroup extends React.Component<
|
||||
className,
|
||||
style,
|
||||
expandIconPosition,
|
||||
children
|
||||
children,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -146,6 +150,9 @@ class CollapseGroup extends React.Component<
|
||||
{
|
||||
'icon-position-right': expandIconPosition === 'right'
|
||||
},
|
||||
{
|
||||
'is-mobile': mobileUI
|
||||
},
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
|
@ -40,6 +40,7 @@ export interface ColorControlState {
|
||||
isOpened: boolean;
|
||||
isFocused: boolean;
|
||||
inputValue: string;
|
||||
tempValue: string;
|
||||
}
|
||||
|
||||
export class ColorControl extends React.PureComponent<
|
||||
@ -56,7 +57,8 @@ export class ColorControl extends React.PureComponent<
|
||||
state = {
|
||||
isOpened: false,
|
||||
isFocused: false,
|
||||
inputValue: this.props.value || ''
|
||||
inputValue: this.props.value || '',
|
||||
tempValue: this.props.value || ''
|
||||
};
|
||||
popover: any;
|
||||
closeTimer: number;
|
||||
@ -70,6 +72,8 @@ export class ColorControl extends React.PureComponent<
|
||||
this.focus = this.focus.bind(this);
|
||||
this.blur = this.blur.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleTempChange = this.handleTempChange.bind(this);
|
||||
this.handleConfirm = this.handleConfirm.bind(this);
|
||||
this.handleFocus = this.handleFocus.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.clearValue = this.clearValue.bind(this);
|
||||
@ -206,6 +210,31 @@ export class ColorControl extends React.PureComponent<
|
||||
// closeOnSelect && this.close();
|
||||
}
|
||||
|
||||
handleTempChange(color: ColorResult) {
|
||||
let {tempValue} = this.state;
|
||||
const {format} = this.props;
|
||||
|
||||
if (format === 'rgba') {
|
||||
tempValue = `rgba(${color.rgb.r}, ${color.rgb.g}, ${color.rgb.b}, ${color.rgb.a})`;
|
||||
} else if (format === 'rgb') {
|
||||
tempValue = `rgb(${color.rgb.r}, ${color.rgb.g}, ${color.rgb.b})`;
|
||||
} else if (format === 'hsl') {
|
||||
tempValue = `hsl(${Math.round(color.hsl.h)}, ${Math.round(
|
||||
color.hsl.s * 100
|
||||
)}%, ${Math.round(color.hsl.l * 100)}%)`;
|
||||
} else {
|
||||
tempValue = color.hex;
|
||||
}
|
||||
this.setState({tempValue});
|
||||
}
|
||||
|
||||
handleConfirm() {
|
||||
const {onChange} = this.props;
|
||||
const {tempValue} = this.state;
|
||||
onChange(tempValue);
|
||||
this.close();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
classPrefix: ns,
|
||||
@ -227,6 +256,7 @@ export class ColorControl extends React.PureComponent<
|
||||
const __ = this.props.translate;
|
||||
const isOpened = this.state.isOpened;
|
||||
const isFocused = this.state.isFocused;
|
||||
const tempValue = this.state.tempValue;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
@ -328,18 +358,20 @@ export class ColorControl extends React.PureComponent<
|
||||
container={popOverContainer}
|
||||
isShow={isOpened}
|
||||
onHide={this.handleClick}
|
||||
showConfirm
|
||||
onConfirm={this.handleConfirm}
|
||||
>
|
||||
{allowCustomColor ? (
|
||||
<SketchPicker
|
||||
styles={{}}
|
||||
disableAlpha={!!~['rgb', 'hex'].indexOf(format as string)}
|
||||
color={value}
|
||||
color={tempValue}
|
||||
presetColors={presetColors}
|
||||
onChangeComplete={this.handleChange}
|
||||
onChangeComplete={this.handleTempChange}
|
||||
/>
|
||||
) : (
|
||||
<GithubPicker
|
||||
color={value}
|
||||
color={tempValue}
|
||||
colors={
|
||||
Array.isArray(presetColors)
|
||||
? (presetColors
|
||||
@ -355,7 +387,7 @@ export class ColorControl extends React.PureComponent<
|
||||
) as string[])
|
||||
: undefined
|
||||
}
|
||||
onChangeComplete={this.handleChange}
|
||||
onChangeComplete={this.handleTempChange}
|
||||
/>
|
||||
)}
|
||||
</PopUp>
|
||||
|
@ -2,8 +2,15 @@ import React from 'react';
|
||||
import Modal from './Modal';
|
||||
import Button from './Button';
|
||||
import Drawer from './Drawer';
|
||||
import {localeable, LocaleProps, themeable, ThemeProps} from 'amis-core';
|
||||
import {
|
||||
localeable,
|
||||
LocaleProps,
|
||||
themeable,
|
||||
ThemeProps,
|
||||
isMobile
|
||||
} from 'amis-core';
|
||||
import Spinner from './Spinner';
|
||||
import PopUp from './PopUp';
|
||||
|
||||
export interface ConfirmBoxProps extends LocaleProps, ThemeProps {
|
||||
show?: boolean;
|
||||
@ -34,6 +41,7 @@ export interface ConfirmBoxProps extends LocaleProps, ThemeProps {
|
||||
headerClassName?: string;
|
||||
bodyClassName?: string;
|
||||
footerClassName?: string;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export function ConfirmBox({
|
||||
@ -56,7 +64,8 @@ export function ConfirmBox({
|
||||
classnames: cx,
|
||||
className,
|
||||
bodyClassName,
|
||||
footerClassName
|
||||
footerClassName,
|
||||
useMobileUI
|
||||
}: ConfirmBoxProps) {
|
||||
const [loading, setLoading] = React.useState<boolean>();
|
||||
const [error, setError] = React.useState<string>();
|
||||
@ -90,7 +99,22 @@ export function ConfirmBox({
|
||||
}, [show]);
|
||||
|
||||
function renderDialog() {
|
||||
return (
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
return mobileUI ? (
|
||||
<PopUp
|
||||
isShow={show}
|
||||
showConfirm
|
||||
onConfirm={handleConfirm}
|
||||
onHide={onCancel}
|
||||
>
|
||||
{typeof children === 'function'
|
||||
? children({
|
||||
bodyRef: bodyRef,
|
||||
loading
|
||||
})
|
||||
: children}
|
||||
</PopUp>
|
||||
) : (
|
||||
<Modal
|
||||
size={size}
|
||||
closeOnEsc={closeOnEsc}
|
||||
|
@ -1019,7 +1019,8 @@ export class DateRangePicker extends React.Component<
|
||||
}
|
||||
|
||||
selectRannge(range: PlainObject) {
|
||||
const {closeOnSelect, minDate, maxDate} = this.props;
|
||||
const {closeOnSelect, minDate, maxDate, useMobileUI} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
const now = moment();
|
||||
this.setState(
|
||||
{
|
||||
@ -1032,7 +1033,7 @@ export class DateRangePicker extends React.Component<
|
||||
? moment.min(maxDate, range.endDate(now.clone()))
|
||||
: range.endDate(now.clone())
|
||||
},
|
||||
closeOnSelect ? this.confirm : noop
|
||||
closeOnSelect && !mobileUI ? this.confirm : noop
|
||||
);
|
||||
}
|
||||
|
||||
@ -1330,7 +1331,9 @@ export class DateRangePicker extends React.Component<
|
||||
locale,
|
||||
embed,
|
||||
type,
|
||||
viewMode = 'days'
|
||||
viewMode = 'days',
|
||||
label,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const __ = this.props.translate;
|
||||
const {startDate, endDate, editState} = this.state;
|
||||
@ -1348,12 +1351,48 @@ export class DateRangePicker extends React.Component<
|
||||
!endDate ||
|
||||
!startDate?.isValid() ||
|
||||
!endDate?.isValid()));
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div className={cx(`${ns}DateRangePicker-wrap`)} ref={this.calendarRef}>
|
||||
<div
|
||||
className={cx(`${ns}DateRangePicker-wrap`, {'is-mobile': mobileUI})}
|
||||
ref={this.calendarRef}
|
||||
>
|
||||
{mobileUI && !embed ? (
|
||||
<div className={cx('PickerColumns-header')}>
|
||||
{
|
||||
<Button
|
||||
className="PickerColumns-cancel"
|
||||
level="link"
|
||||
onClick={() => this.close(false)}
|
||||
>
|
||||
{__('cancel')}
|
||||
</Button>
|
||||
}
|
||||
{label && typeof label === 'string'
|
||||
? label
|
||||
: __('Calendar.datepicker')}
|
||||
{
|
||||
<Button
|
||||
className="PickerColumns-confirm"
|
||||
level="link"
|
||||
disabled={isConfirmBtnDisbaled || !startDate || !endDate}
|
||||
onClick={this.confirm}
|
||||
>
|
||||
{__('confirm')}
|
||||
</Button>
|
||||
}
|
||||
</div>
|
||||
) : null}
|
||||
{this.renderRanges(ranges)}
|
||||
<div className={cx(`${ns}DateRangePicker-picker-wrap`)}>
|
||||
{(!isTimeRange || (editState === 'start' && !embed)) && (
|
||||
<div
|
||||
className={cx(`${ns}DateRangePicker-picker-wrap`, {
|
||||
'is-vertical': embed
|
||||
})}
|
||||
>
|
||||
{(!isTimeRange ||
|
||||
(editState === 'start' && !embed) ||
|
||||
(mobileUI && isTimeRange)) && (
|
||||
<Calendar
|
||||
className={`${ns}DateRangePicker-start`}
|
||||
value={startDate}
|
||||
@ -1380,9 +1419,12 @@ export class DateRangePicker extends React.Component<
|
||||
renderYear={this.renderYear}
|
||||
locale={locale}
|
||||
timeRangeHeader="开始时间"
|
||||
embed={embed}
|
||||
/>
|
||||
)}
|
||||
{(!isTimeRange || (editState === 'end' && !embed)) && (
|
||||
{(!isTimeRange ||
|
||||
(editState === 'end' && !embed) ||
|
||||
(mobileUI && isTimeRange)) && (
|
||||
<Calendar
|
||||
className={`${ns}DateRangePicker-end`}
|
||||
value={endDate}
|
||||
@ -1409,11 +1451,12 @@ export class DateRangePicker extends React.Component<
|
||||
renderYear={this.renderYear}
|
||||
locale={locale}
|
||||
timeRangeHeader="结束时间"
|
||||
embed={embed}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{embed ? null : (
|
||||
{embed || mobileUI ? null : (
|
||||
<div key="button" className={`${ns}DateRangePicker-actions`}>
|
||||
{/* this.close 这里不可以传参 */}
|
||||
<Button size="sm" onClick={() => this.close()}>
|
||||
@ -1571,18 +1614,23 @@ export class DateRangePicker extends React.Component<
|
||||
close={this.close}
|
||||
confirm={this.confirm}
|
||||
onChange={this.handleMobileChange}
|
||||
footerExtra={this.renderRanges(ranges)}
|
||||
ranges={ranges}
|
||||
showViewMode={
|
||||
viewMode === 'quarters' || viewMode === 'months' ? 'years' : 'months'
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
if (embed) {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
`${ns}DateRangeCalendar`,
|
||||
{
|
||||
'is-mobile': mobileUI
|
||||
},
|
||||
{
|
||||
'is-disabled': disabled
|
||||
},
|
||||
@ -1686,7 +1734,8 @@ export class DateRangePicker extends React.Component<
|
||||
`${ns}CalendarMobile-pop--${viewMode}`
|
||||
)}
|
||||
onHide={this.close}
|
||||
header={CalendarMobileTitle}
|
||||
showClose={false}
|
||||
// header={CalendarMobileTitle}
|
||||
>
|
||||
{useCalendarMobile ? calendarMobile : this.renderCalendar()}
|
||||
</PopUp>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {ThemeProps, themeable} from 'amis-core';
|
||||
import {ThemeProps, themeable, isMobile} from 'amis-core';
|
||||
import Input from './Input';
|
||||
import {autobind, ucFirst} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
@ -18,6 +18,7 @@ export interface InputBoxProps
|
||||
prefix?: JSX.Element;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
borderMode?: 'full' | 'half' | 'none';
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface InputBoxState {
|
||||
@ -83,13 +84,16 @@ export class InputBox extends React.Component<InputBoxProps, InputBoxState> {
|
||||
children,
|
||||
borderMode,
|
||||
onClick,
|
||||
useMobileUI,
|
||||
...rest
|
||||
} = this.props;
|
||||
const isFocused = this.state.isFocused;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx('InputBox', className, {
|
||||
'is-mobile': mobileUI,
|
||||
'is-focused': isFocused,
|
||||
'is-disabled': disabled,
|
||||
'is-error': hasError,
|
||||
|
@ -20,6 +20,7 @@ export interface InputBoxWithSuggestionProps extends ThemeProps, LocaleProps {
|
||||
hasError?: boolean;
|
||||
placeholder?: string;
|
||||
clearable?: boolean;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
const option2value = (item: any) => item.value;
|
||||
@ -65,7 +66,8 @@ export class InputBoxWithSuggestion extends React.Component<InputBoxWithSuggesti
|
||||
searchable,
|
||||
popOverContainer,
|
||||
clearable,
|
||||
hasError
|
||||
hasError,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const options = this.filterOptions(
|
||||
Array.isArray(this.props.options) ? this.props.options : []
|
||||
@ -103,6 +105,7 @@ export class InputBoxWithSuggestion extends React.Component<InputBoxWithSuggesti
|
||||
clearable={clearable}
|
||||
onClick={onClick}
|
||||
hasError={hasError}
|
||||
useMobileUI={useMobileUI}
|
||||
>
|
||||
<span className={cx('InputBox-caret')}>
|
||||
<Icon icon="caret" className="icon" />
|
||||
|
@ -161,7 +161,7 @@ export function InputTable({
|
||||
|
||||
function renderBody() {
|
||||
return (
|
||||
<div className={cx(`Table`, className)}>
|
||||
<div className={cx(`Table`)}>
|
||||
<div className={cx(`Table-contentWrap`)}>
|
||||
<table className={cx(`Table-table`)}>
|
||||
<thead>
|
||||
|
@ -2,6 +2,7 @@ import {ThemeProps, themeable} from 'amis-core';
|
||||
import React from 'react';
|
||||
import {Options, Option} from 'amis-core';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
export interface ListMenuProps extends ThemeProps, LocaleProps {
|
||||
options: Options;
|
||||
@ -14,6 +15,7 @@ export interface ListMenuProps extends ThemeProps, LocaleProps {
|
||||
getItemProps: (props: {item: Option; index: number}) => any;
|
||||
prefix?: JSX.Element;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
interface RenderResult {
|
||||
@ -36,6 +38,7 @@ export class ListMenu extends React.Component<ListMenuProps> {
|
||||
getItemProps,
|
||||
highlightIndex,
|
||||
selectedOptions,
|
||||
useMobileUI,
|
||||
onSelect
|
||||
} = this.props;
|
||||
|
||||
@ -86,11 +89,20 @@ export class ListMenu extends React.Component<ListMenuProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {classnames: cx, options, placeholder, prefix, children} = this.props;
|
||||
const {
|
||||
classnames: cx,
|
||||
options,
|
||||
placeholder,
|
||||
prefix,
|
||||
children,
|
||||
useMobileUI,
|
||||
selectedOptions
|
||||
} = this.props;
|
||||
const __ = this.props.translate;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div className={cx('ListMenu')}>
|
||||
<div className={cx('ListMenu', {'is-mobile': mobileUI})}>
|
||||
{prefix}
|
||||
{Array.isArray(options) && options.length ? (
|
||||
options.reduce(
|
||||
|
@ -8,6 +8,8 @@ import Alert2 from './Alert2';
|
||||
import BaiduMapPicker from './BaiduMapPicker';
|
||||
import GaodeMapPicker from './GaodeMapPicker';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import {isMobile} from 'amis-core';
|
||||
import PopUp from './PopUp';
|
||||
|
||||
export interface LocationProps extends ThemeProps, LocaleProps {
|
||||
vendor: 'baidu' | 'gaode' | 'tenxun';
|
||||
@ -26,6 +28,7 @@ export interface LocationProps extends ThemeProps, LocaleProps {
|
||||
popoverClassName?: string;
|
||||
onChange: (value: any) => void;
|
||||
popOverContainer?: any;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface LocationState {
|
||||
@ -42,6 +45,7 @@ export class LocationPicker extends React.Component<
|
||||
clearable: false
|
||||
};
|
||||
domRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
tempValue: any;
|
||||
state = {
|
||||
isFocused: false,
|
||||
isOpened: false
|
||||
@ -93,6 +97,7 @@ export class LocationPicker extends React.Component<
|
||||
},
|
||||
fn
|
||||
);
|
||||
this.tempValue = this.props.value;
|
||||
}
|
||||
|
||||
@autobind
|
||||
@ -127,6 +132,23 @@ export class LocationPicker extends React.Component<
|
||||
this.props.onChange(value);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleTempChange(value: any) {
|
||||
if (value) {
|
||||
value = {
|
||||
...value,
|
||||
vendor: this.props.vendor
|
||||
};
|
||||
}
|
||||
this.tempValue = value;
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleConfirm() {
|
||||
this.props.onChange(this.tempValue);
|
||||
this.close();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
classnames: cx,
|
||||
@ -139,10 +161,12 @@ export class LocationPicker extends React.Component<
|
||||
popOverContainer,
|
||||
vendor,
|
||||
coordinatesType,
|
||||
ak
|
||||
ak,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const __ = this.props.translate;
|
||||
const {isFocused, isOpened} = this.state;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
const picker = (() => {
|
||||
switch (vendor) {
|
||||
@ -165,9 +189,9 @@ export class LocationPicker extends React.Component<
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (<Alert2>{__(`${vendor} 地图控件不支持`, {vendor})}</Alert2>);
|
||||
return <Alert2>{__(`${vendor} 地图控件不支持`, {vendor})}</Alert2>;
|
||||
}
|
||||
})()
|
||||
})();
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -178,6 +202,7 @@ export class LocationPicker extends React.Component<
|
||||
className={cx(
|
||||
`LocationPicker`,
|
||||
{
|
||||
'is-mobile': mobileUI,
|
||||
'is-disabled': disabled,
|
||||
'is-focused': isFocused,
|
||||
'is-active': isOpened
|
||||
@ -205,22 +230,55 @@ export class LocationPicker extends React.Component<
|
||||
<Icon icon="location" className="icon" />
|
||||
</a>
|
||||
|
||||
<Overlay
|
||||
target={this.getTarget}
|
||||
container={popOverContainer || this.getParent}
|
||||
rootClose={false}
|
||||
show={isOpened}
|
||||
>
|
||||
<PopOver
|
||||
className={cx('LocationPicker-popover', popoverClassName)}
|
||||
{mobileUI ? (
|
||||
<PopUp
|
||||
className={cx(`LocationPicker-popup`)}
|
||||
container={popOverContainer || this.getParent}
|
||||
isShow={isOpened}
|
||||
onHide={this.close}
|
||||
overlay
|
||||
onClick={this.handlePopOverClick}
|
||||
style={{width: this.getTarget()?.offsetWidth}}
|
||||
showConfirm
|
||||
onConfirm={this.handleConfirm}
|
||||
>
|
||||
{picker}
|
||||
</PopOver>
|
||||
</Overlay>
|
||||
<div className={cx('LocationPicker-popup-inner')}>
|
||||
{vendor === 'baidu' ? (
|
||||
<BaiduMapPicker
|
||||
ak={ak}
|
||||
value={value}
|
||||
coordinatesType={coordinatesType}
|
||||
onChange={this.handleTempChange}
|
||||
/>
|
||||
) : (
|
||||
<Alert2>{__('${vendor} 地图控件不支持', {vendor})}</Alert2>
|
||||
)}
|
||||
</div>
|
||||
</PopUp>
|
||||
) : (
|
||||
<Overlay
|
||||
target={this.getTarget}
|
||||
container={popOverContainer || this.getParent}
|
||||
rootClose={false}
|
||||
show={isOpened}
|
||||
>
|
||||
<PopOver
|
||||
className={cx('LocationPicker-popover', popoverClassName)}
|
||||
onHide={this.close}
|
||||
overlay
|
||||
onClick={this.handlePopOverClick}
|
||||
style={{width: this.getTarget()?.offsetWidth}}
|
||||
>
|
||||
{vendor === 'baidu' ? (
|
||||
<BaiduMapPicker
|
||||
ak={ak}
|
||||
value={value}
|
||||
coordinatesType={coordinatesType}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
) : (
|
||||
<Alert2>{__('${vendor} 地图控件不支持', {vendor})}</Alert2>
|
||||
)}
|
||||
</PopOver>
|
||||
</Overlay>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -264,6 +264,22 @@ export class Modal extends React.Component<ModalProps, ModalState> {
|
||||
this.isRootClosed && !e.defaultPrevented && onHide(e);
|
||||
}
|
||||
|
||||
getZIndex() {
|
||||
let defaultIndex = 1400;
|
||||
const {classnames: cx} = this.props;
|
||||
const modals = document.querySelectorAll('.' + cx('Modal'));
|
||||
const popups = document.querySelectorAll('.' + cx('PopUp'));
|
||||
[...modals, ...popups].forEach((node: HTMLDialogElement) => {
|
||||
const style = getComputedStyle(node);
|
||||
const curIndex = parseInt(style.zIndex, 10);
|
||||
if (curIndex > defaultIndex) {
|
||||
defaultIndex = curIndex;
|
||||
}
|
||||
});
|
||||
|
||||
return defaultIndex;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
@ -301,6 +317,7 @@ export class Modal extends React.Component<ModalProps, ModalState> {
|
||||
},
|
||||
className
|
||||
)}
|
||||
style={{zIndex: this.getZIndex()}}
|
||||
>
|
||||
{overlay ? (
|
||||
<div className={cx(`Modal-overlay`, fadeStyles[status])} />
|
||||
|
@ -10,7 +10,14 @@ import getMiniDecimal, {
|
||||
} from '@rc-component/mini-decimal';
|
||||
|
||||
import {Icon} from './icons';
|
||||
import {ThemeProps, themeable, isNumeric, autobind, ucFirst} from 'amis-core';
|
||||
import {
|
||||
ThemeProps,
|
||||
themeable,
|
||||
isNumeric,
|
||||
autobind,
|
||||
ucFirst,
|
||||
isMobile
|
||||
} from 'amis-core';
|
||||
|
||||
export type ValueType = string | number;
|
||||
|
||||
@ -74,6 +81,7 @@ export interface NumberProps extends ThemeProps {
|
||||
* 数字输入框类名
|
||||
*/
|
||||
inputControlClassName?: string;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface NumberState {
|
||||
@ -304,11 +312,13 @@ export class NumberInput extends React.Component<NumberProps, NumberState> {
|
||||
displayMode,
|
||||
inputRef,
|
||||
keyboard,
|
||||
inputControlClassName
|
||||
inputControlClassName,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const precisionProps: any = {
|
||||
precision: NumberInput.normalizePrecision(precision, step)
|
||||
};
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<InputNumber
|
||||
@ -320,6 +330,9 @@ export class NumberInput extends React.Component<NumberProps, NumberState> {
|
||||
: inputControlClassName,
|
||||
{
|
||||
[`Number--border${ucFirst(borderMode)}`]: borderMode
|
||||
},
|
||||
{
|
||||
'is-mobile': mobileUI
|
||||
}
|
||||
)}
|
||||
ref={inputRef}
|
||||
@ -353,9 +366,11 @@ export class NumberInput extends React.Component<NumberProps, NumberState> {
|
||||
borderMode,
|
||||
readOnly,
|
||||
displayMode,
|
||||
inputControlClassName
|
||||
inputControlClassName,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
return (
|
||||
<>
|
||||
{displayMode === 'enhance' ? (
|
||||
|
@ -28,7 +28,7 @@ export interface PickerColumnItem {
|
||||
visibleItemCount?: number;
|
||||
itemHeight?: number;
|
||||
options?: PickerOption[];
|
||||
optionRender?: (option: string | object | PickerOption) => React.ReactNode;
|
||||
optionRender?: (...params: any) => React.ReactNode;
|
||||
onChange?: (
|
||||
value?: PickerOption | string,
|
||||
index?: number,
|
||||
@ -319,7 +319,7 @@ const PickerColumn = forwardRef<{}, PickerColumnProps>((props, ref) => {
|
||||
return (
|
||||
<li {...data} ref={menuItemRef}>
|
||||
{props.optionRender ? (
|
||||
props.optionRender(option)
|
||||
props.optionRender(option, {index, checked: state.index === index})
|
||||
) : (
|
||||
<div {...childData} />
|
||||
)}
|
||||
|
@ -33,6 +33,7 @@ export interface PickerContainerProps
|
||||
|
||||
onPickerOpen?: (props: PickerContainerProps) => any;
|
||||
popOverContainer?: any;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface PickerContainerState {
|
||||
@ -133,7 +134,8 @@ export class PickerContainer extends React.Component<
|
||||
size,
|
||||
showFooter,
|
||||
closeOnEsc,
|
||||
popOverContainer
|
||||
popOverContainer,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
return (
|
||||
<>
|
||||
@ -156,6 +158,7 @@ export class PickerContainer extends React.Component<
|
||||
showFooter={showFooter}
|
||||
beforeConfirm={this.confirm}
|
||||
popOverContainer={popOverContainer}
|
||||
useMobileUI={useMobileUI}
|
||||
>
|
||||
{() =>
|
||||
popOverRender({
|
||||
|
@ -24,12 +24,14 @@ export interface PopOverContainerProps {
|
||||
placement?: string;
|
||||
overlayWidth?: number | string;
|
||||
overlayWidthField?: 'minWidth' | 'width';
|
||||
showConfirm?: boolean;
|
||||
// 相当于 placement 的简化版
|
||||
align?: OverlayAlignType;
|
||||
/** Popover层隐藏前触发的事件 */
|
||||
onBeforeHide?: () => void;
|
||||
/** Popover层隐藏后触发的事件 */
|
||||
onAfterHide?: () => void;
|
||||
onConfirm?: () => void;
|
||||
}
|
||||
|
||||
export interface PopOverContainerState {
|
||||
@ -91,6 +93,12 @@ export class PopOverContainer extends React.Component<
|
||||
return this.getTarget()?.parentElement;
|
||||
}
|
||||
|
||||
@autobind
|
||||
onConfirm() {
|
||||
this.props.onConfirm?.();
|
||||
this.close();
|
||||
}
|
||||
|
||||
static calcOverlayWidth(overlay: PopOverOverlay, targetWidth: number) {
|
||||
const overlayWidth = overlay && overlay.width;
|
||||
|
||||
@ -142,7 +150,9 @@ export class PopOverContainer extends React.Component<
|
||||
popOverClassName,
|
||||
popOverRender: dropdownRender,
|
||||
placement,
|
||||
align
|
||||
align,
|
||||
showConfirm,
|
||||
onConfirm
|
||||
} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
return (
|
||||
@ -157,7 +167,9 @@ export class PopOverContainer extends React.Component<
|
||||
isShow={this.state.isOpened}
|
||||
container={popOverContainer}
|
||||
className={popOverClassName}
|
||||
showConfirm={showConfirm}
|
||||
onHide={this.close}
|
||||
onConfirm={this.onConfirm}
|
||||
>
|
||||
{dropdownRender({onClose: this.close})}
|
||||
</PopUp>
|
||||
|
@ -71,6 +71,22 @@ export class PopUp extends React.PureComponent<PopUpPorps> {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
getZIndex() {
|
||||
let defaultIndex = 3000;
|
||||
const {classnames: cx} = this.props;
|
||||
const modals = document.querySelectorAll('.' + cx('Modal'));
|
||||
const popups = document.querySelectorAll('.' + cx('PopUp'));
|
||||
[...modals, ...popups].forEach((node: HTMLDialogElement) => {
|
||||
const style = getComputedStyle(node);
|
||||
const curIndex = parseInt(style.zIndex, 10);
|
||||
if (curIndex > defaultIndex) {
|
||||
defaultIndex = curIndex;
|
||||
}
|
||||
});
|
||||
|
||||
return defaultIndex;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
style,
|
||||
@ -93,7 +109,8 @@ export class PopUp extends React.PureComponent<PopUpPorps> {
|
||||
} = this.props;
|
||||
|
||||
const outerStyle: any = {
|
||||
...style
|
||||
...style,
|
||||
zIndex: this.getZIndex()
|
||||
};
|
||||
delete outerStyle.top;
|
||||
return (
|
||||
|
@ -17,6 +17,7 @@ import type {ThemeProps} from 'amis-core';
|
||||
import {themeable} from 'amis-core';
|
||||
import {autobind, camel} from 'amis-core';
|
||||
import {stripNumber} from 'amis-core';
|
||||
import {isMobile} from 'amis-core';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import {Icon} from './icons';
|
||||
|
||||
@ -54,6 +55,7 @@ interface HandleItemProps extends ThemeProps {
|
||||
tipFormatter?: (value: Value) => boolean;
|
||||
unit?: string;
|
||||
tooltipPlacement?: string;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
interface LabelProps extends ThemeProps {
|
||||
@ -166,6 +168,35 @@ class HandleItem extends React.Component<HandleItemProps, HandleItemState> {
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
onTouchStart() {
|
||||
this.setState({
|
||||
isDrag: true,
|
||||
labelActive: true
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
onTouchMove(e: any) {
|
||||
const {isDrag} = this.state;
|
||||
const {type = 'min'} = this.props;
|
||||
if (!isDrag) {
|
||||
return;
|
||||
}
|
||||
this.props.onChange(e.touches[0].clientX, type);
|
||||
}
|
||||
|
||||
@autobind
|
||||
onTouchEnd() {
|
||||
const {isDrag} = this.state;
|
||||
if (isDrag) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
labelActive: false
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
classnames: cx,
|
||||
@ -176,7 +207,8 @@ class HandleItem extends React.Component<HandleItemProps, HandleItemState> {
|
||||
tooltipVisible,
|
||||
tipFormatter,
|
||||
unit,
|
||||
tooltipPlacement = 'auto'
|
||||
tooltipPlacement = 'auto',
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const {isDrag, labelActive} = this.state;
|
||||
const style = {
|
||||
@ -184,6 +216,8 @@ class HandleItem extends React.Component<HandleItemProps, HandleItemState> {
|
||||
zIndex: isDrag ? 2 : 1
|
||||
};
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return disabled ? (
|
||||
<div className={cx('InputRange-handle')} style={style}>
|
||||
<div className={cx('InputRange-handle-icon')}>
|
||||
@ -192,7 +226,9 @@ class HandleItem extends React.Component<HandleItemProps, HandleItemState> {
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cx('InputRange-handle')}
|
||||
className={cx('InputRange-handle', {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
style={style}
|
||||
ref={this.handleRef}
|
||||
>
|
||||
@ -203,6 +239,9 @@ class HandleItem extends React.Component<HandleItemProps, HandleItemState> {
|
||||
onMouseDown={this.onMouseDown}
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
onTouchStart={this.onTouchStart}
|
||||
onTouchMove={this.onTouchMove}
|
||||
onTouchEnd={this.onTouchEnd}
|
||||
>
|
||||
<Icon icon="slider-handle" className="icon" />
|
||||
</div>
|
||||
@ -277,6 +316,7 @@ class Label extends React.Component<LabelProps, any> {
|
||||
// @todo 丰富这个
|
||||
export interface RangeItemProps extends ThemeProps {
|
||||
[propName: string]: any;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export class Range extends React.Component<RangeItemProps, any> {
|
||||
@ -443,13 +483,15 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
* @returns
|
||||
*/
|
||||
@autobind
|
||||
getOffsetLeft(value: number | string) {
|
||||
getOffsetLeft(value: number | string, getNumber?: boolean) {
|
||||
const {max, min} = this.props;
|
||||
if (isString(value) && MARKS_REG.test(value)) {
|
||||
return value;
|
||||
return getNumber ? parseFloat(value) : value;
|
||||
}
|
||||
value = Math.min(Math.max(+value, min), max);
|
||||
return ((value - min) * 100) / (max - min) + '%';
|
||||
return getNumber
|
||||
? ((value - min) * 100) / (max - min)
|
||||
: ((value - min) * 100) / (max - min) + '%';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -489,7 +531,8 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
unit,
|
||||
tooltipPlacement,
|
||||
tipFormatter,
|
||||
onAfterChange
|
||||
onAfterChange,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
// trace
|
||||
@ -520,6 +563,14 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
};
|
||||
}
|
||||
|
||||
const sortMaks = marks
|
||||
? keys(marks).sort(
|
||||
(a: keyof MarksType, b: keyof MarksType) =>
|
||||
(this.getOffsetLeft(a, true) as number) -
|
||||
(this.getOffsetLeft(b, true) as number)
|
||||
)
|
||||
: [];
|
||||
|
||||
return (
|
||||
<div className={cx('InputRange-wrap')}>
|
||||
<div
|
||||
@ -550,6 +601,7 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
tooltipVisible={tooltipVisible}
|
||||
tipFormatter={tipFormatter}
|
||||
unit={unit}
|
||||
useMobileUI={useMobileUI}
|
||||
tooltipPlacement={tooltipPlacement}
|
||||
onAfterChange={onAfterChange}
|
||||
onChange={this.onGetChangeValue.bind(this)}
|
||||
@ -566,6 +618,7 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
tooltipVisible={tooltipVisible}
|
||||
tipFormatter={tipFormatter}
|
||||
unit={unit}
|
||||
useMobileUI={useMobileUI}
|
||||
tooltipPlacement={tooltipPlacement}
|
||||
onAfterChange={onAfterChange}
|
||||
onChange={this.onChange.bind(this)}
|
||||
@ -575,9 +628,10 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
{/* 刻度标记 */}
|
||||
{marks && (
|
||||
<div className={cx('InputRange-marks')}>
|
||||
{keys(marks).map((key: keyof MarksType) => {
|
||||
const offsetLeft = this.getOffsetLeft(key);
|
||||
{sortMaks.map((key: keyof MarksType) => {
|
||||
const offsetLeft = this.getOffsetLeft(key) as string;
|
||||
const markMaxWidth = this.getMarkMaxWidth(key, marks);
|
||||
|
||||
if (MARKS_REG.test(offsetLeft)) {
|
||||
return (
|
||||
<div
|
||||
|
@ -29,16 +29,22 @@ export interface ResultBoxProps
|
||||
overflowTagPopover?: TooltipObject;
|
||||
actions?: JSX.Element | JSX.Element[];
|
||||
showInvalidMatch?: boolean;
|
||||
showArrow?: boolean;
|
||||
}
|
||||
|
||||
export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
static defaultProps: Pick<
|
||||
ResultBoxProps,
|
||||
'clearable' | 'placeholder' | 'itemRender' | 'inputPlaceholder'
|
||||
| 'clearable'
|
||||
| 'placeholder'
|
||||
| 'itemRender'
|
||||
| 'inputPlaceholder'
|
||||
| 'showArrow'
|
||||
> = {
|
||||
clearable: false,
|
||||
placeholder: 'placeholder.noData',
|
||||
inputPlaceholder: 'placeholder.enter',
|
||||
showArrow: true,
|
||||
itemRender: (option: any) => (
|
||||
<span>{`${option.scopeLabel || ''}${option.label}`}</span>
|
||||
)
|
||||
@ -68,6 +74,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
|
||||
@autobind
|
||||
handleFocus(e: any) {
|
||||
console.log('eeeeeeee', e);
|
||||
const onFocus = this.props.onFocus;
|
||||
onFocus && onFocus(e);
|
||||
this.setState({
|
||||
@ -241,11 +248,14 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
onClear,
|
||||
maxTagCount,
|
||||
overflowTagPopover,
|
||||
showArrow,
|
||||
...rest
|
||||
} = this.props;
|
||||
const isFocused = this.state.isFocused;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
console.log('onResultClick-----', onResultClick);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx('ResultBox', className, {
|
||||
@ -326,7 +336,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</span>
|
||||
)}
|
||||
{!allowInput && mobileUI ? (
|
||||
{!allowInput && mobileUI && showArrow ? (
|
||||
<span className={cx('ResultBox-arrow')}>
|
||||
<Icon icon="caret" className="icon" />
|
||||
</span>
|
||||
|
@ -6,7 +6,7 @@ import moment from 'moment';
|
||||
import {ThemeProps, themeable} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
import {uncontrollable} from 'amis-core';
|
||||
import {autobind} from 'amis-core';
|
||||
import {autobind, isMobile} from 'amis-core';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
|
||||
export interface HistoryRecord {
|
||||
@ -48,6 +48,7 @@ export interface SearchBoxProps extends ThemeProps, LocaleProps {
|
||||
/** 历史记录配置 */
|
||||
history?: SearchHistoryOptions;
|
||||
clearAndSubmit?: boolean;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchBoxState {
|
||||
@ -284,10 +285,12 @@ export class SearchBox extends React.Component<SearchBoxProps, SearchBoxState> {
|
||||
mini,
|
||||
enhance,
|
||||
clearable,
|
||||
useMobileUI,
|
||||
translate: __
|
||||
} = this.props;
|
||||
const {isFocused, inputValue} = this.state;
|
||||
const {enable} = this.getHistoryOptions();
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -298,7 +301,8 @@ export class SearchBox extends React.Component<SearchBoxProps, SearchBoxState> {
|
||||
disabled ? 'is-disabled' : '',
|
||||
isFocused ? 'is-focused' : '',
|
||||
!mini || active ? 'is-active' : '',
|
||||
{'is-history': enable}
|
||||
{'is-history': enable},
|
||||
{'is-mobile': mobileUI}
|
||||
)}
|
||||
style={style}
|
||||
>
|
||||
|
@ -41,6 +41,7 @@ import {RemoteOptionsProps, withRemoteConfig} from './WithRemoteConfig';
|
||||
import Picker from './Picker';
|
||||
import PopUp from './PopUp';
|
||||
import BasePopover, {PopOverOverlay} from './PopOverContainer';
|
||||
import SelectMobile from './SelectMobile';
|
||||
|
||||
import type {TooltipObject} from '../components/TooltipWrapper';
|
||||
|
||||
@ -303,7 +304,7 @@ export function normalizeOptions(
|
||||
|
||||
const DownshiftChangeTypes = Downshift.stateChangeTypes;
|
||||
|
||||
interface SelectProps
|
||||
export interface SelectProps
|
||||
extends OptionProps,
|
||||
ThemeProps,
|
||||
LocaleProps,
|
||||
@ -1145,10 +1146,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
};
|
||||
|
||||
const mobileUI = isMobile() && useMobileUI;
|
||||
const column = {
|
||||
labelField: 'label',
|
||||
options: filtedOptions
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<div
|
||||
ref={this.menu}
|
||||
@ -1229,14 +1227,21 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
</div>
|
||||
);
|
||||
return mobileUI ? (
|
||||
<PopUp
|
||||
className={cx(`Select-popup`)}
|
||||
container={popOverContainer}
|
||||
isShow={this.state.isOpen}
|
||||
onHide={this.close}
|
||||
>
|
||||
{menu}
|
||||
</PopUp>
|
||||
<SelectMobile
|
||||
{...this.props}
|
||||
highlightedIndex={highlightedIndex}
|
||||
isOpen={isOpen}
|
||||
getItemProps={getItemProps}
|
||||
getInputProps={getInputProps}
|
||||
selectedItem={selectedItem}
|
||||
onChange={selection => {
|
||||
this.setState({
|
||||
isOpen: false
|
||||
});
|
||||
this.props.onChange(selection);
|
||||
}}
|
||||
onClose={this.close}
|
||||
/>
|
||||
) : (
|
||||
<Overlay
|
||||
container={popOverContainer || this.getTarget}
|
||||
|
520
packages/amis-ui/src/components/SelectMobile.tsx
Normal file
520
packages/amis-ui/src/components/SelectMobile.tsx
Normal file
@ -0,0 +1,520 @@
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import Picker from './Picker';
|
||||
import PopUp from './PopUp';
|
||||
import {autobind, highlight} from 'amis-core';
|
||||
import merge from 'lodash/merge';
|
||||
// @ts-ignore
|
||||
import {matchSorter} from 'match-sorter';
|
||||
import {Option, Options, value2array, SelectProps} from './Select';
|
||||
import VirtualList from './virtual-list';
|
||||
import Checkbox from './Checkbox';
|
||||
import Input from './Input';
|
||||
import {closeIcon, Icon} from './icons';
|
||||
|
||||
interface SelectState {
|
||||
isFocused: boolean;
|
||||
inputValue: string;
|
||||
itemHeight: number;
|
||||
selection: Array<Option>;
|
||||
}
|
||||
|
||||
interface Props extends SelectProps {
|
||||
isOpen: boolean;
|
||||
highlightedIndex: any;
|
||||
selectedItem: any;
|
||||
getInputProps: (...params: any) => any;
|
||||
getItemProps: (...params: any) => any;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default class SelectMobile extends React.Component<Props, SelectState> {
|
||||
input: HTMLInputElement;
|
||||
target: HTMLElement;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selection: value2array(props.value, props),
|
||||
isFocused: false,
|
||||
inputValue: '',
|
||||
itemHeight: 32 /** Select选项高度保持一致 */
|
||||
};
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleChange([item]: any) {
|
||||
const {onChange, multiple, simpleValue, valueField, options} = this.props;
|
||||
let {selection} = this.state;
|
||||
|
||||
if (!multiple && item === undefined) {
|
||||
console.log('执行了');
|
||||
}
|
||||
|
||||
// 单选是字符串
|
||||
const selectItem = options.find((option: Option) =>
|
||||
multiple
|
||||
? option[valueField] === item[valueField]
|
||||
: option[valueField] === item
|
||||
);
|
||||
if (multiple) {
|
||||
const selectionValues = selection.map(item => item[valueField]);
|
||||
selection = selection.concat();
|
||||
const idx = selectionValues.indexOf(selectItem?.[valueField]);
|
||||
if (~idx) {
|
||||
selection.splice(idx, 1);
|
||||
} else {
|
||||
selectItem && selection.push(selectItem);
|
||||
}
|
||||
this.setState({
|
||||
selection
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
selection: selectItem ? [selectItem] : []
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleInputChange(evt: React.ChangeEvent<HTMLInputElement>) {
|
||||
const {loadOptions} = this.props;
|
||||
this.setState(
|
||||
{
|
||||
inputValue: evt.currentTarget.value
|
||||
},
|
||||
() => loadOptions && loadOptions(this.state.inputValue)
|
||||
);
|
||||
}
|
||||
|
||||
@autobind
|
||||
getTarget() {
|
||||
if (!this.target) {
|
||||
this.target = findDOMNode(this) as HTMLElement;
|
||||
}
|
||||
return this.target as HTMLElement;
|
||||
}
|
||||
|
||||
@autobind
|
||||
inputRef(ref: HTMLInputElement) {
|
||||
this.input = ref;
|
||||
}
|
||||
|
||||
@autobind
|
||||
toggleCheckAll() {
|
||||
const {
|
||||
options,
|
||||
onChange,
|
||||
simpleValue,
|
||||
checkAllBySearch,
|
||||
labelField,
|
||||
valueField
|
||||
} = this.props;
|
||||
const inputValue = this.state.inputValue;
|
||||
let {selection} = this.state;
|
||||
let filtedOptions: Array<Option> =
|
||||
inputValue && checkAllBySearch !== false
|
||||
? matchSorter(options, inputValue, {
|
||||
keys: [labelField || 'label', valueField || 'value']
|
||||
})
|
||||
: options.concat();
|
||||
const optionsValues = filtedOptions.map(option => option.value);
|
||||
const selectionValues = selection.map(select => select.value);
|
||||
const checkedAll = optionsValues.every(
|
||||
option => selectionValues.indexOf(option) > -1
|
||||
);
|
||||
|
||||
selection = checkedAll ? [] : filtedOptions;
|
||||
|
||||
this.setState({selection});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleAddClick() {
|
||||
const {onAdd} = this.props;
|
||||
onAdd && onAdd();
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleEditClick(e: Event, item: any) {
|
||||
const {onEdit} = this.props;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onEdit && onEdit(item);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleDeleteClick(e: Event, item: any) {
|
||||
const {onDelete} = this.props;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onDelete && onDelete(item);
|
||||
}
|
||||
|
||||
@autobind
|
||||
onFocus(e: any) {
|
||||
const {simpleValue} = this.props;
|
||||
const {selection} = this.state;
|
||||
const value = simpleValue ? selection.map(item => item.value) : selection;
|
||||
|
||||
this.props.disabled ||
|
||||
this.props.isOpen ||
|
||||
this.setState(
|
||||
{
|
||||
isFocused: true
|
||||
},
|
||||
this.focus
|
||||
);
|
||||
|
||||
this.props.onFocus &&
|
||||
this.props.onFocus({
|
||||
...e,
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
onBlur(e: any) {
|
||||
const {simpleValue} = this.props;
|
||||
const {selection} = this.state;
|
||||
const value = simpleValue ? selection.map(item => item.value) : selection;
|
||||
|
||||
this.setState({
|
||||
isFocused: false
|
||||
});
|
||||
|
||||
this.props.onBlur &&
|
||||
this.props.onBlur({
|
||||
...e,
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
focus() {
|
||||
this.input
|
||||
? this.input.focus()
|
||||
: this.getTarget() && this.getTarget().focus();
|
||||
}
|
||||
|
||||
blur() {
|
||||
this.input
|
||||
? this.input.blur()
|
||||
: this.getTarget() && this.getTarget().blur();
|
||||
}
|
||||
|
||||
@autobind
|
||||
clearSearchValue() {
|
||||
const {loadOptions} = this.props;
|
||||
this.setState(
|
||||
{
|
||||
inputValue: ''
|
||||
},
|
||||
() => loadOptions?.('')
|
||||
);
|
||||
}
|
||||
|
||||
@autobind
|
||||
onConfirm() {
|
||||
const {selection} = this.state;
|
||||
const {
|
||||
multiple,
|
||||
onChange,
|
||||
simpleValue,
|
||||
valueField,
|
||||
options,
|
||||
loadOptions,
|
||||
labelField
|
||||
} = this.props;
|
||||
if (multiple) {
|
||||
onChange(
|
||||
simpleValue ? selection.map(item => item[valueField]) : selection
|
||||
);
|
||||
} else {
|
||||
const inputValue = this.state.inputValue;
|
||||
let filtedOptions: Array<Option> = (
|
||||
inputValue && !loadOptions
|
||||
? matchSorter(options, inputValue, {
|
||||
keys: [labelField || 'label', valueField || 'value']
|
||||
})
|
||||
: options.concat()
|
||||
).filter((option: Option) => !option.hidden && option.visible !== false);
|
||||
// picker 打开未滑动时选中第一项
|
||||
if (!selection.length && filtedOptions.length) {
|
||||
onChange(
|
||||
simpleValue ? filtedOptions[0]?.[valueField] : filtedOptions[0]
|
||||
);
|
||||
} else {
|
||||
onChange(simpleValue ? selection[0]?.[valueField] : selection[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
popOverContainer,
|
||||
options,
|
||||
value,
|
||||
valueField,
|
||||
labelField,
|
||||
noResultsText,
|
||||
loadOptions,
|
||||
creatable,
|
||||
multiple,
|
||||
valuesNoWrap,
|
||||
classnames: cx,
|
||||
popoverClassName,
|
||||
popOverContainerSelector,
|
||||
checkAll,
|
||||
checkAllLabel,
|
||||
checkAllBySearch,
|
||||
searchable,
|
||||
createBtnLabel,
|
||||
disabled,
|
||||
searchPromptText,
|
||||
editable,
|
||||
removable,
|
||||
overlayPlacement,
|
||||
translate: __,
|
||||
hideSelected,
|
||||
renderMenu,
|
||||
mobileClassName,
|
||||
virtualThreshold = 100,
|
||||
useMobileUI = false,
|
||||
overlay,
|
||||
isOpen,
|
||||
highlightedIndex,
|
||||
onClose,
|
||||
getInputProps,
|
||||
getItemProps,
|
||||
selectedItem
|
||||
} = this.props;
|
||||
|
||||
const {selection} = this.state;
|
||||
|
||||
const inputValue = this.state.inputValue;
|
||||
let checkedAll = false;
|
||||
let checkedPartial = false;
|
||||
let filtedOptions: Array<Option> = (
|
||||
inputValue && isOpen && !loadOptions
|
||||
? matchSorter(options, inputValue, {
|
||||
keys: [labelField || 'label', valueField || 'value']
|
||||
})
|
||||
: options.concat()
|
||||
).filter((option: Option) => !option.hidden && option.visible !== false);
|
||||
|
||||
const enableVirtualRender =
|
||||
filtedOptions.length && filtedOptions.length > virtualThreshold;
|
||||
const selectionValues = selection.map(select => select[valueField]);
|
||||
if (multiple && checkAll) {
|
||||
const optionsValues = (
|
||||
checkAllBySearch !== false ? filtedOptions : options
|
||||
).map(option => option[valueField]);
|
||||
|
||||
checkedAll = optionsValues.every(
|
||||
option => selectionValues.indexOf(option) > -1
|
||||
);
|
||||
checkedPartial = optionsValues.some(
|
||||
option => selectionValues.indexOf(option) > -1
|
||||
);
|
||||
}
|
||||
|
||||
// 用于虚拟渲染的每项高度
|
||||
const virtualItemHeight = this.props.itemHeight || this.state.itemHeight;
|
||||
|
||||
// 渲染单个选项
|
||||
const renderItem = ({index, style}: {index: number; style?: object}) => {
|
||||
const item = filtedOptions[index];
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
const checked =
|
||||
selectedItem === item || !!~selectionValues.indexOf(item[valueField]);
|
||||
|
||||
if (hideSelected && checked) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
{...getItemProps({
|
||||
key:
|
||||
typeof item.value === 'string'
|
||||
? `${item.label}-${item.value}`
|
||||
: index,
|
||||
index,
|
||||
item,
|
||||
disabled: item.disabled
|
||||
})}
|
||||
style={merge(style, enableVirtualRender ? {width: '100%'} : {})}
|
||||
className={cx(`Select-option`, {
|
||||
'is-disabled': item.disabled,
|
||||
'is-active': checked,
|
||||
'is-mobile': true
|
||||
})}
|
||||
>
|
||||
{renderMenu ? (
|
||||
multiple ? (
|
||||
renderMenu(item, {
|
||||
multiple,
|
||||
checkAll,
|
||||
checked,
|
||||
onChange: () => this.handleChange(item),
|
||||
inputValue: inputValue || '',
|
||||
searchable,
|
||||
index
|
||||
})
|
||||
) : (
|
||||
renderMenu(item, {
|
||||
multiple,
|
||||
checkAll,
|
||||
checked,
|
||||
onChange: () => this.handleChange(item),
|
||||
inputValue: inputValue || '',
|
||||
searchable,
|
||||
index
|
||||
})
|
||||
)
|
||||
) : multiple ? (
|
||||
<>
|
||||
<div
|
||||
title={item[labelField]}
|
||||
className={cx('Select-option-item-check')}
|
||||
onClick={() => !item.disabled && this.handleChange([item])}
|
||||
>
|
||||
{item.disabled
|
||||
? item[labelField]
|
||||
: highlight(
|
||||
item[labelField],
|
||||
inputValue as string,
|
||||
cx('Select-option-hl')
|
||||
)}
|
||||
|
||||
{item.tip}
|
||||
</div>
|
||||
{checked ? (
|
||||
<Icon icon="check" className={cx('Select-option-mcheck')} />
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<span
|
||||
className={cx('Select-option-content')}
|
||||
title={
|
||||
typeof item[labelField] === 'string' ? item[labelField] : ''
|
||||
}
|
||||
>
|
||||
{item.disabled
|
||||
? item[labelField]
|
||||
: highlight(
|
||||
item[labelField],
|
||||
inputValue as string,
|
||||
cx('Select-option-hl')
|
||||
)}
|
||||
{item.tip}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<div
|
||||
className={cx('Select-menu', {
|
||||
'Select--longlist': enableVirtualRender,
|
||||
'is-mobile': true
|
||||
})}
|
||||
>
|
||||
{searchable ? (
|
||||
<div
|
||||
className={cx(`Select-input`, {
|
||||
'is-focused': this.state.isFocused
|
||||
})}
|
||||
>
|
||||
<Icon icon="search" className="icon" />
|
||||
<Input
|
||||
{...getInputProps({
|
||||
onFocus: this.onFocus,
|
||||
onBlur: this.onBlur,
|
||||
disabled: disabled,
|
||||
placeholder: __(searchPromptText),
|
||||
onChange: this.handleInputChange,
|
||||
ref: this.inputRef
|
||||
})}
|
||||
/>
|
||||
{inputValue?.length ? (
|
||||
<a onClick={this.clearSearchValue} className={cx('Select-clear')}>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{multiple && valuesNoWrap ? (
|
||||
<div className={cx('Select-option')}>
|
||||
已选择({selectionValues.length})
|
||||
</div>
|
||||
) : null}
|
||||
{multiple && checkAll && filtedOptions.length ? (
|
||||
<div className={cx('Select-option')}>
|
||||
<Checkbox
|
||||
checked={checkedPartial}
|
||||
partial={checkedPartial && !checkedAll}
|
||||
onChange={this.toggleCheckAll}
|
||||
size="sm"
|
||||
>
|
||||
{__(checkAllLabel)}
|
||||
</Checkbox>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{filtedOptions.length ? (
|
||||
filtedOptions.length > virtualThreshold ? ( // 较多数据时才启用 virtuallist,避免滚动条问题
|
||||
<VirtualList
|
||||
height={
|
||||
filtedOptions.length > 8
|
||||
? 266
|
||||
: filtedOptions.length * virtualItemHeight
|
||||
}
|
||||
itemCount={filtedOptions.length}
|
||||
itemSize={virtualItemHeight}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
) : (
|
||||
filtedOptions.map((item, index) => {
|
||||
return renderItem({index});
|
||||
})
|
||||
)
|
||||
) : (
|
||||
<div className={cx('Select-noResult')}>{__(noResultsText)}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<PopUp
|
||||
className={cx(`Select-popup`)}
|
||||
container={popOverContainer}
|
||||
isShow={isOpen}
|
||||
showConfirm={true}
|
||||
onConfirm={this.onConfirm}
|
||||
onHide={onClose}
|
||||
>
|
||||
{multiple ? (
|
||||
menu
|
||||
) : (
|
||||
<Picker
|
||||
className={'Select-picker'}
|
||||
columns={{
|
||||
options: filtedOptions as Option[],
|
||||
optionRender: renderMenu
|
||||
}}
|
||||
onChange={item => this.handleChange(item as any)}
|
||||
showToolbar={false}
|
||||
labelField={labelField}
|
||||
valueField={valueField}
|
||||
itemHeight={40}
|
||||
value={[selection[0]?.[valueField]]}
|
||||
/>
|
||||
)}
|
||||
</PopUp>
|
||||
);
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ import {Icon} from './icons';
|
||||
import debounce from 'lodash/debounce';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import TooltipWrapper from './TooltipWrapper';
|
||||
import {resizeSensor} from 'amis-core';
|
||||
import {resizeSensor, isMobile} from 'amis-core';
|
||||
import PopOverContainer from './PopOverContainer';
|
||||
|
||||
import Sortable from 'sortablejs';
|
||||
@ -45,6 +45,8 @@ export interface TabProps extends ThemeProps {
|
||||
iconPosition?: 'left' | 'right';
|
||||
disabled?: boolean | string;
|
||||
eventKey: string | number;
|
||||
prevKey?: string | number;
|
||||
nextKey?: string | number;
|
||||
tab?: Schema;
|
||||
className?: string;
|
||||
activeKey?: string | number;
|
||||
@ -53,12 +55,52 @@ export interface TabProps extends ThemeProps {
|
||||
unmountOnExit?: boolean;
|
||||
toolbar?: React.ReactNode;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
useMobileUI?: boolean;
|
||||
swipeable?: boolean;
|
||||
onSelect?: (eventKey: string | number) => void;
|
||||
}
|
||||
|
||||
class TabComponent extends React.PureComponent<TabProps> {
|
||||
contentDom: any;
|
||||
touch: any = {};
|
||||
touchStartTime: number;
|
||||
contentRef = (ref: any) => (this.contentDom = ref);
|
||||
|
||||
@autobind
|
||||
onTouchStart(event: TouchEvent) {
|
||||
this.touch.startX = event.touches[0].clientX;
|
||||
this.touch.startY = event.touches[0].clientY;
|
||||
this.touchStartTime = Date.now();
|
||||
}
|
||||
|
||||
@autobind
|
||||
onTouchMove(event: TouchEvent) {
|
||||
const touch = event.touches[0];
|
||||
const newState = {...this.touch};
|
||||
|
||||
newState.deltaX = touch.clientX < 0 ? 0 : touch.clientX - newState.startX;
|
||||
newState.deltaY = touch.clientY - newState.startY;
|
||||
newState.offsetX = Math.abs(newState.deltaX);
|
||||
newState.offsetY = Math.abs(newState.deltaY);
|
||||
this.touch = newState;
|
||||
}
|
||||
|
||||
@autobind
|
||||
onTouchEnd() {
|
||||
const duration = Date.now() - this.touchStartTime;
|
||||
const speed = this.touch.deltaX / duration;
|
||||
const shouldSwipe = Math.abs(speed) > 0.25;
|
||||
const {prevKey, nextKey, onSelect} = this.props;
|
||||
|
||||
if (shouldSwipe) {
|
||||
if (this.touch.deltaX > 0) {
|
||||
prevKey !== undefined && onSelect?.(prevKey);
|
||||
} else {
|
||||
nextKey && onSelect?.(nextKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
classnames: cx,
|
||||
@ -68,9 +110,13 @@ class TabComponent extends React.PureComponent<TabProps> {
|
||||
eventKey,
|
||||
activeKey,
|
||||
children,
|
||||
className
|
||||
className,
|
||||
swipeable,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<Transition
|
||||
in={activeKey === eventKey}
|
||||
@ -91,6 +137,10 @@ class TabComponent extends React.PureComponent<TabProps> {
|
||||
'Tabs-pane',
|
||||
className
|
||||
)}
|
||||
onTouchStart={swipeable && mobileUI && this.onTouchStart}
|
||||
onTouchMove={swipeable && mobileUI && this.onTouchMove}
|
||||
onTouchEnd={swipeable && mobileUI && this.onTouchEnd}
|
||||
onTouchCancel={swipeable && mobileUI && this.onTouchEnd}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
@ -132,6 +182,7 @@ export interface TabsProps extends ThemeProps, LocaleProps {
|
||||
collapseBtnLabel?: string;
|
||||
popOverContainer?: any;
|
||||
children?: React.ReactNode | Array<React.ReactNode>;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface IDragInfo {
|
||||
@ -271,6 +322,29 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
if (!this.scroll && !this.draging && isTabsModified) {
|
||||
this.computedWidth();
|
||||
}
|
||||
|
||||
// 移动端取消箭头切换,改为滚动切换激活项居中
|
||||
const {classPrefix: ns, activeKey, useMobileUI} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
if (mobileUI && preProps.activeKey !== activeKey) {
|
||||
const {classPrefix: ns} = this.props;
|
||||
const dom = findDOMNode(this) as HTMLElement;
|
||||
const activeTab = dom.querySelector(
|
||||
`.${ns}Tabs-link.is-active`
|
||||
) as HTMLElement;
|
||||
const parentWidth = (activeTab.parentNode?.parentNode as any).offsetWidth;
|
||||
const offsetLeft = activeTab.offsetLeft;
|
||||
const offsetWidth = activeTab.offsetWidth;
|
||||
if (activeTab.parentNode) {
|
||||
(activeTab.parentNode as any).scrollLeft =
|
||||
offsetLeft > parentWidth
|
||||
? (offsetLeft / parentWidth) * parentWidth -
|
||||
parentWidth / 2 +
|
||||
offsetWidth / 2
|
||||
: offsetLeft - parentWidth / 2 + offsetWidth / 2;
|
||||
}
|
||||
}
|
||||
|
||||
this.scroll = false;
|
||||
}
|
||||
|
||||
@ -760,9 +834,11 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
addable,
|
||||
draggable,
|
||||
sidePosition,
|
||||
addBtnText
|
||||
addBtnText,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
const {isOverflow} = this.state;
|
||||
if (!Array.isArray(children)) {
|
||||
return null;
|
||||
@ -811,10 +887,12 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
isOverflow && 'Tabs-linksContainer--overflow'
|
||||
)}
|
||||
>
|
||||
{this.renderArrow('left')}
|
||||
{!mobileUI ? this.renderArrow('left') : null}
|
||||
<div className={cx('Tabs-linksContainer-main')}>
|
||||
<ul
|
||||
className={cx('Tabs-links', linksClassName)}
|
||||
className={cx('Tabs-links', linksClassName, {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
role="tablist"
|
||||
ref={this.navMain}
|
||||
>
|
||||
@ -823,13 +901,18 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
{!isOverflow && toolButtons}
|
||||
</ul>
|
||||
</div>
|
||||
{this.renderArrow('right')}
|
||||
{!mobileUI ? this.renderArrow('right') : null}
|
||||
</div>
|
||||
{isOverflow && toolButtons}
|
||||
</div>
|
||||
) : (
|
||||
<div className={cx('Tabs-linksWrapper')}>
|
||||
<ul className={cx('Tabs-links', linksClassName)} role="tablist">
|
||||
<ul
|
||||
className={cx('Tabs-links', linksClassName, {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
role="tablist"
|
||||
>
|
||||
{this.renderNavs()}
|
||||
{additionBtns}
|
||||
{toolbar}
|
||||
|
@ -15,6 +15,7 @@ import {ItemRenderStates} from './Selection';
|
||||
import {Icon} from './icons';
|
||||
import debounce from 'lodash/debounce';
|
||||
import {SpinnerExtraProps} from './Spinner';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
export interface TabsTransferProps
|
||||
extends Omit<
|
||||
@ -51,6 +52,7 @@ export interface TabsTransferProps
|
||||
activeKey: number;
|
||||
onlyChildren?: boolean;
|
||||
ctx?: Record<string, any>;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface TabsTransferState {
|
||||
@ -266,9 +268,11 @@ export class TabsTransfer extends React.Component<
|
||||
activeKey,
|
||||
classnames: cx,
|
||||
translate: __,
|
||||
ctx
|
||||
ctx,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const showOptions = options.filter(item => item.visible !== false);
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
if (!Array.isArray(options) || !options.length) {
|
||||
return (
|
||||
@ -296,7 +300,9 @@ export class TabsTransfer extends React.Component<
|
||||
className="TabsTransfer-tab"
|
||||
>
|
||||
{option.searchable ? (
|
||||
<div className={cx('TabsTransfer-search')}>
|
||||
<div
|
||||
className={cx('TabsTransfer-search', {'is-mobile': mobileUI})}
|
||||
>
|
||||
<InputBox
|
||||
value={this.state.inputValue}
|
||||
onChange={(text: string) => this.handleSearch(text, option)}
|
||||
@ -478,12 +484,14 @@ export class TabsTransfer extends React.Component<
|
||||
classnames: cx,
|
||||
optionItemRender,
|
||||
onSearch,
|
||||
useMobileUI,
|
||||
...reset
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Transfer
|
||||
{...reset}
|
||||
useMobileUI={useMobileUI}
|
||||
statistics={false}
|
||||
classnames={cx}
|
||||
className={cx('TabsTransfer', className)}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {localeable} from 'amis-core';
|
||||
import {isMobile, localeable} from 'amis-core';
|
||||
import {themeable} from 'amis-core';
|
||||
import {uncontrollable} from 'amis-core';
|
||||
import React from 'react';
|
||||
@ -14,6 +14,7 @@ export interface TabsTransferPickerProps
|
||||
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export class TransferPicker extends React.Component<TabsTransferPickerProps> {
|
||||
@ -45,12 +46,15 @@ export class TransferPicker extends React.Component<TabsTransferPickerProps> {
|
||||
onChange,
|
||||
size,
|
||||
labelField = 'label',
|
||||
useMobileUI,
|
||||
...rest
|
||||
} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<PickerContainer
|
||||
title={__('Select.placeholder')}
|
||||
useMobileUI={useMobileUI}
|
||||
onFocus={this.onFoucs}
|
||||
onClose={this.onBlur}
|
||||
bodyRender={({onClose, value, onChange, setState, ...states}) => {
|
||||
@ -59,6 +63,7 @@ export class TransferPicker extends React.Component<TabsTransferPickerProps> {
|
||||
{...rest}
|
||||
{...states}
|
||||
value={value}
|
||||
useMobileUI={useMobileUI}
|
||||
onChange={(value: any, optionModified) => {
|
||||
if (optionModified) {
|
||||
let options = mapTree(rest.options, item => {
|
||||
@ -96,10 +101,13 @@ export class TransferPicker extends React.Component<TabsTransferPickerProps> {
|
||||
itemRender={option => (
|
||||
<span>{(option && option[labelField]) || 'undefiend'}</span>
|
||||
)}
|
||||
useMobileUI={useMobileUI}
|
||||
>
|
||||
<span className={cx('TransferPicker-icon')}>
|
||||
<Icon icon="pencil" className="icon" />
|
||||
</span>
|
||||
{!mobileUI ? (
|
||||
<span className={cx('TransferPicker-icon')}>
|
||||
<Icon icon="pencil" className="icon" />
|
||||
</span>
|
||||
) : null}
|
||||
</ResultBox>
|
||||
)}
|
||||
</PickerContainer>
|
||||
|
@ -60,6 +60,7 @@ export interface TextAreaProps extends ThemeProps, LocaleProps {
|
||||
disabled?: boolean;
|
||||
|
||||
forwardRef?: {current: HTMLTextAreaElement | null};
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface TextAreaState {
|
||||
|
@ -13,6 +13,7 @@ import {ThemeProps, themeable, findTree} from 'amis-core';
|
||||
import {BaseSelectionProps, BaseSelection, ItemRenderStates} from './Selection';
|
||||
import {Options, Option} from './Select';
|
||||
import {uncontrollable} from 'amis-core';
|
||||
import {isMobile} from 'amis-core';
|
||||
import ResultList from './ResultList';
|
||||
import TableSelection from './TableSelection';
|
||||
import {autobind, flattenTree} from 'amis-core';
|
||||
@ -115,9 +116,11 @@ export interface TransferProps
|
||||
checkAllLabel?: string;
|
||||
/** 树形模式下,给 tree 的属性 */
|
||||
onlyChildren?: boolean;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface TransferState {
|
||||
tempValue?: Array<Option> | Option;
|
||||
inputValue: string;
|
||||
searchResult: Options | null;
|
||||
isTreeDeferLoad: boolean;
|
||||
@ -366,7 +369,8 @@ export class Transfer<
|
||||
options,
|
||||
statistics,
|
||||
translate: __,
|
||||
searchPlaceholder = __('Transfer.searchKeyword')
|
||||
searchPlaceholder = __('Transfer.searchKeyword'),
|
||||
useMobileUI
|
||||
} = props;
|
||||
|
||||
if (selectRender) {
|
||||
@ -395,6 +399,8 @@ export class Transfer<
|
||||
isEqual
|
||||
).length;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@ -437,7 +443,7 @@ export class Transfer<
|
||||
</div>
|
||||
|
||||
{onSearch ? (
|
||||
<div className={cx('Transfer-search')}>
|
||||
<div className={cx('Transfer-search', {'is-mobile': mobileUI})}>
|
||||
<InputBox
|
||||
value={this.state.inputValue}
|
||||
onChange={this.handleSearch}
|
||||
@ -818,7 +824,8 @@ export class Transfer<
|
||||
resultListModeFollowSelect,
|
||||
selectMode = 'list',
|
||||
translate: __,
|
||||
valueField = 'value'
|
||||
valueField = 'value',
|
||||
useMobileUI
|
||||
} = this.props as any;
|
||||
const {searchResult} = this.state;
|
||||
|
||||
@ -832,6 +839,7 @@ export class Transfer<
|
||||
);
|
||||
|
||||
const tableType = resultListModeFollowSelect && selectMode === 'table';
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -847,7 +855,7 @@ export class Transfer<
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={cx('Transfer-result')}>
|
||||
<div className={cx('Transfer-result', {'is-mobile': mobileUI})}>
|
||||
<div
|
||||
className={cx(
|
||||
'Transfer-title',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {localeable} from 'amis-core';
|
||||
import {themeable} from 'amis-core';
|
||||
import {Transfer, TransferProps} from './Transfer';
|
||||
import {Transfer, TransferProps, TransferState} from './Transfer';
|
||||
import {uncontrollable, autobind} from 'amis-core';
|
||||
import React from 'react';
|
||||
import ResultBox from './ResultBox';
|
||||
@ -29,11 +29,51 @@ export interface TransferDropDownProps extends TransferProps {
|
||||
}
|
||||
|
||||
export class TransferDropDown extends Transfer<TransferDropDownProps> {
|
||||
constructor(props: TransferDropDownProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
tempValue: props.value,
|
||||
inputValue: '',
|
||||
searchResult: null,
|
||||
isTreeDeferLoad: false,
|
||||
resultSelectMode: 'list'
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: TransferDropDownProps) {
|
||||
if (this.props.value !== prevProps.value) {
|
||||
this.setState({
|
||||
tempValue: this.props.value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleAfterPopoverHide() {
|
||||
this.setState({inputValue: '', searchResult: null});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleChange(value: any, onClose: () => void) {
|
||||
const {multiple, onChange, useMobileUI} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
if (mobileUI) {
|
||||
this.setState({tempValue: value});
|
||||
} else {
|
||||
onChange?.(value);
|
||||
if (!multiple) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
onConfirm() {
|
||||
const {onChange} = this.props;
|
||||
onChange?.(this.state.tempValue as typeof Option[]);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
classnames: cx,
|
||||
@ -68,6 +108,8 @@ export class TransferDropDown extends Transfer<TransferDropDownProps> {
|
||||
overlayWidth={overlay && overlay?.width}
|
||||
align={overlay && overlay?.align}
|
||||
popOverClassName={cx('TransferDropDown-popover')}
|
||||
showConfirm
|
||||
onConfirm={this.onConfirm}
|
||||
popOverRender={({onClose}) => (
|
||||
<div
|
||||
className={cx('TransferDropDown-content', {
|
||||
@ -96,24 +138,14 @@ export class TransferDropDown extends Transfer<TransferDropDownProps> {
|
||||
{searchResult !== null
|
||||
? this.renderSearchResult({
|
||||
...this.props,
|
||||
value,
|
||||
onChange: multiple
|
||||
? onChange
|
||||
: (value: any) => {
|
||||
onClose();
|
||||
onChange?.(value);
|
||||
},
|
||||
value: this.state.tempValue,
|
||||
onChange: value => this.handleChange(value, onClose),
|
||||
multiple
|
||||
})
|
||||
: this.renderOptions({
|
||||
...this.props,
|
||||
value,
|
||||
onChange: multiple
|
||||
? onChange
|
||||
: (value: any) => {
|
||||
onClose();
|
||||
onChange?.(value);
|
||||
},
|
||||
value: this.state.tempValue,
|
||||
onChange: value => this.handleChange(value, onClose),
|
||||
multiple
|
||||
})}
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@ import React from 'react';
|
||||
import ResultBox from './ResultBox';
|
||||
import {Icon} from './icons';
|
||||
import PickerContainer from './PickerContainer';
|
||||
import {autobind, mapTree} from 'amis-core';
|
||||
import {autobind, mapTree, isMobile} from 'amis-core';
|
||||
|
||||
export interface TransferPickerProps extends Omit<TransferProps, 'itemRender'> {
|
||||
// 新的属性?
|
||||
@ -20,6 +20,7 @@ export interface TransferPickerProps extends Omit<TransferProps, 'itemRender'> {
|
||||
onFocus?: () => void;
|
||||
|
||||
onBlur?: () => void;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export class TransferPicker extends React.Component<TransferPickerProps> {
|
||||
@ -51,17 +52,21 @@ export class TransferPicker extends React.Component<TransferPickerProps> {
|
||||
size,
|
||||
borderMode,
|
||||
labelField = 'label',
|
||||
useMobileUI,
|
||||
...rest
|
||||
} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<PickerContainer
|
||||
title={__('Select.placeholder')}
|
||||
onFocus={this.onFoucs}
|
||||
onClose={this.onBlur}
|
||||
useMobileUI={useMobileUI}
|
||||
bodyRender={({onClose, value, onChange, setState, ...states}) => {
|
||||
return (
|
||||
<Transfer
|
||||
useMobileUI={useMobileUI}
|
||||
{...rest}
|
||||
{...states}
|
||||
value={value}
|
||||
@ -103,10 +108,13 @@ export class TransferPicker extends React.Component<TransferPickerProps> {
|
||||
itemRender={option => (
|
||||
<span>{(option && option[labelField]) || 'undefined'}</span>
|
||||
)}
|
||||
useMobileUI={useMobileUI}
|
||||
>
|
||||
<span className={cx('TransferPicker-icon')}>
|
||||
<Icon icon="pencil" className="icon" />
|
||||
</span>
|
||||
{!mobileUI ? (
|
||||
<span className={cx('TransferPicker-icon')}>
|
||||
<Icon icon="pencil" className="icon" />
|
||||
</span>
|
||||
) : null}
|
||||
</ResultBox>
|
||||
)}
|
||||
</PickerContainer>
|
||||
|
@ -32,7 +32,7 @@ import {Option, Options, value2array} from './Select';
|
||||
import {themeable, ThemeProps, highlight} from 'amis-core';
|
||||
import {Icon, getIcon} from './icons';
|
||||
import Checkbox from './Checkbox';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import {LocaleProps, localeable, isMobile} from 'amis-core';
|
||||
import Spinner, {SpinnerExtraProps} from './Spinner';
|
||||
import {ItemRenderStates} from './Selection';
|
||||
import VirtualList from './virtual-list';
|
||||
@ -149,6 +149,7 @@ interface TreeSelectorProps extends ThemeProps, LocaleProps, SpinnerExtraProps {
|
||||
// 全选按钮文案
|
||||
checkAllLabel?: string;
|
||||
enableDefaultIcon?: boolean;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
interface TreeSelectorState {
|
||||
@ -716,12 +717,21 @@ export class TreeSelector extends React.Component<
|
||||
}
|
||||
|
||||
renderInput(prfix: JSX.Element | null = null) {
|
||||
const {classnames: cx, translate: __} = this.props;
|
||||
const {classnames: cx, useMobileUI, translate: __} = this.props;
|
||||
const {inputValue} = this.state;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div className={cx('Tree-itemLabel')}>
|
||||
<div className={cx('Tree-itemInput')}>
|
||||
<div
|
||||
className={cx('Tree-itemLabel', {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={cx('Tree-itemInput', {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
>
|
||||
{prfix}
|
||||
<input
|
||||
onChange={this.handleInputChange}
|
||||
@ -1064,9 +1074,11 @@ export class TreeSelector extends React.Component<
|
||||
draggable,
|
||||
loadingConfig,
|
||||
enableDefaultIcon,
|
||||
valueField
|
||||
valueField,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
const item = this.state.flattenedOptions[index];
|
||||
|
||||
if (!item) {
|
||||
@ -1160,7 +1172,7 @@ export class TreeSelector extends React.Component<
|
||||
|
||||
{checkbox}
|
||||
|
||||
<div className={cx('Tree-itemLabel-item')}>
|
||||
<div className={cx('Tree-itemLabel-item', {'is-mobile': mobileUI})}>
|
||||
{showIcon ? (
|
||||
<i
|
||||
className={cx(
|
||||
@ -1308,7 +1320,8 @@ export class TreeSelector extends React.Component<
|
||||
checkAllLabel,
|
||||
classnames: cx,
|
||||
translate: __,
|
||||
disabled
|
||||
disabled,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
if (!multiple || !checkAll) {
|
||||
@ -1323,6 +1336,7 @@ export class TreeSelector extends React.Component<
|
||||
const checkedPartial = availableOptions.some(option =>
|
||||
this.isItemChecked(option)
|
||||
);
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -1336,7 +1350,11 @@ export class TreeSelector extends React.Component<
|
||||
partial={checkedPartial && !checkedAll}
|
||||
/>
|
||||
|
||||
<div className={cx('Tree-itemLabel-item')}>
|
||||
<div
|
||||
className={cx('Tree-itemLabel-item', {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
>
|
||||
<span className={cx('Tree-itemText')}>{__(checkAllLabel)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
import {PickerOption} from '../PickerColumn';
|
||||
import 'moment/locale/zh-cn';
|
||||
import 'moment/locale/de';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
export type DateType =
|
||||
| 'year'
|
||||
@ -690,6 +691,8 @@ class BaseDatePicker extends React.Component<
|
||||
key="dt"
|
||||
className={cx(
|
||||
'rdtPicker',
|
||||
{'is-mobile-year': isMobile() && viewMode === 'years'},
|
||||
{'is-mobile-embed': isMobile() && viewProps.embed},
|
||||
timeFormat && !dateFormat
|
||||
? 'rdtPickerTimeWithoutD'
|
||||
: timeFormat && dateFormat
|
||||
|
@ -683,9 +683,11 @@ export class CustomTimeView extends React.Component<
|
||||
<div
|
||||
key={option.value}
|
||||
className={cx('CalendarInput-sugsItem', {
|
||||
'is-mobile': isMobile(),
|
||||
'is-highlight': selectedDate
|
||||
? option.value === date.format(formatMap[type])
|
||||
: option.value === options?.[0]?.value
|
||||
: option.value === options?.[0]?.value &&
|
||||
!isMobile()
|
||||
})}
|
||||
onClick={() => {
|
||||
this.setTime(type, parseInt(option.value, 10));
|
||||
|
@ -22,6 +22,7 @@ import FuncList from './FuncList';
|
||||
import VariableList from './VariableList';
|
||||
import CodeMirrorEditor from '../CodeMirror';
|
||||
import {toast} from '../Toast';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
export interface VariableItem {
|
||||
label: string;
|
||||
@ -358,7 +359,9 @@ export class FormulaEditor extends React.Component<
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section className={cx('FormulaEditor-settings')}>
|
||||
<section
|
||||
className={cx('FormulaEditor-settings', {'is-mobile': isMobile()})}
|
||||
>
|
||||
<div className={cx('FormulaEditor-panel')}>
|
||||
{variableMode !== 'tabs' ? (
|
||||
<div className={cx('FormulaEditor-panel-header')}>
|
||||
|
@ -44,7 +44,7 @@ export function FuncList(props: FuncListProps) {
|
||||
<div className={cx('FormulaEditor-panel-header')}>{title}</div>
|
||||
<div className={cx('FormulaEditor-panel-body')}>
|
||||
<div className={cx('FormulaEditor-FuncList-searchBox')}>
|
||||
<SearchBox mini={false} onSearch={onSearch} />
|
||||
<SearchBox mini={false} onSearch={onSearch} useMobileUI />
|
||||
</div>
|
||||
<div className={cx('FormulaEditor-FuncList-body', bodyClassName)}>
|
||||
<CollapseGroup
|
||||
|
@ -20,7 +20,8 @@ import ResultBox from '../ResultBox';
|
||||
import Button from '../Button';
|
||||
import {Icon} from '../icons';
|
||||
import Modal from '../Modal';
|
||||
import Input from '../Input';
|
||||
import PopUp from '../PopUp';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
export interface FormulaPickerProps extends FormulaEditorProps {
|
||||
// 新的属性?
|
||||
@ -121,6 +122,7 @@ export interface FormulaPickerProps extends FormulaEditorProps {
|
||||
onConfirm?: (value?: any) => void;
|
||||
|
||||
onRef?: (node: any) => void;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface FormulaPickerState {
|
||||
@ -330,10 +332,12 @@ export class FormulaPicker extends React.Component<
|
||||
variableMode,
|
||||
mixedMode,
|
||||
evalMode,
|
||||
useMobileUI,
|
||||
...rest
|
||||
} = this.props;
|
||||
const {isOpened, value, editorValue, isError} = this.state;
|
||||
const iconElement = generateIcon(cx, icon, 'Icon');
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -408,6 +412,8 @@ export class FormulaPicker extends React.Component<
|
||||
disabled={disabled}
|
||||
borderMode={borderMode}
|
||||
placeholder={placeholder}
|
||||
useMobileUI={useMobileUI}
|
||||
showArrow={false}
|
||||
/>
|
||||
|
||||
<Button
|
||||
@ -449,6 +455,8 @@ export class FormulaPicker extends React.Component<
|
||||
disabled={disabled}
|
||||
borderMode={borderMode}
|
||||
placeholder={placeholder}
|
||||
useMobileUI={useMobileUI}
|
||||
showArrow={false}
|
||||
/>
|
||||
|
||||
<a
|
||||
@ -461,41 +469,71 @@ export class FormulaPicker extends React.Component<
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Modal
|
||||
size="md"
|
||||
closeOnEsc
|
||||
show={this.state.isOpened}
|
||||
onHide={this.close}
|
||||
>
|
||||
<Modal.Header onClose={this.close} className="font-bold">
|
||||
{__(title || 'FormulaEditor.title')}
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Editor
|
||||
{...rest}
|
||||
evalMode={mixedMode ? true : evalMode}
|
||||
variables={this.state.variables ?? variables}
|
||||
functions={this.state.functions ?? functions}
|
||||
variableMode={this.state.variableMode ?? variableMode}
|
||||
value={editorValue}
|
||||
onChange={this.handleEditorChange}
|
||||
selfVariableName={this.props.selfVariableName}
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
{!!isError ? (
|
||||
<div className={cx('Dialog-info')} key="info">
|
||||
<span className={cx('Dialog-error')}>
|
||||
{__('FormulaEditor.invalidData', {err: isError})}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
<Button onClick={this.close}>{__('cancel')}</Button>
|
||||
<Button onClick={this.handleEditorConfirm} level="primary">
|
||||
{__('confirm')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
{mobileUI ? (
|
||||
<PopUp
|
||||
className={cx(`FormulaPicker-popup`)}
|
||||
isShow={this.state.isOpened}
|
||||
showConfirm
|
||||
onHide={this.close}
|
||||
onConfirm={this.handleEditorConfirm}
|
||||
>
|
||||
<div className={cx('FormulaPicker-popup-inner')}>
|
||||
<Editor
|
||||
{...rest}
|
||||
evalMode={mixedMode ? true : evalMode}
|
||||
variables={this.state.variables ?? variables}
|
||||
functions={this.state.functions ?? functions}
|
||||
variableMode={this.state.variableMode ?? variableMode}
|
||||
value={editorValue}
|
||||
onChange={this.handleEditorChange}
|
||||
selfVariableName={this.props.selfVariableName}
|
||||
/>
|
||||
{!!isError ? (
|
||||
<div className={cx('Dialog-info')} key="info">
|
||||
<span className={cx('Dialog-error')}>
|
||||
{__('FormulaEditor.invalidData', {err: isError})}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</PopUp>
|
||||
) : (
|
||||
<Modal
|
||||
size="md"
|
||||
closeOnEsc
|
||||
show={this.state.isOpened}
|
||||
onHide={this.close}
|
||||
>
|
||||
<Modal.Header onClose={this.close} className="font-bold">
|
||||
{__(title || 'FormulaEditor.title')}
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Editor
|
||||
{...rest}
|
||||
evalMode={mixedMode ? true : evalMode}
|
||||
variables={this.state.variables ?? variables}
|
||||
functions={this.state.functions ?? functions}
|
||||
variableMode={this.state.variableMode ?? variableMode}
|
||||
value={editorValue}
|
||||
onChange={this.handleEditorChange}
|
||||
selfVariableName={this.props.selfVariableName}
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
{!!isError ? (
|
||||
<div className={cx('Dialog-info')} key="info">
|
||||
<span className={cx('Dialog-error')}>
|
||||
{__('FormulaEditor.invalidData', {err: isError})}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
<Button onClick={this.close}>{__('cancel')}</Button>
|
||||
<Button onClick={this.handleEditorConfirm} level="primary">
|
||||
{__('confirm')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ function VariableList(props: VariableListProps) {
|
||||
function renderSearchBox() {
|
||||
return (
|
||||
<div className={cx('FormulaEditor-VariableList-searchBox')}>
|
||||
<SearchBox mini={false} onSearch={onSearch} />
|
||||
<SearchBox mini={false} onSearch={onSearch} useMobileUI />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import Button from '../Button';
|
||||
import {Icon} from '../icons';
|
||||
import type {InputJSONSchemaItemProps} from './index';
|
||||
import {InputJSONSchemaItem} from './Item';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
type JSONSchemaArrayMember = {
|
||||
key: string;
|
||||
@ -19,7 +20,8 @@ export function InputJSONSchemaArray(props: InputJSONSchemaItemProps) {
|
||||
disabled,
|
||||
translate: __,
|
||||
collapsable,
|
||||
renderValue
|
||||
renderValue,
|
||||
useMobileUI
|
||||
} = props;
|
||||
const buildMembers = React.useCallback((schema: any, value: any) => {
|
||||
const members: Array<JSONSchemaArrayMember> = [];
|
||||
@ -137,6 +139,7 @@ export function InputJSONSchemaArray(props: InputJSONSchemaItemProps) {
|
||||
// todo additionalProperties 还有其他格式
|
||||
const allowAdd = !maxContains || maxContains > members.length;
|
||||
const allowDelete = !minContains || minContains < members.length;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -153,6 +156,7 @@ export function InputJSONSchemaArray(props: InputJSONSchemaItemProps) {
|
||||
|
||||
<div
|
||||
className={cx('JSONSchemaObject', {
|
||||
'is-mobile': mobileUI,
|
||||
'is-expanded': collapsable && !collapsed
|
||||
})}
|
||||
>
|
||||
|
@ -20,6 +20,7 @@ export function InputJSONSchemaItem(props: InputJSONSchemaItemProps) {
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
placeholder={props.placeholder}
|
||||
useMobileUI={props.useMobileUI}
|
||||
/>
|
||||
);
|
||||
} else if (schema.type == 'integer') {
|
||||
@ -29,6 +30,7 @@ export function InputJSONSchemaItem(props: InputJSONSchemaItemProps) {
|
||||
onChange={props.onChange}
|
||||
precision={0}
|
||||
placeholder={props.placeholder}
|
||||
useMobileUI={props.useMobileUI}
|
||||
/>
|
||||
);
|
||||
} else if (schema.type == 'boolean') {
|
||||
@ -42,6 +44,7 @@ export function InputJSONSchemaItem(props: InputJSONSchemaItemProps) {
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
placeholder={props.placeholder}
|
||||
useMobileUI={props.useMobileUI}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import InputBoxWithSuggestion from '../InputBoxWithSuggestion';
|
||||
import Select from '../Select';
|
||||
import type {InputJSONSchemaItemProps} from './index';
|
||||
import {InputJSONSchemaItem} from './Item';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
type JSONSchemaObjectMember = {
|
||||
key: string;
|
||||
@ -25,7 +26,8 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
|
||||
translate: __,
|
||||
renderKey,
|
||||
collapsable,
|
||||
renderValue
|
||||
renderValue,
|
||||
useMobileUI
|
||||
} = props;
|
||||
const buildMembers = React.useCallback((schema: any, value: any) => {
|
||||
const members: Array<JSONSchemaObjectMember> = [];
|
||||
@ -189,6 +191,7 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
|
||||
options.every(o => members.find(m => m.name === o.value))
|
||||
);
|
||||
const allowInput = props.schema.additionalProperties !== false;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -205,6 +208,7 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
|
||||
|
||||
<div
|
||||
className={cx('JSONSchemaObject', {
|
||||
'is-mobile': mobileUI,
|
||||
'is-expanded': collapsable && !collapsed
|
||||
})}
|
||||
>
|
||||
@ -228,7 +232,11 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
|
||||
|
||||
return (
|
||||
<div key={member.key} className={cx('JSONSchemaMember')}>
|
||||
<div className={cx('JSONSchemaMember-key')}>
|
||||
<div
|
||||
className={cx('JSONSchemaMember-key', {
|
||||
'is-mobile': mobileUI
|
||||
})}
|
||||
>
|
||||
{member.nameMutable ? (
|
||||
<>
|
||||
{renderKey ? (
|
||||
@ -247,6 +255,7 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
|
||||
clearable={false}
|
||||
placeholder={__('JSONSchema.key')}
|
||||
options={filtedOptions}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
) : (
|
||||
<Select
|
||||
@ -258,6 +267,7 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
|
||||
clearable={false}
|
||||
placeholder={__('JSONSchema.key')}
|
||||
options={filtedOptions}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
@ -267,6 +277,7 @@ export function InputJSONSchemaObject(props: InputJSONSchemaItemProps) {
|
||||
onChange={onMemberKeyChange.bind(null, member)}
|
||||
clearable={false}
|
||||
placeholder={__('JSONSchema.key')}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -29,6 +29,7 @@ export interface InputJSONSchemaItemProps extends ThemeProps, LocaleProps {
|
||||
) => JSX.Element;
|
||||
collapsable?: boolean;
|
||||
placeholder?: string;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export interface InputJSONSchemaProps
|
||||
|
@ -41,7 +41,8 @@ export class SchemaEditorItemArray extends SchemaEditorItemCommon {
|
||||
onTypeChange,
|
||||
enableAdvancedSetting,
|
||||
popOverContainer,
|
||||
placeholder
|
||||
placeholder,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const items = value?.items || {
|
||||
type: 'string'
|
||||
@ -73,6 +74,7 @@ export class SchemaEditorItemArray extends SchemaEditorItemCommon {
|
||||
enableAdvancedSetting={enableAdvancedSetting}
|
||||
popOverContainer={popOverContainer}
|
||||
placeholder={placeholder}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -54,6 +54,7 @@ export interface SchemaEditorItemCommonProps extends LocaleProps, ThemeProps {
|
||||
/** 各属性输入控件的placeholder */
|
||||
placeholder?: SchemaEditorItemPlaceholder;
|
||||
popOverContainer?: any;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export class SchemaEditorItemCommon<
|
||||
@ -102,7 +103,8 @@ export class SchemaEditorItemCommon<
|
||||
prefix,
|
||||
affix,
|
||||
types,
|
||||
placeholder
|
||||
placeholder,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -118,6 +120,7 @@ export class SchemaEditorItemCommon<
|
||||
clearable={false}
|
||||
disabled={disabled || typeMutable === false}
|
||||
simpleValue
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -135,6 +138,7 @@ export class SchemaEditorItemCommon<
|
||||
|
||||
{enableAdvancedSetting ? (
|
||||
<PickerContainer
|
||||
useMobileUI={useMobileUI}
|
||||
value={value}
|
||||
bodyRender={({isOpened, value, onChange, ref}) => {
|
||||
return isOpened ? (
|
||||
@ -152,6 +156,7 @@ export class SchemaEditorItemCommon<
|
||||
{...field}
|
||||
disabled={disabled}
|
||||
placeholder={__(placeholder?.title ?? '')}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -164,6 +169,7 @@ export class SchemaEditorItemCommon<
|
||||
<Textarea
|
||||
{...field}
|
||||
disabled={disabled}
|
||||
useMobileUI={useMobileUI}
|
||||
placeholder={__(placeholder?.description ?? '')}
|
||||
/>
|
||||
)}
|
||||
@ -178,6 +184,7 @@ export class SchemaEditorItemCommon<
|
||||
{...field}
|
||||
disabled={disabled}
|
||||
placeholder={__(placeholder?.default ?? '')}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -7,7 +7,9 @@ import {SchemaEditorItemObject} from './Object';
|
||||
export interface SchemaEditorItemProps
|
||||
extends SchemaEditorItemCommonProps,
|
||||
LocaleProps,
|
||||
ThemeProps {}
|
||||
ThemeProps {
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export class SchemaEditorItem extends React.Component<SchemaEditorItemProps> {
|
||||
render() {
|
||||
|
@ -199,7 +199,8 @@ export class SchemaEditorItemObject extends SchemaEditorItemCommon<
|
||||
onTypeChange,
|
||||
enableAdvancedSetting,
|
||||
popOverContainer,
|
||||
placeholder
|
||||
placeholder,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const members = this.state.members;
|
||||
|
||||
@ -212,6 +213,7 @@ export class SchemaEditorItemObject extends SchemaEditorItemCommon<
|
||||
{members.length ? (
|
||||
members.map((member, index) => (
|
||||
<SchemaEditorItem
|
||||
useMobileUI={useMobileUI}
|
||||
key={member.id}
|
||||
types={types}
|
||||
onTypeChange={onTypeChange}
|
||||
@ -226,6 +228,7 @@ export class SchemaEditorItemObject extends SchemaEditorItemCommon<
|
||||
onChange={this.handlePropKeyChange.bind(this, index)}
|
||||
placeholder={__(placeholder?.key ?? '')}
|
||||
disabled={disabled || !!value?.$ref}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
|
||||
<InputBox
|
||||
@ -234,6 +237,7 @@ export class SchemaEditorItemObject extends SchemaEditorItemCommon<
|
||||
onChange={this.handlePropTitleChange.bind(this, index)}
|
||||
placeholder={__(placeholder?.title ?? '')}
|
||||
disabled={disabled || !!value?.$ref}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ export interface SchemaEditorProps extends LocaleProps, ThemeProps {
|
||||
* 各属性输入控件的占位提示文本
|
||||
*/
|
||||
placeholder?: SchemaEditorItemPlaceholder;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
export class SchemaEditor extends React.Component<SchemaEditorProps> {
|
||||
@ -171,7 +172,8 @@ export class SchemaEditor extends React.Component<SchemaEditorProps> {
|
||||
definitions,
|
||||
enableAdvancedSetting,
|
||||
popOverContainer,
|
||||
placeholder
|
||||
placeholder,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const value: JSONSchema = this.props.value || {
|
||||
type: defaultType || 'object'
|
||||
@ -231,6 +233,7 @@ export class SchemaEditor extends React.Component<SchemaEditorProps> {
|
||||
enableAdvancedSetting={enableAdvancedSetting}
|
||||
popOverContainer={popOverContainer}
|
||||
placeholder={placeholder}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -92,6 +92,10 @@ export interface CollapseSchema extends BaseSchema {
|
||||
* 卡片隐藏就销毁内容。
|
||||
*/
|
||||
unmountOnExit?: boolean;
|
||||
/**
|
||||
* 标题内容分割线
|
||||
*/
|
||||
divideLine?: boolean;
|
||||
}
|
||||
|
||||
export interface CollapseProps
|
||||
@ -147,9 +151,13 @@ export default class Collapse extends React.Component<CollapseProps, {}> {
|
||||
disabled,
|
||||
collapsed,
|
||||
propsUpdate,
|
||||
onCollapse
|
||||
onCollapse,
|
||||
useMobileUI,
|
||||
divideLine
|
||||
} = this.props;
|
||||
|
||||
console.log('cooooooo', divideLine);
|
||||
|
||||
return (
|
||||
<BasicCollapse
|
||||
id={id}
|
||||
@ -194,7 +202,9 @@ export default class Collapse extends React.Component<CollapseProps, {}> {
|
||||
? render('body', body)
|
||||
: null
|
||||
}
|
||||
useMobileUI={useMobileUI}
|
||||
onCollapse={onCollapse}
|
||||
divideLine={divideLine}
|
||||
></BasicCollapse>
|
||||
);
|
||||
}
|
||||
|
@ -60,7 +60,8 @@ export class CollapseGroupRender extends React.Component<
|
||||
body,
|
||||
className,
|
||||
style,
|
||||
render
|
||||
render,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
return (
|
||||
<CollapseGroup
|
||||
@ -70,6 +71,7 @@ export class CollapseGroupRender extends React.Component<
|
||||
expandIconPosition={expandIconPosition}
|
||||
className={className}
|
||||
style={style}
|
||||
useMobileUI={useMobileUI}
|
||||
>
|
||||
{render('body', body || '')}
|
||||
</CollapseGroup>
|
||||
|
@ -46,6 +46,7 @@ import {
|
||||
} from '../../Schema';
|
||||
import {ListenerAction} from 'amis-core';
|
||||
import type {SchemaTokenizeableString} from '../../Schema';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
export type ComboCondition = {
|
||||
test: string;
|
||||
@ -1260,9 +1261,12 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
itemRemovableOn,
|
||||
disabled,
|
||||
removable,
|
||||
deleteBtn
|
||||
deleteBtn,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
const finnalRemovable =
|
||||
store.removable !== false && // minLength ?
|
||||
!disabled && // 控件自身是否禁用
|
||||
@ -1330,7 +1334,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
onClick={this.deleteItem.bind(this, index)}
|
||||
key="delete"
|
||||
className={cx(`Combo-delBtn ${!store.removable ? 'is-disabled' : ''}`)}
|
||||
data-tooltip={__('delete')}
|
||||
data-tooltip={!mobileUI ? __('delete') : null}
|
||||
data-position="bottom"
|
||||
>
|
||||
{deleteIcon ? (
|
||||
@ -1435,11 +1439,13 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
translate: __,
|
||||
itemClassName,
|
||||
itemsWrapperClassName,
|
||||
static: isStatic
|
||||
static: isStatic,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
let items = this.props.items;
|
||||
let value = this.props.value;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
if (flat && typeof value === 'string') {
|
||||
value = value.split(delimiter || ',');
|
||||
@ -1449,6 +1455,9 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
<div
|
||||
className={cx(
|
||||
`Combo Combo--multi`,
|
||||
{
|
||||
'is-mobile': mobileUI
|
||||
},
|
||||
multiLine ? `Combo--ver` : `Combo--hor`,
|
||||
noBorder ? `Combo--noBorder` : '',
|
||||
disabled ? 'is-disabled' : '',
|
||||
@ -1564,9 +1573,11 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
typeSwitchable,
|
||||
nullable,
|
||||
translate: __,
|
||||
itemClassName
|
||||
itemClassName,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
let items = this.props.items;
|
||||
const data = isObject(value) ? this.formatValue(value) : this.defaultValue;
|
||||
let condition: ComboCondition | null = null;
|
||||
@ -1580,6 +1591,9 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
<div
|
||||
className={cx(
|
||||
`Combo Combo--single`,
|
||||
{
|
||||
'is-mobile': mobileUI
|
||||
},
|
||||
multiLine ? `Combo--ver` : `Combo--hor`,
|
||||
noBorder ? `Combo--noBorder` : '',
|
||||
disabled ? 'is-disabled' : ''
|
||||
|
@ -196,7 +196,8 @@ export class InputFormulaRenderer extends React.Component<InputFormulaProps> {
|
||||
functionClassName,
|
||||
data,
|
||||
onPickerOpen,
|
||||
selfVariableName
|
||||
selfVariableName,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
let {variables, functions} = this.props;
|
||||
|
||||
@ -238,6 +239,7 @@ export class InputFormulaRenderer extends React.Component<InputFormulaProps> {
|
||||
onPickerOpen={onPickerOpen}
|
||||
selfVariableName={selfVariableName}
|
||||
mixedMode={mixedMode}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
anyChanged
|
||||
} from 'amis-core';
|
||||
import {FormBaseControlSchema, SchemaCollection} from '../../Schema';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
/**
|
||||
* InputGroup
|
||||
@ -190,7 +191,8 @@ export class InputGroup extends React.Component<
|
||||
data,
|
||||
classnames: cx,
|
||||
static: isStatic,
|
||||
disabled
|
||||
disabled,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const {errorMode} = this.getValidationConfig();
|
||||
|
||||
@ -218,6 +220,8 @@ export class InputGroup extends React.Component<
|
||||
(formHorizontal
|
||||
? makeHorizontalDeeper(formHorizontal as any, inputs.length)
|
||||
: undefined);
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
@ -226,6 +230,9 @@ export class InputGroup extends React.Component<
|
||||
className,
|
||||
{
|
||||
'is-focused': this.state.isFocused
|
||||
},
|
||||
{
|
||||
'is-mobile': mobileUI
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
@ -11,7 +11,8 @@ import {
|
||||
autobind,
|
||||
stripNumber,
|
||||
filter,
|
||||
ActionObject
|
||||
ActionObject,
|
||||
isMobile
|
||||
} from 'amis-core';
|
||||
import {Range as InputRange, NumberInput, Icon} from 'amis-ui';
|
||||
import {FormBaseControlSchema, SchemaObject} from '../../Schema';
|
||||
@ -436,7 +437,8 @@ export class Input extends React.Component<RangeItemProps, any> {
|
||||
classPrefix: ns,
|
||||
disabled,
|
||||
max,
|
||||
min
|
||||
min,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const _value = multiple
|
||||
? type === 'min'
|
||||
@ -454,6 +456,7 @@ export class Input extends React.Component<RangeItemProps, any> {
|
||||
disabled={disabled}
|
||||
onBlur={this.onBlur}
|
||||
onFocus={this.onFocus}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -638,7 +641,8 @@ export default class RangeControl extends React.PureComponent<
|
||||
max,
|
||||
render,
|
||||
marks,
|
||||
region
|
||||
region,
|
||||
useMobileUI
|
||||
} = props;
|
||||
|
||||
// 处理自定义json配置
|
||||
@ -653,6 +657,7 @@ export default class RangeControl extends React.PureComponent<
|
||||
(renderMarks[key] = render(region, item as SchemaObject));
|
||||
}
|
||||
});
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -660,6 +665,7 @@ export default class RangeControl extends React.PureComponent<
|
||||
'RangeControl',
|
||||
`${ns}InputRange`,
|
||||
{'is-disabled': disabled},
|
||||
{'is-mobile': mobileUI},
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
@ -74,6 +74,7 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
|
||||
placeholder,
|
||||
disabled,
|
||||
classPrefix: ns,
|
||||
useMobileUI,
|
||||
translate: __
|
||||
} = this.props;
|
||||
|
||||
@ -216,6 +217,7 @@ export default class RepeatControl extends React.Component<RepeatProps, any> {
|
||||
searchable={false}
|
||||
disabled={disabled}
|
||||
joinValues={false}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,6 +8,9 @@ import {Icon} from 'amis-ui';
|
||||
import {FormBaseControlSchema, FormSchema, SchemaClassName} from '../../Schema';
|
||||
import Sortable from 'sortablejs';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import {isMobile} from 'amis-core';
|
||||
import {PopUp} from 'amis-ui';
|
||||
import {autobind} from 'amis-core';
|
||||
|
||||
/**
|
||||
* SubForm 子表单
|
||||
@ -152,6 +155,7 @@ export default class SubFormControl extends React.PureComponent<
|
||||
dragTip?: HTMLElement;
|
||||
sortable?: Sortable;
|
||||
id: string = guid();
|
||||
tempValue: any;
|
||||
constructor(props: SubFormProps) {
|
||||
super(props);
|
||||
|
||||
@ -211,6 +215,7 @@ export default class SubFormControl extends React.PureComponent<
|
||||
return;
|
||||
}
|
||||
|
||||
this.tempValue = value[index];
|
||||
this.setState({
|
||||
dialogData: createObject(this.props.data, value[index]),
|
||||
dialogCtx: {
|
||||
@ -255,6 +260,36 @@ export default class SubFormControl extends React.PureComponent<
|
||||
this.close();
|
||||
}
|
||||
|
||||
@autobind
|
||||
handlePopupConfirm() {
|
||||
const values = this.tempValue;
|
||||
const {multiple, onChange, value} = this.props;
|
||||
const ctx = this.state.dialogCtx;
|
||||
|
||||
if (multiple) {
|
||||
let newValue = Array.isArray(value) ? value.concat() : [];
|
||||
|
||||
if (ctx?.mode === 'add') {
|
||||
newValue.push({
|
||||
...values
|
||||
});
|
||||
} else {
|
||||
newValue[ctx!.index!] = {
|
||||
...newValue[ctx!.index!],
|
||||
...values
|
||||
};
|
||||
}
|
||||
onChange(newValue);
|
||||
} else {
|
||||
onChange({
|
||||
...value,
|
||||
...values
|
||||
});
|
||||
}
|
||||
|
||||
this.close();
|
||||
}
|
||||
|
||||
dragTipRef(ref: any) {
|
||||
if (!this.dragTip && ref) {
|
||||
this.initDragging();
|
||||
@ -332,6 +367,28 @@ export default class SubFormControl extends React.PureComponent<
|
||||
};
|
||||
}
|
||||
|
||||
buildFormSchema() {
|
||||
let {form} = this.props;
|
||||
|
||||
const dialogProps = [
|
||||
'title',
|
||||
'actions',
|
||||
'name',
|
||||
'size',
|
||||
'closeOnEsc',
|
||||
'closeOnOutside',
|
||||
'showErrorMsg',
|
||||
'showCloseButton',
|
||||
'bodyClassName',
|
||||
'type'
|
||||
];
|
||||
|
||||
return {
|
||||
type: 'form',
|
||||
...omit(form, dialogProps)
|
||||
};
|
||||
}
|
||||
|
||||
renderMultipe() {
|
||||
const {
|
||||
addButtonClassName,
|
||||
@ -502,20 +559,48 @@ export default class SubFormControl extends React.PureComponent<
|
||||
}
|
||||
|
||||
render() {
|
||||
const {multiple, classPrefix: ns, className, style, render} = this.props;
|
||||
const {
|
||||
multiple,
|
||||
classPrefix: ns,
|
||||
className,
|
||||
style,
|
||||
render,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
const dialogData = this.state.dialogData;
|
||||
const dialogCtx = this.state.dialogCtx;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div className={cx(`${ns}SubFormControl`, className)}>
|
||||
{multiple ? this.renderMultipe() : this.renderSingle()}
|
||||
{render(`modal`, this.buildDialogSchema(), {
|
||||
show: !!dialogCtx,
|
||||
onClose: this.close,
|
||||
onConfirm: this.handleDialogConfirm,
|
||||
data: dialogData,
|
||||
formStore: undefined
|
||||
})}
|
||||
{!mobileUI ? (
|
||||
render(`modal`, this.buildDialogSchema(), {
|
||||
show: !!dialogCtx,
|
||||
onClose: this.close,
|
||||
onConfirm: this.handleDialogConfirm,
|
||||
data: dialogData,
|
||||
formStore: undefined
|
||||
})
|
||||
) : (
|
||||
<PopUp
|
||||
isShow={!!dialogCtx}
|
||||
showConfirm
|
||||
onConfirm={this.handlePopupConfirm}
|
||||
onHide={this.close}
|
||||
>
|
||||
<div className="flex-1 pl-10 pr-10">
|
||||
{render('form', this.buildFormSchema(), {
|
||||
data: dialogData,
|
||||
formStore: undefined,
|
||||
wrapWithPanel: false,
|
||||
onChange: (val: any) => {
|
||||
this.tempValue = val;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</PopUp>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -10,13 +10,14 @@ import find from 'lodash/find';
|
||||
import isInteger from 'lodash/isInteger';
|
||||
import unionWith from 'lodash/unionWith';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import {ResultBox, SpinnerExtraProps} from 'amis-ui';
|
||||
import {PopUp, ResultBox, SpinnerExtraProps} from 'amis-ui';
|
||||
import {autobind, filterTree, createObject} from 'amis-core';
|
||||
import {Spinner} from 'amis-ui';
|
||||
import {Overlay} from 'amis-core';
|
||||
import {PopOver} from 'amis-core';
|
||||
import {ListMenu} from 'amis-ui';
|
||||
import {ListMenu, Button} from 'amis-ui';
|
||||
import {ActionObject} from 'amis-core';
|
||||
import {isMobile} from 'amis-core';
|
||||
import {FormOptionsSchema} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
import {TooltipWrapperSchema} from '../TooltipWrapper';
|
||||
@ -98,6 +99,8 @@ export interface TagState {
|
||||
inputValue: string;
|
||||
isFocused?: boolean;
|
||||
isOpened?: boolean;
|
||||
selectedOptions: Option[];
|
||||
cacheOptions: Option[];
|
||||
}
|
||||
|
||||
export default class TagControl extends React.PureComponent<
|
||||
@ -116,11 +119,16 @@ export default class TagControl extends React.PureComponent<
|
||||
separator: '-'
|
||||
};
|
||||
|
||||
state = {
|
||||
isOpened: false,
|
||||
inputValue: '',
|
||||
isFocused: false
|
||||
};
|
||||
constructor(props: TagProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpened: false,
|
||||
inputValue: '',
|
||||
isFocused: false,
|
||||
selectedOptions: props.selectedOptions,
|
||||
cacheOptions: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: TagProps) {
|
||||
const props = this.props;
|
||||
@ -242,9 +250,15 @@ export default class TagControl extends React.PureComponent<
|
||||
}
|
||||
|
||||
@autobind
|
||||
getValue(type: 'push' | 'pop' | 'normal' = 'normal', option: any = {}) {
|
||||
const {selectedOptions, joinValues, extractValue, delimiter, valueField} =
|
||||
this.props;
|
||||
getValue(
|
||||
type: 'push' | 'pop' | 'normal' = 'normal',
|
||||
option: any = {},
|
||||
selectedOptions?: Option[]
|
||||
) {
|
||||
const {joinValues, extractValue, delimiter, valueField} = this.props;
|
||||
selectedOptions = selectedOptions
|
||||
? selectedOptions
|
||||
: this.props.selectedOptions;
|
||||
|
||||
const newValue = selectedOptions.concat();
|
||||
if (type === 'push') {
|
||||
@ -286,11 +300,84 @@ export default class TagControl extends React.PureComponent<
|
||||
isPrevented || onChange(newValueRes);
|
||||
}
|
||||
|
||||
// 移动端特殊处理
|
||||
addItem2(option: Option) {
|
||||
const {useMobileUI, valueField = 'value'} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
if (mobileUI) {
|
||||
const selectedOptions = this.state.selectedOptions.concat();
|
||||
let index = selectedOptions.findIndex(
|
||||
item => item[valueField] === option[valueField]
|
||||
);
|
||||
if (~index) {
|
||||
selectedOptions.splice(index, 1);
|
||||
} else if (!this.isReachMaxFromState()) {
|
||||
selectedOptions.push(option);
|
||||
}
|
||||
this.setState({
|
||||
selectedOptions
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 手机端校验
|
||||
isExist(inputValue: string) {
|
||||
const {options, valueField = 'value'} = this.props;
|
||||
const {cacheOptions} = this.state;
|
||||
return (
|
||||
options.some(item => item[valueField] === inputValue) ||
|
||||
cacheOptions.some(item => item[valueField] === inputValue)
|
||||
);
|
||||
}
|
||||
|
||||
@autobind
|
||||
addSelection() {
|
||||
let {inputValue} = this.state;
|
||||
const {maxTagLength} = this.props;
|
||||
const selectedOptions = this.state.selectedOptions.slice();
|
||||
const cacheOptions = this.state.cacheOptions.slice();
|
||||
|
||||
if (maxTagLength !== undefined) {
|
||||
inputValue = inputValue.trim();
|
||||
inputValue = inputValue.slice(0, maxTagLength);
|
||||
}
|
||||
if (this.isExist(inputValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputValue && !this.isReachMaxFromState()) {
|
||||
const addedValues = this.normalizeInputValue(inputValue);
|
||||
selectedOptions.push(addedValues[0]);
|
||||
cacheOptions.push(addedValues[0]);
|
||||
this.setState({
|
||||
inputValue: '',
|
||||
selectedOptions,
|
||||
cacheOptions
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
async onConfirm() {
|
||||
const {selectedOptions} = this.state;
|
||||
const {onChange} = this.props;
|
||||
const newValueRes = this.getValue('normal', {}, selectedOptions);
|
||||
|
||||
const isPrevented = await this.dispatchEvent('change', {
|
||||
value: newValueRes,
|
||||
selectedItems: selectedOptions
|
||||
});
|
||||
|
||||
isPrevented || onChange(newValueRes);
|
||||
this.close();
|
||||
}
|
||||
|
||||
@autobind
|
||||
async handleFocus(e: any) {
|
||||
this.setState({
|
||||
isFocused: true,
|
||||
isOpened: true
|
||||
isOpened: true,
|
||||
selectedOptions: this.props.selectedOptions
|
||||
});
|
||||
|
||||
const newValueRes = this.getValue('normal');
|
||||
@ -303,8 +390,11 @@ export default class TagControl extends React.PureComponent<
|
||||
|
||||
@autobind
|
||||
async handleBlur(e: any) {
|
||||
const {selectedOptions, onChange} = this.props;
|
||||
|
||||
const {selectedOptions, onChange, useMobileUI, options} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
if (mobileUI && options.length) {
|
||||
return;
|
||||
}
|
||||
const value = this.state.inputValue.trim();
|
||||
|
||||
if (!this.validateInputValue(value)) {
|
||||
@ -420,6 +510,12 @@ export default class TagControl extends React.PureComponent<
|
||||
|
||||
@autobind
|
||||
handleOptionChange(option: Option) {
|
||||
const {useMobileUI} = this.props;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
if (mobileUI) {
|
||||
this.addItem2(option);
|
||||
return;
|
||||
}
|
||||
if (this.isReachMax() || this.state.inputValue || !option) {
|
||||
return;
|
||||
}
|
||||
@ -448,6 +544,13 @@ export default class TagControl extends React.PureComponent<
|
||||
return max != null && isInteger(max) && selectedOptions.length >= max;
|
||||
}
|
||||
|
||||
@autobind
|
||||
isReachMaxFromState() {
|
||||
const {selectedOptions} = this.state;
|
||||
const {max} = this.props;
|
||||
return max != null && isInteger(max) && selectedOptions.length >= max;
|
||||
}
|
||||
|
||||
@supportStatic()
|
||||
render() {
|
||||
const {
|
||||
@ -468,16 +571,17 @@ export default class TagControl extends React.PureComponent<
|
||||
overflowTagPopover,
|
||||
translate: __,
|
||||
loadingConfig,
|
||||
valueField
|
||||
valueField,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
const finnalOptions = Array.isArray(options)
|
||||
? filterTree(
|
||||
options,
|
||||
item =>
|
||||
(Array.isArray(item.children) && !!item.children.length) ||
|
||||
(item[valueField || 'value'] !== undefined &&
|
||||
!~selectedOptions.indexOf(item)),
|
||||
(mobileUI || !~selectedOptions.indexOf(item))),
|
||||
0,
|
||||
true
|
||||
)
|
||||
@ -488,7 +592,7 @@ export default class TagControl extends React.PureComponent<
|
||||
return (
|
||||
<Downshift
|
||||
selectedItem={selectedOptions}
|
||||
isOpen={this.state.isFocused}
|
||||
isOpen={mobileUI ? this.state.isOpened : this.state.isFocused}
|
||||
inputValue={this.state.inputValue}
|
||||
onChange={this.handleOptionChange}
|
||||
itemToString={this.renderItem}
|
||||
@ -517,7 +621,8 @@ export default class TagControl extends React.PureComponent<
|
||||
clearable={clearable}
|
||||
maxTagCount={maxTagCount}
|
||||
overflowTagPopover={overflowTagPopover}
|
||||
allowInput
|
||||
allowInput={!mobileUI || (mobileUI && !options?.length)}
|
||||
useMobileUI={useMobileUI}
|
||||
>
|
||||
{loading ? (
|
||||
<Spinner loadingConfig={loadingConfig} size="sm" />
|
||||
@ -525,40 +630,96 @@ export default class TagControl extends React.PureComponent<
|
||||
</ResultBox>
|
||||
|
||||
{dropdown !== false ? (
|
||||
<Overlay
|
||||
container={popOverContainer || this.getParent}
|
||||
target={this.getTarget}
|
||||
placement={'auto'}
|
||||
show={isOpen && !!finnalOptions.length}
|
||||
>
|
||||
<PopOver
|
||||
overlay
|
||||
className={cx('TagControl-popover')}
|
||||
mobileUI ? (
|
||||
<PopUp
|
||||
className={cx(`Tag-popup`)}
|
||||
container={popOverContainer}
|
||||
isShow={isOpen && !!finnalOptions.length}
|
||||
showConfirm={true}
|
||||
onConfirm={this.onConfirm}
|
||||
onHide={this.close}
|
||||
>
|
||||
<ListMenu
|
||||
options={finnalOptions}
|
||||
itemRender={this.renderItem}
|
||||
highlightIndex={highlightedIndex}
|
||||
getItemProps={({
|
||||
item,
|
||||
index
|
||||
}: {
|
||||
item: Option;
|
||||
index: number;
|
||||
}) => ({
|
||||
...getItemProps({
|
||||
index,
|
||||
<div>
|
||||
<ListMenu
|
||||
selectedOptions={selectedOptions}
|
||||
useMobileUI={useMobileUI}
|
||||
options={finnalOptions.concat(this.state.cacheOptions)}
|
||||
itemRender={this.renderItem}
|
||||
highlightIndex={highlightedIndex}
|
||||
getItemProps={({
|
||||
item,
|
||||
disabled: reachMax || item.disabled,
|
||||
className: cx('ListMenu-item', {
|
||||
'is-disabled': reachMax
|
||||
index
|
||||
}: {
|
||||
item: Option;
|
||||
index: number;
|
||||
}) => ({
|
||||
...getItemProps({
|
||||
index,
|
||||
item,
|
||||
className: cx('ListMenu-item', {
|
||||
'is-active': ~(
|
||||
this.state.selectedOptions.map(
|
||||
item => item[valueField]
|
||||
) || []
|
||||
).indexOf(item[valueField])
|
||||
})
|
||||
})
|
||||
})
|
||||
})}
|
||||
/>
|
||||
</PopOver>
|
||||
</Overlay>
|
||||
})}
|
||||
/>
|
||||
{mobileUI && !this.isReachMaxFromState() ? (
|
||||
<div className={cx('ListMenu-add-wrap')}>
|
||||
<ResultBox
|
||||
placeholder={__('SubForm.add')}
|
||||
allowInput
|
||||
value={this.state.inputValue}
|
||||
useMobileUI={useMobileUI}
|
||||
clearable
|
||||
maxTagCount={maxTagCount}
|
||||
onChange={value => {
|
||||
this.setState({inputValue: value});
|
||||
}}
|
||||
onBlur={this.addSelection}
|
||||
></ResultBox>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</PopUp>
|
||||
) : (
|
||||
<Overlay
|
||||
container={popOverContainer || this.getParent}
|
||||
target={this.getTarget}
|
||||
placement={'auto'}
|
||||
show={isOpen && !!finnalOptions.length}
|
||||
>
|
||||
<PopOver
|
||||
overlay
|
||||
className={cx('TagControl-popover')}
|
||||
onHide={this.close}
|
||||
>
|
||||
<ListMenu
|
||||
options={finnalOptions}
|
||||
itemRender={this.renderItem}
|
||||
highlightIndex={highlightedIndex}
|
||||
getItemProps={({
|
||||
item,
|
||||
index
|
||||
}: {
|
||||
item: Option;
|
||||
index: number;
|
||||
}) => ({
|
||||
...getItemProps({
|
||||
index,
|
||||
item,
|
||||
disabled: reachMax || item.disabled,
|
||||
className: cx('ListMenu-item', {
|
||||
'is-disabled': reachMax
|
||||
})
|
||||
})
|
||||
})}
|
||||
/>
|
||||
</PopOver>
|
||||
</Overlay>
|
||||
)
|
||||
) : (
|
||||
// 保留原来的展现方式,不推荐
|
||||
<div className={cx('TagControl-sug')}>
|
||||
|
@ -173,6 +173,7 @@ export interface TreeProps
|
||||
SpinnerExtraProps {
|
||||
enableNodePath?: boolean;
|
||||
pathSeparator?: string;
|
||||
useMobileUI?: boolean;
|
||||
}
|
||||
|
||||
interface TreeState {
|
||||
@ -377,7 +378,8 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
|
||||
enableDefaultIcon,
|
||||
searchable,
|
||||
searchConfig = {},
|
||||
heightAuto
|
||||
heightAuto,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
let {highlightTxt} = this.props;
|
||||
const {filteredOptions, keyword} = this.state;
|
||||
@ -439,6 +441,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
|
||||
itemHeight={toNumber(itemHeight) > 0 ? toNumber(itemHeight) : undefined}
|
||||
itemRender={menuTpl ? this.renderOptionItem : undefined}
|
||||
enableDefaultIcon={enableDefaultIcon}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -467,6 +470,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
|
||||
clearable={true}
|
||||
{...omit(searchConfig, 'className', 'sticky')}
|
||||
onSearch={this.handleSearch}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
{TreeCmpt}
|
||||
</>
|
||||
|
@ -138,11 +138,12 @@ export default class JSONSchemaEditorControl extends React.PureComponent<JSONSch
|
||||
}
|
||||
|
||||
render() {
|
||||
const {enableAdvancedSetting, env, ...rest} = this.props;
|
||||
const {enableAdvancedSetting, useMobileUI, env, ...rest} = this.props;
|
||||
|
||||
return (
|
||||
<JSONSchemaEditor
|
||||
{...rest}
|
||||
useMobileUI={useMobileUI}
|
||||
placeholder={this.normalizePlaceholder()}
|
||||
enableAdvancedSetting={enableAdvancedSetting}
|
||||
renderModalProps={this.renderModalProps}
|
||||
|
@ -946,7 +946,7 @@ export default class NestedSelectControl extends React.Component<
|
||||
onKeyDown={this.handleInputKeyDown}
|
||||
clearable={clearable}
|
||||
hasDropDownArrow={true}
|
||||
allowInput={searchable}
|
||||
allowInput={searchable && !mobileUI}
|
||||
>
|
||||
{loading ? (
|
||||
<Spinner loadingConfig={loadingConfig} size="sm" />
|
||||
|
@ -23,8 +23,10 @@ import {
|
||||
resolveEventData
|
||||
} from 'amis-core';
|
||||
import {Html, Icon} from 'amis-ui';
|
||||
import {isMobile} from 'amis-core';
|
||||
import {FormOptionsSchema, SchemaTpl} from '../../Schema';
|
||||
import intersectionWith from 'lodash/intersectionWith';
|
||||
import {PopUp} from 'amis-ui';
|
||||
|
||||
/**
|
||||
* Picker
|
||||
@ -521,11 +523,14 @@ export default class PickerControl extends React.PureComponent<
|
||||
translate: __,
|
||||
popOverContainer,
|
||||
modalTitle,
|
||||
data
|
||||
data,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div className={cx(`PickerControl`, className)}>
|
||||
<div className={cx(`PickerControl`, {'is-mobile': mobileUI}, className)}>
|
||||
{embed ? (
|
||||
<div className={cx('Picker')}>
|
||||
{this.renderBody({popOverContainer})}
|
||||
@ -556,6 +561,7 @@ export default class PickerControl extends React.PureComponent<
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
readOnly={mobileUI}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -302,7 +302,8 @@ export class TabsTransferRenderer extends BaseTabsTransferRenderer<TabsTransferP
|
||||
loadingConfig,
|
||||
valueField = 'value',
|
||||
labelField = 'label',
|
||||
data
|
||||
data,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -333,6 +334,7 @@ export class TabsTransferRenderer extends BaseTabsTransferRenderer<TabsTransferP
|
||||
labelField={labelField}
|
||||
valueField={valueField}
|
||||
ctx={data}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
|
@ -106,7 +106,8 @@ export class TabsTransferPickerRenderer extends BaseTabsTransferRenderer<TabsTra
|
||||
virtualThreshold,
|
||||
loadingConfig,
|
||||
labelField = 'label',
|
||||
valueField = 'value'
|
||||
valueField = 'value',
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -139,6 +140,7 @@ export class TabsTransferPickerRenderer extends BaseTabsTransferRenderer<TabsTra
|
||||
virtualThreshold={virtualThreshold}
|
||||
labelField={labelField}
|
||||
valueField={valueField}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
|
@ -532,7 +532,8 @@ export class BaseTransferRenderer<
|
||||
itemHeight,
|
||||
loadingConfig,
|
||||
showInvalidMatch,
|
||||
onlyChildren
|
||||
onlyChildren,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
// 目前 LeftOptions 没有接口可以动态加载
|
||||
@ -593,6 +594,7 @@ export class BaseTransferRenderer<
|
||||
}
|
||||
loadingConfig={loadingConfig}
|
||||
showInvalidMatch={showInvalidMatch}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
|
@ -85,7 +85,8 @@ export class TransferPickerRenderer extends BaseTransferRenderer<TabsTransferPro
|
||||
virtualThreshold,
|
||||
loadingConfig,
|
||||
labelField = 'label',
|
||||
valueField = 'value'
|
||||
valueField = 'value',
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
// 目前 LeftOptions 没有接口可以动态加载
|
||||
@ -135,6 +136,7 @@ export class TransferPickerRenderer extends BaseTransferRenderer<TabsTransferPro
|
||||
toNumber(itemHeight) > 0 ? toNumber(itemHeight) : undefined
|
||||
}
|
||||
virtualThreshold={virtualThreshold}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
|
@ -135,6 +135,7 @@ export interface TreeSelectProps
|
||||
export interface TreeSelectState {
|
||||
isOpened: boolean;
|
||||
inputValue: string;
|
||||
tempValue: string;
|
||||
}
|
||||
|
||||
export default class TreeSelectControl extends React.Component<
|
||||
@ -182,12 +183,15 @@ export default class TreeSelectControl extends React.Component<
|
||||
|
||||
this.state = {
|
||||
inputValue: '',
|
||||
tempValue: '',
|
||||
isOpened: false
|
||||
};
|
||||
|
||||
this.open = this.open.bind(this);
|
||||
this.close = this.close.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleTempChange = this.handleTempChange.bind(this);
|
||||
this.handleConfirm = this.handleConfirm.bind(this);
|
||||
this.clearValue = this.clearValue.bind(this);
|
||||
this.handleFocus = this.handleFocus.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
@ -316,6 +320,22 @@ export default class TreeSelectControl extends React.Component<
|
||||
);
|
||||
}
|
||||
|
||||
handleTempChange(value: any) {
|
||||
this.setState({
|
||||
tempValue: value
|
||||
});
|
||||
}
|
||||
|
||||
handleConfirm() {
|
||||
this.close();
|
||||
this.setState(
|
||||
{
|
||||
inputValue: ''
|
||||
},
|
||||
() => this.resultChangeEvent(this.state.tempValue)
|
||||
);
|
||||
}
|
||||
|
||||
handleInputChange(value: string) {
|
||||
const {autoComplete, data} = this.props;
|
||||
|
||||
@ -582,13 +602,15 @@ export default class TreeSelectControl extends React.Component<
|
||||
virtualThreshold,
|
||||
itemHeight,
|
||||
menuTpl,
|
||||
enableDefaultIcon
|
||||
enableDefaultIcon,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
let filtedOptions =
|
||||
!isEffectiveApi(autoComplete) && searchable && this.state.inputValue
|
||||
? this.filterOptions(options, this.state.inputValue)
|
||||
: options;
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<TreeSelector
|
||||
@ -599,7 +621,7 @@ export default class TreeSelectControl extends React.Component<
|
||||
labelField={labelField}
|
||||
valueField={valueField}
|
||||
disabled={disabled}
|
||||
onChange={this.handleChange}
|
||||
onChange={mobileUI ? this.handleTempChange : this.handleChange}
|
||||
joinValues={joinValues}
|
||||
extractValue={extractValue}
|
||||
delimiter={delimiter}
|
||||
@ -644,6 +666,7 @@ export default class TreeSelectControl extends React.Component<
|
||||
itemHeight={toNumber(itemHeight) > 0 ? toNumber(itemHeight) : undefined}
|
||||
itemRender={menuTpl ? this.renderOptionItem : undefined}
|
||||
enableDefaultIcon={enableDefaultIcon}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -711,8 +734,10 @@ export default class TreeSelectControl extends React.Component<
|
||||
onBlur={this.handleBlur}
|
||||
onKeyDown={this.handleInputKeyDown}
|
||||
clearable={clearable}
|
||||
allowInput={searchable || isEffectiveApi(autoComplete)}
|
||||
allowInput={!mobileUI && (searchable || isEffectiveApi(autoComplete))}
|
||||
hasDropDownArrow
|
||||
readOnly={mobileUI}
|
||||
useMobileUI
|
||||
>
|
||||
{loading ? (
|
||||
<Spinner loadingConfig={loadingConfig} size="sm" />
|
||||
@ -745,6 +770,8 @@ export default class TreeSelectControl extends React.Component<
|
||||
className={cx(`${ns}TreeSelect-popup`)}
|
||||
isShow={isOpened}
|
||||
onHide={this.close}
|
||||
showConfirm
|
||||
onConfirm={this.handleConfirm}
|
||||
>
|
||||
{this.renderOuter()}
|
||||
</PopUp>
|
||||
|
@ -196,7 +196,8 @@ export class SearchBoxRenderer extends React.Component<
|
||||
placeholder,
|
||||
onChange,
|
||||
className,
|
||||
style
|
||||
style,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
const value = this.state.value;
|
||||
@ -221,6 +222,7 @@ export class SearchBoxRenderer extends React.Component<
|
||||
onChange={this.handleChange}
|
||||
onFocus={() => this.dispatchEvent('focus')}
|
||||
onBlur={() => this.dispatchEvent('blur')}
|
||||
useMobileUI={useMobileUI}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ import {getStyleNumber} from 'amis-core';
|
||||
import {exportExcel} from './exportExcel';
|
||||
import type {IColumn, IRow} from 'amis-core';
|
||||
import intersection from 'lodash/intersection';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
/**
|
||||
* 表格列,不指定类型时默认为文本类型。
|
||||
@ -3038,7 +3039,8 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
classnames: cx,
|
||||
affixColumns,
|
||||
autoFillHeight,
|
||||
autoGenerateFilter
|
||||
autoGenerateFilter,
|
||||
useMobileUI
|
||||
} = this.props;
|
||||
|
||||
this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了,已经渲染了就不重复渲染了。
|
||||
@ -3048,10 +3050,11 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
const tableClassName = cx('Table-table', this.props.tableClassName, {
|
||||
'Table-table--withCombine': store.combineNum > 0
|
||||
});
|
||||
const mobileUI = useMobileUI && isMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx('Table', className, {
|
||||
className={cx('Table', {'is-mobile': mobileUI}, className, {
|
||||
'Table--unsaved': !!store.modified || !!store.moved,
|
||||
'Table--autoFillHeight': autoFillHeight
|
||||
})}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user