@rainbow-oh/yee-c 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Field/demo/standalone.d.ts +3 -0
- package/dist/cjs/Field/demo/standalone.js +56 -0
- package/dist/cjs/Field/field.js +20 -11
- package/dist/cjs/Field/interface.d.ts +7 -4
- package/dist/cjs/Field/style/index.less +1 -0
- package/dist/cjs/Menu/wrapper.js +6 -0
- package/dist/cjs/Table/style/index.less +17 -0
- package/dist/cjs/Table/theader/filter.js +2 -2
- package/dist/cjs/style/themes.css +1 -1
- package/dist/cjs/style/variables.css +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Field/demo/standalone.d.ts +3 -0
- package/dist/esm/Field/demo/standalone.js +43 -0
- package/dist/esm/Field/field.js +24 -11
- package/dist/esm/Field/interface.d.ts +7 -4
- package/dist/esm/Field/style/index.less +1 -0
- package/dist/esm/Menu/wrapper.js +8 -0
- package/dist/esm/Table/style/index.less +17 -0
- package/dist/esm/Table/theader/filter.js +4 -2
- package/dist/esm/style/themes.css +1 -1
- package/dist/esm/style/variables.css +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/yee-c.min.css +1 -1
- package/dist/umd/yee-c.min.js +1 -1
- package/package.json +1 -1
- package/src/Field/demo/standalone.tsx +30 -0
- package/src/Field/field.tsx +38 -22
- package/src/Field/index.en-US.md +8 -4
- package/src/Field/index.zh-CN.md +8 -4
- package/src/Field/interface.ts +7 -4
- package/src/Field/style/index.less +1 -0
- package/src/Menu/wrapper.tsx +8 -0
- package/src/Table/style/index.less +17 -0
- package/src/Table/theader/filter.tsx +9 -7
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Field, Input, TextArea } from '@rainbow-oh/yee-c';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
export default () => {
|
|
5
|
+
const [name, setName] = useState('');
|
|
6
|
+
const [remark, setRemark] = useState('');
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div style={{ maxWidth: 400 }}>
|
|
10
|
+
<Field label="Name" required>
|
|
11
|
+
<Input
|
|
12
|
+
value={name}
|
|
13
|
+
onChange={(v: string) => setName(v)}
|
|
14
|
+
placeholder="Standalone controlled Input"
|
|
15
|
+
/>
|
|
16
|
+
</Field>
|
|
17
|
+
<Field label="Remark">
|
|
18
|
+
<TextArea
|
|
19
|
+
value={remark}
|
|
20
|
+
onChange={(v: string) => setRemark(v)}
|
|
21
|
+
placeholder="Standalone controlled TextArea"
|
|
22
|
+
rows={3}
|
|
23
|
+
/>
|
|
24
|
+
</Field>
|
|
25
|
+
<Field>
|
|
26
|
+
<Input placeholder="Field without label" />
|
|
27
|
+
</Field>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
};
|
package/src/Field/field.tsx
CHANGED
|
@@ -28,7 +28,7 @@ const Field: FC<FieldProps> = (props) => {
|
|
|
28
28
|
|
|
29
29
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
30
30
|
|
|
31
|
-
const { form } = useVirtualField(formName, name, {
|
|
31
|
+
const { form } = useVirtualField(formName ?? '', name ?? '', {
|
|
32
32
|
props,
|
|
33
33
|
onStoreChange: forceUpdate,
|
|
34
34
|
});
|
|
@@ -49,20 +49,33 @@ const Field: FC<FieldProps> = (props) => {
|
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
const getControlled = (children: React.ReactElement) => {
|
|
52
|
-
const
|
|
53
|
-
|
|
52
|
+
const childProps = (children as React.ReactElement<any>).props;
|
|
53
|
+
|
|
54
|
+
// Pure layout mode (no form): children keep their own value/onChange.
|
|
55
|
+
// Only a Field-level disabled is merged down.
|
|
56
|
+
// Pure layout mode (no form or no field name): children keep their own
|
|
57
|
+
// value/onChange. Only a Field-level disabled is merged down.
|
|
58
|
+
if (!form || name === undefined) {
|
|
59
|
+
if (disabled === undefined) return children;
|
|
60
|
+
return React.cloneElement(children as React.ReactElement<any>, {
|
|
61
|
+
disabled: disabled || childProps.disabled,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const mergedDisabled = disabled || childProps.disabled;
|
|
66
|
+
const fieldName = name;
|
|
54
67
|
|
|
55
68
|
const attr = {
|
|
56
|
-
value: getFieldValue?.(
|
|
69
|
+
value: getFieldValue?.(fieldName),
|
|
57
70
|
name: formName,
|
|
58
|
-
id:
|
|
71
|
+
id: fieldName,
|
|
59
72
|
disabled: mergedDisabled,
|
|
60
73
|
onChange: (value: unknown, ...rest: Array<unknown>) => {
|
|
61
74
|
const callbacks = getCallbacks?.();
|
|
62
75
|
let _value = value;
|
|
63
76
|
const fieldsValue = getFieldsValue?.();
|
|
64
77
|
const feedback = callbacks?.onValuesBeforeChange?.(
|
|
65
|
-
{ [
|
|
78
|
+
{ [fieldName]: _value },
|
|
66
79
|
fieldsValue,
|
|
67
80
|
);
|
|
68
81
|
|
|
@@ -70,20 +83,20 @@ const Field: FC<FieldProps> = (props) => {
|
|
|
70
83
|
_value = feedback;
|
|
71
84
|
}
|
|
72
85
|
|
|
73
|
-
setFieldsValue?.({ [
|
|
86
|
+
setFieldsValue?.({ [fieldName]: _value }, 'onChange');
|
|
74
87
|
|
|
75
88
|
// Call child component's onChange
|
|
76
89
|
if ((children.props as any)?.onChange) {
|
|
77
90
|
(children.props as any).onChange(_value, ...rest);
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
callbacks?.['onValuesChange']?.({ [
|
|
93
|
+
callbacks?.['onValuesChange']?.({ [fieldName]: _value }, fieldsValue);
|
|
81
94
|
|
|
82
95
|
forceUpdate();
|
|
83
96
|
},
|
|
84
97
|
onBlur: (event: unknown) => {
|
|
85
98
|
// Fire-and-forget async validation; store notifies onStoreChange on completion.
|
|
86
|
-
void validateField?.(
|
|
99
|
+
void validateField?.(fieldName, 'onBlur');
|
|
87
100
|
|
|
88
101
|
// Call child component's onBlur
|
|
89
102
|
if ((children.props as any).onBlur) {
|
|
@@ -95,7 +108,8 @@ const Field: FC<FieldProps> = (props) => {
|
|
|
95
108
|
return React.cloneElement(children, attr);
|
|
96
109
|
};
|
|
97
110
|
|
|
98
|
-
const validate =
|
|
111
|
+
const validate =
|
|
112
|
+
name !== undefined ? getFieldValidate?.(name) : undefined;
|
|
99
113
|
|
|
100
114
|
return (
|
|
101
115
|
<div
|
|
@@ -109,18 +123,20 @@ const Field: FC<FieldProps> = (props) => {
|
|
|
109
123
|
)}
|
|
110
124
|
style={style}
|
|
111
125
|
>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
{label ? (
|
|
127
|
+
<label
|
|
128
|
+
className={clsx(
|
|
129
|
+
`${prefixCls}-label`,
|
|
130
|
+
{
|
|
131
|
+
[`${prefixCls}-required`]: isRequired(),
|
|
132
|
+
},
|
|
133
|
+
classNames?.label,
|
|
134
|
+
)}
|
|
135
|
+
style={styles?.label}
|
|
136
|
+
>
|
|
137
|
+
{label}
|
|
138
|
+
</label>
|
|
139
|
+
) : null}
|
|
124
140
|
<div
|
|
125
141
|
className={clsx(`${prefixCls}-content`, classNames?.content)}
|
|
126
142
|
style={styles?.content}
|
package/src/Field/index.en-US.md
CHANGED
|
@@ -10,11 +10,15 @@ toc: 'content'
|
|
|
10
10
|
|
|
11
11
|
# Field
|
|
12
12
|
|
|
13
|
-
A standalone form field component that works without a `<Form>` wrapper.
|
|
13
|
+
A standalone form field component that works without a `<Form>` wrapper. Two usage modes:
|
|
14
|
+
|
|
15
|
+
- **Virtual form mode**: create a virtual form with `useVirtualForm`; Field manages value and validation (pass `formName` + `name`)
|
|
16
|
+
- **Pure layout mode**: omit `formName` (or the form is not registered); Field only renders the label layout and required asterisk, while `value`/`onChange` pass straight through to the child — state is managed by the consumer
|
|
14
17
|
|
|
15
18
|
## Code Demo
|
|
16
19
|
|
|
17
20
|
<code src="./demo/basic.tsx" title="Basic" description="Create a virtual form with useVirtualForm and bind Field to Input"></code>
|
|
21
|
+
<code src="./demo/standalone.tsx" title="Pure Layout" description="Without formName, state is managed by the consumer and Field only handles label layout"></code>
|
|
18
22
|
<code src="./demo/validation.tsx" title="Validation" description="required, minLength, regexp, validator rules and validateTrigger"></code>
|
|
19
23
|
<code src="./demo/layout.tsx" title="Layout & Style" description="Vertical/horizontal layout and semantic style customization"></code>
|
|
20
24
|
<code src="./demo/disabled.tsx" title="Disabled" description="Disable the entire field via the disabled prop"></code>
|
|
@@ -25,14 +29,14 @@ A standalone form field component that works without a `<Form>` wrapper. Uses `u
|
|
|
25
29
|
|
|
26
30
|
| Property | Type | Description | Default |
|
|
27
31
|
| --- | --- | --- | --- |
|
|
28
|
-
| formName | `string` | Virtual form name
|
|
29
|
-
| name | `string` | Field name (required) | - |
|
|
32
|
+
| formName | `string` | Virtual form name; omit for pure layout mode | - |
|
|
33
|
+
| name | `string` | Field name (required in virtual form mode) | - |
|
|
30
34
|
| label | `React.ReactNode` | Field label | - |
|
|
31
35
|
| children | `React.ReactElement` | Form control (must be a single React element) | - |
|
|
32
36
|
| rules | `Rule[]` | Validation rules | - |
|
|
33
37
|
| required | `boolean` | Whether the field is required (shows asterisk) | `false` |
|
|
34
38
|
| layout | `'vertical' \| 'horizontal'` | Layout direction | `'vertical'` |
|
|
35
|
-
| disabled | `boolean` | Whether the field is disabled | `false` |
|
|
39
|
+
| disabled | `boolean` | Whether the field is disabled (merged onto the child, also works in pure layout mode) | `false` |
|
|
36
40
|
| prefixCls | `string` | Class name prefix | `'yee-field'` |
|
|
37
41
|
| className | `string` | Root element class name | - |
|
|
38
42
|
| style | `React.CSSProperties` | Root element style | - |
|
package/src/Field/index.zh-CN.md
CHANGED
|
@@ -10,11 +10,15 @@ toc: 'content'
|
|
|
10
10
|
|
|
11
11
|
# Field 字段
|
|
12
12
|
|
|
13
|
-
脱离 `<Form>`
|
|
13
|
+
脱离 `<Form>` 组件独立使用的表单字段组件。两种用法:
|
|
14
|
+
|
|
15
|
+
- **虚拟表单模式**:通过 `useVirtualForm` 创建虚拟表单,Field 负责值管理和校验(传 `formName` + `name`)
|
|
16
|
+
- **纯布局模式**:不传 `formName`(或该表单未注册),Field 只负责 label 布局与必填星号,`value`/`onChange` 由使用者自己控制并直接透传给子控件
|
|
14
17
|
|
|
15
18
|
## 代码演示
|
|
16
19
|
|
|
17
20
|
<code src="./demo/basic.tsx" title="基础用法" description="使用 useVirtualForm 创建虚拟表单,配合 Field 绑定 Input"></code>
|
|
21
|
+
<code src="./demo/standalone.tsx" title="纯布局模式" description="不传 formName,受控状态自己管理,Field 只负责标签布局"></code>
|
|
18
22
|
<code src="./demo/validation.tsx" title="校验规则" description="required、minLength、regexp、validator 等校验规则,以及 validateTrigger 控制触发时机"></code>
|
|
19
23
|
<code src="./demo/layout.tsx" title="布局与样式" description="垂直/水平布局、语义化样式定制"></code>
|
|
20
24
|
<code src="./demo/disabled.tsx" title="禁用状态" description="通过 disabled 属性禁用整个字段"></code>
|
|
@@ -25,14 +29,14 @@ toc: 'content'
|
|
|
25
29
|
|
|
26
30
|
| 属性名 | 类型 | 描述 | 默认值 |
|
|
27
31
|
| --- | --- | --- | --- |
|
|
28
|
-
| formName | `string` |
|
|
29
|
-
| name | `string` |
|
|
32
|
+
| formName | `string` | 所属虚拟表单名称;不传时 Field 为纯布局模式 | - |
|
|
33
|
+
| name | `string` | 字段名称(虚拟表单模式下必填) | - |
|
|
30
34
|
| label | `React.ReactNode` | 字段标签 | - |
|
|
31
35
|
| children | `React.ReactElement` | 表单控件(必须是单个 React 元素) | - |
|
|
32
36
|
| rules | `Rule[]` | 校验规则 | - |
|
|
33
37
|
| required | `boolean` | 是否必填(显示星号标记) | `false` |
|
|
34
38
|
| layout | `'vertical' \| 'horizontal'` | 布局方式 | `'vertical'` |
|
|
35
|
-
| disabled | `boolean` |
|
|
39
|
+
| disabled | `boolean` | 是否禁用(会合并到子控件上,纯布局模式下同样生效) | `false` |
|
|
36
40
|
| prefixCls | `string` | 类名前缀 | `'yee-field'` |
|
|
37
41
|
| className | `string` | 根元素类名 | - |
|
|
38
42
|
| style | `React.CSSProperties` | 根元素样式 | - |
|
package/src/Field/interface.ts
CHANGED
|
@@ -28,13 +28,16 @@ export interface FieldProps {
|
|
|
28
28
|
*/
|
|
29
29
|
label?: React.ReactNode;
|
|
30
30
|
/**
|
|
31
|
-
* Field name
|
|
31
|
+
* Field name (required only when used with a registered virtual form)
|
|
32
32
|
*/
|
|
33
|
-
name
|
|
33
|
+
name?: string;
|
|
34
34
|
/**
|
|
35
|
-
* Name of the parent form
|
|
35
|
+
* Name of the parent form.
|
|
36
|
+
* When omitted (or no form is registered under this name), Field renders
|
|
37
|
+
* as a pure layout wrapper: the child keeps its own value/onChange and
|
|
38
|
+
* no validation runs.
|
|
36
39
|
*/
|
|
37
|
-
formName
|
|
40
|
+
formName?: string;
|
|
38
41
|
/**
|
|
39
42
|
* Whether disabled
|
|
40
43
|
*/
|
package/src/Menu/wrapper.tsx
CHANGED
|
@@ -74,6 +74,14 @@ const MenuWrapper = (baseprops: MenuProps) => {
|
|
|
74
74
|
defaultValue: defaultSelectedKeys,
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
// Reset `openKeys` when inline collapsed
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (inlineCollapsed) {
|
|
80
|
+
setMergedOpenKeys([]);
|
|
81
|
+
onOpenChange?.([]);
|
|
82
|
+
}
|
|
83
|
+
}, [inlineCollapsed]);
|
|
84
|
+
|
|
77
85
|
// -------- Keyboard control ------------------------------
|
|
78
86
|
const [focusedKey, setFocusedKey] = useState<string | undefined>(undefined);
|
|
79
87
|
const latestOpenKeys = useLatest(mergedOpenKeys);
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
@{pls} {
|
|
4
4
|
position: relative;
|
|
5
5
|
|
|
6
|
+
&-spin {
|
|
7
|
+
z-index: 2;
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
&-bordered {
|
|
7
11
|
> @{pls}-thead,
|
|
8
12
|
> @{pls}-tbody {
|
|
@@ -121,6 +125,19 @@
|
|
|
121
125
|
display: inline-flex;
|
|
122
126
|
cursor: pointer;
|
|
123
127
|
}
|
|
128
|
+
|
|
129
|
+
&-popup {
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-direction: column;
|
|
132
|
+
max-height: calc(
|
|
133
|
+
var(--yee-table-filter-popup-height) - var(--yee-popover-padding) * 2
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
&-list {
|
|
138
|
+
min-height: 0;
|
|
139
|
+
overflow: auto;
|
|
140
|
+
}
|
|
124
141
|
}
|
|
125
142
|
|
|
126
143
|
&-content {
|
|
@@ -176,13 +176,15 @@ const HeaderFilter = React.memo((props: any) => {
|
|
|
176
176
|
<div className={clsx(`${prefixCls}-filter-popup`)}>
|
|
177
177
|
{searchInputNode}
|
|
178
178
|
<Divider style={{ margin: '8px 0' }} />
|
|
179
|
-
<
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
179
|
+
<div className={`${prefixCls}-filter-list`}>
|
|
180
|
+
<Tree
|
|
181
|
+
dataSource={dataSource}
|
|
182
|
+
checkable
|
|
183
|
+
multiple
|
|
184
|
+
checkedKeys={selectNodes}
|
|
185
|
+
onCheck={(keys) => setSelectNodes(keys as Array<string | number>)}
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
186
188
|
<Divider style={{ margin: '8px 0' }} />
|
|
187
189
|
<Space block style={{ justifyContent: 'flex-end' }}>
|
|
188
190
|
<Button size="small" onClick={reset}>
|