@seed-design/react 1.0.7 → 1.1.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/lib/components/ActionButton/ActionButton.cjs +7 -1
- package/lib/components/ActionButton/ActionButton.d.ts +7 -1
- package/lib/components/ActionButton/ActionButton.d.ts.map +1 -1
- package/lib/components/ActionButton/ActionButton.js +7 -1
- package/lib/components/BottomSheet/BottomSheet.cjs +14 -18
- package/lib/components/BottomSheet/BottomSheet.d.ts +12 -19
- package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/lib/components/BottomSheet/BottomSheet.js +14 -18
- package/lib/components/BottomSheet/BottomSheet.namespace.cjs +2 -0
- package/lib/components/BottomSheet/BottomSheet.namespace.d.ts +1 -0
- package/lib/components/BottomSheet/BottomSheet.namespace.d.ts.map +1 -1
- package/lib/components/BottomSheet/BottomSheet.namespace.js +1 -0
- package/lib/components/BottomSheetHandle/BottomSheetHandle.cjs +20 -0
- package/lib/components/BottomSheetHandle/BottomSheetHandle.d.ts +6 -0
- package/lib/components/BottomSheetHandle/BottomSheetHandle.d.ts.map +1 -0
- package/lib/components/BottomSheetHandle/BottomSheetHandle.js +16 -0
- package/lib/components/BottomSheetHandle/index.cjs +9 -0
- package/lib/components/BottomSheetHandle/index.d.ts +2 -0
- package/lib/components/BottomSheetHandle/index.d.ts.map +1 -0
- package/lib/components/BottomSheetHandle/index.js +1 -0
- package/lib/components/Chip/Chip.cjs +4 -3
- package/lib/components/Chip/Chip.d.ts.map +1 -1
- package/lib/components/Chip/Chip.js +4 -3
- package/lib/components/Field/Field.cjs +108 -0
- package/lib/components/Field/Field.d.ts +41 -0
- package/lib/components/Field/Field.d.ts.map +1 -0
- package/lib/components/Field/Field.js +96 -0
- package/lib/components/Field/Field.namespace.cjs +17 -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 +1 -0
- package/lib/components/Field/index.cjs +19 -0
- package/lib/components/Field/index.d.ts +3 -0
- package/lib/components/Field/index.d.ts.map +1 -0
- package/lib/components/Field/index.js +3 -0
- package/lib/components/FieldButton/FieldButton.cjs +201 -0
- package/lib/components/FieldButton/FieldButton.d.ts +61 -0
- package/lib/components/FieldButton/FieldButton.d.ts.map +1 -0
- package/lib/components/FieldButton/FieldButton.js +161 -0
- package/lib/components/FieldButton/FieldButton.namespace.cjs +26 -0
- package/lib/components/FieldButton/FieldButton.namespace.d.ts +2 -0
- package/lib/components/FieldButton/FieldButton.namespace.d.ts.map +1 -0
- package/lib/components/FieldButton/FieldButton.namespace.js +1 -0
- package/lib/components/FieldButton/index.cjs +28 -0
- package/lib/components/FieldButton/index.d.ts +3 -0
- package/lib/components/FieldButton/index.d.ts.map +1 -0
- package/lib/components/FieldButton/index.js +3 -0
- package/lib/components/HelpBubble/HelpBubble.d.ts +1 -1
- package/lib/components/List/List.cjs +5 -4
- package/lib/components/List/List.d.ts.map +1 -1
- package/lib/components/List/List.js +5 -4
- package/lib/components/PageBanner/PageBanner.cjs +8 -3
- package/lib/components/PageBanner/PageBanner.d.ts +5 -2
- package/lib/components/PageBanner/PageBanner.d.ts.map +1 -1
- package/lib/components/PageBanner/PageBanner.js +7 -3
- package/lib/components/PageBanner/PageBanner.namespace.cjs +2 -1
- package/lib/components/PageBanner/PageBanner.namespace.d.ts +1 -1
- package/lib/components/PageBanner/PageBanner.namespace.d.ts.map +1 -1
- package/lib/components/PageBanner/PageBanner.namespace.js +1 -1
- package/lib/components/PageBanner/index.cjs +2 -1
- package/lib/components/PageBanner/index.d.ts +1 -1
- package/lib/components/PageBanner/index.d.ts.map +1 -1
- package/lib/components/PageBanner/index.js +1 -1
- package/lib/components/Slider/Slider.cjs +110 -0
- package/lib/components/Slider/Slider.d.ts +51 -0
- package/lib/components/Slider/Slider.d.ts.map +1 -0
- package/lib/components/Slider/Slider.js +94 -0
- package/lib/components/Slider/Slider.namespace.cjs +21 -0
- package/lib/components/Slider/Slider.namespace.d.ts +2 -0
- package/lib/components/Slider/Slider.namespace.d.ts.map +1 -0
- package/lib/components/Slider/Slider.namespace.js +1 -0
- package/lib/components/Slider/index.cjs +23 -0
- package/lib/components/Slider/index.d.ts +3 -0
- package/lib/components/Slider/index.d.ts.map +1 -0
- package/lib/components/Slider/index.js +3 -0
- package/lib/components/TextField/TextField.cjs +54 -74
- package/lib/components/TextField/TextField.d.ts +2 -35
- package/lib/components/TextField/TextField.d.ts.map +1 -1
- package/lib/components/TextField/TextField.js +56 -65
- package/lib/components/TextField/TextField.namespace.cjs +0 -11
- package/lib/components/TextField/TextField.namespace.d.ts +1 -1
- package/lib/components/TextField/TextField.namespace.d.ts.map +1 -1
- package/lib/components/TextField/TextField.namespace.js +1 -1
- package/lib/components/TextField/TextField.test.d.ts +1 -0
- package/lib/components/TextField/TextField.test.d.ts.map +1 -0
- package/lib/components/TextField/index.cjs +2 -11
- package/lib/components/TextField/index.d.ts +2 -1
- package/lib/components/TextField/index.d.ts.map +1 -1
- package/lib/components/TextField/index.js +2 -1
- package/lib/components/TextField/memoize.cjs +18 -0
- package/lib/components/TextField/memoize.d.ts +2 -0
- package/lib/components/TextField/memoize.d.ts.map +1 -0
- package/lib/components/TextField/memoize.js +14 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.cjs +52 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.d.ts +23 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.d.ts.map +1 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.js +48 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.test.d.ts +1 -0
- package/lib/components/TextField/useTextFieldWithGraphemes.test.d.ts.map +1 -0
- package/lib/components/index.cjs +53 -12
- package/lib/components/index.d.ts +3 -0
- package/lib/components/index.d.ts.map +1 -1
- package/lib/components/index.js +12 -2
- package/lib/index.cjs +53 -12
- package/lib/index.js +12 -2
- package/lib/node_modules/unicode-segmenter/_grapheme_data.cjs +23 -0
- package/lib/node_modules/unicode-segmenter/_grapheme_data.js +19 -0
- package/lib/node_modules/unicode-segmenter/_incb_data.cjs +29 -0
- package/lib/node_modules/unicode-segmenter/_incb_data.js +25 -0
- package/lib/node_modules/unicode-segmenter/core.cjs +83 -0
- package/lib/node_modules/unicode-segmenter/core.js +78 -0
- package/lib/node_modules/unicode-segmenter/grapheme.cjs +312 -0
- package/lib/node_modules/unicode-segmenter/grapheme.js +307 -0
- package/lib/primitive.cjs +7 -0
- package/lib/primitive.d.ts +1 -0
- package/lib/primitive.d.ts.map +1 -1
- package/lib/primitive.js +1 -0
- package/lib/utils/createWithStateProps.cjs +8 -4
- package/lib/utils/createWithStateProps.d.ts +6 -3
- package/lib/utils/createWithStateProps.d.ts.map +1 -1
- package/lib/utils/createWithStateProps.js +8 -4
- package/package.json +9 -6
- package/src/components/ActionButton/ActionButton.tsx +21 -2
- package/src/components/BottomSheet/BottomSheet.namespace.ts +5 -0
- package/src/components/BottomSheet/BottomSheet.tsx +24 -38
- package/src/components/BottomSheetHandle/BottomSheetHandle.tsx +22 -0
- package/src/components/BottomSheetHandle/index.ts +1 -0
- package/src/components/Chip/Chip.tsx +4 -3
- package/src/components/Field/Field.namespace.ts +19 -0
- package/src/components/Field/Field.tsx +136 -0
- package/src/components/Field/index.ts +21 -0
- package/src/components/FieldButton/FieldButton.namespace.ts +38 -0
- package/src/components/FieldButton/FieldButton.tsx +249 -0
- package/src/components/FieldButton/index.ts +40 -0
- package/src/components/HelpBubble/HelpBubble.tsx +1 -1
- package/src/components/List/List.tsx +5 -4
- package/src/components/PageBanner/PageBanner.namespace.ts +4 -2
- package/src/components/PageBanner/PageBanner.tsx +10 -3
- package/src/components/PageBanner/index.ts +4 -2
- package/src/components/Slider/Slider.namespace.ts +28 -0
- package/src/components/Slider/Slider.tsx +154 -0
- package/src/components/Slider/index.ts +30 -0
- package/src/components/TextField/TextField.namespace.ts +2 -24
- package/src/components/TextField/TextField.test.tsx +260 -0
- package/src/components/TextField/TextField.tsx +67 -143
- package/src/components/TextField/index.ts +7 -24
- package/src/components/TextField/memoize.ts +14 -0
- package/src/components/TextField/useTextFieldWithGraphemes.test.tsx +301 -0
- package/src/components/TextField/useTextFieldWithGraphemes.ts +65 -0
- package/src/components/index.ts +3 -0
- package/src/primitive.ts +1 -0
- package/src/utils/createWithStateProps.tsx +14 -9
- package/lib/components/List/ListHeader.namespace.d.ts +0 -2
- package/lib/components/List/ListHeader.namespace.d.ts.map +0 -1
- package/src/components/List/ListHeader.namespace.ts +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import "@testing-library/jest-dom/vitest";
|
|
2
|
+
import { cleanup, render } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { ReactElement } from "react";
|
|
6
|
+
import React from "react";
|
|
7
|
+
|
|
8
|
+
import { Field } from "@seed-design/react-field";
|
|
9
|
+
import {
|
|
10
|
+
TextFieldRoot,
|
|
11
|
+
TextFieldInput,
|
|
12
|
+
TextFieldTextarea,
|
|
13
|
+
TextFieldPrefixIcon,
|
|
14
|
+
TextFieldPrefixText,
|
|
15
|
+
TextFieldSuffixIcon,
|
|
16
|
+
TextFieldSuffixText,
|
|
17
|
+
} from "./TextField";
|
|
18
|
+
|
|
19
|
+
afterEach(cleanup);
|
|
20
|
+
|
|
21
|
+
function setUp(jsx: ReactElement) {
|
|
22
|
+
return {
|
|
23
|
+
user: userEvent.setup(),
|
|
24
|
+
...render(jsx),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("TextField", () => {
|
|
29
|
+
describe("props merging", () => {
|
|
30
|
+
describe("TextFieldInput", () => {
|
|
31
|
+
it("should merge props from TextFieldRoot", () => {
|
|
32
|
+
const { getByRole } = setUp(
|
|
33
|
+
<TextFieldRoot defaultValue="initial">
|
|
34
|
+
<TextFieldInput placeholder="Placeholder" />
|
|
35
|
+
</TextFieldRoot>,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const input = getByRole("textbox");
|
|
39
|
+
expect(input).toHaveValue("initial");
|
|
40
|
+
expect(input).toHaveAttribute("placeholder", "Placeholder");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should merge props from Field context", () => {
|
|
44
|
+
const { getByRole } = setUp(
|
|
45
|
+
<Field.Root required invalid disabled name="test-field">
|
|
46
|
+
<TextFieldRoot>
|
|
47
|
+
<TextFieldInput />
|
|
48
|
+
</TextFieldRoot>
|
|
49
|
+
</Field.Root>,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const input = getByRole("textbox");
|
|
53
|
+
expect(input).toHaveAttribute("name", "test-field");
|
|
54
|
+
expect(input).toHaveAttribute("aria-required", "true");
|
|
55
|
+
expect(input).toHaveAttribute("aria-invalid", "true");
|
|
56
|
+
expect(input).toBeDisabled();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should prioritize direct props over context props", () => {
|
|
60
|
+
const { getByRole } = setUp(
|
|
61
|
+
<Field.Root name="field-name">
|
|
62
|
+
<TextFieldRoot>
|
|
63
|
+
<TextFieldInput name="direct-name" />
|
|
64
|
+
</TextFieldRoot>
|
|
65
|
+
</Field.Root>,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const input = getByRole("textbox");
|
|
69
|
+
expect(input).toHaveAttribute("name", "direct-name");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should merge data attributes", () => {
|
|
73
|
+
const { getByRole } = setUp(
|
|
74
|
+
<Field.Root disabled readOnly invalid>
|
|
75
|
+
<TextFieldRoot>
|
|
76
|
+
<TextFieldInput data-custom="value" />
|
|
77
|
+
</TextFieldRoot>
|
|
78
|
+
</Field.Root>,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const input = getByRole("textbox");
|
|
82
|
+
expect(input).toHaveAttribute("data-disabled", "");
|
|
83
|
+
expect(input).toHaveAttribute("data-readonly", "");
|
|
84
|
+
expect(input).toHaveAttribute("data-invalid", "");
|
|
85
|
+
expect(input).toHaveAttribute("data-custom", "value");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should handle uncontrolled modes", async () => {
|
|
89
|
+
const handleChange = vi.fn();
|
|
90
|
+
const { getByRole, user } = setUp(
|
|
91
|
+
<TextFieldRoot onValueChange={handleChange}>
|
|
92
|
+
<TextFieldInput />
|
|
93
|
+
</TextFieldRoot>,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const input = getByRole("textbox");
|
|
97
|
+
|
|
98
|
+
await user.type(input, "test");
|
|
99
|
+
expect(handleChange).toHaveBeenCalledWith("test");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should handle controlled modes", async () => {
|
|
103
|
+
function Controlled() {
|
|
104
|
+
const [value, setValue] = React.useState("");
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<TextFieldRoot value={value} onValueChange={setValue}>
|
|
108
|
+
<TextFieldInput />
|
|
109
|
+
</TextFieldRoot>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const { getByRole, user } = setUp(<Controlled />);
|
|
114
|
+
|
|
115
|
+
const input = getByRole("textbox");
|
|
116
|
+
|
|
117
|
+
await user.type(input, "test");
|
|
118
|
+
expect(input).toHaveValue("test");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("TextFieldTextarea", () => {
|
|
123
|
+
it("should merge props from TextFieldRoot", () => {
|
|
124
|
+
const { getByRole } = setUp(
|
|
125
|
+
<TextFieldRoot defaultValue="initial">
|
|
126
|
+
<TextFieldTextarea placeholder="Placeholder" />
|
|
127
|
+
</TextFieldRoot>,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const input = getByRole("textbox");
|
|
131
|
+
expect(input).toHaveValue("initial");
|
|
132
|
+
expect(input).toHaveAttribute("placeholder", "Placeholder");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should merge props from Field context", () => {
|
|
136
|
+
const { getByRole } = setUp(
|
|
137
|
+
<Field.Root required invalid disabled name="test-field">
|
|
138
|
+
<TextFieldRoot>
|
|
139
|
+
<TextFieldTextarea />
|
|
140
|
+
</TextFieldRoot>
|
|
141
|
+
</Field.Root>,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const input = getByRole("textbox");
|
|
145
|
+
expect(input).toHaveAttribute("name", "test-field");
|
|
146
|
+
expect(input).toHaveAttribute("aria-required", "true");
|
|
147
|
+
expect(input).toHaveAttribute("aria-invalid", "true");
|
|
148
|
+
expect(input).toBeDisabled();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("should prioritize direct props over context props", () => {
|
|
152
|
+
const { getByRole } = setUp(
|
|
153
|
+
<Field.Root disabled name="field-name">
|
|
154
|
+
<TextFieldRoot>
|
|
155
|
+
<TextFieldTextarea disabled={false} name="direct-name" />
|
|
156
|
+
</TextFieldRoot>
|
|
157
|
+
</Field.Root>,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const input = getByRole("textbox");
|
|
161
|
+
expect(input).toHaveAttribute("name", "direct-name");
|
|
162
|
+
expect(input).not.toBeDisabled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should merge data attributes", () => {
|
|
166
|
+
const { getByRole } = setUp(
|
|
167
|
+
<Field.Root disabled readOnly invalid>
|
|
168
|
+
<TextFieldRoot>
|
|
169
|
+
<TextFieldTextarea data-custom="value" />
|
|
170
|
+
</TextFieldRoot>
|
|
171
|
+
</Field.Root>,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const input = getByRole("textbox");
|
|
175
|
+
expect(input).toHaveAttribute("data-disabled", "");
|
|
176
|
+
expect(input).toHaveAttribute("data-readonly", "");
|
|
177
|
+
expect(input).toHaveAttribute("data-invalid", "");
|
|
178
|
+
expect(input).toHaveAttribute("data-custom", "value");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should handle uncontrolled modes", async () => {
|
|
182
|
+
const handleChange = vi.fn();
|
|
183
|
+
const { getByRole, user } = setUp(
|
|
184
|
+
<TextFieldRoot onValueChange={handleChange}>
|
|
185
|
+
<TextFieldTextarea />
|
|
186
|
+
</TextFieldRoot>,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const input = getByRole("textbox");
|
|
190
|
+
|
|
191
|
+
await user.type(input, "test");
|
|
192
|
+
expect(handleChange).toHaveBeenCalledWith("test");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should handle controlled modes", async () => {
|
|
196
|
+
function Controlled() {
|
|
197
|
+
const [value, setValue] = React.useState("");
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<TextFieldRoot value={value} onValueChange={setValue}>
|
|
201
|
+
<TextFieldTextarea />
|
|
202
|
+
</TextFieldRoot>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const { getByRole, user } = setUp(<Controlled />);
|
|
207
|
+
|
|
208
|
+
const input = getByRole("textbox");
|
|
209
|
+
|
|
210
|
+
await user.type(input, "test");
|
|
211
|
+
expect(input).toHaveValue("test");
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe("Prefix and Suffix components", () => {
|
|
216
|
+
it("should apply state props to affix elements", () => {
|
|
217
|
+
const { getByTestId } = setUp(
|
|
218
|
+
<TextFieldRoot disabled invalid readOnly>
|
|
219
|
+
<TextFieldPrefixText>Prefix</TextFieldPrefixText>
|
|
220
|
+
<TextFieldPrefixIcon svg={<svg data-testid="prefix-icon" />} />
|
|
221
|
+
<TextFieldInput />
|
|
222
|
+
<TextFieldSuffixIcon svg={<svg data-testid="suffix-icon" />} />
|
|
223
|
+
<TextFieldSuffixText>Suffix</TextFieldSuffixText>
|
|
224
|
+
</TextFieldRoot>,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const prefixIcon = getByTestId("prefix-icon");
|
|
228
|
+
expect(prefixIcon).toHaveAttribute("data-disabled", "");
|
|
229
|
+
expect(prefixIcon).toHaveAttribute("data-invalid", "");
|
|
230
|
+
expect(prefixIcon).toHaveAttribute("data-readonly", "");
|
|
231
|
+
|
|
232
|
+
const suffixIcon = getByTestId("suffix-icon");
|
|
233
|
+
expect(suffixIcon).toHaveAttribute("data-disabled", "");
|
|
234
|
+
expect(suffixIcon).toHaveAttribute("data-invalid", "");
|
|
235
|
+
expect(suffixIcon).toHaveAttribute("data-readonly", "");
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe("Complex prop merging scenarios", () => {
|
|
240
|
+
// this is possible, but you shouldn't be doing this
|
|
241
|
+
it("should handle nested Field and TextField contexts", () => {
|
|
242
|
+
const { getByRole } = setUp(
|
|
243
|
+
<Field.Root required name="field">
|
|
244
|
+
<Field.Label>Label</Field.Label>
|
|
245
|
+
<TextFieldRoot invalid>
|
|
246
|
+
<TextFieldInput />
|
|
247
|
+
</TextFieldRoot>
|
|
248
|
+
<Field.Description>Description</Field.Description>
|
|
249
|
+
</Field.Root>,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const input = getByRole("textbox");
|
|
253
|
+
expect(input).toHaveAttribute("name", "field");
|
|
254
|
+
expect(input).toHaveAttribute("aria-required", "true");
|
|
255
|
+
expect(input).toHaveAttribute("aria-invalid", "true");
|
|
256
|
+
expect(input).toHaveAttribute("aria-describedby");
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useLayoutEffect } from "@radix-ui/react-use-layout-effect";
|
|
2
2
|
import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive";
|
|
3
3
|
import { TextField, useTextFieldContext } from "@seed-design/react-text-field";
|
|
4
|
-
import {
|
|
4
|
+
import { useFieldContext } from "@seed-design/react-field";
|
|
5
|
+
import { textInput, type TextInputVariantProps } from "@seed-design/css/recipes/text-input";
|
|
5
6
|
import clsx from "clsx";
|
|
6
7
|
import type * as React from "react";
|
|
7
8
|
import { forwardRef, useCallback, useRef } from "react";
|
|
@@ -9,74 +10,27 @@ import { createSlotRecipeContext } from "../../utils/createSlotRecipeContext";
|
|
|
9
10
|
import { createWithStateProps } from "../../utils/createWithStateProps";
|
|
10
11
|
import { InternalIcon, type InternalIconProps } from "../private/Icon";
|
|
11
12
|
import { composeRefs } from "@radix-ui/react-compose-refs";
|
|
13
|
+
import { mergeProps } from "@seed-design/dom-utils";
|
|
12
14
|
|
|
13
|
-
const { withProvider, withContext, useClassNames } = createSlotRecipeContext(
|
|
14
|
-
|
|
15
|
+
const { withProvider, withContext, useClassNames } = createSlotRecipeContext(textInput);
|
|
16
|
+
|
|
17
|
+
const withFieldStateProps = createWithStateProps([{ useContext: useFieldContext, strict: false }]);
|
|
18
|
+
const withStateProps = createWithStateProps([
|
|
19
|
+
useTextFieldContext,
|
|
20
|
+
{ useContext: useFieldContext, strict: false },
|
|
21
|
+
]);
|
|
15
22
|
|
|
16
23
|
////////////////////////////////////////////////////////////////////////////////////
|
|
17
24
|
|
|
18
|
-
export interface TextFieldRootProps extends
|
|
25
|
+
export interface TextFieldRootProps extends TextInputVariantProps, TextField.RootProps {}
|
|
19
26
|
|
|
20
27
|
export const TextFieldRoot = withProvider<HTMLDivElement, TextFieldRootProps>(
|
|
21
|
-
TextField.Root,
|
|
28
|
+
withFieldStateProps(TextField.Root),
|
|
22
29
|
"root",
|
|
23
30
|
);
|
|
24
31
|
|
|
25
32
|
////////////////////////////////////////////////////////////////////////////////////
|
|
26
33
|
|
|
27
|
-
export interface TextFieldHeaderProps
|
|
28
|
-
extends PrimitiveProps,
|
|
29
|
-
React.HTMLAttributes<HTMLDivElement> {}
|
|
30
|
-
|
|
31
|
-
export const TextFieldHeader = withContext<HTMLDivElement, TextFieldHeaderProps>(
|
|
32
|
-
withStateProps(Primitive.div),
|
|
33
|
-
"header",
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
37
|
-
|
|
38
|
-
export interface TextFieldLabelProps extends TextField.LabelProps {}
|
|
39
|
-
|
|
40
|
-
export const TextFieldLabel = withContext<HTMLLabelElement, TextFieldLabelProps>(
|
|
41
|
-
TextField.Label,
|
|
42
|
-
"label",
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
46
|
-
|
|
47
|
-
export interface TextFieldIndicatorProps
|
|
48
|
-
extends PrimitiveProps,
|
|
49
|
-
React.HTMLAttributes<HTMLSpanElement> {}
|
|
50
|
-
|
|
51
|
-
export const TextFieldIndicator = forwardRef<HTMLSpanElement, TextFieldIndicatorProps>(
|
|
52
|
-
(props, ref) => {
|
|
53
|
-
const { className, ...otherProps } = props;
|
|
54
|
-
const classNames = useClassNames();
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<>
|
|
58
|
-
<Primitive.span> </Primitive.span>
|
|
59
|
-
<Primitive.span
|
|
60
|
-
ref={ref}
|
|
61
|
-
className={clsx(classNames.indicator, className)}
|
|
62
|
-
{...otherProps}
|
|
63
|
-
/>
|
|
64
|
-
</>
|
|
65
|
-
);
|
|
66
|
-
},
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
70
|
-
|
|
71
|
-
export interface TextFieldFieldProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {}
|
|
72
|
-
|
|
73
|
-
export const TextFieldField = withContext<HTMLDivElement, TextFieldFieldProps>(
|
|
74
|
-
withStateProps(Primitive.div),
|
|
75
|
-
"field",
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
79
|
-
|
|
80
34
|
export interface TextFieldPrefixIconProps extends InternalIconProps {}
|
|
81
35
|
|
|
82
36
|
export const TextFieldPrefixIcon = withContext<SVGSVGElement, TextFieldPrefixIconProps>(
|
|
@@ -84,8 +38,6 @@ export const TextFieldPrefixIcon = withContext<SVGSVGElement, TextFieldPrefixIco
|
|
|
84
38
|
"prefixIcon",
|
|
85
39
|
);
|
|
86
40
|
|
|
87
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
88
|
-
|
|
89
41
|
export interface TextFieldPrefixTextProps
|
|
90
42
|
extends PrimitiveProps,
|
|
91
43
|
React.HTMLAttributes<HTMLSpanElement> {}
|
|
@@ -95,8 +47,6 @@ export const TextFieldPrefixText = withContext<HTMLSpanElement, TextFieldPrefixT
|
|
|
95
47
|
"prefixText",
|
|
96
48
|
);
|
|
97
49
|
|
|
98
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
99
|
-
|
|
100
50
|
export interface TextFieldSuffixIconProps extends InternalIconProps {}
|
|
101
51
|
|
|
102
52
|
export const TextFieldSuffixIcon = withContext<SVGSVGElement, TextFieldSuffixIconProps>(
|
|
@@ -104,8 +54,6 @@ export const TextFieldSuffixIcon = withContext<SVGSVGElement, TextFieldSuffixIco
|
|
|
104
54
|
"suffixIcon",
|
|
105
55
|
);
|
|
106
56
|
|
|
107
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
108
|
-
|
|
109
57
|
export interface TextFieldSuffixTextProps
|
|
110
58
|
extends PrimitiveProps,
|
|
111
59
|
React.HTMLAttributes<HTMLSpanElement> {}
|
|
@@ -119,12 +67,37 @@ export const TextFieldSuffixText = withContext<HTMLSpanElement, TextFieldSuffixT
|
|
|
119
67
|
|
|
120
68
|
export interface TextFieldInputProps extends TextField.InputProps {}
|
|
121
69
|
|
|
122
|
-
export const TextFieldInput =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
);
|
|
70
|
+
export const TextFieldInput = forwardRef<HTMLInputElement, TextFieldInputProps>(
|
|
71
|
+
({ className, ...otherProps }, ref) => {
|
|
72
|
+
const classNames = useClassNames();
|
|
73
|
+
const textFieldContext = useTextFieldContext();
|
|
74
|
+
const fieldContext = useFieldContext({ strict: false });
|
|
75
|
+
|
|
76
|
+
const mergedProps = mergeProps(
|
|
77
|
+
fieldContext ? fieldContext.stateProps : {},
|
|
78
|
+
fieldContext ? fieldContext.inputAriaAttributes : {},
|
|
79
|
+
textFieldContext.inputProps,
|
|
80
|
+
fieldContext ? fieldContext.inputProps : {},
|
|
81
|
+
otherProps,
|
|
82
|
+
);
|
|
126
83
|
|
|
127
|
-
|
|
84
|
+
if (
|
|
85
|
+
// if not in field, then must have aria-label or aria-labelledby
|
|
86
|
+
!fieldContext &&
|
|
87
|
+
!otherProps["aria-label"] &&
|
|
88
|
+
!otherProps["aria-labelledby"]
|
|
89
|
+
) {
|
|
90
|
+
console.warn(
|
|
91
|
+
"TextFieldInput: Please provide `aria-label` or `aria-labelledby` for accessibility, or put `TextFieldInput` inside a `Field` where a `FieldLabel` is provided.",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<TextField.Input ref={ref} {...mergedProps} className={clsx(classNames.value, className)} />
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
TextFieldInput.displayName = "TextFieldInput";
|
|
128
101
|
|
|
129
102
|
export interface TextFieldTextareaProps extends TextField.TextareaProps {
|
|
130
103
|
/**
|
|
@@ -135,10 +108,29 @@ export interface TextFieldTextareaProps extends TextField.TextareaProps {
|
|
|
135
108
|
}
|
|
136
109
|
|
|
137
110
|
export const TextFieldTextarea = forwardRef<HTMLTextAreaElement, TextFieldTextareaProps>(
|
|
138
|
-
(
|
|
139
|
-
const { className, autoresize = true, ...otherProps } = props;
|
|
111
|
+
({ className, autoresize = true, ...otherProps }, ref) => {
|
|
140
112
|
const classNames = useClassNames();
|
|
141
|
-
const
|
|
113
|
+
const textFieldContext = useTextFieldContext();
|
|
114
|
+
const fieldContext = useFieldContext({ strict: false });
|
|
115
|
+
|
|
116
|
+
const mergedProps = mergeProps(
|
|
117
|
+
fieldContext ? fieldContext.stateProps : {},
|
|
118
|
+
fieldContext ? fieldContext.inputAriaAttributes : {},
|
|
119
|
+
textFieldContext.inputProps,
|
|
120
|
+
fieldContext ? fieldContext.inputProps : {},
|
|
121
|
+
otherProps,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
if (
|
|
125
|
+
// if not in field, then must have aria-label or aria-labelledby
|
|
126
|
+
!fieldContext &&
|
|
127
|
+
!otherProps["aria-label"] &&
|
|
128
|
+
!otherProps["aria-labelledby"]
|
|
129
|
+
) {
|
|
130
|
+
console.warn(
|
|
131
|
+
"TextFieldTextarea: Please provide `aria-label` or `aria-labelledby` for accessibility, or put `TextFieldTextarea` inside a `Field` where a `FieldLabel` is provided.",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
142
134
|
|
|
143
135
|
// referenced from React Spectrum
|
|
144
136
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
@@ -176,83 +168,15 @@ export const TextFieldTextarea = forwardRef<HTMLTextAreaElement, TextFieldTextar
|
|
|
176
168
|
if (inputRef.current) {
|
|
177
169
|
onHeightChange();
|
|
178
170
|
}
|
|
179
|
-
}, [onHeightChange, value, inputRef]);
|
|
171
|
+
}, [onHeightChange, textFieldContext.value, inputRef]);
|
|
180
172
|
|
|
181
173
|
return (
|
|
182
174
|
<TextField.Textarea
|
|
183
175
|
ref={composeRefs(inputRef, ref)}
|
|
184
|
-
{...
|
|
176
|
+
{...mergedProps}
|
|
185
177
|
className={clsx(classNames.value, className)}
|
|
186
178
|
/>
|
|
187
179
|
);
|
|
188
180
|
},
|
|
189
181
|
);
|
|
190
|
-
|
|
191
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
192
|
-
|
|
193
|
-
export interface TextFieldFooterProps
|
|
194
|
-
extends PrimitiveProps,
|
|
195
|
-
React.HTMLAttributes<HTMLDivElement> {}
|
|
196
|
-
|
|
197
|
-
export const TextFieldFooter = withContext<HTMLDivElement, TextFieldFooterProps>(
|
|
198
|
-
withStateProps(Primitive.div),
|
|
199
|
-
"footer",
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
203
|
-
|
|
204
|
-
export interface TextFieldDescriptionProps extends TextField.DescriptionProps {}
|
|
205
|
-
|
|
206
|
-
export const TextFieldDescription = withContext<HTMLSpanElement, TextFieldDescriptionProps>(
|
|
207
|
-
TextField.Description,
|
|
208
|
-
"description",
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
212
|
-
|
|
213
|
-
export interface TextFieldErrorMessageProps extends TextField.ErrorMessageProps {}
|
|
214
|
-
|
|
215
|
-
export const TextFieldErrorMessage = withContext<HTMLSpanElement, TextFieldErrorMessageProps>(
|
|
216
|
-
TextField.ErrorMessage,
|
|
217
|
-
"errorMessage",
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
221
|
-
|
|
222
|
-
export interface TextFieldErrorIconProps extends InternalIconProps {}
|
|
223
|
-
|
|
224
|
-
export const TextFieldErrorIcon = withContext<SVGSVGElement, TextFieldErrorIconProps>(
|
|
225
|
-
withStateProps(InternalIcon),
|
|
226
|
-
"errorIcon",
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
230
|
-
|
|
231
|
-
export interface TextFieldCharacterCountAreaProps
|
|
232
|
-
extends PrimitiveProps,
|
|
233
|
-
React.HTMLAttributes<HTMLDivElement> {}
|
|
234
|
-
|
|
235
|
-
export const TextFieldCharacterCountArea = withContext<
|
|
236
|
-
HTMLDivElement,
|
|
237
|
-
TextFieldCharacterCountAreaProps
|
|
238
|
-
>(withStateProps(Primitive.div), "characterCountArea");
|
|
239
|
-
|
|
240
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
241
|
-
|
|
242
|
-
export interface TextFieldCharacterCountProps extends TextField.GraphemeCountProps {}
|
|
243
|
-
|
|
244
|
-
export const TextFieldCharacterCount = withContext<HTMLDivElement, TextFieldCharacterCountProps>(
|
|
245
|
-
TextField.GraphemeCount,
|
|
246
|
-
"characterCount",
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
////////////////////////////////////////////////////////////////////////////////////
|
|
250
|
-
|
|
251
|
-
export interface TextFieldMaxCharacterCountProps
|
|
252
|
-
extends PrimitiveProps,
|
|
253
|
-
React.HTMLAttributes<HTMLSpanElement> {}
|
|
254
|
-
|
|
255
|
-
export const TextFieldMaxCharacterCount = withContext<
|
|
256
|
-
HTMLSpanElement,
|
|
257
|
-
TextFieldMaxCharacterCountProps
|
|
258
|
-
>(withStateProps(Primitive.span), "maxCharacterCount");
|
|
182
|
+
TextFieldTextarea.displayName = "TextFieldTextarea";
|
|
@@ -1,40 +1,23 @@
|
|
|
1
1
|
export {
|
|
2
|
-
|
|
3
|
-
TextFieldCharacterCountArea,
|
|
4
|
-
TextFieldDescription,
|
|
5
|
-
TextFieldErrorIcon,
|
|
6
|
-
TextFieldErrorMessage,
|
|
7
|
-
TextFieldField,
|
|
8
|
-
TextFieldFooter,
|
|
9
|
-
TextFieldHeader,
|
|
10
|
-
TextFieldIndicator,
|
|
2
|
+
TextFieldRoot,
|
|
11
3
|
TextFieldInput,
|
|
12
4
|
TextFieldTextarea,
|
|
13
|
-
TextFieldLabel,
|
|
14
|
-
TextFieldMaxCharacterCount,
|
|
15
5
|
TextFieldPrefixIcon,
|
|
16
6
|
TextFieldPrefixText,
|
|
17
|
-
TextFieldRoot,
|
|
18
7
|
TextFieldSuffixIcon,
|
|
19
8
|
TextFieldSuffixText,
|
|
20
|
-
type
|
|
21
|
-
type TextFieldCharacterCountProps,
|
|
22
|
-
type TextFieldDescriptionProps,
|
|
23
|
-
type TextFieldErrorIconProps,
|
|
24
|
-
type TextFieldErrorMessageProps,
|
|
25
|
-
type TextFieldFieldProps,
|
|
26
|
-
type TextFieldFooterProps,
|
|
27
|
-
type TextFieldHeaderProps,
|
|
28
|
-
type TextFieldIndicatorProps,
|
|
9
|
+
type TextFieldRootProps,
|
|
29
10
|
type TextFieldInputProps,
|
|
30
11
|
type TextFieldTextareaProps,
|
|
31
|
-
type TextFieldLabelProps,
|
|
32
|
-
type TextFieldMaxCharacterCountProps,
|
|
33
12
|
type TextFieldPrefixIconProps,
|
|
34
13
|
type TextFieldPrefixTextProps,
|
|
35
|
-
type TextFieldRootProps,
|
|
36
14
|
type TextFieldSuffixIconProps,
|
|
37
15
|
type TextFieldSuffixTextProps,
|
|
38
16
|
} from "./TextField";
|
|
39
17
|
|
|
18
|
+
export {
|
|
19
|
+
useTextFieldWithGraphemes,
|
|
20
|
+
type UseTextFieldWithGraphemesParams,
|
|
21
|
+
} from "./useTextFieldWithGraphemes";
|
|
22
|
+
|
|
40
23
|
export * as TextField from "./TextField.namespace";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function memoize<Arg, Result>(fn: (arg: Arg) => Result): (arg: Arg) => Result {
|
|
2
|
+
const cache = new Map<Arg, Result>();
|
|
3
|
+
|
|
4
|
+
return (arg: Arg) => {
|
|
5
|
+
if (cache.has(arg)) {
|
|
6
|
+
return cache.get(arg) as Result;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const result = fn(arg);
|
|
10
|
+
cache.set(arg, result);
|
|
11
|
+
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
}
|