@seed-design/lynx-react 0.3.0 → 0.4.0
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/LICENSE +201 -0
- package/NOTICE +20 -0
- package/lib/components/Field/Field.d.ts +47 -0
- package/lib/components/Field/Field.d.ts.map +1 -0
- package/lib/components/Field/Field.jsx +121 -0
- package/lib/components/Field/Field.namespace.d.ts +2 -0
- package/lib/components/Field/Field.namespace.d.ts.map +1 -0
- package/lib/components/Field/Field.namespace.js +16 -0
- package/lib/components/Field/context.d.ts +19 -0
- package/lib/components/Field/context.d.ts.map +1 -0
- package/lib/components/Field/context.js +10 -0
- package/lib/components/Field/index.d.ts +3 -0
- package/lib/components/Field/index.d.ts.map +1 -0
- package/lib/components/KeyboardAvoidingScrollView/KeyboardAvoidingScrollView.d.ts +42 -0
- package/lib/components/KeyboardAvoidingScrollView/KeyboardAvoidingScrollView.d.ts.map +1 -0
- package/lib/components/KeyboardAvoidingScrollView/KeyboardAvoidingScrollView.jsx +140 -0
- package/lib/components/KeyboardAvoidingScrollView/context.d.ts +17 -0
- package/lib/components/KeyboardAvoidingScrollView/context.d.ts.map +1 -0
- package/lib/components/KeyboardAvoidingScrollView/context.js +12 -0
- package/lib/components/KeyboardAvoidingScrollView/engine.d.ts +50 -0
- package/lib/components/KeyboardAvoidingScrollView/engine.d.ts.map +1 -0
- package/lib/components/KeyboardAvoidingScrollView/engine.js +259 -0
- package/lib/components/KeyboardAvoidingScrollView/geometry.d.ts +50 -0
- package/lib/components/KeyboardAvoidingScrollView/geometry.d.ts.map +1 -0
- package/lib/components/KeyboardAvoidingScrollView/geometry.js +90 -0
- package/lib/components/KeyboardAvoidingScrollView/index.d.ts +2 -0
- package/lib/components/KeyboardAvoidingScrollView/index.d.ts.map +1 -0
- package/lib/components/KeyboardAvoidingScrollView/keyboard-event-source.d.ts +11 -0
- package/lib/components/KeyboardAvoidingScrollView/keyboard-event-source.d.ts.map +1 -0
- package/lib/components/KeyboardAvoidingScrollView/keyboard-event-source.js +43 -0
- package/lib/components/KeyboardAvoidingScrollView/native-driver.d.ts +34 -0
- package/lib/components/KeyboardAvoidingScrollView/native-driver.d.ts.map +1 -0
- package/lib/components/KeyboardAvoidingScrollView/native-driver.js +133 -0
- package/lib/components/TextField/TextField.d.ts +113 -0
- package/lib/components/TextField/TextField.d.ts.map +1 -0
- package/lib/components/TextField/TextField.jsx +418 -0
- package/lib/components/TextField/TextField.namespace.d.ts +2 -0
- package/lib/components/TextField/TextField.namespace.d.ts.map +1 -0
- package/lib/components/TextField/TextField.namespace.js +14 -0
- package/lib/components/TextField/context.d.ts +22 -0
- package/lib/components/TextField/context.d.ts.map +1 -0
- package/lib/components/TextField/context.js +7 -0
- package/lib/components/TextField/index.d.ts +4 -0
- package/lib/components/TextField/index.d.ts.map +1 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.d.ts +24 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.d.ts.map +1 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.js +47 -0
- package/lib/components/index.d.ts +3 -0
- package/lib/components/index.d.ts.map +1 -1
- package/lib/index.js +7 -1
- package/lib/node_modules/.bun/unicode-segmenter@0.14.5/node_modules/unicode-segmenter/_grapheme_data.js +8 -0
- package/lib/node_modules/.bun/unicode-segmenter@0.14.5/node_modules/unicode-segmenter/_incb_data.js +14 -0
- package/lib/node_modules/.bun/unicode-segmenter@0.14.5/node_modules/unicode-segmenter/core.js +68 -0
- package/lib/node_modules/.bun/unicode-segmenter@0.14.5/node_modules/unicode-segmenter/grapheme.js +175 -0
- package/lib/utils/create-slot-recipe-context.d.ts.map +1 -1
- package/lib/utils/create-slot-recipe-context.jsx +7 -0
- package/package.json +10 -5
- package/src/components/Field/Field.namespace.ts +19 -0
- package/src/components/Field/Field.test.tsx +81 -0
- package/src/components/Field/Field.tsx +252 -0
- package/src/components/Field/context.ts +26 -0
- package/src/components/Field/index.ts +21 -0
- package/src/components/KeyboardAvoidingScrollView/KeyboardAvoidingScrollView.test.tsx +267 -0
- package/src/components/KeyboardAvoidingScrollView/KeyboardAvoidingScrollView.tsx +299 -0
- package/src/components/KeyboardAvoidingScrollView/context.ts +24 -0
- package/src/components/KeyboardAvoidingScrollView/engine.test.ts +451 -0
- package/src/components/KeyboardAvoidingScrollView/engine.ts +417 -0
- package/src/components/KeyboardAvoidingScrollView/geometry.test.ts +315 -0
- package/src/components/KeyboardAvoidingScrollView/geometry.ts +194 -0
- package/src/components/KeyboardAvoidingScrollView/index.ts +5 -0
- package/src/components/KeyboardAvoidingScrollView/keyboard-event-source.test.ts +122 -0
- package/src/components/KeyboardAvoidingScrollView/keyboard-event-source.ts +75 -0
- package/src/components/KeyboardAvoidingScrollView/native-driver.test.ts +223 -0
- package/src/components/KeyboardAvoidingScrollView/native-driver.ts +248 -0
- package/src/components/TextField/TextField.namespace.ts +16 -0
- package/src/components/TextField/TextField.test.tsx +924 -0
- package/src/components/TextField/TextField.tsx +912 -0
- package/src/components/TextField/context.ts +33 -0
- package/src/components/TextField/index.ts +23 -0
- package/src/components/TextField/useTextFieldWithGraphemes.test.tsx +80 -0
- package/src/components/TextField/useTextFieldWithGraphemes.ts +78 -0
- package/src/components/index.ts +3 -0
- package/src/utils/create-slot-recipe-context.test.tsx +51 -0
- package/src/utils/create-slot-recipe-context.tsx +23 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
//#region src/components/KeyboardAvoidingScrollView/native-driver.ts
|
|
2
|
+
function isFiniteNumber(value) {
|
|
3
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
4
|
+
}
|
|
5
|
+
function normalizeNonNegative(value) {
|
|
6
|
+
return Math.max(0, value);
|
|
7
|
+
}
|
|
8
|
+
function normalizeRect(value) {
|
|
9
|
+
if (typeof value !== "object" || value === null) return null;
|
|
10
|
+
const rect = value;
|
|
11
|
+
if (!isFiniteNumber(rect.top) || !isFiniteNumber(rect.bottom)) return null;
|
|
12
|
+
return {
|
|
13
|
+
top: rect.top,
|
|
14
|
+
bottom: Math.max(rect.top, rect.bottom)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function normalizeScrollMetrics(raw, platform, viewportHeight) {
|
|
18
|
+
const offsetY = isFiniteNumber(raw.scrollY) ? raw.scrollY : isFiniteNumber(raw.scrollTop) ? raw.scrollTop : null;
|
|
19
|
+
if (offsetY === null) return null;
|
|
20
|
+
const normalizedViewportHeight = normalizeNonNegative(viewportHeight);
|
|
21
|
+
let maxOffsetY = null;
|
|
22
|
+
if (isFiniteNumber(raw.maxScrollOffset)) maxOffsetY = raw.maxScrollOffset;
|
|
23
|
+
else if (isFiniteNumber(raw.scrollRange)) maxOffsetY = platform === "iOS" ? raw.scrollRange - normalizedViewportHeight : raw.scrollRange;
|
|
24
|
+
else if (isFiniteNumber(raw.scrollHeight)) maxOffsetY = platform === "iOS" ? raw.scrollHeight - normalizedViewportHeight : raw.scrollHeight;
|
|
25
|
+
if (maxOffsetY === null) return null;
|
|
26
|
+
return {
|
|
27
|
+
offsetY: normalizeNonNegative(offsetY),
|
|
28
|
+
maxOffsetY: normalizeNonNegative(maxOffsetY)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function resolveKeyboardOcclusionTop(state, platform, screenHeight, rootRect) {
|
|
32
|
+
if (!state.visible || !isFiniteNumber(state.height) || state.height <= 0) return null;
|
|
33
|
+
if (platform === "Android") return rootRect ? rootRect.bottom - state.height : null;
|
|
34
|
+
return isFiniteNumber(screenHeight) && screenHeight > 0 ? screenHeight - state.height : null;
|
|
35
|
+
}
|
|
36
|
+
function getSystemInfo() {
|
|
37
|
+
const value = globalThis.SystemInfo;
|
|
38
|
+
if (value?.platform !== "Android" && value?.platform !== "iOS") return null;
|
|
39
|
+
if (!isFiniteNumber(value.pixelHeight) || !isFiniteNumber(value.pixelRatio)) return null;
|
|
40
|
+
if (value.pixelHeight <= 0 || value.pixelRatio <= 0) return null;
|
|
41
|
+
return {
|
|
42
|
+
platform: value.platform,
|
|
43
|
+
screenHeight: value.pixelHeight / value.pixelRatio
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function measureNode(node) {
|
|
47
|
+
"background only";
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
try {
|
|
50
|
+
node.invoke({
|
|
51
|
+
method: "boundingClientRect",
|
|
52
|
+
params: {
|
|
53
|
+
relativeTo: "screen",
|
|
54
|
+
androidEnableTransformProps: true
|
|
55
|
+
},
|
|
56
|
+
success: (value) => resolve(normalizeRect(value)),
|
|
57
|
+
fail: () => resolve(null)
|
|
58
|
+
}).exec();
|
|
59
|
+
} catch {
|
|
60
|
+
resolve(null);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function measureRoot() {
|
|
65
|
+
"background only";
|
|
66
|
+
try {
|
|
67
|
+
return measureNode(lynx.createSelectorQuery().selectRoot());
|
|
68
|
+
} catch {
|
|
69
|
+
return Promise.resolve(null);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function getScrollMetrics(node, platform, viewportHeight) {
|
|
73
|
+
"background only";
|
|
74
|
+
return new Promise((resolve) => {
|
|
75
|
+
try {
|
|
76
|
+
node.invoke({
|
|
77
|
+
method: "getScrollInfo",
|
|
78
|
+
success: (value) => {
|
|
79
|
+
if (typeof value !== "object" || value === null) {
|
|
80
|
+
resolve(null);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
resolve(normalizeScrollMetrics(value, platform, viewportHeight));
|
|
84
|
+
},
|
|
85
|
+
fail: () => resolve(null)
|
|
86
|
+
}).exec();
|
|
87
|
+
} catch {
|
|
88
|
+
resolve(null);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
var lynxKeyboardAvoidingNativeDriver = {
|
|
93
|
+
measure: measureNode,
|
|
94
|
+
async resolveKeyboardOcclusion(state) {
|
|
95
|
+
"background only";
|
|
96
|
+
const systemInfo = getSystemInfo();
|
|
97
|
+
if (!systemInfo || !state.visible) return null;
|
|
98
|
+
const rootRect = systemInfo.platform === "Android" ? await measureRoot() : null;
|
|
99
|
+
const topInScreenPx = resolveKeyboardOcclusionTop(state, systemInfo.platform, systemInfo.screenHeight, rootRect);
|
|
100
|
+
return topInScreenPx === null ? null : {
|
|
101
|
+
visible: true,
|
|
102
|
+
topInScreenPx
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
setSpacerHeight(node, height) {
|
|
106
|
+
"background only";
|
|
107
|
+
node.setNativeProps({ height: `${normalizeNonNegative(height)}px` }).exec();
|
|
108
|
+
},
|
|
109
|
+
waitForLayout() {
|
|
110
|
+
"background only";
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
requestAnimationFrame(() => resolve());
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
async getScrollMetrics(node, viewportHeight) {
|
|
116
|
+
"background only";
|
|
117
|
+
const systemInfo = getSystemInfo();
|
|
118
|
+
if (!systemInfo) return null;
|
|
119
|
+
return getScrollMetrics(node, systemInfo.platform, viewportHeight);
|
|
120
|
+
},
|
|
121
|
+
scrollTo(node, offset, smooth) {
|
|
122
|
+
"background only";
|
|
123
|
+
node.invoke({
|
|
124
|
+
method: "scrollTo",
|
|
125
|
+
params: {
|
|
126
|
+
offset: normalizeNonNegative(offset),
|
|
127
|
+
smooth
|
|
128
|
+
}
|
|
129
|
+
}).exec();
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
//#endregion
|
|
133
|
+
export { lynxKeyboardAvoidingNativeDriver };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { IntrinsicElements, NodesRef } from '@lynx-js/types';
|
|
2
|
+
import { TextInputVariantProps } from '@seed-design/lynx-css/recipes/text-input';
|
|
3
|
+
import { LynxAccessibilityProps, LynxStyledElementProps } from '../../types';
|
|
4
|
+
import { InternalIconProps } from '../Icon/Icon';
|
|
5
|
+
import * as React from "@lynx-js/react";
|
|
6
|
+
/**
|
|
7
|
+
* @platform Lynx
|
|
8
|
+
*
|
|
9
|
+
* native `<input>`과 `<textarea>`의 값 상태를 소유한다.
|
|
10
|
+
* Lynx native 입력은 value attribute를 제공하지 않으므로 하위 입력 슬롯이
|
|
11
|
+
* `setValue` UI method로 controlled value를 동기화한다.
|
|
12
|
+
*/
|
|
13
|
+
export interface TextFieldRootProps extends Omit<TextInputVariantProps, "focused">, LynxStyledElementProps {
|
|
14
|
+
value?: string;
|
|
15
|
+
defaultValue?: string;
|
|
16
|
+
onValueChange?: (value: string) => void;
|
|
17
|
+
/** @internal `useTextFieldWithGraphemes`가 전달하는 네이티브 입력 사전 제한 값. */
|
|
18
|
+
nativeInsertionMaxLength?: number;
|
|
19
|
+
required?: boolean;
|
|
20
|
+
name?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare const TextFieldRoot: React.ForwardRefExoticComponent<TextFieldRootProps & React.RefAttributes<NodesRef>>;
|
|
23
|
+
type NativeInputProps = IntrinsicElements["input"];
|
|
24
|
+
type NativeTextareaProps = IntrinsicElements["textarea"];
|
|
25
|
+
interface NativeTextControlProps extends Omit<LynxStyledElementProps, "children">, LynxAccessibilityProps {
|
|
26
|
+
id?: NativeInputProps["id"];
|
|
27
|
+
name?: NativeInputProps["name"];
|
|
28
|
+
hidden?: NativeInputProps["hidden"];
|
|
29
|
+
flatten?: NativeInputProps["flatten"];
|
|
30
|
+
focusable?: NativeInputProps["focusable"];
|
|
31
|
+
bindlayoutchange?: NativeInputProps["bindlayoutchange"];
|
|
32
|
+
"main-thread:bindlayoutchange"?: NativeInputProps["main-thread:bindlayoutchange"];
|
|
33
|
+
}
|
|
34
|
+
type AndroidSetSoftInputMode = "unspecified" | "nothing" | "pan" | "resize";
|
|
35
|
+
/**
|
|
36
|
+
* @platform Lynx
|
|
37
|
+
*
|
|
38
|
+
* `readOnly` 상태에서는 native focus·selection·편집 메뉴를 제거하기 위해 `<text>`로 렌더링한다.
|
|
39
|
+
* 이때 ref는 `<text>`를 가리키며 input 전용 UI method와 이벤트는 사용할 수 없다.
|
|
40
|
+
*/
|
|
41
|
+
export interface TextFieldInputProps extends NativeTextControlProps {
|
|
42
|
+
placeholder?: NativeInputProps["placeholder"];
|
|
43
|
+
"confirm-type"?: NativeInputProps["confirm-type"];
|
|
44
|
+
maxlength?: NativeInputProps["maxlength"];
|
|
45
|
+
readonly?: NativeInputProps["readonly"];
|
|
46
|
+
disabled?: NativeInputProps["disabled"];
|
|
47
|
+
"show-soft-input-on-focus"?: NativeInputProps["show-soft-input-on-focus"];
|
|
48
|
+
"input-filter"?: NativeInputProps["input-filter"];
|
|
49
|
+
type?: NativeInputProps["type"];
|
|
50
|
+
"ios-auto-correct"?: NativeInputProps["ios-auto-correct"];
|
|
51
|
+
"ios-spell-check"?: NativeInputProps["ios-spell-check"];
|
|
52
|
+
"android-fullscreen-mode"?: NativeInputProps["android-fullscreen-mode"];
|
|
53
|
+
/** Android host window의 soft input mode를 지정한다. */
|
|
54
|
+
"android-set-soft-input-mode"?: AndroidSetSoftInputMode;
|
|
55
|
+
bindfocus?: NativeInputProps["bindfocus"];
|
|
56
|
+
bindblur?: NativeInputProps["bindblur"];
|
|
57
|
+
bindconfirm?: NativeInputProps["bindconfirm"];
|
|
58
|
+
bindinput?: NativeInputProps["bindinput"];
|
|
59
|
+
bindselection?: NativeInputProps["bindselection"];
|
|
60
|
+
}
|
|
61
|
+
export declare const TextFieldInput: React.ForwardRefExoticComponent<TextFieldInputProps & React.RefAttributes<NodesRef>>;
|
|
62
|
+
/**
|
|
63
|
+
* @platform Lynx
|
|
64
|
+
*
|
|
65
|
+
* `readOnly` 상태에서는 native focus·selection·편집 메뉴를 제거하기 위해 `<text>`로 렌더링한다.
|
|
66
|
+
* 이때 ref는 `<text>`를 가리키며 textarea 전용 UI method와 이벤트는 사용할 수 없다.
|
|
67
|
+
*/
|
|
68
|
+
export interface TextFieldTextareaProps extends NativeTextControlProps {
|
|
69
|
+
/** 내용에 맞춰 높이를 자동으로 조절한다. @defaultValue true */
|
|
70
|
+
autoresize?: boolean;
|
|
71
|
+
placeholder?: NativeTextareaProps["placeholder"];
|
|
72
|
+
"confirm-type"?: NativeTextareaProps["confirm-type"];
|
|
73
|
+
maxlength?: NativeTextareaProps["maxlength"];
|
|
74
|
+
maxlines?: NativeTextareaProps["maxlines"];
|
|
75
|
+
bounces?: NativeTextareaProps["bounces"];
|
|
76
|
+
/**
|
|
77
|
+
* native 줄 간격을 지정한다. 생략하면 Android에서 SEED 기본 typography를 맞추기 위해
|
|
78
|
+
* `3.2px`를 적용하고 iOS에는 전달하지 않는다. Android 기본 보정은 `0`으로 해제할 수 있다.
|
|
79
|
+
*/
|
|
80
|
+
"line-spacing"?: NativeTextareaProps["line-spacing"];
|
|
81
|
+
readonly?: NativeTextareaProps["readonly"];
|
|
82
|
+
disabled?: NativeTextareaProps["disabled"];
|
|
83
|
+
"show-soft-input-on-focus"?: NativeTextareaProps["show-soft-input-on-focus"];
|
|
84
|
+
"input-filter"?: NativeTextareaProps["input-filter"];
|
|
85
|
+
"enable-scroll-bar"?: NativeTextareaProps["enable-scroll-bar"];
|
|
86
|
+
type?: NativeTextareaProps["type"];
|
|
87
|
+
"ios-auto-correct"?: NativeTextareaProps["ios-auto-correct"];
|
|
88
|
+
"ios-spell-check"?: NativeTextareaProps["ios-spell-check"];
|
|
89
|
+
/** Android의 fullscreen extract input을 활성화한다. @defaultValue false */
|
|
90
|
+
"android-fullscreen-mode"?: NativeTextareaProps["android-fullscreen-mode"];
|
|
91
|
+
/** Android host window의 soft input mode를 지정한다. */
|
|
92
|
+
"android-set-soft-input-mode"?: AndroidSetSoftInputMode;
|
|
93
|
+
bindfocus?: NativeTextareaProps["bindfocus"];
|
|
94
|
+
bindblur?: NativeTextareaProps["bindblur"];
|
|
95
|
+
bindconfirm?: NativeTextareaProps["bindconfirm"];
|
|
96
|
+
bindinput?: NativeTextareaProps["bindinput"];
|
|
97
|
+
bindselection?: NativeTextareaProps["bindselection"];
|
|
98
|
+
}
|
|
99
|
+
export declare const TextFieldTextarea: React.ForwardRefExoticComponent<TextFieldTextareaProps & React.RefAttributes<NodesRef>>;
|
|
100
|
+
export interface TextFieldPrefixIconProps extends InternalIconProps {
|
|
101
|
+
}
|
|
102
|
+
export declare const TextFieldPrefixIcon: React.ForwardRefExoticComponent<TextFieldPrefixIconProps & React.RefAttributes<unknown>>;
|
|
103
|
+
export interface TextFieldPrefixTextProps extends LynxStyledElementProps {
|
|
104
|
+
}
|
|
105
|
+
export declare const TextFieldPrefixText: React.ForwardRefExoticComponent<TextFieldPrefixTextProps & React.RefAttributes<unknown>>;
|
|
106
|
+
export interface TextFieldSuffixIconProps extends InternalIconProps {
|
|
107
|
+
}
|
|
108
|
+
export declare const TextFieldSuffixIcon: React.ForwardRefExoticComponent<TextFieldSuffixIconProps & React.RefAttributes<unknown>>;
|
|
109
|
+
export interface TextFieldSuffixTextProps extends LynxStyledElementProps {
|
|
110
|
+
}
|
|
111
|
+
export declare const TextFieldSuffixText: React.ForwardRefExoticComponent<TextFieldSuffixTextProps & React.RefAttributes<unknown>>;
|
|
112
|
+
export {};
|
|
113
|
+
//# sourceMappingURL=TextField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../src/components/TextField/TextField.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { createSlotRecipeContext } from "../../utils/create-slot-recipe-context.jsx";
|
|
2
|
+
import { InternalIcon } from "../Icon/Icon.jsx";
|
|
3
|
+
import { useFieldContext } from "../Field/context.js";
|
|
4
|
+
import { useKeyboardAvoidanceActions } from "../KeyboardAvoidingScrollView/context.js";
|
|
5
|
+
import { NATIVE_TEXT_MAX_LENGTH_UNLIMITED, TextFieldContext } from "./context.js";
|
|
6
|
+
import { textInput } from "@seed-design/lynx-css/vars/component";
|
|
7
|
+
import clsx from "clsx";
|
|
8
|
+
import * as React from "@lynx-js/react";
|
|
9
|
+
import { textInput as textInput$1 } from "@seed-design/lynx-css/recipes/text-input";
|
|
10
|
+
//#region src/components/TextField/TextField.tsx
|
|
11
|
+
var ANDROID_TEXTAREA_DEFAULT_LINE_SPACING = "3.2px";
|
|
12
|
+
function isAndroidRuntime() {
|
|
13
|
+
return (globalThis.SystemInfo ?? (typeof SystemInfo === "undefined" ? void 0 : SystemInfo))?.platform === "Android";
|
|
14
|
+
}
|
|
15
|
+
var { ClassNamesProvider, useClassNames } = createSlotRecipeContext(textInput$1);
|
|
16
|
+
var TextFieldRoot = React.forwardRef((props, forwardedRef) => {
|
|
17
|
+
const [variantProps, otherProps] = textInput$1.splitVariantProps(props);
|
|
18
|
+
const { children, className, value: controlledValue, defaultValue = "", onValueChange, nativeInsertionMaxLength, required: requiredProp, name, ...nativeProps } = otherProps;
|
|
19
|
+
const fieldContext = useFieldContext({ strict: false });
|
|
20
|
+
const rootRef = React.useRef(null);
|
|
21
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
|
|
22
|
+
const [localFocused, setLocalFocused] = React.useState(false);
|
|
23
|
+
const isControlled = controlledValue !== void 0;
|
|
24
|
+
const value = isControlled ? controlledValue : uncontrolledValue;
|
|
25
|
+
const disabled = variantProps.disabled ?? fieldContext?.disabled ?? false;
|
|
26
|
+
const invalid = variantProps.invalid ?? fieldContext?.invalid ?? false;
|
|
27
|
+
const readOnly = variantProps.readOnly ?? fieldContext?.readOnly ?? false;
|
|
28
|
+
const required = requiredProp ?? fieldContext?.required ?? false;
|
|
29
|
+
const focused = fieldContext?.focused ?? localFocused;
|
|
30
|
+
const variant = variantProps.variant ?? "outline";
|
|
31
|
+
const size = variantProps.size ?? "large";
|
|
32
|
+
const valueRef = React.useRef(value);
|
|
33
|
+
const controlledRef = React.useRef(isControlled);
|
|
34
|
+
const onValueChangeRef = React.useRef(onValueChange);
|
|
35
|
+
const fieldContextRef = React.useRef(fieldContext);
|
|
36
|
+
valueRef.current = value;
|
|
37
|
+
controlledRef.current = isControlled;
|
|
38
|
+
onValueChangeRef.current = onValueChange;
|
|
39
|
+
fieldContextRef.current = fieldContext;
|
|
40
|
+
const classes = textInput$1({
|
|
41
|
+
...variantProps,
|
|
42
|
+
variant,
|
|
43
|
+
size,
|
|
44
|
+
disabled,
|
|
45
|
+
focused: focused && !readOnly,
|
|
46
|
+
invalid,
|
|
47
|
+
readOnly
|
|
48
|
+
});
|
|
49
|
+
const mergedRef = React.useCallback((node) => {
|
|
50
|
+
"background only";
|
|
51
|
+
rootRef.current = node;
|
|
52
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
53
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
54
|
+
}, [forwardedRef]);
|
|
55
|
+
const setFocused = React.useCallback((nextFocused) => {
|
|
56
|
+
"background only";
|
|
57
|
+
setLocalFocused(nextFocused);
|
|
58
|
+
fieldContextRef.current?.setFocused(nextFocused);
|
|
59
|
+
}, []);
|
|
60
|
+
const setValue = React.useCallback((nextValue) => {
|
|
61
|
+
"background only";
|
|
62
|
+
if (!controlledRef.current) setUncontrolledValue(nextValue);
|
|
63
|
+
if (nextValue !== valueRef.current) onValueChangeRef.current?.(nextValue);
|
|
64
|
+
}, []);
|
|
65
|
+
const contextValue = React.useMemo(() => ({
|
|
66
|
+
rootRef,
|
|
67
|
+
value,
|
|
68
|
+
controlled: isControlled,
|
|
69
|
+
nativeInsertionMaxLength,
|
|
70
|
+
disabled,
|
|
71
|
+
invalid,
|
|
72
|
+
readOnly,
|
|
73
|
+
required,
|
|
74
|
+
name,
|
|
75
|
+
focused,
|
|
76
|
+
setValue,
|
|
77
|
+
setFocused
|
|
78
|
+
}), [
|
|
79
|
+
disabled,
|
|
80
|
+
focused,
|
|
81
|
+
invalid,
|
|
82
|
+
name,
|
|
83
|
+
nativeInsertionMaxLength,
|
|
84
|
+
readOnly,
|
|
85
|
+
required,
|
|
86
|
+
setFocused,
|
|
87
|
+
setValue,
|
|
88
|
+
value,
|
|
89
|
+
isControlled
|
|
90
|
+
]);
|
|
91
|
+
return <TextFieldContext.Provider value={contextValue}>
|
|
92
|
+
<ClassNamesProvider value={classes}>
|
|
93
|
+
<view ref={mergedRef} className={clsx(classes.root, className)} {...nativeProps}>
|
|
94
|
+
<view className={classes.baseStroke} accessibility-elements-hidden={true} />
|
|
95
|
+
<view className={classes.stroke} accessibility-elements-hidden={true} />
|
|
96
|
+
{children}
|
|
97
|
+
</view>
|
|
98
|
+
</ClassNamesProvider>
|
|
99
|
+
</TextFieldContext.Provider>;
|
|
100
|
+
});
|
|
101
|
+
TextFieldRoot.displayName = "TextFieldRoot";
|
|
102
|
+
function resolveNativeMaxLength(explicitMaxLength, insertionMaxLength) {
|
|
103
|
+
if (explicitMaxLength === void 0) return insertionMaxLength;
|
|
104
|
+
if (insertionMaxLength === void 0) return explicitMaxLength;
|
|
105
|
+
return Math.min(explicitMaxLength, insertionMaxLength);
|
|
106
|
+
}
|
|
107
|
+
function useWasDefined(value) {
|
|
108
|
+
const isDefined = value !== void 0;
|
|
109
|
+
const [wasDefined, setWasDefined] = React.useState(isDefined);
|
|
110
|
+
React.useEffect(() => {
|
|
111
|
+
if (isDefined) setWasDefined(true);
|
|
112
|
+
}, [isDefined]);
|
|
113
|
+
return wasDefined || isDefined;
|
|
114
|
+
}
|
|
115
|
+
function useResolvedNativeMaxLength(explicitMaxLength, insertionMaxLength) {
|
|
116
|
+
if (!useWasDefined(explicitMaxLength !== void 0 || insertionMaxLength !== void 0 ? true : void 0)) return void 0;
|
|
117
|
+
return resolveNativeMaxLength(explicitMaxLength, insertionMaxLength ?? 2147483647);
|
|
118
|
+
}
|
|
119
|
+
function useNativeTextControl({ forwardedRef, disabled: disabledProp, readOnly: readOnlyProp, bindinput, bindfocus, bindblur }) {
|
|
120
|
+
const textFieldContext = React.useContext(TextFieldContext);
|
|
121
|
+
if (!textFieldContext) throw new Error("TextField input components must be rendered inside <TextField.Root>.");
|
|
122
|
+
const fieldContext = useFieldContext({ strict: false });
|
|
123
|
+
const keyboardAvoidance = useKeyboardAvoidanceActions();
|
|
124
|
+
const disabled = disabledProp ?? textFieldContext.disabled;
|
|
125
|
+
const readOnly = readOnlyProp ?? textFieldContext.readOnly;
|
|
126
|
+
const nativeRef = React.useRef(null);
|
|
127
|
+
const initialNativeValueRef = React.useRef(textFieldContext.value);
|
|
128
|
+
const ownerRef = React.useRef({});
|
|
129
|
+
const lastNativeValueRef = React.useRef(initialNativeValueRef.current);
|
|
130
|
+
const committedValueRef = React.useRef(textFieldContext.value);
|
|
131
|
+
const controlledRef = React.useRef(textFieldContext.controlled);
|
|
132
|
+
const readOnlyRef = React.useRef(readOnly);
|
|
133
|
+
const isComposingRef = React.useRef(false);
|
|
134
|
+
const canApplyInsertionMaxLengthRef = React.useRef(true);
|
|
135
|
+
const reconciliationRevisionRef = React.useRef(0);
|
|
136
|
+
const [canApplyInsertionMaxLength, setCanApplyInsertionMaxLength] = React.useState(true);
|
|
137
|
+
const wasInsertionMaxLengthManaged = useWasDefined(textFieldContext.nativeInsertionMaxLength);
|
|
138
|
+
committedValueRef.current = textFieldContext.value;
|
|
139
|
+
controlledRef.current = textFieldContext.controlled;
|
|
140
|
+
readOnlyRef.current = readOnly;
|
|
141
|
+
const syncNativeValue = React.useCallback((node, value) => {
|
|
142
|
+
"background only";
|
|
143
|
+
if (lastNativeValueRef.current === value || typeof node.invoke !== "function") return;
|
|
144
|
+
lastNativeValueRef.current = value;
|
|
145
|
+
try {
|
|
146
|
+
node.invoke({
|
|
147
|
+
method: "setValue",
|
|
148
|
+
params: { value },
|
|
149
|
+
fail() {
|
|
150
|
+
"background only";
|
|
151
|
+
if (lastNativeValueRef.current === value) lastNativeValueRef.current = null;
|
|
152
|
+
}
|
|
153
|
+
}).exec();
|
|
154
|
+
} catch {
|
|
155
|
+
lastNativeValueRef.current = null;
|
|
156
|
+
}
|
|
157
|
+
}, []);
|
|
158
|
+
const mergedRef = React.useCallback((node) => {
|
|
159
|
+
"background only";
|
|
160
|
+
nativeRef.current = node;
|
|
161
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
162
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
163
|
+
}, [forwardedRef]);
|
|
164
|
+
const updateEditingState = React.useCallback((selectionStart, selectionEnd, isComposing = isComposingRef.current) => {
|
|
165
|
+
"background only";
|
|
166
|
+
isComposingRef.current = isComposing;
|
|
167
|
+
const nextCanApplyInsertionMaxLength = !isComposing && selectionStart >= 0 && selectionStart === selectionEnd;
|
|
168
|
+
if (canApplyInsertionMaxLengthRef.current === nextCanApplyInsertionMaxLength) return;
|
|
169
|
+
canApplyInsertionMaxLengthRef.current = nextCanApplyInsertionMaxLength;
|
|
170
|
+
setCanApplyInsertionMaxLength(nextCanApplyInsertionMaxLength);
|
|
171
|
+
}, []);
|
|
172
|
+
const reconcileControlledValue = React.useCallback((nativeValue) => {
|
|
173
|
+
"background only";
|
|
174
|
+
if (!controlledRef.current) return;
|
|
175
|
+
reconciliationRevisionRef.current += 1;
|
|
176
|
+
const revision = reconciliationRevisionRef.current;
|
|
177
|
+
const nodeAtInput = nativeRef.current;
|
|
178
|
+
Promise.resolve().then(() => {
|
|
179
|
+
"background only";
|
|
180
|
+
}).then(() => {
|
|
181
|
+
"background only";
|
|
182
|
+
if (revision !== reconciliationRevisionRef.current || !controlledRef.current || readOnlyRef.current || nativeRef.current !== nodeAtInput) return;
|
|
183
|
+
const committedValue = committedValueRef.current;
|
|
184
|
+
if (committedValue === nativeValue) return;
|
|
185
|
+
const node = nativeRef.current;
|
|
186
|
+
if (node) syncNativeValue(node, committedValue);
|
|
187
|
+
});
|
|
188
|
+
}, [syncNativeValue]);
|
|
189
|
+
React.useEffect(() => {
|
|
190
|
+
if (readOnly) return;
|
|
191
|
+
if (lastNativeValueRef.current === textFieldContext.value) return;
|
|
192
|
+
const node = nativeRef.current;
|
|
193
|
+
if (node) syncNativeValue(node, textFieldContext.value);
|
|
194
|
+
}, [
|
|
195
|
+
readOnly,
|
|
196
|
+
syncNativeValue,
|
|
197
|
+
textFieldContext.value
|
|
198
|
+
]);
|
|
199
|
+
React.useEffect(() => () => {
|
|
200
|
+
keyboardAvoidance?.unregister(ownerRef.current);
|
|
201
|
+
reconciliationRevisionRef.current += 1;
|
|
202
|
+
}, [keyboardAvoidance]);
|
|
203
|
+
const handleInput = React.useCallback((event) => {
|
|
204
|
+
"background only";
|
|
205
|
+
const { value: nextValue, selectionStart, selectionEnd, isComposing } = event.detail;
|
|
206
|
+
if (disabled || readOnly) {
|
|
207
|
+
lastNativeValueRef.current = nextValue;
|
|
208
|
+
if (nextValue !== committedValueRef.current) {
|
|
209
|
+
const node = nativeRef.current;
|
|
210
|
+
if (node) syncNativeValue(node, committedValueRef.current);
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
updateEditingState(selectionStart, selectionEnd, isComposing === true);
|
|
215
|
+
lastNativeValueRef.current = nextValue;
|
|
216
|
+
textFieldContext.setValue(nextValue);
|
|
217
|
+
bindinput?.(event);
|
|
218
|
+
reconcileControlledValue(nextValue);
|
|
219
|
+
}, [
|
|
220
|
+
bindinput,
|
|
221
|
+
disabled,
|
|
222
|
+
readOnly,
|
|
223
|
+
reconcileControlledValue,
|
|
224
|
+
syncNativeValue,
|
|
225
|
+
textFieldContext.setValue,
|
|
226
|
+
updateEditingState
|
|
227
|
+
]);
|
|
228
|
+
const handleSelectionChange = React.useCallback((detail) => {
|
|
229
|
+
"background only";
|
|
230
|
+
updateEditingState(detail.selectionStart, detail.selectionEnd);
|
|
231
|
+
}, [updateEditingState]);
|
|
232
|
+
const handleFocus = React.useCallback((event) => {
|
|
233
|
+
"background only";
|
|
234
|
+
textFieldContext.setFocused(true);
|
|
235
|
+
keyboardAvoidance?.focus({
|
|
236
|
+
owner: ownerRef.current,
|
|
237
|
+
nativeRef,
|
|
238
|
+
controlRef: textFieldContext.rootRef,
|
|
239
|
+
fieldRef: fieldContext?.rootRef,
|
|
240
|
+
enabled: !disabled && !readOnly
|
|
241
|
+
});
|
|
242
|
+
bindfocus?.(event);
|
|
243
|
+
}, [
|
|
244
|
+
bindfocus,
|
|
245
|
+
disabled,
|
|
246
|
+
fieldContext?.rootRef,
|
|
247
|
+
keyboardAvoidance,
|
|
248
|
+
readOnly,
|
|
249
|
+
textFieldContext.rootRef,
|
|
250
|
+
textFieldContext.setFocused
|
|
251
|
+
]);
|
|
252
|
+
const handleBlur = React.useCallback((event) => {
|
|
253
|
+
"background only";
|
|
254
|
+
textFieldContext.setFocused(false);
|
|
255
|
+
keyboardAvoidance?.blur(ownerRef.current);
|
|
256
|
+
bindblur?.(event);
|
|
257
|
+
}, [
|
|
258
|
+
bindblur,
|
|
259
|
+
keyboardAvoidance,
|
|
260
|
+
textFieldContext.setFocused
|
|
261
|
+
]);
|
|
262
|
+
const notifyLayoutChanged = React.useCallback(() => {
|
|
263
|
+
"background only";
|
|
264
|
+
keyboardAvoidance?.layoutChanged(ownerRef.current);
|
|
265
|
+
}, [keyboardAvoidance]);
|
|
266
|
+
const focusNativeControl = React.useCallback(() => {
|
|
267
|
+
"background only";
|
|
268
|
+
const node = nativeRef.current;
|
|
269
|
+
if (!node || typeof node.invoke !== "function") return;
|
|
270
|
+
try {
|
|
271
|
+
node.invoke({ method: "focus" }).exec();
|
|
272
|
+
} catch {}
|
|
273
|
+
}, []);
|
|
274
|
+
return {
|
|
275
|
+
context: textFieldContext,
|
|
276
|
+
disabled,
|
|
277
|
+
readOnly,
|
|
278
|
+
defaultValueProps: { "default-value": initialNativeValueRef.current },
|
|
279
|
+
mergedRef,
|
|
280
|
+
handleInput,
|
|
281
|
+
handleFocus,
|
|
282
|
+
handleBlur,
|
|
283
|
+
notifyLayoutChanged,
|
|
284
|
+
focusNativeControl,
|
|
285
|
+
handleSelectionChange,
|
|
286
|
+
nativeInsertionMaxLength: wasInsertionMaxLengthManaged && canApplyInsertionMaxLength && textFieldContext.nativeInsertionMaxLength !== void 0 ? textFieldContext.nativeInsertionMaxLength : wasInsertionMaxLengthManaged ? NATIVE_TEXT_MAX_LENGTH_UNLIMITED : void 0
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function getAccessibilityProps(props) {
|
|
290
|
+
return {
|
|
291
|
+
"accessibility-label": props["accessibility-label"],
|
|
292
|
+
"accessibility-traits": props["accessibility-traits"],
|
|
293
|
+
"accessibility-element": props["accessibility-element"],
|
|
294
|
+
"accessibility-value": props["accessibility-value"],
|
|
295
|
+
"accessibility-role-description": props["accessibility-role-description"],
|
|
296
|
+
"accessibility-elements-hidden": props["accessibility-elements-hidden"],
|
|
297
|
+
"accessibility-heading": props["accessibility-heading"],
|
|
298
|
+
"accessibility-actions": props["accessibility-actions"],
|
|
299
|
+
"accessibility-exclusive-focus": props["accessibility-exclusive-focus"],
|
|
300
|
+
"ios-platform-accessibility-id": props["ios-platform-accessibility-id"]
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function getReadOnlyTextStyle({ style, disabled, placeholder, multiline }) {
|
|
304
|
+
return {
|
|
305
|
+
...style,
|
|
306
|
+
...multiline ? {
|
|
307
|
+
whiteSpace: "normal",
|
|
308
|
+
wordBreak: "break-all"
|
|
309
|
+
} : { alignSelf: "center" },
|
|
310
|
+
...placeholder ? { color: disabled ? textInput.base.disabled.placeholder.color : textInput.base.enabled.placeholder.color } : {}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
var TextFieldInput = React.forwardRef((props, ref) => {
|
|
314
|
+
const classes = useClassNames();
|
|
315
|
+
const { className, disabled, readonly, name, maxlength, bindinput, bindselection, bindfocus, bindblur, "show-soft-input-on-focus": showSoftInputOnFocus, "android-set-soft-input-mode": androidSetSoftInputMode, ...nativeProps } = props;
|
|
316
|
+
const control = useNativeTextControl({
|
|
317
|
+
forwardedRef: ref,
|
|
318
|
+
disabled,
|
|
319
|
+
readOnly: readonly,
|
|
320
|
+
bindinput,
|
|
321
|
+
bindfocus,
|
|
322
|
+
bindblur
|
|
323
|
+
});
|
|
324
|
+
const resolvedMaxLength = useResolvedNativeMaxLength(maxlength, control.nativeInsertionMaxLength);
|
|
325
|
+
const handleSelection = React.useCallback((event) => {
|
|
326
|
+
"background only";
|
|
327
|
+
control.handleSelectionChange(event.detail);
|
|
328
|
+
bindselection?.(event);
|
|
329
|
+
}, [bindselection, control.handleSelectionChange]);
|
|
330
|
+
if (control.readOnly) {
|
|
331
|
+
const value = control.context.value;
|
|
332
|
+
const isPlaceholder = value === "";
|
|
333
|
+
const displayValue = isPlaceholder ? props.placeholder : props.type === "password" ? Array.from(value, () => "•").join("") : value;
|
|
334
|
+
return <text ref={control.mergedRef} id={props.id} hidden={props.hidden} flatten={props.flatten} focusable={props.focusable} bindlayoutchange={props.bindlayoutchange} main-thread:bindlayoutchange={props["main-thread:bindlayoutchange"]} className={clsx(classes.value, className)} style={getReadOnlyTextStyle({
|
|
335
|
+
style: props.style,
|
|
336
|
+
disabled: control.disabled,
|
|
337
|
+
placeholder: isPlaceholder,
|
|
338
|
+
multiline: false
|
|
339
|
+
})} {...getAccessibilityProps(props)}>
|
|
340
|
+
{displayValue}
|
|
341
|
+
</text>;
|
|
342
|
+
}
|
|
343
|
+
return <input {...control.defaultValueProps} ref={control.mergedRef} className={clsx(classes.value, className)} disabled={control.disabled} readonly={control.readOnly} show-soft-input-on-focus={control.readOnly ? false : showSoftInputOnFocus} android-set-soft-input-mode={androidSetSoftInputMode} name={name ?? control.context.name} maxlength={resolvedMaxLength} bindinput={control.handleInput} bindselection={handleSelection} bindfocus={control.handleFocus} bindblur={control.handleBlur} {...nativeProps} />;
|
|
344
|
+
});
|
|
345
|
+
TextFieldInput.displayName = "TextFieldInput";
|
|
346
|
+
var TextFieldTextarea = React.forwardRef((props, ref) => {
|
|
347
|
+
const classes = useClassNames();
|
|
348
|
+
const { className, disabled, readonly, name, maxlength, bindinput, bindselection, bindfocus, bindblur, bindlayoutchange, "show-soft-input-on-focus": showSoftInputOnFocus, bounces, "line-spacing": lineSpacing, "android-fullscreen-mode": androidFullscreenMode, "android-set-soft-input-mode": androidSetSoftInputMode, autoresize = true, ...nativeProps } = props;
|
|
349
|
+
const control = useNativeTextControl({
|
|
350
|
+
forwardedRef: ref,
|
|
351
|
+
disabled,
|
|
352
|
+
readOnly: readonly,
|
|
353
|
+
bindinput,
|
|
354
|
+
bindfocus,
|
|
355
|
+
bindblur
|
|
356
|
+
});
|
|
357
|
+
const resolvedMaxLength = useResolvedNativeMaxLength(maxlength, control.nativeInsertionMaxLength);
|
|
358
|
+
const isAndroid = isAndroidRuntime();
|
|
359
|
+
const resolvedLineSpacing = lineSpacing !== void 0 ? lineSpacing : isAndroid ? ANDROID_TEXTAREA_DEFAULT_LINE_SPACING : void 0;
|
|
360
|
+
const handleSelection = React.useCallback((event) => {
|
|
361
|
+
"background only";
|
|
362
|
+
control.handleSelectionChange(event.detail);
|
|
363
|
+
bindselection?.(event);
|
|
364
|
+
}, [bindselection, control.handleSelectionChange]);
|
|
365
|
+
const handleTextareaRootTap = React.useCallback((event) => {
|
|
366
|
+
"background only";
|
|
367
|
+
if (event.target.uid !== event.currentTarget.uid) return;
|
|
368
|
+
control.focusNativeControl();
|
|
369
|
+
}, [control.focusNativeControl]);
|
|
370
|
+
if (control.readOnly) {
|
|
371
|
+
const value = control.context.value;
|
|
372
|
+
const isPlaceholder = value === "";
|
|
373
|
+
return <text ref={control.mergedRef} id={props.id} hidden={props.hidden} flatten={props.flatten} focusable={props.focusable} bindlayoutchange={props.bindlayoutchange} main-thread:bindlayoutchange={props["main-thread:bindlayoutchange"]} className={clsx(classes.value, classes.textareaValue, classes.textareaFixed, className)} style={getReadOnlyTextStyle({
|
|
374
|
+
style: props.style,
|
|
375
|
+
disabled: control.disabled,
|
|
376
|
+
placeholder: isPlaceholder,
|
|
377
|
+
multiline: true
|
|
378
|
+
})} {...getAccessibilityProps(props)}>
|
|
379
|
+
{isPlaceholder ? props.placeholder : value}
|
|
380
|
+
</text>;
|
|
381
|
+
}
|
|
382
|
+
const textarea = <textarea {...control.defaultValueProps} ref={control.mergedRef} className={clsx(classes.value, classes.textareaValue, autoresize ? classes.textareaNativeAutoresize : classes.textareaFixed, autoresize && isAndroid && classes.textareaAndroidAutoresize, className)} disabled={control.disabled} readonly={control.readOnly} show-soft-input-on-focus={control.readOnly ? false : showSoftInputOnFocus} bounces={bounces ?? (autoresize ? false : void 0)} line-spacing={resolvedLineSpacing} android-fullscreen-mode={androidFullscreenMode ?? false} android-set-soft-input-mode={androidSetSoftInputMode} name={name ?? control.context.name} maxlength={resolvedMaxLength} bindinput={control.handleInput} bindselection={handleSelection} bindfocus={control.handleFocus} bindblur={control.handleBlur} bindlayoutchange={bindlayoutchange} {...nativeProps} />;
|
|
383
|
+
if (!autoresize) return textarea;
|
|
384
|
+
return <view className={clsx(classes.textareaRoot, !isAndroid && classes.textareaAutoresizeRoot)} bindtap={handleTextareaRootTap} bindlayoutchange={control.notifyLayoutChanged}>
|
|
385
|
+
{textarea}
|
|
386
|
+
</view>;
|
|
387
|
+
});
|
|
388
|
+
TextFieldTextarea.displayName = "TextFieldTextarea";
|
|
389
|
+
var TextFieldPrefixIcon = React.forwardRef((props, ref) => {
|
|
390
|
+
const classes = useClassNames();
|
|
391
|
+
const { className, ...otherProps } = props;
|
|
392
|
+
return <InternalIcon ref={ref} className={clsx(classes.prefixIcon, className)} {...otherProps} />;
|
|
393
|
+
});
|
|
394
|
+
TextFieldPrefixIcon.displayName = "TextFieldPrefixIcon";
|
|
395
|
+
var TextFieldPrefixText = React.forwardRef((props, ref) => {
|
|
396
|
+
const classes = useClassNames();
|
|
397
|
+
const { children, className, ...nativeProps } = props;
|
|
398
|
+
return <text {...ref ? { ref } : {}} className={clsx(classes.prefixText, className)} {...nativeProps}>
|
|
399
|
+
{children}
|
|
400
|
+
</text>;
|
|
401
|
+
});
|
|
402
|
+
TextFieldPrefixText.displayName = "TextFieldPrefixText";
|
|
403
|
+
var TextFieldSuffixIcon = React.forwardRef((props, ref) => {
|
|
404
|
+
const classes = useClassNames();
|
|
405
|
+
const { className, ...otherProps } = props;
|
|
406
|
+
return <InternalIcon ref={ref} className={clsx(classes.suffixIcon, className)} {...otherProps} />;
|
|
407
|
+
});
|
|
408
|
+
TextFieldSuffixIcon.displayName = "TextFieldSuffixIcon";
|
|
409
|
+
var TextFieldSuffixText = React.forwardRef((props, ref) => {
|
|
410
|
+
const classes = useClassNames();
|
|
411
|
+
const { children, className, ...nativeProps } = props;
|
|
412
|
+
return <text {...ref ? { ref } : {}} className={clsx(classes.suffixText, className)} {...nativeProps}>
|
|
413
|
+
{children}
|
|
414
|
+
</text>;
|
|
415
|
+
});
|
|
416
|
+
TextFieldSuffixText.displayName = "TextFieldSuffixText";
|
|
417
|
+
//#endregion
|
|
418
|
+
export { TextFieldInput, TextFieldPrefixIcon, TextFieldPrefixText, TextFieldRoot, TextFieldSuffixIcon, TextFieldSuffixText, TextFieldTextarea };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { TextFieldPrefixIcon as PrefixIcon, TextFieldPrefixText as PrefixText, TextFieldRoot as Root, TextFieldSuffixIcon as SuffixIcon, TextFieldSuffixText as SuffixText, TextFieldInput as Input, TextFieldTextarea as Textarea, type TextFieldPrefixIconProps as PrefixIconProps, type TextFieldPrefixTextProps as PrefixTextProps, type TextFieldRootProps as RootProps, type TextFieldSuffixIconProps as SuffixIconProps, type TextFieldSuffixTextProps as SuffixTextProps, type TextFieldInputProps as InputProps, type TextFieldTextareaProps as TextareaProps, } from './TextField';
|
|
2
|
+
//# sourceMappingURL=TextField.namespace.d.ts.map
|