niuma-ui 1.2.6 → 1.2.8
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/CHANGELOG.md +16 -0
- package/dist/components/RsAutoComplete.d.ts +39 -0
- package/dist/components/RsAutoComplete.impl.js +306 -0
- package/dist/components/RsAutoComplete.js +5 -0
- package/dist/components/RsCascader.css +81 -0
- package/dist/components/RsCascader.d.ts +34 -0
- package/dist/components/RsCascader.impl.js +138 -0
- package/dist/components/RsCascader.js +8 -0
- package/dist/components/RsMentions-1.css +29 -0
- package/dist/components/RsMentions.css +19 -0
- package/dist/components/RsMentions.d.ts +32 -0
- package/dist/components/RsMentions.impl.js +242 -0
- package/dist/components/RsMentions.js +10 -0
- package/dist/components/RsSelect.css +51 -36
- package/dist/components/RsSelect.d.ts +27 -7
- package/dist/components/RsSelect.impl.js +145 -30
- package/dist/components/RsSelect.js +1 -1
- package/dist/components/RsTabs.d.ts +1 -1
- package/dist/components/RsToaster.d.ts +1 -1
- package/dist/components/RsTree.d.ts +4 -4
- package/dist/components/RsTree.impl.js +1 -1
- package/dist/components/RsTreeSelect.css +60 -0
- package/dist/components/RsTreeSelect.d.ts +33 -0
- package/dist/components/RsTreeSelect.impl.js +145 -0
- package/dist/components/RsTreeSelect.js +8 -0
- package/dist/components/cascader-utils.d.ts +16 -0
- package/dist/components/cascader-utils.js +39 -0
- package/dist/components/mentions-utils.d.ts +46 -0
- package/dist/components/mentions-utils.js +128 -0
- package/dist/components/overlay-utils.d.ts +31 -0
- package/dist/components/overlay-utils.js +29 -1
- package/dist/components/select-utils.d.ts +15 -0
- package/dist/components/select-utils.js +37 -1
- package/dist/components/table/RsTableCellEditor.impl.js +7 -1
- package/dist/components/use-rs-select.d.ts +5 -2
- package/dist/components/use-rs-select.js +31 -19
- package/dist/composables/useRsTableShell.js +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.js +6 -0
- package/dist/locale/messages.js +6 -0
- package/dist/styles.css +160 -0
- package/package.json +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import './RsTreeSelect.css'
|
|
2
|
+
import _plugin_vue_export_helper_default from "../_virtual/_plugin-vue_export-helper.js";
|
|
3
|
+
import RsTreeSelect_vue_vue_type_script_setup_true_lang_default from "./RsTreeSelect.impl.js";
|
|
4
|
+
|
|
5
|
+
//#region src/components/RsTreeSelect.vue
|
|
6
|
+
var RsTreeSelect_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RsTreeSelect_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-a057dd37"]]);
|
|
7
|
+
//#endregion
|
|
8
|
+
export { RsTreeSelect_default as default };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RsSelectValue } from './select-utils';
|
|
2
|
+
/** 级联选项,对齐 Ant Cascader options */
|
|
3
|
+
export interface RsCascaderOption {
|
|
4
|
+
label: string;
|
|
5
|
+
value: RsSelectValue;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
children?: RsCascaderOption[];
|
|
8
|
+
isLeaf?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export type RsCascaderPath = RsSelectValue[];
|
|
11
|
+
export type RsCascaderExpandTrigger = 'click' | 'hover';
|
|
12
|
+
export declare function findCascaderOption(options: readonly RsCascaderOption[], value: RsSelectValue): RsCascaderOption | undefined;
|
|
13
|
+
export declare function cascaderColumns(options: readonly RsCascaderOption[], path: RsCascaderPath): RsCascaderOption[][];
|
|
14
|
+
export declare function cascaderLabels(options: readonly RsCascaderOption[], path: RsCascaderPath): string[];
|
|
15
|
+
export declare function cascaderDisplay(options: readonly RsCascaderOption[], path: RsCascaderPath, separator?: string): string;
|
|
16
|
+
export declare function isCascaderLeaf(option: RsCascaderOption): boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/components/cascader-utils.ts
|
|
2
|
+
function findCascaderOption(options, value) {
|
|
3
|
+
return options.find((item) => String(item.value) === String(value));
|
|
4
|
+
}
|
|
5
|
+
function cascaderColumns(options, path) {
|
|
6
|
+
const columns = [options.slice()];
|
|
7
|
+
let level = options;
|
|
8
|
+
for (const token of path) {
|
|
9
|
+
const children = findCascaderOption(level, token)?.children;
|
|
10
|
+
if (!children?.length) break;
|
|
11
|
+
columns.push(children);
|
|
12
|
+
level = children;
|
|
13
|
+
}
|
|
14
|
+
return columns;
|
|
15
|
+
}
|
|
16
|
+
function cascaderLabels(options, path) {
|
|
17
|
+
const labels = [];
|
|
18
|
+
let level = options;
|
|
19
|
+
for (const token of path) {
|
|
20
|
+
const current = findCascaderOption(level, token);
|
|
21
|
+
if (!current) {
|
|
22
|
+
labels.push(String(token));
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
labels.push(current.label);
|
|
26
|
+
level = current.children ?? [];
|
|
27
|
+
}
|
|
28
|
+
return labels;
|
|
29
|
+
}
|
|
30
|
+
function cascaderDisplay(options, path, separator = " / ") {
|
|
31
|
+
return cascaderLabels(options, path).join(separator);
|
|
32
|
+
}
|
|
33
|
+
function isCascaderLeaf(option) {
|
|
34
|
+
if (option.isLeaf === true) return true;
|
|
35
|
+
if (option.isLeaf === false) return false;
|
|
36
|
+
return !option.children?.length;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { cascaderColumns, cascaderDisplay, cascaderLabels, findCascaderOption, isCascaderLeaf };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface RsMentionOption {
|
|
2
|
+
label: string;
|
|
3
|
+
value: string;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface RsMentionActive {
|
|
7
|
+
prefix: string;
|
|
8
|
+
query: string;
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveMentionPrefixes(prefix: string | readonly string[]): string[];
|
|
13
|
+
/** 光标前最近一个前缀 + 查询词(不含空格) */
|
|
14
|
+
export declare function findActiveMention(text: string, cursor: number, prefixes: readonly string[], split?: string): RsMentionActive | null;
|
|
15
|
+
export declare function applyMention(text: string, active: RsMentionActive, value: string, split?: string): {
|
|
16
|
+
text: string;
|
|
17
|
+
cursor: number;
|
|
18
|
+
};
|
|
19
|
+
export interface RsMentionCaretBox {
|
|
20
|
+
top: number;
|
|
21
|
+
left: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}
|
|
24
|
+
export interface RsMentionPopupBox {
|
|
25
|
+
top: number;
|
|
26
|
+
left: number;
|
|
27
|
+
placement: 'top' | 'bottom';
|
|
28
|
+
}
|
|
29
|
+
export declare function stepMentionIndex(options: readonly RsMentionOption[], current: number, delta: 1 | -1): number;
|
|
30
|
+
/** 视口内避让:下边不够就翻到上方,左右夹进窗口。 */
|
|
31
|
+
export declare function placeMentionPopup(caret: RsMentionCaretBox, popup: {
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
}, viewport: {
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
}, gap?: number): RsMentionPopupBox;
|
|
38
|
+
export interface RsTextareaCaretMeter {
|
|
39
|
+
measure: (textarea: HTMLTextAreaElement, index: number) => RsMentionCaretBox;
|
|
40
|
+
dispose: () => void;
|
|
41
|
+
}
|
|
42
|
+
/** 复用隐藏镜像节点,避免每次按键 insert/remove 逼 reflow。 */
|
|
43
|
+
export declare function createTextareaCaretMeter(): RsTextareaCaretMeter;
|
|
44
|
+
/** 相对 textarea 内容区的光标坐标(已扣滚动)。测试 / 单次调用用;热路径请复用 meter。 */
|
|
45
|
+
export declare function measureTextareaCaret(textarea: HTMLTextAreaElement, index: number): RsMentionCaretBox;
|
|
46
|
+
export declare function filterMentionOptions(options: readonly RsMentionOption[], query: string): RsMentionOption[];
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { placeAnchoredPopup, stepEnabledIndex } from "./overlay-utils.js";
|
|
2
|
+
//#region src/components/mentions-utils.ts
|
|
3
|
+
function resolveMentionPrefixes(prefix) {
|
|
4
|
+
return (Array.isArray(prefix) ? prefix : [prefix]).map(String).filter(Boolean);
|
|
5
|
+
}
|
|
6
|
+
/** 光标前最近一个前缀 + 查询词(不含空格) */
|
|
7
|
+
function findActiveMention(text, cursor, prefixes, split = " ") {
|
|
8
|
+
const head = text.slice(0, Math.max(0, cursor));
|
|
9
|
+
let found = null;
|
|
10
|
+
for (const token of prefixes) {
|
|
11
|
+
const at = head.lastIndexOf(token);
|
|
12
|
+
if (at < 0) continue;
|
|
13
|
+
const after = head.slice(at + token.length);
|
|
14
|
+
if (after.includes(split) || after.includes("\n")) continue;
|
|
15
|
+
if (!found || at > found.start) found = {
|
|
16
|
+
prefix: token,
|
|
17
|
+
query: after,
|
|
18
|
+
start: at,
|
|
19
|
+
end: cursor
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return found;
|
|
23
|
+
}
|
|
24
|
+
function applyMention(text, active, value, split = " ") {
|
|
25
|
+
const insert = `${active.prefix}${value}${split}`;
|
|
26
|
+
return {
|
|
27
|
+
text: `${text.slice(0, active.start)}${insert}${text.slice(active.end)}`,
|
|
28
|
+
cursor: active.start + insert.length
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function stepMentionIndex(options, current, delta) {
|
|
32
|
+
return stepEnabledIndex(options, current, delta);
|
|
33
|
+
}
|
|
34
|
+
/** 视口内避让:下边不够就翻到上方,左右夹进窗口。 */
|
|
35
|
+
function placeMentionPopup(caret, popup, viewport, gap = 4) {
|
|
36
|
+
const box = placeAnchoredPopup(caret, popup, viewport, gap);
|
|
37
|
+
return {
|
|
38
|
+
top: box.top,
|
|
39
|
+
left: box.left,
|
|
40
|
+
placement: box.placement
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
var MIRROR_STYLE_KEYS = [
|
|
44
|
+
"boxSizing",
|
|
45
|
+
"width",
|
|
46
|
+
"height",
|
|
47
|
+
"overflowX",
|
|
48
|
+
"overflowY",
|
|
49
|
+
"borderTopWidth",
|
|
50
|
+
"borderRightWidth",
|
|
51
|
+
"borderBottomWidth",
|
|
52
|
+
"borderLeftWidth",
|
|
53
|
+
"paddingTop",
|
|
54
|
+
"paddingRight",
|
|
55
|
+
"paddingBottom",
|
|
56
|
+
"paddingLeft",
|
|
57
|
+
"fontStyle",
|
|
58
|
+
"fontVariant",
|
|
59
|
+
"fontWeight",
|
|
60
|
+
"fontStretch",
|
|
61
|
+
"fontSize",
|
|
62
|
+
"fontFamily",
|
|
63
|
+
"letterSpacing",
|
|
64
|
+
"textIndent",
|
|
65
|
+
"textTransform",
|
|
66
|
+
"wordSpacing",
|
|
67
|
+
"tabSize",
|
|
68
|
+
"lineHeight",
|
|
69
|
+
"whiteSpace",
|
|
70
|
+
"wordWrap"
|
|
71
|
+
];
|
|
72
|
+
/** 复用隐藏镜像节点,避免每次按键 insert/remove 逼 reflow。 */
|
|
73
|
+
function createTextareaCaretMeter() {
|
|
74
|
+
let mirror = null;
|
|
75
|
+
let marker = null;
|
|
76
|
+
function ensure() {
|
|
77
|
+
if (mirror && marker) return {
|
|
78
|
+
mirror,
|
|
79
|
+
marker
|
|
80
|
+
};
|
|
81
|
+
mirror = document.createElement("div");
|
|
82
|
+
mirror.setAttribute("aria-hidden", "true");
|
|
83
|
+
const s = mirror.style;
|
|
84
|
+
s.position = "absolute";
|
|
85
|
+
s.visibility = "hidden";
|
|
86
|
+
s.pointerEvents = "none";
|
|
87
|
+
s.whiteSpace = "pre-wrap";
|
|
88
|
+
s.wordWrap = "break-word";
|
|
89
|
+
s.top = "0";
|
|
90
|
+
s.left = "-9999px";
|
|
91
|
+
marker = document.createElement("span");
|
|
92
|
+
marker.textContent = ".";
|
|
93
|
+
document.body.appendChild(mirror);
|
|
94
|
+
return {
|
|
95
|
+
mirror,
|
|
96
|
+
marker
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function measure(textarea, index) {
|
|
100
|
+
const { mirror: box, marker: caret } = ensure();
|
|
101
|
+
const style = window.getComputedStyle(textarea);
|
|
102
|
+
for (const key of MIRROR_STYLE_KEYS) box.style.setProperty(key.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`), style[key]);
|
|
103
|
+
const text = textarea.value.slice(0, Math.max(0, index));
|
|
104
|
+
box.textContent = text.endsWith("\n") ? `${text}\u00a0` : text;
|
|
105
|
+
box.appendChild(caret);
|
|
106
|
+
return {
|
|
107
|
+
top: caret.offsetTop - textarea.scrollTop,
|
|
108
|
+
left: caret.offsetLeft - textarea.scrollLeft,
|
|
109
|
+
height: caret.offsetHeight || parseFloat(style.lineHeight) || 16
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function dispose() {
|
|
113
|
+
mirror?.remove();
|
|
114
|
+
mirror = null;
|
|
115
|
+
marker = null;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
measure,
|
|
119
|
+
dispose
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function filterMentionOptions(options, query) {
|
|
123
|
+
const q = query.trim().toLowerCase();
|
|
124
|
+
if (!q) return options.filter((item) => !item.disabled);
|
|
125
|
+
return options.filter((item) => !item.disabled && (item.label.toLowerCase().includes(q) || item.value.toLowerCase().includes(q)));
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
export { applyMention, createTextareaCaretMeter, filterMentionOptions, findActiveMention, placeMentionPopup, resolveMentionPrefixes, stepMentionIndex };
|
|
@@ -21,3 +21,34 @@ export declare const RS_TOAST_DEFAULT_POSITION: RsToastPosition;
|
|
|
21
21
|
export declare const RS_TOAST_DEFAULT_GAP = 4;
|
|
22
22
|
export declare const rsToastPositions: readonly ["top-center", "top-left", "top-right", "bottom-center", "bottom-left", "bottom-right"];
|
|
23
23
|
export declare function rsFeedbackIconClass(tone: RsFeedbackTone): string;
|
|
24
|
+
/** 视口内锚点矩形(输入框、插入符等)。 */
|
|
25
|
+
export interface RsOverlayAnchorBox {
|
|
26
|
+
top: number;
|
|
27
|
+
left: number;
|
|
28
|
+
height: number;
|
|
29
|
+
width?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface RsOverlayPopupSize {
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
}
|
|
35
|
+
export interface RsOverlayViewport {
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
}
|
|
39
|
+
/** 锚点浮层最终盒子。placement 供翻转后的样式钩子使用。 */
|
|
40
|
+
export interface RsOverlayBox {
|
|
41
|
+
top: number;
|
|
42
|
+
left: number;
|
|
43
|
+
width: number;
|
|
44
|
+
placement: 'top' | 'bottom';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 把浮层贴到锚点:优先下方,视口不够则翻到上方,左右夹进窗口。
|
|
48
|
+
* 对齐 WAI-ARIA APG 浮层惯例与 CSS overflow 避让,不跟某一家组件库的 API。
|
|
49
|
+
*/
|
|
50
|
+
export declare function placeAnchoredPopup(anchor: RsOverlayAnchorBox, popup: RsOverlayPopupSize, viewport: RsOverlayViewport, gap?: number): RsOverlayBox;
|
|
51
|
+
/** 在可选项里按方向跳过 disabled,供 combobox / listbox 方向键与 Home / End。 */
|
|
52
|
+
export declare function stepEnabledIndex<T extends {
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
}>(options: readonly T[], current: number, delta: 1 | -1): number;
|
|
@@ -13,5 +13,33 @@ var rsToastPositions = [
|
|
|
13
13
|
function rsFeedbackIconClass(tone) {
|
|
14
14
|
return `rs-feedback-icon rs-feedback-icon--${tone}`;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* 把浮层贴到锚点:优先下方,视口不够则翻到上方,左右夹进窗口。
|
|
18
|
+
* 对齐 WAI-ARIA APG 浮层惯例与 CSS overflow 避让,不跟某一家组件库的 API。
|
|
19
|
+
*/
|
|
20
|
+
function placeAnchoredPopup(anchor, popup, viewport, gap = 4) {
|
|
21
|
+
const below = anchor.top + anchor.height + gap;
|
|
22
|
+
const above = anchor.top - popup.height - gap;
|
|
23
|
+
const placement = below + popup.height <= viewport.height || above < gap ? "bottom" : "top";
|
|
24
|
+
const top = placement === "bottom" ? below : Math.max(gap, above);
|
|
25
|
+
const width = Math.min(popup.width, Math.max(0, viewport.width - gap * 2));
|
|
26
|
+
const maxLeft = Math.max(gap, viewport.width - width - gap);
|
|
27
|
+
return {
|
|
28
|
+
top,
|
|
29
|
+
left: Math.min(Math.max(gap, anchor.left), maxLeft),
|
|
30
|
+
width,
|
|
31
|
+
placement
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** 在可选项里按方向跳过 disabled,供 combobox / listbox 方向键与 Home / End。 */
|
|
35
|
+
function stepEnabledIndex(options, current, delta) {
|
|
36
|
+
if (!options.length) return 0;
|
|
37
|
+
let index = current;
|
|
38
|
+
for (let n = 0; n < options.length; n += 1) {
|
|
39
|
+
index = (index + delta + options.length) % options.length;
|
|
40
|
+
if (!options[index]?.disabled) return index;
|
|
41
|
+
}
|
|
42
|
+
return Math.max(0, current);
|
|
43
|
+
}
|
|
16
44
|
//#endregion
|
|
17
|
-
export { RS_TOAST_DEFAULT_GAP, RS_TOAST_DEFAULT_POSITION, rsFeedbackIconClass, rsToastPositions };
|
|
45
|
+
export { RS_TOAST_DEFAULT_GAP, RS_TOAST_DEFAULT_POSITION, placeAnchoredPopup, rsFeedbackIconClass, rsToastPositions, stepEnabledIndex };
|
|
@@ -3,6 +3,14 @@ export type RsSelectValue = string | number;
|
|
|
3
3
|
export type RsSelectOptionFilterProp = string;
|
|
4
4
|
export type RsSelectPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
5
5
|
export type RsSelectStatus = 'error' | 'warning' | '';
|
|
6
|
+
/** 对齐 Ant Design 5 Select variant;搜索仍在面板内,不改触发器打字。 */
|
|
7
|
+
export type RsSelectVariant = 'outlined' | 'filled' | 'borderless';
|
|
8
|
+
/** 多选折叠:数字为上限;responsive 按触发器宽度收。 */
|
|
9
|
+
export type RsSelectMaxTagCount = number | 'responsive';
|
|
10
|
+
export declare function splitSelectLabelHighlight(label: string, keyword: string): Array<{
|
|
11
|
+
text: string;
|
|
12
|
+
highlight: boolean;
|
|
13
|
+
}>;
|
|
6
14
|
/** 自定义过滤(对齐 Ant Design filterOption)。返回 false 则隐藏该项。 */
|
|
7
15
|
export type RsSelectFilterOption = (query: string, option: RsSelectOption) => boolean;
|
|
8
16
|
/** 过滤后排序(对齐 Ant Design filterSort) */
|
|
@@ -41,6 +49,7 @@ export type RsSelectModelValue = RsSelectValue | RsSelectValue[] | RsSelectLabel
|
|
|
41
49
|
* 按泛型收窄后的 v-model。
|
|
42
50
|
* 默认单选、值为 string:宿主 `@update:model-value="(v: string) => void"` 可直接赋值。
|
|
43
51
|
* `multiple` / `labelInValue` 为字面量 true 时收成数组或 labeled;为 `boolean` 时保留联合。
|
|
52
|
+
* 组件本体的 defineModel 用 RsSelectModelValue(multiple / labelInValue 是运行时 boolean)。
|
|
44
53
|
*/
|
|
45
54
|
export type RsSelectResolvedModel<Value extends RsSelectValue = string, Multiple extends boolean = false, LabelInValue extends boolean = false> = LabelInValue extends true ? Multiple extends true ? RsSelectLabeledValue[] : Multiple extends false ? RsSelectLabeledValue | '' : RsSelectLabeledValue | RsSelectLabeledValue[] | '' : Multiple extends true ? Value[] : Multiple extends false ? Value | '' : Value | Value[] | '';
|
|
46
55
|
/**
|
|
@@ -48,6 +57,12 @@ export type RsSelectResolvedModel<Value extends RsSelectValue = string, Multiple
|
|
|
48
57
|
* 选项若传入 value: '',对内映射为此哨兵,避免崩溃;对外读写仍为 ''。
|
|
49
58
|
*/
|
|
50
59
|
export declare const RS_SELECT_EMPTY_VALUE = "__rs_select_empty__";
|
|
60
|
+
/**
|
|
61
|
+
* Vue 对泛型 boolean prop 不会按 Boolean 收口。
|
|
62
|
+
* 模板写 `multiple` 时运行时可能是 '';个别版本还会落到 attrs 变成 'true' / 'multiple'。
|
|
63
|
+
* Boolean('') 为 false,会把多选打成单选。
|
|
64
|
+
*/
|
|
65
|
+
export declare function isSelectMultiple(value: unknown): boolean;
|
|
51
66
|
/** 业务 value → ComboboxItem token(空串走哨兵;数字转为十进制字符串) */
|
|
52
67
|
export declare function toComboboxValue(value: RsSelectValue): string;
|
|
53
68
|
/** ComboboxItem token → 业务字符串(仅还原空串哨兵,不恢复 number) */
|
|
@@ -1,9 +1,45 @@
|
|
|
1
1
|
//#region src/components/select-utils.ts
|
|
2
|
+
function splitSelectLabelHighlight(label, keyword) {
|
|
3
|
+
const query = keyword.trim();
|
|
4
|
+
if (!query) return [{
|
|
5
|
+
text: label,
|
|
6
|
+
highlight: false
|
|
7
|
+
}];
|
|
8
|
+
const lowerLabel = label.toLowerCase();
|
|
9
|
+
const lowerQuery = query.toLowerCase();
|
|
10
|
+
const index = lowerLabel.indexOf(lowerQuery);
|
|
11
|
+
if (index < 0) return [{
|
|
12
|
+
text: label,
|
|
13
|
+
highlight: false
|
|
14
|
+
}];
|
|
15
|
+
const parts = [];
|
|
16
|
+
if (index > 0) parts.push({
|
|
17
|
+
text: label.slice(0, index),
|
|
18
|
+
highlight: false
|
|
19
|
+
});
|
|
20
|
+
parts.push({
|
|
21
|
+
text: label.slice(index, index + query.length),
|
|
22
|
+
highlight: true
|
|
23
|
+
});
|
|
24
|
+
if (index + query.length < label.length) parts.push({
|
|
25
|
+
text: label.slice(index + query.length),
|
|
26
|
+
highlight: false
|
|
27
|
+
});
|
|
28
|
+
return parts;
|
|
29
|
+
}
|
|
2
30
|
/**
|
|
3
31
|
* Reka ComboboxItem 禁止 value 为空串(空串表示未选中 / placeholder)。
|
|
4
32
|
* 选项若传入 value: '',对内映射为此哨兵,避免崩溃;对外读写仍为 ''。
|
|
5
33
|
*/
|
|
6
34
|
var RS_SELECT_EMPTY_VALUE = "__rs_select_empty__";
|
|
35
|
+
/**
|
|
36
|
+
* Vue 对泛型 boolean prop 不会按 Boolean 收口。
|
|
37
|
+
* 模板写 `multiple` 时运行时可能是 '';个别版本还会落到 attrs 变成 'true' / 'multiple'。
|
|
38
|
+
* Boolean('') 为 false,会把多选打成单选。
|
|
39
|
+
*/
|
|
40
|
+
function isSelectMultiple(value) {
|
|
41
|
+
return value === true || value === "" || value === "true" || value === "multiple";
|
|
42
|
+
}
|
|
7
43
|
/** 业务 value → ComboboxItem token(空串走哨兵;数字转为十进制字符串) */
|
|
8
44
|
function toComboboxValue(value) {
|
|
9
45
|
return value === "" ? RS_SELECT_EMPTY_VALUE : String(value);
|
|
@@ -208,4 +244,4 @@ function splitByTokenSeparators(raw, separators) {
|
|
|
208
244
|
return raw.split(found);
|
|
209
245
|
}
|
|
210
246
|
//#endregion
|
|
211
|
-
export { RS_SELECT_EMPTY_VALUE, buildOptionDisabledMap, buildOptionLabelMap, comboboxBindingToTokens, filterSelectOptions, findSelectOption, flattenSelectOptions, flattenSelectValues, fromComboboxValue, isSelectLabeledValue, isSelectOptionGroup, normalizeSelectOptions, optionDisplayLabel, packSelectModel, restoreSelectValue, restoreSelectValues, selectModelEntries, sortSelectOptions, splitByTokenSeparators, toComboboxValue, toLabeledValue, toSelectedTokens, unwrapSelectEntry };
|
|
247
|
+
export { RS_SELECT_EMPTY_VALUE, buildOptionDisabledMap, buildOptionLabelMap, comboboxBindingToTokens, filterSelectOptions, findSelectOption, flattenSelectOptions, flattenSelectValues, fromComboboxValue, isSelectLabeledValue, isSelectMultiple, isSelectOptionGroup, normalizeSelectOptions, optionDisplayLabel, packSelectModel, restoreSelectValue, restoreSelectValues, selectModelEntries, sortSelectOptions, splitByTokenSeparators, splitSelectLabelHighlight, toComboboxValue, toLabeledValue, toSelectedTokens, unwrapSelectEntry };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useRsI18n } from "../../composables/useRsI18n.js";
|
|
2
2
|
import RsInput_default from "../RsInput.js";
|
|
3
3
|
import RsInputNumber_default from "../RsInputNumber.js";
|
|
4
|
+
import { unwrapSelectEntry } from "../select-utils.js";
|
|
4
5
|
import RsSelect_default from "../RsSelect.js";
|
|
5
6
|
import RsDatePicker_default from "../RsDatePicker.js";
|
|
6
7
|
import { applyFocusMode, isNullDraft, nullToEditText, resolveCellEditorInputType, usesOverlayEditor } from "./table-edit-utils.js";
|
|
@@ -194,8 +195,13 @@ var RsTableCellEditor_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
|
|
|
194
195
|
emit("commit");
|
|
195
196
|
}
|
|
196
197
|
}
|
|
198
|
+
function selectDraftTokens(value) {
|
|
199
|
+
if (Array.isArray(value)) return value.map((item) => unwrapSelectEntry(item)).filter((item) => item !== null).map(String);
|
|
200
|
+
const raw = unwrapSelectEntry(value);
|
|
201
|
+
return raw == null ? "" : String(raw);
|
|
202
|
+
}
|
|
197
203
|
function onSelectUpdate(value) {
|
|
198
|
-
const tokens =
|
|
204
|
+
const tokens = selectDraftTokens(value);
|
|
199
205
|
if (tokens === "" || Array.isArray(tokens) && tokens.length === 0) {
|
|
200
206
|
if (!props.editorOptions?.clearable) return;
|
|
201
207
|
model.value = props.allowNull ? nullToEditText() : "";
|
|
@@ -15,7 +15,9 @@ export interface RsSelectEngineProps {
|
|
|
15
15
|
remote?: boolean;
|
|
16
16
|
virtual?: boolean;
|
|
17
17
|
virtualThreshold?: number;
|
|
18
|
-
maxTagCount?: number;
|
|
18
|
+
maxTagCount?: number | 'responsive';
|
|
19
|
+
/** 远程 @search 防抖毫秒;0 立即发 */
|
|
20
|
+
debounce?: number;
|
|
19
21
|
maxTagPlaceholder?: string | ((omitted: number) => string);
|
|
20
22
|
maxTagTooltip?: boolean;
|
|
21
23
|
maxTagTextLength?: number;
|
|
@@ -40,12 +42,13 @@ export type RsSelectEngineEmit = {
|
|
|
40
42
|
* Select 过滤、选中、多选 tag、creatable / 分隔符提交。
|
|
41
43
|
* 组件模板只消费返回值;表单校验仍留在 SFC。
|
|
42
44
|
*/
|
|
43
|
-
export declare function useRsSelect(props: RsSelectEngineProps, model: ModelRef<
|
|
45
|
+
export declare function useRsSelect<T extends RsSelectModelValue = RsSelectModelValue>(props: RsSelectEngineProps, model: ModelRef<T>, open: ModelRef<boolean>, searchQuery: ModelRef<string>, emit: RsSelectEngineEmit, t: RsTranslateFn): {
|
|
44
46
|
normalizedOptions: import("vue").ComputedRef<import("./select-utils").RsSelectOptions>;
|
|
45
47
|
resolvedPlaceholder: import("vue").ComputedRef<string>;
|
|
46
48
|
resolvedSearchPlaceholder: import("vue").ComputedRef<string>;
|
|
47
49
|
resolvedEmptyText: import("vue").ComputedRef<string>;
|
|
48
50
|
resolvedLoadingText: import("vue").ComputedRef<string>;
|
|
51
|
+
isMultiple: import("vue").ComputedRef<boolean>;
|
|
49
52
|
isSearchable: import("vue").ComputedRef<boolean>;
|
|
50
53
|
labelMap: import("vue").ComputedRef<Map<string, string>>;
|
|
51
54
|
useVirtual: import("vue").ComputedRef<boolean>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useFilter } from "./reka.js";
|
|
2
|
-
import { buildOptionDisabledMap, buildOptionLabelMap, comboboxBindingToTokens, filterSelectOptions, findSelectOption, flattenSelectOptions, flattenSelectValues, fromComboboxValue, isSelectLabeledValue, normalizeSelectOptions, optionDisplayLabel, packSelectModel, restoreSelectValue, restoreSelectValues, selectModelEntries, sortSelectOptions, splitByTokenSeparators, toComboboxValue, toSelectedTokens } from "./select-utils.js";
|
|
3
|
-
import { computed, nextTick, watch } from "vue";
|
|
2
|
+
import { buildOptionDisabledMap, buildOptionLabelMap, comboboxBindingToTokens, filterSelectOptions, findSelectOption, flattenSelectOptions, flattenSelectValues, fromComboboxValue, isSelectLabeledValue, isSelectMultiple, normalizeSelectOptions, optionDisplayLabel, packSelectModel, restoreSelectValue, restoreSelectValues, selectModelEntries, sortSelectOptions, splitByTokenSeparators, toComboboxValue, toSelectedTokens } from "./select-utils.js";
|
|
3
|
+
import { computed, nextTick, onUnmounted, watch } from "vue";
|
|
4
4
|
//#region src/components/use-rs-select.ts
|
|
5
5
|
/**
|
|
6
6
|
* Select 过滤、选中、多选 tag、creatable / 分隔符提交。
|
|
@@ -8,6 +8,7 @@ import { computed, nextTick, watch } from "vue";
|
|
|
8
8
|
*/
|
|
9
9
|
function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
10
10
|
const { contains } = useFilter({ sensitivity: "base" });
|
|
11
|
+
const isMultiple = computed(() => isSelectMultiple(props.multiple));
|
|
11
12
|
const normalizedOptions = computed(() => normalizeSelectOptions(props.options, props.fieldNames));
|
|
12
13
|
const resolvedPlaceholder = computed(() => props.placeholder ?? t("select.placeholder"));
|
|
13
14
|
const resolvedSearchPlaceholder = computed(() => props.searchPlaceholder ?? t("select.searchPlaceholder"));
|
|
@@ -46,11 +47,12 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
46
47
|
if (!canCreate.value) return values;
|
|
47
48
|
return [createValue.value, ...values.filter((item) => String(item) !== createValue.value)];
|
|
48
49
|
});
|
|
49
|
-
const selectedValues = computed(() => toSelectedTokens(model.value,
|
|
50
|
+
const selectedValues = computed(() => toSelectedTokens(model.value, isMultiple.value));
|
|
50
51
|
const hasValue = computed(() => selectedValues.value.length > 0);
|
|
51
52
|
const visibleTagTokens = computed(() => {
|
|
52
53
|
const all = selectedValues.value;
|
|
53
|
-
if (!
|
|
54
|
+
if (!isMultiple.value || props.maxTagCount == null || props.maxTagCount === "responsive") return all;
|
|
55
|
+
if (all.length <= props.maxTagCount) return all;
|
|
54
56
|
return all.slice(0, Math.max(0, props.maxTagCount));
|
|
55
57
|
});
|
|
56
58
|
const omittedTagCount = computed(() => Math.max(0, selectedValues.value.length - visibleTagTokens.value.length));
|
|
@@ -82,7 +84,7 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
82
84
|
if (!max || max < 1 || label.length <= max) return label;
|
|
83
85
|
return `${label.slice(0, max)}…`;
|
|
84
86
|
}
|
|
85
|
-
const atMultipleLimit = computed(() =>
|
|
87
|
+
const atMultipleLimit = computed(() => isMultiple.value && props.multipleLimit != null && props.multipleLimit > 0 && selectedValues.value.length >= props.multipleLimit);
|
|
86
88
|
function isOptionLimited(option) {
|
|
87
89
|
if (!atMultipleLimit.value) return false;
|
|
88
90
|
return !selectedValues.value.includes(toComboboxValue(option.value));
|
|
@@ -93,11 +95,11 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
93
95
|
return option ? isOptionLimited(option) : false;
|
|
94
96
|
}
|
|
95
97
|
const singleDisplayLabel = computed(() => {
|
|
96
|
-
if (
|
|
98
|
+
if (isMultiple.value || !hasValue.value) return "";
|
|
97
99
|
return tokenLabel(selectedValues.value[0]);
|
|
98
100
|
});
|
|
99
101
|
function writeTokens(tokens) {
|
|
100
|
-
model.value = packSelectModel(restoreSelectValues(tokens, normalizedOptions.value), normalizedOptions.value,
|
|
102
|
+
model.value = packSelectModel(restoreSelectValues(tokens, normalizedOptions.value), normalizedOptions.value, isMultiple.value, Boolean(props.labelInValue));
|
|
101
103
|
}
|
|
102
104
|
function emitSelectionDiff(prev, next) {
|
|
103
105
|
const prevSet = new Set(prev);
|
|
@@ -113,12 +115,12 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
113
115
|
}
|
|
114
116
|
const comboboxModel = computed({
|
|
115
117
|
get() {
|
|
116
|
-
if (
|
|
118
|
+
if (isMultiple.value) return selectedValues.value;
|
|
117
119
|
return hasValue.value ? selectedValues.value[0] : void 0;
|
|
118
120
|
},
|
|
119
121
|
set(value) {
|
|
120
122
|
const prev = selectedValues.value;
|
|
121
|
-
const tokens = comboboxBindingToTokens(value,
|
|
123
|
+
const tokens = comboboxBindingToTokens(value, isMultiple.value);
|
|
122
124
|
emitSelectionDiff(prev, tokens);
|
|
123
125
|
writeTokens(tokens);
|
|
124
126
|
if (props.autoClearSearchValue) searchQuery.value = "";
|
|
@@ -132,7 +134,7 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
132
134
|
if (!next) return;
|
|
133
135
|
const values = currentValues();
|
|
134
136
|
if (values.some((item) => String(item) === next)) return;
|
|
135
|
-
if (
|
|
137
|
+
if (isMultiple.value) {
|
|
136
138
|
if (atMultipleLimit.value) return;
|
|
137
139
|
const prev = selectedValues.value;
|
|
138
140
|
const packed = packSelectModel([...values, next], normalizedOptions.value, true, Boolean(props.labelInValue));
|
|
@@ -147,7 +149,7 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
147
149
|
if (props.autoClearSearchValue) searchQuery.value = "";
|
|
148
150
|
}
|
|
149
151
|
function consumeTokenSeparators(raw) {
|
|
150
|
-
if (!
|
|
152
|
+
if (!isMultiple.value && !props.creatable) return;
|
|
151
153
|
const parts = splitByTokenSeparators(raw, props.tokenSeparators ?? []);
|
|
152
154
|
if (!parts) return;
|
|
153
155
|
const rest = parts.pop() ?? "";
|
|
@@ -159,26 +161,35 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
159
161
|
const prev = selectedValues.value;
|
|
160
162
|
const key = toComboboxValue(matched.value);
|
|
161
163
|
if (prev.includes(key)) continue;
|
|
162
|
-
if (
|
|
163
|
-
const nextTokens =
|
|
164
|
+
if (isMultiple.value && atMultipleLimit.value) continue;
|
|
165
|
+
const nextTokens = isMultiple.value ? [...prev, key] : [key];
|
|
164
166
|
emitSelectionDiff(prev, nextTokens);
|
|
165
167
|
writeTokens(nextTokens);
|
|
166
|
-
if (!
|
|
168
|
+
if (!isMultiple.value) open.value = false;
|
|
167
169
|
} else if (props.creatable) commitCreatedValue(token);
|
|
168
170
|
}
|
|
169
171
|
if (rest !== raw) searchQuery.value = rest;
|
|
170
172
|
}
|
|
173
|
+
let searchTimer;
|
|
171
174
|
watch(searchQuery, (query) => {
|
|
172
|
-
if (props.remote)
|
|
175
|
+
if (props.remote) {
|
|
176
|
+
const wait = props.debounce ?? 0;
|
|
177
|
+
if (searchTimer) clearTimeout(searchTimer);
|
|
178
|
+
if (wait <= 0) emit("search", query);
|
|
179
|
+
else searchTimer = setTimeout(() => emit("search", query), wait);
|
|
180
|
+
}
|
|
173
181
|
if (props.tokenSeparators?.length) consumeTokenSeparators(query);
|
|
174
182
|
});
|
|
183
|
+
onUnmounted(() => {
|
|
184
|
+
if (searchTimer) clearTimeout(searchTimer);
|
|
185
|
+
});
|
|
175
186
|
watch(open, async (isOpen) => {
|
|
176
187
|
if (isOpen) {
|
|
177
188
|
const pending = searchQuery.value;
|
|
178
189
|
await nextTick();
|
|
179
190
|
await nextTick();
|
|
180
191
|
if (pending) searchQuery.value = pending;
|
|
181
|
-
else if (props.fillSearchWithValue && !
|
|
192
|
+
else if (props.fillSearchWithValue && !isMultiple.value && hasValue.value) searchQuery.value = singleDisplayLabel.value;
|
|
182
193
|
else searchQuery.value = "";
|
|
183
194
|
return;
|
|
184
195
|
}
|
|
@@ -196,19 +207,19 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
196
207
|
event.stopPropagation();
|
|
197
208
|
emitSelectionDiff(selectedValues.value, []);
|
|
198
209
|
emit("clear");
|
|
199
|
-
model.value =
|
|
210
|
+
model.value = isMultiple.value ? [] : "";
|
|
200
211
|
}
|
|
201
212
|
function removeTag(value, event) {
|
|
202
213
|
event.preventDefault();
|
|
203
214
|
event.stopPropagation();
|
|
204
|
-
if (!
|
|
215
|
+
if (!isMultiple.value) return;
|
|
205
216
|
const prev = selectedValues.value;
|
|
206
217
|
const tokens = prev.filter((item) => item !== value);
|
|
207
218
|
emitSelectionDiff(prev, tokens);
|
|
208
219
|
writeTokens(tokens);
|
|
209
220
|
}
|
|
210
221
|
function setValue(value) {
|
|
211
|
-
if (
|
|
222
|
+
if (isMultiple.value) {
|
|
212
223
|
writeTokens(toSelectedTokens(Array.isArray(value) ? value : value !== "" && value != null ? [value] : [], true));
|
|
213
224
|
return;
|
|
214
225
|
}
|
|
@@ -229,6 +240,7 @@ function useRsSelect(props, model, open, searchQuery, emit, t) {
|
|
|
229
240
|
resolvedSearchPlaceholder,
|
|
230
241
|
resolvedEmptyText,
|
|
231
242
|
resolvedLoadingText,
|
|
243
|
+
isMultiple,
|
|
232
244
|
isSearchable,
|
|
233
245
|
labelMap,
|
|
234
246
|
useVirtual,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { resolveVirtualListHeight } from "../components/virtual-list-utils.js";
|
|
1
2
|
import { resolveScrollWidth, selectRowKeys } from "../components/table-utils.js";
|
|
2
3
|
import { RS_TABLE_PREFIX_COL_WIDTH, measureRsTablePrefixWidth, useRsTableColumnVirtual } from "./useRsTableColumnVirtual.js";
|
|
3
4
|
import { useRsTableColumnLayout } from "./useRsTableColumnLayout.js";
|
|
4
5
|
import { useRsTableColumnResize } from "./useRsTableColumnResize.js";
|
|
5
6
|
import { useRsTableContextMenu } from "./useRsTableContextMenu.js";
|
|
6
7
|
import { useRsTableInteraction } from "./useRsTableInteraction.js";
|
|
7
|
-
import { resolveVirtualListHeight } from "../components/virtual-list-utils.js";
|
|
8
8
|
import { computed } from "vue";
|
|
9
9
|
//#region src/composables/useRsTableShell.ts
|
|
10
10
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,10 @@ export { default as RsLabel } from './components/RsLabel.js';
|
|
|
35
35
|
export { default as RsLink } from './components/RsLink.js';
|
|
36
36
|
export { default as RsMenu } from './components/RsMenu.js';
|
|
37
37
|
export { default as RsSelect } from './components/RsSelect.js';
|
|
38
|
+
export { default as RsAutoComplete } from './components/RsAutoComplete.js';
|
|
39
|
+
export { default as RsCascader } from './components/RsCascader.js';
|
|
40
|
+
export { default as RsTreeSelect } from './components/RsTreeSelect.js';
|
|
41
|
+
export { default as RsMentions } from './components/RsMentions.js';
|
|
38
42
|
export { default as RsScrollbar } from './components/RsScrollbar.js';
|
|
39
43
|
export { default as RsAvatar } from './components/RsAvatar.js';
|
|
40
44
|
export { default as RsCard } from './components/RsCard.js';
|
|
@@ -100,10 +104,12 @@ export type { RsContextMenuItem } from './components/context-menu-utils';
|
|
|
100
104
|
export type { RsDropdownItem, RsDropdownItemGroup, RsDropdownItems } from './components/dropdown-utils';
|
|
101
105
|
export type { RsMenuItem, RsMenuItemGroup, RsMenuItems } from './components/menu-utils';
|
|
102
106
|
export type { RsScrollbarOrientation, RsScrollbarType } from './components/scrollbar-utils';
|
|
103
|
-
export type { RsSelectFieldNames, RsSelectFilterOption, RsSelectFilterSort, RsSelectGetPopupContainer, RsSelectLabeledValue, RsSelectModelValue, RsSelectResolvedModel, RsSelectOption, RsSelectOptionFilterProp, RsSelectOptionGroup, RsSelectOptionInput, RsSelectOptions, RsSelectOptionsInput, RsSelectPlacement, RsSelectStatus, RsSelectValue, } from './components/select-utils';
|
|
107
|
+
export type { RsSelectFieldNames, RsSelectFilterOption, RsSelectFilterSort, RsSelectGetPopupContainer, RsSelectLabeledValue, RsSelectModelValue, RsSelectResolvedModel, RsSelectOption, RsSelectOptionFilterProp, RsSelectOptionGroup, RsSelectOptionInput, RsSelectOptions, RsSelectOptionsInput, RsSelectPlacement, RsSelectStatus, RsSelectMaxTagCount, RsSelectVariant, RsSelectValue, } from './components/select-utils';
|
|
108
|
+
export type { RsCascaderExpandTrigger, RsCascaderOption, RsCascaderPath, } from './components/cascader-utils';
|
|
109
|
+
export type { RsMentionActive, RsMentionCaretBox, RsMentionOption, RsMentionPopupBox, } from './components/mentions-utils';
|
|
104
110
|
export { RS_SELECT_EMPTY_VALUE, fromComboboxValue, isSelectLabeledValue, normalizeSelectOptions, optionDisplayLabel, packSelectModel, restoreSelectValue, toComboboxValue, unwrapSelectEntry, } from './components/select-utils';
|
|
105
|
-
export type { RsFeedbackTone, RsToastPosition, RsToastType } from './components/overlay-utils';
|
|
106
|
-
export { RS_TOAST_DEFAULT_GAP, RS_TOAST_DEFAULT_POSITION, rsToastPositions, rsFeedbackIconClass } from './components/overlay-utils';
|
|
111
|
+
export type { RsFeedbackTone, RsToastPosition, RsToastType, RsOverlayAnchorBox, RsOverlayBox } from './components/overlay-utils';
|
|
112
|
+
export { RS_TOAST_DEFAULT_GAP, RS_TOAST_DEFAULT_POSITION, rsToastPositions, rsFeedbackIconClass, placeAnchoredPopup, stepEnabledIndex } from './components/overlay-utils';
|
|
107
113
|
export type { RsFormContext, RsFormErrorRender, RsFormErrorRenderContext, RsFormFieldExpose, RsFormItemContext, RsFormListContext, RsFormListField, RsFormListOperations, RsFormFieldValidationResult, RsFormGap, RsFormLabelAlign, RsFormLabelPosition, RsFormMaxWidth, RsFormSize, RsFormValidateStatus, RsFormValidationResult, } from './components/form-utils';
|
|
108
114
|
export { cloneFormFieldValue, isRsFormItemBoundControl, provideRsFormItemContext, provideRsFormListContext, RS_FORM_INJECTION_KEY, RS_FORM_ITEM_INJECTION_KEY, RS_FORM_LIST_INJECTION_KEY, resolveFieldRules, useRsFormContext, useRsFormField, useRsFormItemContext, useRsFormListContext, } from './components/form-utils';
|
|
109
115
|
export type { RsFormNamePath } from './components/form-path';
|