Merge remote-tracking branch 'baidu/master'

This commit is contained in:
2betop 2023-06-08 21:47:02 +08:00
commit 00acc95d45
155 changed files with 3325 additions and 732 deletions

View File

@ -678,7 +678,7 @@ order: 31
}
],
searchable: true,
multiple: false,
multiple: true,
joinValues: true,
clearable: true
}

View File

@ -15,6 +15,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"swipeable": true,
"tabs": [
{
"title": "Tab 1",
@ -789,6 +790,7 @@ order: 68
| sidePosition | `left` / `right` | `left` | `sidebar` 模式下,标签栏位置 |
| collapseOnExceed | `number` | | 当 tabs 超出多少个时开始折叠 |
| collapseBtnLabel | `string` | `more` | 用来设置折叠按钮的文字 |
| swipeable | `boolean` | false | 是否开启手势滑动切换(移动端生效) |
## 事件表

View File

@ -227,7 +227,7 @@ fis.match('*.html:jsx', {
// 这些用了 esm
fis.match(
'{echarts/extension/**.js,zrender/**.js,markdown-it-html5-media/**.js,react-hook-form/**.js,qrcode.react/**.js,axios/**.js}',
'{echarts/**.js,zrender/**.js,echarts-wordcloud/**.js,markdown-it-html5-media/**.js,react-hook-form/**.js,qrcode.react/**.js,axios/**.js}',
{
parser: fis.plugin('typescript', {
sourceMap: false,
@ -464,6 +464,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
'!zrender/**',
'!echarts/**',
'!echarts-stat/**',
'!echarts-wordcloud/**',
'!papaparse/**',
'!exceljs/**',
'!xlsx/**',
@ -533,7 +534,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
'barcode.js': ['src/components/BarCode.tsx', 'jsbarcode/**'],
'charts.js': ['zrender/**', 'echarts/**', 'echarts-stat/**'],
'charts.js': ['zrender/**', 'echarts/**', 'echarts-stat/**', 'echarts-wordcloud/**'],
'ooxml-viewer.js': ['ooxml-viewer/**', 'fflate/**'],
@ -548,6 +549,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
'!amis-ui/lib/components/RichText.js',
'!zrender/**',
'!echarts/**',
'!echarts-wordcloud/**',
'!papaparse/**',
'!exceljs/**',
'!xlsx/**',
@ -760,6 +762,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
'!zrender/**',
'!echarts/**',
'!echarts-stat/**',
'!echarts-wordcloud/**',
'!papaparse/**',
'!exceljs/**',
'!xlsx/**',
@ -835,7 +838,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
'pkg/cropperjs.js': ['cropperjs/**', 'react-cropper/**'],
'pkg/charts.js': ['zrender/**', 'echarts/**', 'echarts-stat/**'],
'pkg/charts.js': ['zrender/**', 'echarts/**', 'echarts-stat/**', 'echarts-wordcloud/**'],
'pkg/api-mock.js': ['mock/*.ts'],
@ -862,6 +865,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
'!amis-ui/lib/components/RichText.tsx',
'!zrender/**',
'!echarts/**',
'!echarts-wordcloud/**',
'!papaparse/**',
'!exceljs/**',
'!xlsx/**',

View File

@ -20,7 +20,11 @@ import {getSchemaTpl, tipedLabel} from '../tpl';
import {EditorNodeType} from '../store/node';
class APIBuilder extends DSBuilder {
public static type = 'api';
static type = 'api';
static accessable = (controlType: string, propKey: string) => {
return true;
};
name = '接口';
@ -39,10 +43,6 @@ class APIBuilder extends DSBuilder {
return false;
};
public static accessable = (controlType: string, propKey: string) => {
return true;
};
public features: Array<DSFeatureType> = [
'List',
'Insert',

View File

@ -258,6 +258,7 @@ export abstract class DSBuilder {
schema: any;
sourceKey: string;
feat: DSFeatureType;
scopeNode?: EditorNodeType;
},
target: EditorNodeType
): Promise<SchemaCollection | void>;

View File

@ -3,7 +3,6 @@ import cx from 'classnames';
import Preview from './Preview';
import {autobind} from '../util';
import {MainStore, EditorStoreType} from '../store/editor';
import type {SchemaObject} from 'amis';
import {EditorManager, EditorManagerConfig, PluginClass} from '../manager';
import {reaction} from 'mobx';
import {RenderOptions, toast} from 'amis';
@ -17,7 +16,9 @@ import {PopOverForm} from './PopOverForm';
import {ContextMenuPanel} from './Panel/ContextMenuPanel';
import {LeftPanels} from './Panel/LeftPanels';
import {RightPanels} from './Panel/RightPanels';
import type {SchemaObject} from 'amis';
import type {VariableGroup, VariableOptions} from '../variable';
import type {EditorNodeType} from '../store/node';
export interface EditorProps extends PluginEventListener {
value: SchemaObject;
@ -48,7 +49,7 @@ export interface EditorProps extends PluginEventListener {
* Preview
* api地址替换成 proxy
*/
schemaFilter?: (schema: any) => any;
schemaFilter?: (schema: any, preview?: boolean) => any;
amisEnv?: RenderOptions;
/**
@ -116,6 +117,17 @@ export interface EditorProps extends PluginEventListener {
onRedo?: () => void; // 用于触发外部 redo 事件
onSave?: () => void; // 用于触发外部 save 事件
onPreview?: (preview: boolean) => void; // 用于触发外部 预览 事件
/** 打开公式编辑器之前触发的事件 */
onFormulaEditorOpen?: (
node: EditorNodeType,
manager: EditorManager,
ctx: Record<string, any>,
host?: {
node?: EditorNodeType;
manager?: EditorManager;
}
) => Promise<void | boolean>;
}
export default class Editor extends Component<EditorProps> {

View File

@ -70,6 +70,7 @@ export class PopOverForm extends React.Component<PopOverFormProps> {
this.buildSchema(),
{
data: createObject(store.ctx, popOverFormContext?.value),
node: popOverFormContext?.node,
manager
},
{

View File

@ -138,6 +138,8 @@ export class ScaffoldModal extends React.Component<SubEditorProps> {
const form = this.amisScope?.getComponents()[0].props.store;
const step = store.scaffoldFormStep + 1;
form.setValueByName('__step', step);
/** 切换步骤导致schema重新渲染Form数据域中的数据会丢失 */
store.updateScaffoldData(form?.data, true);
// 控制按钮
store.setScaffoldStep(step);
@ -150,6 +152,7 @@ export class ScaffoldModal extends React.Component<SubEditorProps> {
const form = this.amisScope?.getComponents()[0].props.store;
const step = store.scaffoldFormStep - 1;
form.setValueByName('__step', step);
store.updateScaffoldData(form?.data, true);
// 控制按钮
store.setScaffoldStep(step);

View File

@ -182,6 +182,21 @@ export class SubEditor extends React.Component<SubEditorProps> {
}
isHiddenProps={config.isHiddenProps}
$schemaUrl={config.$schemaUrl}
onFormulaEditorOpen={async (
node,
subEditormanager,
data
) => {
const fn = manager?.config?.onFormulaEditorOpen;
if (fn && typeof fn === 'function') {
return fn(node, subEditormanager, data, {
node: subEditorContext?.hostNode,
manager: manager
});
}
return;
}}
/>
)
}

View File

@ -2,6 +2,8 @@
* @file 西 compoennt/Editor.tsx
* UI 西
*/
import uniqBy from 'lodash/uniqBy';
import {getRenderers, RenderOptions, mapTree} from 'amis-core';
import {
PluginInterface,
@ -1227,7 +1229,6 @@ export class EditorManager {
}
store.changeValue(value, diff);
this.trigger('after-update', context);
}
/**
@ -1632,10 +1633,12 @@ export class EditorManager {
}
async scaffold(form: any, value: any): Promise<SchemaObject> {
const scaffoldFormData = form.pipeIn ? await form.pipeIn(value) : value;
return new Promise(resolve => {
this.store.openScaffoldForm({
...form,
value: form.pipeIn ? form.pipeIn(value) : value,
value: scaffoldFormData,
callback: resolve
});
});
@ -1823,6 +1826,7 @@ export class EditorManager {
const store = this.store;
const context: PopOverFormContext = {
node,
body: plugin.popOverBodyCreator
? plugin.popOverBodyCreator(this.buildEventContext(node))
: plugin.popOverBody!,
@ -1935,12 +1939,24 @@ export class EditorManager {
scope = this.dataSchema.hasScope(`${from.id}-${from.type}`)
? this.dataSchema.getScope(`${from.id}-${from.type}`)
: undefined;
/** Combo和InputTable作为也有自己的Scope */
if (!scope) {
if (['combo', 'input-table'].includes(from?.info?.type)) {
break;
}
}
from = from.parent;
if (from?.isRegion) {
region = from;
}
}
if (!scope) {
return from?.info.plugin.getAvailableContextFields?.(from, node);
}
while (scope) {
const [id, type] = scope.id.split('-');
const scopeNode = this.store.getNodeById(id, type);

View File

@ -315,6 +315,8 @@ export interface PopOverForm {
export interface ScaffoldForm extends PopOverForm {
// 内容是否是分步骤的如果是body必须是?: Array<{title: string,body: any[]}>
stepsBody?: boolean;
/** 是否可跳过创建向导直接创建 */
canSkip?: boolean;
mode?:
| 'normal'
| 'horizontal'
@ -592,6 +594,9 @@ export type PluginEvent<T, P = any> = {
// 当前值
data?: P;
// value值
value?: any;
};
export type PluginEventFn = (e: PluginEvent<EventContext>) => any;

View File

@ -71,6 +71,8 @@ export type SubEditorContext = {
typeMutable?: boolean;
memberImmutable?: boolean | Array<string>;
props?: any;
/* 宿主节点的Store */
hostNode?: EditorNodeType;
};
export type PatchItem =
@ -105,6 +107,7 @@ export interface PopOverFormContext extends PopOverForm {
target: () => HTMLElement;
value: any;
callback: (value: any, diff: any) => void;
node?: EditorNodeType;
}
/**
@ -1588,11 +1591,15 @@ export const MainStore = types
},
openSubEditor(context: SubEditorContext) {
if (!self.activeId) {
const activeId = self.activeId;
if (!activeId) {
return;
}
self.subEditorContext = {
...context,
hostNode: self.getNodeById(activeId),
data: extendObject(context.data, {
__curCmptTreeWrap: {
label: context.title,
@ -1688,6 +1695,20 @@ export const MainStore = types
self.scaffoldError = msg;
},
updateScaffoldData(value: any, replace?: boolean) {
if (self.scaffoldForm && value) {
self.scaffoldForm = {
...self.scaffoldForm,
value: replace
? value
: {
...self.scaffoldForm.value,
...value
}
};
}
},
openPopOverForm(context: PopOverFormContext) {
self.popOverForm = context;
},

View File

@ -8,7 +8,8 @@ import {
IAnyModelType,
Instance,
isAlive,
types
types,
SnapshotIn
} from 'mobx-state-tree';
import uniq from 'lodash/uniq';
import {RegionConfig, RendererInfo} from '../plugin';
@ -527,6 +528,7 @@ export const EditorNode = types
function getClosestParentByType(type: string): EditorNodeType | void {
let node = self;
while (node === node.parent) {
if (node.schema.type === type) {
return node as EditorNodeType;
@ -537,6 +539,29 @@ export const EditorNode = types
}
}
/** 通过callback function获取上层节点 */
function getParentNodeByCB(
callback: (node: EditorNodeSnapshot) => Boolean
) {
let cursor = self;
if (!callback || typeof callback !== 'function') {
return cursor;
}
while (cursor) {
const res = callback(cursor);
if (res) {
break;
}
cursor = cursor.parent;
}
return cursor;
}
// 放到props会变成 frozen 的。
let component: any;
@ -546,6 +571,7 @@ export const EditorNode = types
return {
getClosestParentByType,
getParentNodeByCB,
updateIsCommonConfig,
addChild(props: {
id: string;
@ -722,3 +748,5 @@ export const EditorNodeContext = React.createContext<EditorNodeType | null>(
null
);
export type EditorNodeType = Instance<typeof EditorNode>;
export type EditorNodeSnapshot = SnapshotIn<typeof EditorNode>;

View File

@ -22,7 +22,7 @@
"scripts": {
"test": "echo \"Warnings: no test specified\"",
"build": "npm run clean-dist && NODE_ENV=production rollup -c ",
"clean-dist": "rimraf lib/* esm/*",
"clean-dist": "rimraf lib/** esm/**",
"i18n:update": "npx i18n update --config=./i18nConfig.js",
"i18n:translate": "npx i18n translate --config=./i18nConfig.js --l=en-US",
"i18n:merge": "npx i18n merge --config=./i18nConfig.js --l=en-US",

View File

@ -9,9 +9,11 @@ import {
tipedLabel,
mockValue,
RegionConfig,
getI18nEnabled
getI18nEnabled,
EditorNodeType,
EditorManager,
DSBuilderManager
} from 'amis-editor-core';
import {setVariable} from 'amis-core';
import {ValidatorTag} from '../../validator';
@ -83,6 +85,16 @@ export class ComboControlPlugin extends BasePlugin {
]
};
// 容器配置
regions: Array<RegionConfig> = [
{
key: 'items',
label: '内容区',
preferTag: '内容区',
renderMethod: 'renderItems'
}
];
// 事件定义
events: RendererPluginEvent[] = [
{
@ -210,6 +222,13 @@ export class ComboControlPlugin extends BasePlugin {
panelJustify = true;
dsBuilderManager: DSBuilderManager;
constructor(manager: EditorManager) {
super(manager);
this.dsBuilderManager = new DSBuilderManager('combo', 'api');
}
panelBodyCreator = (context: BaseEventContext) => {
const i18nEnabled = getI18nEnabled();
return getSchemaTpl('tabs', [
@ -624,15 +643,31 @@ export class ComboControlPlugin extends BasePlugin {
return props;
}
// 容器配置
regions: Array<RegionConfig> = [
{
key: 'items',
label: '内容区',
preferTag: '内容区',
renderMethod: 'renderItems'
async getAvailableContextFields(
scopeNode: EditorNodeType,
target: EditorNodeType,
region?: EditorNodeType
) {
if (target.parent.isRegion && target.parent.region === 'items') {
const scope = scopeNode.parent.parent;
const builder = this.dsBuilderManager.resolveBuilderBySchema(
scope.schema,
'api'
);
if (builder && scope.schema.api) {
return builder.getAvailableContextFileds(
{
schema: scope.schema,
sourceKey: 'api',
feat: scope.schema?.feat ?? 'List',
scopeNode
},
target
);
}
}
];
}
}
registerEditorPlugin(ComboControlPlugin);

View File

@ -166,7 +166,6 @@ export class RichTextControlPlugin extends BasePlugin {
type: 'input-rich-text',
label: '富文本',
name: 'rich-text',
receiver: '',
vendor: 'tinymce'
};
previewSchema: any = {

View File

@ -1,5 +1,3 @@
import React from 'react';
import {Button} from 'amis';
import {
registerEditorPlugin,
BaseEventContext,
@ -16,7 +14,9 @@ import {
getI18nEnabled,
repeatArray,
mockValue,
EditorNodeType
EditorNodeType,
EditorManager,
DSBuilderManager
} from 'amis-editor-core';
import {setVariable, someTree} from 'amis-core';
import {ValidatorTag} from '../../validator';
@ -741,6 +741,13 @@ export class TableControlPlugin extends BasePlugin {
}
];
dsBuilderManager: DSBuilderManager;
constructor(manager: EditorManager) {
super(manager);
this.dsBuilderManager = new DSBuilderManager('input-table', 'api');
}
panelBodyCreator = (context: BaseEventContext) => {
const isCRUDBody = context.schema.type === 'crud';
const i18nEnabled = getI18nEnabled();
@ -1056,6 +1063,32 @@ export class TableControlPlugin extends BasePlugin {
items: itemsSchema
};
}
async getAvailableContextFields(
scopeNode: EditorNodeType,
target: EditorNodeType,
region?: EditorNodeType
) {
if (target.parent.isRegion && target.parent.region === 'columns') {
const scope = scopeNode.parent.parent;
const builder = this.dsBuilderManager.resolveBuilderBySchema(
scope.schema,
'api'
);
if (builder && scope.schema.api) {
return builder.getAvailableContextFileds(
{
schema: scope.schema,
sourceKey: 'api',
feat: scope.schema?.feat ?? 'List',
scopeNode
},
target
);
}
}
}
}
registerEditorPlugin(TableControlPlugin);

View File

@ -47,7 +47,7 @@ export enum FormulaDateType {
}
export function renderFormulaValue(item: any) {
const html = {__html: typeof item === 'string' ? item : item.html};
const html = {__html: typeof item === 'string' ? item : item?.html};
// bca-disable-next-line
return <span dangerouslySetInnerHTML={html}></span>;
}
@ -153,6 +153,8 @@ interface FormulaControlState {
variableMode?: 'tree' | 'tabs';
formulaPickerOpen: boolean;
loading: boolean;
}
export default class FormulaControl extends React.Component<
@ -175,7 +177,8 @@ export default class FormulaControl extends React.Component<
this.state = {
variables: [],
variableMode: 'tabs',
formulaPickerOpen: false
formulaPickerOpen: false,
loading: false
};
}
@ -374,8 +377,42 @@ export default class FormulaControl extends React.Component<
this.closeFormulaPicker();
}
/**
*
*/
@autobind
handleFormulaClick() {
async beforeFormulaEditorOpen() {
const {node, manager, data} = this.props;
const onFormulaEditorOpen = manager?.config?.onFormulaEditorOpen;
this.setState({loading: true});
try {
if (
manager &&
onFormulaEditorOpen &&
typeof onFormulaEditorOpen === 'function'
) {
const res = await onFormulaEditorOpen(node, manager, data);
if (res !== false) {
const variables = await getVariables(this);
this.setState({variables});
}
}
} catch (error) {
console.error('[amis-editor] onFormulaEditorOpen failed: ', error?.stack);
}
this.setState({loading: false});
}
@autobind
async handleFormulaClick() {
try {
await this.beforeFormulaEditorOpen();
} catch (error) {}
this.setState({
formulaPickerOpen: true
});
@ -520,8 +557,7 @@ export default class FormulaControl extends React.Component<
render,
...rest
} = this.props;
const {formulaPickerOpen, variables, variableMode} = this.state;
const {formulaPickerOpen, variables, variableMode, loading} = this.state;
// 判断是否含有公式表达式
const isExpr = isExpression(value);
@ -631,7 +667,7 @@ export default class FormulaControl extends React.Component<
result={{
html: this.hasDateShortcutkey(value)
? value
: highlightValue.html
: highlightValue?.html
}}
itemRender={renderFormulaValue}
onChange={this.handleInputChange}
@ -662,6 +698,7 @@ export default class FormulaControl extends React.Component<
mouseLeaveDelay: 0
}}
onClick={this.handleFormulaClick}
loading={loading}
>
<Icon
icon="input-fx"

View File

@ -1159,8 +1159,8 @@
);
--inputNumber-enhance-default-paddingTop: var(--sizes-size-3);
--inputNumber-enhance-default-paddingBottom: var(--sizes-size-3);
--inputNumber-enhance-default-paddingLeft: var(--sizes-size-7);
--inputNumber-enhance-default-paddingRight: var(--sizes-size-7);
--inputNumber-enhance-default-paddingLeft: var(--sizes-size-5);
--inputNumber-enhance-default-paddingRight: var(--sizes-size-5);
--inputNumber-enhance-default-bg-color: var(--colors-neutral-fill-11);
--inputNumber-enhance-hover-top-border-color: var(--colors-brand-5);
--inputNumber-enhance-hover-top-border-width: var(--borders-width-2);
@ -1273,6 +1273,11 @@
);
--inputNumber-enhance-leftIcon-default-icon: '<svg viewBox="0 0 12 2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="icon icon-minus"><g id="minus" fill="currentColor" fill-rule="nonzero"><polygon id="path-1" points="0 1.6 0 0.4 12 0.4 12 1.6"></polygon></g></svg>';
--inputNumber-enhance-rightIcon-default-icon: '<svg viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" class="icon icon-plus"><path d="M6.6 6.6V12H5.4V6.6H0V5.4h5.4V0h1.2v5.4H12v1.2z" fill="currentColor" fill-rule="nonzero"></path></svg>';
--inputNumber-enhance-mobile-input-width: #{px2rem(56px)};
--inputNumber-enhance-mobile-icon-width: #{px2rem(28px)};
--inputNumber-enhance-mobile-icon-height: #{px2rem(28px)};
--inputNumber-enhance-mobile-icon-inner-width: #{px2rem(27px)};
--inputNumber-enhance-mobile-icon-inner-height: #{px2rem(27px)};
--Form-input-onHover-borderColor: var(--colors-brand-5);
--Form-input-onFocused-borderColor: var(--colors-brand-5);
@ -1626,6 +1631,7 @@
// 表单
--Form-item-gap: var(--sizes-base-12);
--Form-item-mobile-gap: var(--sizes-base-4);
--Form-item-color: var(--colors-neutral-text-4);
--Form-item-fontColor: var(--Form-item-color);
--Form-item-fontSize: var(--fonts-size-7);
@ -1671,6 +1677,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-10);
--InputRange-handle-mobile-width: var(--sizes-base-10);
--InputRange-handle-bg: var(--colors-neutral-fill-11);
--InputRange-handle-top-border-color: var(--colors-brand-5);
--InputRange-handle-top-border-width: 0.0625rem;
@ -1725,6 +1733,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);
@ -2816,6 +2825,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)

View File

@ -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;
@ -634,6 +638,7 @@ $Table-strip-bg: transparent;
--PopOverAble-onHover-iconColor: inherit;
--PopUp-cancelAction-color: #666;
--PopUp-confirmAction-color: var(--primary);
--Property-title-bg: #f2f2f2;
--Property-label-bg: #f7f7f7;
@ -768,6 +773,24 @@ $Table-strip-bg: transparent;
--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)};
--Tag-borderRadius: #{px2rem(2px)};
--Tag-fontColor: #151a26;
--Tag-default-color: #f2f2f4;
--Tag-inactive-color: #b8babf;
--Tag-active-color: var(--colors-brand-5);
--Tag-processing-color: var(--colors-brand-6);
--Tag-success-color: var(--colors-success-5);
--Tag-error-color: var(--colors-error-5);
--Tag-warning-color: var(--colors-warning-5);
--Tag-checkable-bgColor: #f2f2f4;
--Tag-checkable-bgColor-onDisable: #e6e6e8;
--Tag-checkable-bgColor-onHover: #e6e6e8;

View File

@ -109,7 +109,8 @@
.#{$ns}CalendarMobile {
height: 100%;
width: 100%;
overflow: scroll;
overflow-y: scroll;
overflow-x: hidden;
&-pop {
position: fixed;
@ -122,9 +123,21 @@
overflow: hidden;
border-width: 0;
&--quarters,
&--months {
height: px2rem(400px);
}
&--time {
height: fit-content;
height: px2rem(360px);
max-height: 90vh;
.#{$ns}PopUp-content {
overflow: hidden;
}
}
&--years {
height: px2rem(360px);
}
}
@ -204,6 +217,7 @@
overflow-y: scroll;
table {
padding: 0;
border-spacing: 0 px2rem(4px);
}
@ -238,14 +252,15 @@
}
.calendar-wrap {
position: relative;
width: px2rem(50px);
position: relative;
height: 100%;
text-align: center;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: px2rem(4px);
padding: 0 px2rem(10px);
}
}
@ -260,6 +275,7 @@
flex-shrink: 0;
.date-range-confirm {
width: 100%;
height: px2rem(36px);
margin: px2rem(7px) 0;
border-radius: var(--borderRadiusMd);
@ -267,13 +283,7 @@
justify-content: center;
align-items: center;
}
.is-disabled {
opacity: 0.5;
color: var(--Button--primary-color);
background: var(--Button--primary-bg);
border-color: var(--Button--primary-bg);
filter: none;
}
&-toolbar {
padding: 0 px2rem(16px);
}
@ -287,11 +297,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;

View File

@ -12,9 +12,12 @@
.#{$ns}Cascader-tab {
flex: 1;
width: calc((100vw - 20px) / 3);
height: px2rem(370px);
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);

View File

@ -1,4 +1,5 @@
.#{$ns}CityArea {
flex: 1;
text-align: right;
&-popup {
@ -15,6 +16,11 @@
font-size: var(--Form-input-fontSize);
display: inline-flex !important;
&.is-focused,
&:hover {
border: none !important;
}
&::placeholder {
color: var(--Form-input-placeholderColor);
user-select: none;

View File

@ -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);
}
}
}
}
}

View File

@ -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

View File

@ -67,11 +67,6 @@
text-decoration: var(--link-decoration);
background: var(--table-togglable-bg-color);
&:hover {
background: var(--table-togglable-hover-bg-color);
color: var(--DropDown-menuItem-onHover-color);
}
&.is-active {
color: var(--DropDown-menuItem-onActive-color);
}
@ -97,6 +92,13 @@
}
}
&-menu:not(.is-mobile) > li {
&:hover {
background: var(--table-togglable-hover-bg-color);
color: var(--DropDown-menuItem-onHover-color);
}
}
&-menu > li a {
color: inherit;
display: block;

View File

@ -1,3 +1,6 @@
.#{$ns}ConditionBuilderControl.is-mobile {
overflow-x: auto;
}
.#{$ns}CBGroup {
font-size: var(--fontSizeSm);
position: relative;
@ -331,9 +334,31 @@
opacity: 0;
}
&:hover > &-body > &-body-item > &-dragbar,
&:hover > &-body > &-body-item > .#{$ns}CBDelete {
opacity: 1;
&:is(.is-mobile) {
> .#{$ns}CBGroupOrItem-body
> .#{$ns}CBGroupOrItem-body-group
> .#{$ns}CBGroupOrItem-dragbar,
> .#{$ns}CBGroupOrItem-body
> .#{$ns}CBGroupOrItem-body-item
> .#{$ns}CBGroupOrItem-dragbar,
> .#{$ns}CBGroupOrItem-body
> .#{$ns}CBGroupOrItem-body-item
> .#{$ns}CBDelete {
opacity: 1;
}
}
&:not(.is-mobile) {
&:hover
> .#{$ns}CBGroupOrItem-body
> .#{$ns}CBGroupOrItem-body-item
> .#{$ns}CBGroupOrItem-dragbar,
&:hover
> .#{$ns}CBGroupOrItem-body
> .#{$ns}CBGroupOrItem-body-item
> .#{$ns}CBDelete {
opacity: 1;
}
}
&-simple {

View File

@ -30,21 +30,21 @@
font-weight: var(--Pick-base-value-fontWeight);
background: var(--Pick-base-value-bgColor);
border-width: var(--Pick-base-value-top-border-width)
var(--Pick-base-value-right-border-width)
var(--Pick-base-value-bottom-border-width)
var(--Pick-base-value-left-border-width);
var(--Pick-base-value-right-border-width)
var(--Pick-base-value-bottom-border-width)
var(--Pick-base-value-left-border-width);
border-style: var(--Pick-base-value-top-border-style)
var(--Pick-base-value-right-border-style)
var(--Pick-base-value-bottom-border-style)
var(--Pick-base-value-left-border-style);
var(--Pick-base-value-right-border-style)
var(--Pick-base-value-bottom-border-style)
var(--Pick-base-value-left-border-style);
border-color: var(--Pick-base-value-top-border-color)
var(--Pick-base-value-right-border-color)
var(--Pick-base-value-bottom-border-color)
var(--Pick-base-value-left-border-color);
var(--Pick-base-value-right-border-color)
var(--Pick-base-value-bottom-border-color)
var(--Pick-base-value-left-border-color);
border-radius: var(--Pick-base-top-left-border-radius)
var(--Pick-base-top-right-border-radius)
var(--Pick-base-bottom-right-border-radius)
var(--Pick-base-bottom-left-border-radius);
var(--Pick-base-top-right-border-radius)
var(--Pick-base-bottom-right-border-radius)
var(--Pick-base-bottom-left-border-radius);
margin-right: var(--gap-xs);
margin-top: var(--gap-xs);
@ -90,6 +90,10 @@
display: inline-flex;
align-items: center;
&.is-mobile {
margin-top: 0;
}
&--left:not(:first-child) {
margin-left: var(--Crud-toolbar-gap);
}
@ -137,6 +141,12 @@
&-filter {
margin-bottom: var(--gap-base);
}
&.is-mobile {
.#{$ns}Crud-toolbar {
flex: 1;
}
}
}
@include media-breakpoint-up(sm) {

View File

@ -75,11 +75,6 @@
color: var(--DropDown-menuItem-color);
text-decoration: var(--link-decoration);
&:hover {
background: var(--DropDown-menuItem-onHover-bg);
color: var(--DropDown-menuItem-onHover-color);
}
&.is-active {
background: var(--DropDown-menuItem-onHover-bg);
color: var(--DropDown-menuItem-onActive-color);
@ -128,6 +123,14 @@
}
}
&-menuItem,
&-menu:not(.is-mobile) > li {
&:hover {
background: var(--DropDown-menuItem-onHover-bg);
color: var(--DropDown-menuItem-onHover-color);
}
}
&-menu > li a {
color: inherit;
display: block;

View File

@ -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: px2rem(580px);
&-inner {
width: 100%;
box-sizing: border-box;
padding: 0 px2rem(16px);
}
}
&-input {
flex: 1;
margin-right: #{px2rem(10px)};

View File

@ -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);
}
}
}

View File

@ -24,6 +24,26 @@
color: var(--Form-input-color);
}
&.is-mobile {
& > span {
border-radius: 0;
border: none;
border-bottom: var(--Form-input-borderWidth) solid
var(--Form-input-borderColor);
}
.#{$ns}InputBox {
height: 100%;
line-height: 1;
> .#{$ns}InputBox-caret {
svg {
top: 0.125em;
}
}
}
}
}
&-value {
@ -121,4 +141,12 @@
}
}
}
&.is-mobile {
.#{$ns}Number {
border-radius: 0;
border-bottom: var(--Number-borderWidth) solid
var(--Form-input-borderColor);
}
}
}

View File

@ -67,6 +67,46 @@
)
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;
margin: var(--ListMenu-item-mobile-margin);
text-align: left;
.#{$ns}ResultBox {
width: 100%;
}
input {
width: 100%;
padding: var(--Form-item-mobile-gap) 0;
border-bottom: 1px solid var(--borderColor);
border-radius: 0;
}
}
.#{$ns}PopOver > .#{$ns}ListMenu {

View File

@ -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 {

View File

@ -35,11 +35,11 @@
}
&-confirm {
color: var(--PickerColumns-confirmAction-color) !important;
color: var(--PickerColumns-confirmAction-color);
}
&-cancel {
color: var(--PickerColumns-cancelAction-color) !important;
color: var(--PickerColumns-cancelAction-color);
}
&-title {
@ -94,8 +94,6 @@
}
}
&-mask {
position: absolute;
top: 0;

View File

@ -120,7 +120,9 @@
}
&-confirm {
color: var(--PopUp-confirmAction-color);
margin-right: var(--gap-sm);
border: none;
}
&-content {
@ -128,6 +130,10 @@
height: 100%;
display: flex;
flex: 1;
> div {
flex: 1;
}
}
& > * {
@ -155,6 +161,7 @@
margin-top: px2rem(-4px);
}
&-safearea {
height: px2rem(16px);
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
}

View File

@ -324,6 +324,11 @@
border: none;
justify-content: flex-end;
&.is-focused,
&:hover {
border: none !important;
}
.#{$ns}ResultBox-clear {
@include input-clear();
width: px2rem(26px);

View File

@ -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 {

View File

@ -207,6 +207,14 @@
}
}
&.is-mobile {
.#{$ns}Table-toolbar {
.#{$ns}Form-item {
padding: 0;
}
}
}
&-header + &-toolbar {
padding-top: 0;
}
@ -657,6 +665,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 {

View File

@ -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%;

View File

@ -64,6 +64,14 @@
}
}
&.is-mobile {
> i {
+ span {
vertical-align: middle;
}
}
}
&:hover > i + span > a {
display: inline-block;
}

View File

@ -10,10 +10,6 @@
color: var(--ColorPicker-color);
border-radius: var(--borderRadius);
&-popup {
height: px2rem(400px);
}
&:not(.is-disabled) {
cursor: pointer;
@ -122,3 +118,28 @@
border-radius: var(--borderRadius) !important;
box-shadow: var(--ColorPicker-boxShadow) !important;
}
.#{$ns}ColorPicker-popup {
height: px2rem(500px);
.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;
}
}
}
}
}

View File

@ -320,6 +320,32 @@
background: rgba(0, 0, 0, 0.1);
}
}
&.is-mobile {
.#{$ns}Form-item {
.#{$ns}Form-rowInner {
flex: 1;
}
}
.#{$ns}Combo-delBtn {
height: px2rem(20px);
padding-top: 0;
}
.#{$ns}Combo-item {
align-items: center;
border-bottom: var(--Form-input-borderWidth) solid var(--borderColor);
}
}
&.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);
}
}
}
.#{$ns}ComboTabs > .#{$ns}Tabs-links {

View File

@ -19,7 +19,7 @@
display: none;
}
&:not(.is-disabled) {
&:not(.is-disabled):not(.is-mobile) {
&:hover {
background: var(--DatePicker-onHover-bg);
border-color: var(--DatePicker-onHover-borderColor);
@ -81,6 +81,7 @@
font-size: var(--DatePicker-fontSize);
font-weight: var(--inputDate-default-fontWeight);
color: var(--inputDate-default-color);
text-align: center;
&::placeholder {
color: var(--colors-neutral-text-6);
@ -185,10 +186,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,
@ -214,6 +227,7 @@
list-style: none;
background: #f7f7f9;
line-height: px2rem(32px);
overflow-x: auto;
}
.#{$ns}DateRangePicker-ranger {
@ -274,6 +288,10 @@
border-color: var(--DatePicker-borderColor);
background: var(--DatePicker-bg);
border-radius: var(--DatePicker-borderRadius);
&.is-mobile {
display: block;
}
}
// 移动端输入框样式
@ -281,6 +299,11 @@
border: 0;
justify-content: flex-end;
&.is-focused,
&:hover {
border: none;
}
span,
a {
&:focus {
@ -303,8 +326,4 @@
flex-grow: unset;
flex-basis: unset;
}
.#{$ns}DateRangePicker-toggler {
margin-top: -3px;
}
}

View File

@ -42,7 +42,7 @@
@include input-border();
&:not(.is-disabled) {
&:not(.is-disabled):not(.is-mobile) {
cursor: pointer;
&:hover {
@ -207,15 +207,16 @@
}
}
.#{$ns}DatePicker-popup {
height: px2rem(300px);
}
// 移动端输入框样式
.#{$ns}DatePicker.is-mobile {
border: 0;
justify-content: flex-end;
&.is-focused,
&:hover {
border: none;
}
span,
a {
&:focus {
@ -238,13 +239,10 @@
flex-grow: unset;
flex-basis: unset;
}
.#{$ns}DatePicker-toggler {
margin-top: -3px;
}
}
.#{$ns}DatePicker-popup.#{$ns}DatePicker-mobile {
height: px2rem(320px);
color: red;
.rdt {
width: 100%;
@ -454,7 +452,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);
}
@ -464,6 +462,7 @@
.#{$ns}TimeContentWrapper {
display: flex;
justify-content: center;
}
.#{$ns}TimeRangeHeaderWrapper {
@ -471,6 +470,7 @@
padding-top: 10px;
text-align: center;
border-bottom: 1px solid var(--Calendar-input-borderColor);
margin: 0 px2rem(4px);
}
.#{$ns}TimeFooterWrapper {

View File

@ -109,6 +109,10 @@
min-height: 500px;
}
}
.monaco-editor .iPadShowKeyboard {
display: none;
}
}
.monaco-inputbox > .wrapper {

View File

@ -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);
}
}
}

View File

@ -415,6 +415,8 @@
background-color: rgb(250, 250, 250);
border-radius: var(--borderRadius);
position: relative;
word-wrap: break-word;
word-break: break-all;
}
// .#{$ns}Form--quickEdit {
@ -489,177 +491,307 @@
/* 移动端样式调整 */
@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 {
border-bottom: none;
}
}
.#{$ns}Form-item {
display: flex;
// flex-wrap: wrap;
margin-bottom: 0;
padding: var(--Form-item-mobile-gap) 0;
position: relative;
&::after {
position: absolute;
box-sizing: border-box;
content: ' ';
pointer-events: none;
right: 0;
top: 0;
bottom: 0;
left: 0;
border-bottom: var(--Form-input-borderWidth) solid var(--borderColor);
}
.#{$ns}Form-item {
display: flex;
flex-wrap: wrap;
.#{$ns}InputGroup-addOn,
.#{$ns}TextControl-addOn {
border: none;
}
.#{$ns}Form-label {
flex: 0 0 28%;
max-width: 28%;
min-height: 1px;
text-align: left;
padding-right: calc(var(--Form--horizontal-gutterWidth) / 2);
overflow-wrap: break-word;
margin-right: 0;
margin-bottom: 0;
padding: var(--Form-item-gap) 0;
position: relative;
font-size: var(--fontSizeLg);
&::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);
> span {
line-height: px2rem(32px);
display: inline-block;
}
}
.#{$ns}InputGroup-addOn,
.#{$ns}TextControl-addOn {
border: none;
.#{$ns}Form-description {
font-size: var(--fontSizeBase);
}
.#{$ns}TextControl-input {
font-size: var(--fontSizeLg);
input {
height: calc(var(--Form-input-lineHeight) * var(--fontSizeLg));
}
}
> .#{$ns}Form-label {
flex: 0 0 28%;
max-width: 28%;
min-height: 1px;
text-align: left;
padding-right: calc(var(--Form--horizontal-gutterWidth) / 2);
overflow-wrap: break-word;
margin-right: 0;
margin-bottom: 0;
font-size: var(--fontSizeLg);
}
.#{$ns}Form-description {
font-size: var(--fontSizeBase);
}
.#{$ns}TextControl-input {
font-size: var(--fontSizeLg);
input {
height: calc(var(--Form-input-lineHeight) * var(--fontSizeLg));
}
}
.#{$ns}Form-value,
.#{$ns}Form-control {
.#{$ns}NumberControl {
.#{$ns}Number {
flex: 1;
flex-wrap: wrap;
font-size: var(--fontSizeLg);
&.is-disabled > .#{$ns}TextControl-input {
background: transparent;
}
}
.#{$ns}SelectControl.#{$ns}Form-control {
overflow: hidden;
}
.#{$ns}Form-hint,
.#{$ns}Form-remark,
.#{$ns}Form-static,
.#{$ns}Form-group--hor .#{$ns}Form-item,
.#{$ns}SwitchControl,
.#{$ns}CheckboxControl,
.#{$ns}RadiosControl,
.#{$ns}CheckboxesControl {
padding-top: 0;
padding-bottom: 0;
}
.#{$ns}Form-group--horizontal .#{$ns}TextControl-input input {
height: var(--Form-input-height);
}
.#{$ns}Form-hint {
font-size: var(--fontSizeBase);
margin-left: 0;
color: var(--text--muted-color);
}
.#{$ns}TextControl-placeholder {
top: 0;
}
.#{$ns}Form-static {
min-height: 0;
}
.#{$ns}Form-description,
.#{$ns}Form-feedback {
font-size: var(--fontSizeBase);
}
.#{$ns}InputGroup {
.#{$ns}Select,
.#{$ns}InputGroup-btn .#{$ns}Button {
border: none;
}
> .#{$ns}TextControl-input input {
height: var(--Form-input-height);
}
}
.#{$ns}ColorPicker {
padding: 0;
border: none;
.#{$ns}ColorPicker-arrow {
.#{$ns}Number-handler-wrap {
display: none;
}
}
.#{$ns}Form-group--hor .#{$ns}Form-item .#{$ns}Button {
margin-bottom: var(--gap-xs);
}
.#{$ns}TextareaControl > textarea,
.#{$ns}Form-control > .#{$ns}TextControl-input,
.#{$ns}TextControl.is-focused > .#{$ns}TextControl-input {
.#{$ns}Select {
flex: none;
border: none;
padding: 0 var(--Form-input-paddingX) 0 0;
box-shadow: none;
}
}
&:hover,
&:focus,
&.active {
border: none;
outline: none;
outline-style: none;
.#{$ns}Number--enhance {
float: right;
border: none;
&-left-icon,
&-right-icon {
width: var(--inputNumber-enhance-mobile-icon-width);
height: var(--inputNumber-enhance-mobile-icon-height);
border: var(--Form-input-borderWidth) solid var(--borderColor);
border-radius: var(--Form-input-borderRadius);
user-select: none;
svg.icon {
top: 0;
}
}
.#{$ns}Form-control > .#{$ns}TextControl-input--multiple {
padding: 0;
min-height: 0;
.#{$ns}Number-input {
text-align: center;
}
.#{$ns}Number--enhance-input {
width: var(--inputNumber-enhance-mobile-input-width);
text-align: center;
}
.#{$ns}InputNumber-enhance-minus,
.#{$ns}InputNumber-enhance-plus {
width: var(--inputNumber-enhance-mobile-icon-inner-width);
height: var(--inputNumber-enhance-mobile-icon-inner-height);
display: flex;
justify-content: center;
align-items: center;
}
&:hover {
.#{$ns}Number-input {
padding-left: var(--inputNumber-enhance-default-paddingLeft);
padding-right: var(--inputNumber-enhance-default-paddingRight);
}
}
}
.#{$ns}Form-groupColumn {
margin-bottom: 0;
.#{$ns}Form-value,
.#{$ns}Form-control {
flex: 1;
flex-wrap: wrap;
font-size: var(--fontSizeLg);
overflow-x: hidden;
.#{$ns}ColorPicker {
height: 100%;
> input {
height: 100%;
}
}
.#{$ns}ResultBox {
height: 100%;
.#{$ns}ResultBox-value-wrap {
justify-content: flex-end;
text-align: right;
}
}
&.is-disabled > .#{$ns}TextControl-input {
background: transparent;
}
}
.#{$ns}SelectControl.#{$ns}Form-control {
overflow: hidden;
}
> .#{$ns}Form-item.is-placeholder::after {
display: none;
.#{$ns}Form-hint,
.#{$ns}Form-remark,
// .#{$ns}Form-static,
.#{$ns}Form-group--hor .#{$ns}Form-item,
.#{$ns}SwitchControl,
.#{$ns}CheckboxControl,
.#{$ns}RadiosControl,
.#{$ns}CheckboxesControl {
padding-top: 0;
padding-bottom: 0;
}
.#{$ns}Divider {
display: none;
.#{$ns}CheckboxControl {
padding-top: px2rem(2px);
}
.#{$ns}Tabs-pane {
.#{$ns}Form-static {
.#{$ns}Progress {
margin-top: px2rem(3px);
}
.#{$ns}Field-quickEditBtn {
vertical-align: baseline;
}
}
.#{$ns}Form-group--horizontal .#{$ns}TextControl-input input {
height: var(--Form-input-height);
}
.#{$ns}Form-hint {
font-size: var(--fontSizeBase);
margin-left: 0;
color: var(--text--muted-color);
}
.#{$ns}TextControl-placeholder {
top: 0;
}
.#{$ns}Form-static {
min-height: 0;
}
.#{$ns}Form-description,
.#{$ns}Form-feedback {
font-size: var(--fontSizeBase);
}
.#{$ns}InputGroup {
.#{$ns}Select,
.#{$ns}InputGroup-btn .#{$ns}Button {
border: none;
}
> .#{$ns}TextControl-input input {
height: var(--Form-input-height);
}
}
.#{$ns}ColorPicker {
padding: 0;
border: none;
.#{$ns}ColorPicker-arrow {
display: none;
}
}
.#{$ns}Form-item .#{$ns}Form-groupColumn > .#{$ns}Form-item {
padding-bottom: var(--Form-input-paddingX);
.#{$ns}Form-group--hor .#{$ns}Form-item .#{$ns}Button {
margin-bottom: var(--gap-xs);
}
.#{$ns}TextareaControl > textarea,
.#{$ns}Form-control > .#{$ns}TextControl-input,
.#{$ns}TextControl.is-focused > .#{$ns}TextControl-input {
border: none;
padding: 0 var(--Form-input-paddingX) 0 0;
box-shadow: none;
border-radius: 0;
&:hover,
&:focus,
&.active {
border: none;
outline: none;
outline-style: none;
}
}
.#{$ns}Form-control > .#{$ns}TextControl-input--multiple {
padding: 0;
min-height: 0;
}
}
.#{$ns}Form-groupColumn {
margin-bottom: 0;
}
.#{$ns}Form-item:nth-last-of-type(1) {
&::after {
display: none;
}
}
.#{$ns}Form-item.is-placeholder::after {
display: none;
}
// .#{$ns}Divider {
// display: none;
// }
.#{$ns}Tabs-pane {
padding: 0;
}
.#{$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;
}
}
}

View File

@ -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) {

View File

@ -39,6 +39,16 @@
}
}
&.is-mobile {
border: none;
.#{$ns}LocationPicker-toggler {
svg {
top: 0.125rem;
}
}
}
&-placeholder {
color: var(--colors-neutral-text-6);
user-select: none;
@ -51,6 +61,8 @@
margin-right: var(--gap-base);
flex-basis: 0;
flex-grow: 1;
overflow-x: auto;
max-width: calc(100% - px2rem(20px));
}
&-toggler {
@ -72,6 +84,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 {
@ -81,6 +114,12 @@
border: none;
box-shadow: var(--boxShadow);
}
&.is-mobile {
flex: 1;
text-align: right;
overflow: hidden;
}
}
.#{$ns}LocationControl:not(.is-inline) > .#{$ns}LocationPicker {

View File

@ -1,4 +1,6 @@
.#{$ns}MatrixControl {
max-width: 100%;
&-error {
margin-bottom: 0;
}

View File

@ -11,6 +11,11 @@
.#{$ns}NestedSelect {
position: relative;
@include input-border();
&.is-mobile {
text-align: right;
}
&-optionArrowRight {
display: inline-block;
padding-right: var(--Form-select-icon-rigin);
@ -113,6 +118,6 @@
}
}
&-popup {
height: px2rem(460px);
height: px2rem(340px);
}
}

View File

@ -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)

View File

@ -206,3 +206,15 @@
min-width: px2rem(150px);
}
}
.#{$ns}PickerControl.is-mobile {
width: 100%;
.#{$ns}Form-item {
padding: 0;
}
.#{$ns}Picker-input {
border: none;
}
}

View File

@ -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%;
@ -126,10 +149,12 @@
}
}
&-drage {
transform: var(--InputRange-handle-onActive-transform);
box-shadow: var(--InputRange-handle-onFocus-boxShadow);
border-width: var(--InputRange-handle-onDrage-border-width);
&:not(.is-mobile) {
.#{$ns}InputRange-handle-drage {
transform: var(--InputRange-handle-onActive-transform);
box-shadow: var(--InputRange-handle-onFocus-boxShadow);
border-width: var(--InputRange-handle-onDrage-border-width);
}
}
.input-range--disabled & {
@ -279,6 +304,13 @@
}
}
&.is-mobile {
.#{$ns}InputRange-label {
display: block;
white-space: nowrap;
}
}
// marks
&-marks {
position: relative;
@ -304,4 +336,16 @@
}
}
}
&.is-mobile {
.#{$ns}InputRange-marks {
div {
top: 0;
&:nth-child(2n) {
top: px2rem(-30px);
}
}
}
}
}

View File

@ -269,7 +269,7 @@
}
&.is-mobile {
min-height: calc(var(--Form-input-lineHeight) * var(--fontSizeLg));
min-height: var(--Form-select-height);
border: none;
padding: 0;
font-size: var(--fontSizeLg);
@ -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 {

View File

@ -189,7 +189,7 @@
var(--transfer-search-paddingRight) var(--transfer-search-paddingBottom)
var(--transfer-search-paddingLeft);
width: 100%;
.#{$ns}InputBox {
.#{$ns}InputBox:not(.is-mobile) {
font-size: var(--transfer-search-fontSize);
font-weight: var(--transfer-search-fontWeight);
line-height: var(--transfer-search-lineHeight);
@ -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 {
@ -244,6 +254,10 @@
display: flex;
flex-direction: column;
justify-content: center;
&.is-mobile {
display: none;
}
}
&-arrow {
@ -363,6 +377,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 +496,16 @@
}
}
}
.#{$ns}TabsTransfer-search {
&.is-mobile {
.#{$ns}InputBox {
border: none;
border-bottom: 1px solid var(--TabsTransfer-border-color);
border-radius: 0;
}
}
}
}
}
}
@ -528,7 +571,13 @@
&.is-mobile {
width: 100%;
min-width: auto;
.#{$ns}Table-table > thead > tr > th:first-child.#{$ns}Table-checkCell {
padding-left: var(--select-table-option-paddingLeft);
}
}
& > .#{$ns}Transfer-selection {
flex-grow: 1;
max-height: var(--Transfer-selection-maxHeight);

View File

@ -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;
}
}
}
}
}
}

View File

@ -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 {

View File

@ -101,7 +101,7 @@ export class BaiduMapPicker extends React.Component<
componentWillUnmount() {
this.ac?.dispose?.();
document.body.removeChild(this.placeholderInput!);
this.placeholderInput && document.body.removeChild(this.placeholderInput!);
delete this.placeholderInput;
delete this.map;
@ -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
});
}
}
@ -319,10 +323,10 @@ export class BaiduMapPicker extends React.Component<
const results = local.getResults();
const poi = results.getPoi(0);
this.setState({
inputValue: poi.title,
inputValue: poi?.title,
sugs: []
});
this.getLocations(poi.point, true);
this.getLocations(poi?.point, true);
}
});
local.search(value);

View File

@ -10,6 +10,7 @@ import Calendar from './calendar/Calendar';
import {themeable, ThemeProps} from 'amis-core';
import {LocaleProps, localeable} from 'amis-core';
import {autobind} from 'amis-core';
import {ShortCuts} from './DatePicker';
export interface CalendarMobileProps extends ThemeProps, LocaleProps {
className?: string;
@ -147,6 +148,7 @@ export class CalendarMobile extends React.Component<
componentDidUpdate(prevProps: CalendarMobileProps) {
const props = this.props;
const {classPrefix: ns} = props;
if (
prevProps.minDate !== props.minDate ||
@ -167,6 +169,28 @@ export class CalendarMobile extends React.Component<
() => this.initMonths()
);
}
if (
(prevProps.startDate !== props.startDate &&
props.startDate !== this.state.startDate) ||
(prevProps.endDate !== props.endDate &&
props.endDate !== this.state.endDate)
) {
this.setState(
{
startDate: props.startDate,
endDate: props.endDate
},
() =>
requestAnimationFrame(() => {
document
.querySelector(
`.${ns}CalendarMobile:not(.${ns}CalendarMobile-embed) .rdtRangeStart:not(.rdtNew)`
)
?.scrollIntoView();
})
);
}
}
componentWillUnmount() {
@ -354,22 +378,18 @@ export class CalendarMobile extends React.Component<
dateTime: newTime,
startDate: endDate
? startDate
: startDate
?.clone()
.set({
hour: newTime[0],
minute: newTime[1],
second: newTime[2] || 0
}),
: startDate?.clone().set({
hour: newTime[0],
minute: newTime[1],
second: newTime[2] || 0
}),
endDate: !endDate
? endDate
: endDate
?.clone()
.set({
hour: newTime[0],
minute: newTime[1],
second: newTime[2] || 0
})
: endDate?.clone().set({
hour: newTime[0],
minute: newTime[1],
second: newTime[2] || 0
})
};
this.setState(obj, () => {
onChange && onChange(this.state);

View File

@ -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>
@ -585,14 +595,14 @@ export class Cascader extends React.Component<CascaderProps, CascaderState> {
<div className={cx(`Cascader-btnGroup`)}>
<Button
className={cx(`Cascader-btnCancel`)}
level="text"
level="link"
onClick={onClose}
>
{__('cancel')}
</Button>
<Button
className={cx(`Cascader-btnConfirm`)}
level="text"
level="link"
onClick={this.confirm}
disabled={this.state.disableConfirm}
>

View File

@ -5,7 +5,7 @@
import React from 'react';
import {ClassNamesFn, themeable} from 'amis-core';
import {autobind} from 'amis-core';
import {autobind, isMobile} from 'amis-core';
const preventEvent = (e: any) => e.stopPropagation();
@ -30,6 +30,7 @@ interface CheckboxProps {
partial?: boolean;
optionType?: 'default' | 'button';
children?: React.ReactNode | Array<React.ReactNode>;
useMobileUI?: boolean;
}
export class Checkbox extends React.Component<CheckboxProps, any> {
@ -93,7 +94,8 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
'Checkbox--button--disabled--unchecked':
optionType === 'button' && disabled && !_checked,
'Checkbox--button--disabled--checked':
optionType === 'button' && disabled && _checked
optionType === 'button' && disabled && _checked,
'is-mobile': isMobile()
})}
data-role="checkbox"
>

View File

@ -281,7 +281,7 @@ const CityArea = memo<AreaProps>(props => {
result={result}
onResultChange={() => {}}
onResultClick={() => setIsOpened(!isOpened)}
placeholder={__('Condition.cond_placeholder')}
placeholder={__('Select.placeholder')}
useMobileUI={useMobileUI}
></ResultBox>
{allowStreet && values[0] ? (
@ -293,6 +293,7 @@ const CityArea = memo<AreaProps>(props => {
}
placeholder={__('City.street')}
disabled={disabled}
readOnly={true}
/>
) : null}
<PopUp

View File

@ -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,8 @@ export interface CollapseProps {
propsUpdate?: boolean;
partial?: boolean;
children?: React.ReactNode | Array<React.ReactNode>;
useMobileUI?: boolean;
divideLine?: boolean;
}
export interface CollapseState {
@ -170,19 +173,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 +258,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,

View File

@ -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}

View File

@ -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>

View File

@ -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}

View File

@ -1035,7 +1035,9 @@ export class DateRangePicker extends React.Component<
}
selectShortcut(shortcut: PlainObject) {
const {closeOnSelect, minDateRaw, maxDateRaw, format, data} = this.props;
const {closeOnSelect, minDateRaw, maxDateRaw, format, data, useMobileUI} =
this.props;
const mobileUI = useMobileUI && isMobile();
const now = moment();
/** minDate和maxDate要实时计算因为用户可能设置为${NOW()},暂时不考虑毫秒级的时间差 */
const minDate = minDateRaw
@ -1056,7 +1058,7 @@ export class DateRangePicker extends React.Component<
endDate:
maxDate && maxDate?.isValid() ? moment.min(maxDate, endDate) : endDate
},
closeOnSelect ? this.confirm : noop
closeOnSelect && !mobileUI ? this.confirm : noop
);
}
@ -1382,7 +1384,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;
@ -1400,12 +1404,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.renderShortcuts(ranges || shortcuts)}
<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}
@ -1432,9 +1472,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}
@ -1461,11 +1504,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()}>
@ -1631,11 +1675,16 @@ export class DateRangePicker extends React.Component<
/>
);
const mobileUI = useMobileUI && isMobile();
if (embed) {
return (
<div
className={cx(
`${ns}DateRangeCalendar`,
{
'is-mobile': mobileUI
},
{
'is-disabled': disabled
},
@ -1740,6 +1789,7 @@ export class DateRangePicker extends React.Component<
)}
onHide={this.close}
header={CalendarMobileTitle}
showClose={false}
>
{useCalendarMobile ? calendarMobile : this.renderCalendar()}
</PopUp>

View File

@ -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,

View File

@ -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" />

View File

@ -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>

View File

@ -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(

View File

@ -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>
);
}

View File

@ -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' ? (

View File

@ -5,7 +5,7 @@
*/
import React from 'react';
import isInteger from 'lodash/isInteger';
import {localeable, LocaleProps} from 'amis-core';
import {isMobile, localeable, LocaleProps} from 'amis-core';
import {themeable, ThemeProps} from 'amis-core';
import {autobind} from 'amis-core';
import {Icon} from './icons';
@ -94,6 +94,8 @@ export interface BasicPaginationProps {
popOverContainerSelector?: string;
onPageChange?: (page: number, perPage?: number) => void;
useMobileUI?: boolean;
}
export interface PaginationProps
extends BasicPaginationProps,
@ -257,7 +259,6 @@ export class Pagination extends React.Component<
render() {
const {
layout,
maxButtons,
mode,
activePage,
total,
@ -271,10 +272,13 @@ export class Pagination extends React.Component<
hasNext,
popOverContainer,
popOverContainerSelector,
useMobileUI,
translate: __
} = this.props;
let maxButtons = this.props.maxButtons;
const {pageNum, perPage} = this.state;
const lastPage = this.getLastPage();
const mobileUI = useMobileUI && isMobile();
// 简易模式
if (mode === 'simple') {
@ -441,11 +445,21 @@ export class Pagination extends React.Component<
</li>
);
if (mobileUI) {
pageButtons = [
pageButtons[0],
this.renderPageItem(activePage),
pageButtons[pageButtons.length - 1]
];
}
const go = (
<div className={cx('Pagination-inputGroup Pagination-item')} key="go">
<span className={cx('Pagination-inputGroup-left')} key="go-left">
{__('Pagination.goto')}
</span>
{!mobileUI ? (
<span className={cx('Pagination-inputGroup-left')} key="go-left">
{__('Pagination.goto')}
</span>
) : null}
<input
className={cx('Pagination-inputGroup-input')}
key="go-input"

View File

@ -123,7 +123,7 @@ const Picker = memo<PickerProps>(props => {
{showToolbar && (
<Button
className="PickerColumns-cancel"
level="default"
level="link"
onClick={close}
>
{__('cancel')}
@ -133,7 +133,7 @@ const Picker = memo<PickerProps>(props => {
{showToolbar && (
<Button
className="PickerColumns-confirm"
level="primary"
level="link"
onClick={confirm}
>
{__('confirm')}

View File

@ -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} />
)}

View File

@ -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({

View File

@ -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 (
@ -155,9 +165,11 @@ export class PopOverContainer extends React.Component<
{mobileUI ? (
<PopUp
isShow={this.state.isOpened}
container={popOverContainer}
container={document.body}
className={popOverClassName}
showConfirm={showConfirm}
onHide={this.close}
onConfirm={this.onConfirm}
>
{dropdownRender({onClose: this.close})}
</PopUp>

View File

@ -126,7 +126,7 @@ export class PopUp extends React.PureComponent<PopUpPorps> {
<div className={cx(`${ns}PopUp-toolbar`)}>
<Button
className={cx(`${ns}PopUp-cancel`)}
level="text"
level="link"
onClick={onHide}
>
{__('cancel')}
@ -136,7 +136,7 @@ export class PopUp extends React.PureComponent<PopUpPorps> {
)}
<Button
className={cx(`${ns}PopUp-confirm`)}
level="text"
level="link"
onClick={onConfirm}
>
{__('confirm')}

View File

@ -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,31 @@ 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() {
this.setState({
labelActive: false
});
}
render() {
const {
classnames: cx,
@ -176,7 +203,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 +212,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 +222,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 +235,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 +312,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 +479,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) + '%';
}
/**
@ -461,7 +499,8 @@ export class Range extends React.Component<RangeItemProps, any> {
*/
@autobind
getMarkMaxWidth(value: keyof MarksType, marks: MarksType) {
const {max, min} = this.props;
const {max, min, useMobileUI} = this.props;
const mobileUI = useMobileUI && isMobile();
const curNum = isString(value) ? parseInt(value, 10) : value;
// 给最大宽度赋初始值 默认最大
let maxWidth = Math.abs(max - min);
@ -473,7 +512,9 @@ export class Range extends React.Component<RangeItemProps, any> {
}
});
// 差值的1/2 即为此刻度标记的最大宽度
return Math.floor(maxWidth / 2) + '%';
return mobileUI
? Math.floor(maxWidth) + '%'
: Math.floor(maxWidth / 2) + '%';
}
render() {
@ -489,7 +530,8 @@ export class Range extends React.Component<RangeItemProps, any> {
unit,
tooltipPlacement,
tipFormatter,
onAfterChange
onAfterChange,
useMobileUI
} = this.props;
// trace
@ -520,6 +562,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 +600,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 +617,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 +627,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

View File

@ -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>
)
@ -241,6 +247,7 @@ export class ResultBox extends React.Component<ResultBoxProps> {
onClear,
maxTagCount,
overflowTagPopover,
showArrow,
...rest
} = this.props;
const isFocused = this.state.isFocused;
@ -326,7 +333,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>

View File

@ -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}
>

View File

@ -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}

View File

@ -0,0 +1,508 @@
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, value2array, SelectProps} from './Select';
import VirtualList from './virtual-list';
import Checkbox from './Checkbox';
import Input from './Input';
import {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;
// 单选是字符串
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,
valueField,
labelField,
noResultsText,
loadOptions,
multiple,
valuesNoWrap,
classnames: cx,
checkAll,
checkAllLabel,
checkAllBySearch,
searchable,
disabled,
searchPromptText,
translate: __,
hideSelected,
renderMenu,
virtualThreshold = 100,
isOpen,
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 &&
option[labelField || 'label']
);
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>
);
}
}

View File

@ -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}

View File

@ -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)}

View File

@ -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>

View File

@ -60,6 +60,7 @@ export interface TextAreaProps extends ThemeProps, LocaleProps {
disabled?: boolean;
forwardRef?: {current: HTMLTextAreaElement | null};
useMobileUI?: boolean;
}
export interface TextAreaState {

View File

@ -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,13 +443,14 @@ 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}
clearable={false}
onKeyDown={this.handleSearchKeyDown}
placeholder={searchPlaceholder}
useMobileUI
>
{this.state.searchResult !== null ? (
<a onClick={this.handleSeachCancel}>
@ -818,7 +825,8 @@ export class Transfer<
resultListModeFollowSelect,
selectMode = 'list',
translate: __,
valueField = 'value'
valueField = 'value',
useMobileUI
} = this.props as any;
const {searchResult} = this.state;
@ -832,6 +840,7 @@ export class Transfer<
);
const tableType = resultListModeFollowSelect && selectMode === 'table';
const mobileUI = useMobileUI && isMobile();
return (
<div
@ -840,14 +849,14 @@ export class Transfer<
<div className={cx('Transfer-select')}>
{this.renderSelect(this.props)}
</div>
<div className={cx('Transfer-mid')}>
<div className={cx('Transfer-mid', {'is-mobile': mobileUI})}>
{showArrow /*todo 需要改成确认模式,即:点了按钮才到右边 */ ? (
<div className={cx('Transfer-arrow')}>
<Icon icon="right-arrow" className="icon" />
</div>
) : null}
</div>
<div className={cx('Transfer-result')}>
<div className={cx('Transfer-result', {'is-mobile': mobileUI})}>
<div
className={cx(
'Transfer-title',

View File

@ -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', {
@ -82,6 +124,7 @@ export class TransferDropDown extends Transfer<TransferDropDownProps> {
placeholder={placeholder ?? __('Transfer.searchKeyword')}
clearable={false}
onKeyDown={this.handleSearchKeyDown}
useMobileUI
>
{searchResult !== null ? (
<a onClick={this.handleSeachCancel}>
@ -96,24 +139,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>

View File

@ -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>

View File

@ -31,7 +31,6 @@ export class TransferSearch extends React.Component<
TransferSearchProps,
TransferSearchState
> {
state: TransferSearchState = {
inputValue: ''
};
@ -86,11 +85,7 @@ export class TransferSearch extends React.Component<
}
render() {
const {
classnames: cx,
translate: __,
placeholder
} = this.props;
const {classnames: cx, translate: __, placeholder} = this.props;
const {inputValue} = this.state;
@ -102,6 +97,7 @@ export class TransferSearch extends React.Component<
clearable={false}
onKeyDown={this.handleSearchKeyDown}
placeholder={placeholder}
useMobileUI
>
{!!inputValue ? (
<a onClick={this.handleSeachCancel}>

View File

@ -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>

View File

@ -813,6 +813,7 @@ export class UserSelect extends React.Component<
onChange={this.handleSearch}
placeholder={searchPlaceholder}
clearable={false}
useMobileUI
>
{this.state.isSearch ? (
<a onClick={this.handleSeachCancel}>

View File

@ -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'
@ -638,7 +639,11 @@ class BaseDatePicker extends React.Component<
this.state.viewDate ||
moment()
).clone();
const date = convertArrayValueToMoment(value, types, currentDate);
let date = convertArrayValueToMoment(value, types, currentDate);
if (types?.[1] === 'quarter') {
date = date.startOf('quarter').date(currentDate.date());
}
if (!this.props.value) {
this.setState({
@ -690,6 +695,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

View File

@ -1,6 +1,12 @@
import moment from 'moment';
import React from 'react';
import {localeable, LocaleProps, ThemeProps} from 'amis-core';
import Picker from '../Picker';
import {PickerOption} from '../PickerColumn';
import {PickerColumnItem} from '../PickerColumn';
import {isMobile} from 'amis-core';
import {getRange} from 'amis-core';
import {autobind} from 'amis-core';
export interface QuarterViewProps extends LocaleProps, ThemeProps {
viewDate: moment.Moment;
@ -23,9 +29,17 @@ export interface QuarterViewProps extends LocaleProps, ThemeProps {
renderQuarter: any;
isValidDate: (date: moment.Moment) => boolean;
hideHeader?: boolean;
useMobileUI?: boolean;
onConfirm?: (value: number[], types?: string[]) => void;
onClose?: () => void;
}
export class QuarterView extends React.Component<QuarterViewProps> {
state = {
columns: [],
pickerValue: [this.props.viewDate.year(), this.props.viewDate.quarter()]
};
renderYear() {
const __ = this.props.translate;
const showYearHead = !/^mm$/i.test(this.props.inputFormat || '');
@ -139,10 +153,59 @@ export class QuarterView extends React.Component<QuarterViewProps> {
return true;
}
render() {
const {classnames: cx, hideHeader} = this.props;
onPickerConfirm = (value: number[]) => {
this.props.onConfirm && this.props.onConfirm(value, ['year', 'quarter']);
};
onPickerChange = (value: number[], index: number) => {
this.setState({pickerValue: value});
};
@autobind
cancel() {
this.props.onClose?.();
}
renderPicker() {
const {translate: __} = this.props;
const title = __('Date.titleQuarter');
const minYear = new Date().getFullYear() - 100;
const maxYear = new Date().getFullYear() + 100;
const columns: PickerColumnItem[] = [
{
options: getRange(minYear, maxYear, 1)
},
{
options: getRange(1, 4).map(item => {
return {
text: 'Q' + item,
value: item
};
})
}
];
return (
<Picker
translate={this.props.translate}
locale={this.props.locale}
title={title}
columns={columns}
value={this.state.pickerValue}
onChange={this.onPickerChange}
onConfirm={this.onPickerConfirm}
onClose={this.cancel}
/>
);
}
render() {
const {classnames: cx, hideHeader, useMobileUI} = this.props;
const mobileUI = useMobileUI && isMobile();
return mobileUI ? (
this.renderPicker()
) : (
<div className={cx('ClalendarQuarter')}>
{hideHeader ? null : this.renderYear()}
<table>

View File

@ -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));

View File

@ -11,7 +11,8 @@ import {
localeable,
LocaleProps,
findTree,
noop
noop,
isMobile
} from 'amis-core';
import {Icon} from '../icons';
import SearchBox from '../SearchBox';
@ -109,9 +110,10 @@ export class ConditionField extends React.Component<
return (
<PopOverContainer
useMobileUI
popOverContainer={popOverContainer || (() => findDOMNode(this))}
popOverRender={({onClose}) => (
<>
<div>
{searchable ? (
<SearchBox mini={false} onSearch={this.onSearch} />
) : null}
@ -139,7 +141,7 @@ export class ConditionField extends React.Component<
}
/>
)}
</>
</div>
)}
>
{({onClick, ref, isOpened}) => (
@ -159,10 +161,13 @@ export class ConditionField extends React.Component<
onResultClick={onClick}
placeholder={__('Condition.field_placeholder')}
disabled={disabled}
useMobileUI
>
<span className={cx('CBGroup-fieldCaret')}>
<Icon icon="caret" className="icon" />
</span>
{!isMobile() ? (
<span className={cx('CBGroup-fieldCaret')}>
<Icon icon="caret" className="icon" />
</span>
) : null}
</ResultBox>
</div>
)}

Some files were not shown because too many files have changed in this diff Show More