@xsolla/xui-input-edit 0.176.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/README.md +164 -0
- package/native/index.d.mts +117 -0
- package/native/index.d.ts +117 -0
- package/native/index.js +952 -0
- package/native/index.js.map +1 -0
- package/native/index.mjs +930 -0
- package/native/index.mjs.map +1 -0
- package/package.json +59 -0
- package/web/index.d.mts +117 -0
- package/web/index.d.ts +117 -0
- package/web/index.js +973 -0
- package/web/index.js.map +1 -0
- package/web/index.mjs +944 -0
- package/web/index.mjs.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Input Edit
|
|
2
|
+
|
|
3
|
+
A multi-line text field for editing freeform text — comments, messages, notes — with inline (click-to-edit) and form (always-editable) modes; the field grows with its content. Works on web and React Native via `XUIProvider` themed contexts.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @xsolla/xui-input-edit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Imports
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { InputEdit } from '@xsolla/xui-input-edit';
|
|
15
|
+
import type { InputEditProps } from '@xsolla/xui-input-edit';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
const [value, setValue] = useState('Input text');
|
|
22
|
+
|
|
23
|
+
<InputEdit
|
|
24
|
+
value={value}
|
|
25
|
+
onChangeText={setValue}
|
|
26
|
+
onConfirm={(v) => save(v)}
|
|
27
|
+
/>;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## API Reference
|
|
31
|
+
|
|
32
|
+
### `<InputEdit>`
|
|
33
|
+
|
|
34
|
+
| Prop | Type | Default | Description |
|
|
35
|
+
| --- | --- | --- | --- |
|
|
36
|
+
| `value` | `string` | — | Controlled value. |
|
|
37
|
+
| `defaultValue` | `string` | `''` | Initial value for uncontrolled usage. |
|
|
38
|
+
| `placeholder` | `string` | `'Placeholder'` | Placeholder shown when empty. |
|
|
39
|
+
| `onChangeText` | `(text: string) => void` | — | Change handler receiving the current text (HTML when `richText` is set). |
|
|
40
|
+
| `onConfirm` | `(value: string) => void` | — | Called on confirm — check button, `Cmd`/`Ctrl` + `Enter`, or commit-on-blur (only when the value changed). |
|
|
41
|
+
| `onCancel` | `() => void` | — | Called on cancel (cross button or `Escape`); reverts to the last committed value. |
|
|
42
|
+
| `mode` | `'inline' \| 'form'` | `'inline'` | `inline` rests as plain text and reveals the editor on click; `form` is always an open, editable field. |
|
|
43
|
+
| `editControl` | `boolean` | `true` | Show the floating confirm / cancel control while active. |
|
|
44
|
+
| `icon` | `boolean` | `true` | Show the edit affordance icon in the inline display state. |
|
|
45
|
+
| `editIcon` | `ReactNode` | `<Edit />` | Override the default edit (pencil) icon. |
|
|
46
|
+
| `tools` | `ReactNode` | — | Formatting toolbar (e.g. `IconButton`s) shown in a floating bar below the field while active. |
|
|
47
|
+
| `rows` | `number` | `1` | Minimum number of visible text rows while editing. |
|
|
48
|
+
| `textStyle` | `'h1'…'h5' \| 'display' \| 'body-lg'…'body-xxs'` | — | Named typography style; overrides the default font. |
|
|
49
|
+
| `richText` | `boolean` | `false` | Enable rich-text editing (web only); the value becomes HTML. Wire a toolbar via `tools` + `document.execCommand`. Falls back to a plain field on native. |
|
|
50
|
+
| `disabled` | `boolean` | `false` | Disable the control. |
|
|
51
|
+
| `error` | `boolean` | `false` | Apply error styling. |
|
|
52
|
+
| `errorMessage` | `string` | — | Error message; also marks the field invalid. |
|
|
53
|
+
| `id` | `string` | `auto` | HTML id for the field. |
|
|
54
|
+
| `aria-label` | `string` | — | Accessible label when no visible label is present. |
|
|
55
|
+
| `aria-labelledby` | `string` | — | ID of an external element that labels the field. |
|
|
56
|
+
| `testID` | `string` | — | Test identifier. |
|
|
57
|
+
|
|
58
|
+
`InputEdit` also accepts the standard `TextareaHTMLAttributes`, except `size`, `value`, and `defaultValue` (redefined above) and `onChange` (use `onChangeText`).
|
|
59
|
+
|
|
60
|
+
Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).
|
|
61
|
+
|
|
62
|
+
## Examples
|
|
63
|
+
|
|
64
|
+
### Inline editing
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
const [value, setValue] = useState('Input text');
|
|
68
|
+
|
|
69
|
+
<InputEdit value={value} onChangeText={setValue} onConfirm={(v) => save(v)} />;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Form mode
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
<InputEdit
|
|
76
|
+
mode="form"
|
|
77
|
+
placeholder="Add a description…"
|
|
78
|
+
onConfirm={(v) => save(v)}
|
|
79
|
+
/>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Text styles
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<InputEdit textStyle="h1" defaultValue="Editable heading" />
|
|
86
|
+
<InputEdit textStyle="body-lg" defaultValue="Body text" />
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Formatting tools
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { IconButton } from '@xsolla/xui-button';
|
|
93
|
+
import { BoldText, ItalicText, Link } from '@xsolla/xui-icons-base';
|
|
94
|
+
|
|
95
|
+
<InputEdit
|
|
96
|
+
defaultValue="Input text"
|
|
97
|
+
tools={
|
|
98
|
+
<>
|
|
99
|
+
<IconButton variant="tertiary" tone="mono" size="xs" icon={<BoldText />} aria-label="Bold" />
|
|
100
|
+
<IconButton variant="tertiary" tone="mono" size="xs" icon={<ItalicText />} aria-label="Italic" />
|
|
101
|
+
<IconButton variant="tertiary" tone="mono" size="xs" icon={<Link />} aria-label="Add link" />
|
|
102
|
+
</>
|
|
103
|
+
}
|
|
104
|
+
/>;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Rich text
|
|
108
|
+
|
|
109
|
+
Set `richText` to make the field a `contenteditable` surface whose value is HTML. The toolbar runs `document.execCommand` on the focused selection — vary it per product (alignment, bold/italic/underline, link, …). Web only; falls back to a plain field on native. The HTML is not sanitized — sanitize before rendering untrusted content.
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { IconButton } from '@xsolla/xui-button';
|
|
113
|
+
import {
|
|
114
|
+
TextAlignLeft, TextAlignCenter, TextAlignRight,
|
|
115
|
+
BoldText, ItalicText, UnderlineText, Link,
|
|
116
|
+
} from '@xsolla/xui-icons-base';
|
|
117
|
+
|
|
118
|
+
const [value, setValue] = useState('<b>Bold</b> text');
|
|
119
|
+
const exec = (command, arg) => () => document.execCommand(command, false, arg);
|
|
120
|
+
|
|
121
|
+
const formats = [
|
|
122
|
+
{ icon: <TextAlignLeft />, label: 'Align left', run: exec('justifyLeft') },
|
|
123
|
+
{ icon: <TextAlignCenter />, label: 'Align center', run: exec('justifyCenter') },
|
|
124
|
+
{ icon: <TextAlignRight />, label: 'Align right', run: exec('justifyRight') },
|
|
125
|
+
{ icon: <BoldText />, label: 'Bold', run: exec('bold') },
|
|
126
|
+
{ icon: <ItalicText />, label: 'Italic', run: exec('italic') },
|
|
127
|
+
{ icon: <UnderlineText />, label: 'Underline', run: exec('underline') },
|
|
128
|
+
{ icon: <Link />, label: 'Add link', run: () => {
|
|
129
|
+
const url = window.prompt('Link URL');
|
|
130
|
+
if (url) document.execCommand('createLink', false, url);
|
|
131
|
+
} },
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
<InputEdit
|
|
135
|
+
richText
|
|
136
|
+
value={value}
|
|
137
|
+
onChangeText={setValue}
|
|
138
|
+
tools={formats.map((f) => (
|
|
139
|
+
<IconButton key={f.label} variant="tertiary" tone="mono" size="xs"
|
|
140
|
+
icon={f.icon} aria-label={f.label} onPress={f.run} />
|
|
141
|
+
))}
|
|
142
|
+
/>;
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Error state
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
const [value, setValue] = useState('Input text');
|
|
149
|
+
|
|
150
|
+
<InputEdit
|
|
151
|
+
value={value}
|
|
152
|
+
onChangeText={setValue}
|
|
153
|
+
errorMessage="This field is required"
|
|
154
|
+
/>;
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Accessibility
|
|
158
|
+
|
|
159
|
+
- Renders a `<textarea>` (or a `contenteditable` `role="textbox"` when `richText` is set); provide a label via `aria-label` or `aria-labelledby` (there is no visible label slot).
|
|
160
|
+
- Error messages are linked through `aria-describedby` and sit in a polite live region (`aria-live="polite"`).
|
|
161
|
+
- `aria-invalid` and `aria-disabled` reflect error and disabled state.
|
|
162
|
+
- The decorative edit icon is `aria-hidden`; the confirm / cancel buttons expose `aria-label`s.
|
|
163
|
+
- Keyboard: `Tab` to focus, `Escape` cancels, `Cmd`/`Ctrl` + `Enter` commits, `Enter` inserts a new line.
|
|
164
|
+
- Rich-text mode is web only and the stored HTML is not sanitized — sanitize before rendering untrusted values.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { TextareaHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { ThemeOverrideProps } from '@xsolla/xui-core';
|
|
3
|
+
|
|
4
|
+
type InputEditMode = "inline" | "form";
|
|
5
|
+
/**
|
|
6
|
+
* Named typography styles, resolved from the theme's typography tokens. Drives the
|
|
7
|
+
* field's font family / size / weight / line-height so the text can match surrounding
|
|
8
|
+
* content (e.g. a heading) rather than only the fixed body sizes.
|
|
9
|
+
*/
|
|
10
|
+
type InputEditTextStyle = "h1" | "h2" | "h3" | "h4" | "h5" | "display" | "body-lg" | "body-md" | "body-sm" | "body-xs" | "body-xxs";
|
|
11
|
+
interface InputEditProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "size" | "onChange" | "value" | "defaultValue">, ThemeOverrideProps {
|
|
12
|
+
/**
|
|
13
|
+
* Controlled value of the field.
|
|
14
|
+
*/
|
|
15
|
+
value?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Initial value for uncontrolled usage.
|
|
18
|
+
*/
|
|
19
|
+
defaultValue?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Placeholder shown when the field is empty.
|
|
22
|
+
*/
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Change handler receiving the current text (HTML when `richText` is set).
|
|
26
|
+
*/
|
|
27
|
+
onChangeText?: (text: string) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Called when the edit is confirmed (check button or `Cmd`/`Ctrl` + `Enter`).
|
|
30
|
+
*/
|
|
31
|
+
onConfirm?: (value: string) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Called when the edit is cancelled (cross button or `Escape`). The value is reverted.
|
|
34
|
+
*/
|
|
35
|
+
onCancel?: () => void;
|
|
36
|
+
/**
|
|
37
|
+
* Minimum number of visible text rows while editing.
|
|
38
|
+
*/
|
|
39
|
+
rows?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Show the floating confirm / cancel control while editing.
|
|
42
|
+
*/
|
|
43
|
+
editControl?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Show the edit affordance icon in display mode.
|
|
46
|
+
*/
|
|
47
|
+
icon?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Override the default edit (pencil) icon.
|
|
50
|
+
*/
|
|
51
|
+
editIcon?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* Optional formatting toolbar shown in a floating bar below the field while editing
|
|
54
|
+
* (e.g. text-align / bold / link `IconButton`s). Rendered to the left, mirroring the
|
|
55
|
+
* confirm / cancel control on the right.
|
|
56
|
+
*/
|
|
57
|
+
tools?: ReactNode;
|
|
58
|
+
/**
|
|
59
|
+
* Activation mode. `"inline"` (default) rests as plain text and reveals the editor on
|
|
60
|
+
* click; `"form"` is always an open, editable field (like a standard form text area).
|
|
61
|
+
*/
|
|
62
|
+
mode?: InputEditMode;
|
|
63
|
+
/**
|
|
64
|
+
* Named typography style applied to the text and editor (e.g. `"h1"`, `"body-lg"`).
|
|
65
|
+
* Overrides the default font.
|
|
66
|
+
*/
|
|
67
|
+
textStyle?: InputEditTextStyle;
|
|
68
|
+
/**
|
|
69
|
+
* Enable rich-text editing (web only). The field becomes a `contenteditable` surface
|
|
70
|
+
* and `value` / `onChangeText` / `onConfirm` carry HTML. Wire a toolbar via `tools`
|
|
71
|
+
* using `document.execCommand`. Falls back to a plain text field on React Native.
|
|
72
|
+
* The HTML is not sanitized — sanitize before rendering untrusted values.
|
|
73
|
+
*/
|
|
74
|
+
richText?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Disable the control.
|
|
77
|
+
*/
|
|
78
|
+
disabled?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Highlight the control as invalid.
|
|
81
|
+
*/
|
|
82
|
+
error?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Error message displayed below the field (implies `error`).
|
|
85
|
+
*/
|
|
86
|
+
errorMessage?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Unique identifier for the input element. Used for accessibility linking.
|
|
89
|
+
*/
|
|
90
|
+
id?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Accessible label for screen readers (use when no visible label is present).
|
|
93
|
+
*/
|
|
94
|
+
"aria-label"?: string;
|
|
95
|
+
/**
|
|
96
|
+
* ID of an external element that labels the field (e.g. a heading placed above it).
|
|
97
|
+
*/
|
|
98
|
+
"aria-labelledby"?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Test identifier.
|
|
101
|
+
*/
|
|
102
|
+
testID?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Optional class name applied to the root element (web).
|
|
105
|
+
*/
|
|
106
|
+
className?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A multi-line text field for editing freeform text. In `"inline"` mode it rests as
|
|
110
|
+
* plain text and reveals a highlighted editor on click; in `"form"` mode it is always
|
|
111
|
+
* an open, editable field. `Enter` inserts a new line; confirm via the floating control
|
|
112
|
+
* or `Cmd`/`Ctrl` + `Enter`; clicking outside also commits. `Escape` cancels and
|
|
113
|
+
* reverts.
|
|
114
|
+
*/
|
|
115
|
+
declare const InputEdit: React.ForwardRefExoticComponent<InputEditProps & React.RefAttributes<HTMLTextAreaElement | HTMLDivElement>>;
|
|
116
|
+
|
|
117
|
+
export { InputEdit, type InputEditProps };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { TextareaHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { ThemeOverrideProps } from '@xsolla/xui-core';
|
|
3
|
+
|
|
4
|
+
type InputEditMode = "inline" | "form";
|
|
5
|
+
/**
|
|
6
|
+
* Named typography styles, resolved from the theme's typography tokens. Drives the
|
|
7
|
+
* field's font family / size / weight / line-height so the text can match surrounding
|
|
8
|
+
* content (e.g. a heading) rather than only the fixed body sizes.
|
|
9
|
+
*/
|
|
10
|
+
type InputEditTextStyle = "h1" | "h2" | "h3" | "h4" | "h5" | "display" | "body-lg" | "body-md" | "body-sm" | "body-xs" | "body-xxs";
|
|
11
|
+
interface InputEditProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "size" | "onChange" | "value" | "defaultValue">, ThemeOverrideProps {
|
|
12
|
+
/**
|
|
13
|
+
* Controlled value of the field.
|
|
14
|
+
*/
|
|
15
|
+
value?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Initial value for uncontrolled usage.
|
|
18
|
+
*/
|
|
19
|
+
defaultValue?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Placeholder shown when the field is empty.
|
|
22
|
+
*/
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Change handler receiving the current text (HTML when `richText` is set).
|
|
26
|
+
*/
|
|
27
|
+
onChangeText?: (text: string) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Called when the edit is confirmed (check button or `Cmd`/`Ctrl` + `Enter`).
|
|
30
|
+
*/
|
|
31
|
+
onConfirm?: (value: string) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Called when the edit is cancelled (cross button or `Escape`). The value is reverted.
|
|
34
|
+
*/
|
|
35
|
+
onCancel?: () => void;
|
|
36
|
+
/**
|
|
37
|
+
* Minimum number of visible text rows while editing.
|
|
38
|
+
*/
|
|
39
|
+
rows?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Show the floating confirm / cancel control while editing.
|
|
42
|
+
*/
|
|
43
|
+
editControl?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Show the edit affordance icon in display mode.
|
|
46
|
+
*/
|
|
47
|
+
icon?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Override the default edit (pencil) icon.
|
|
50
|
+
*/
|
|
51
|
+
editIcon?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* Optional formatting toolbar shown in a floating bar below the field while editing
|
|
54
|
+
* (e.g. text-align / bold / link `IconButton`s). Rendered to the left, mirroring the
|
|
55
|
+
* confirm / cancel control on the right.
|
|
56
|
+
*/
|
|
57
|
+
tools?: ReactNode;
|
|
58
|
+
/**
|
|
59
|
+
* Activation mode. `"inline"` (default) rests as plain text and reveals the editor on
|
|
60
|
+
* click; `"form"` is always an open, editable field (like a standard form text area).
|
|
61
|
+
*/
|
|
62
|
+
mode?: InputEditMode;
|
|
63
|
+
/**
|
|
64
|
+
* Named typography style applied to the text and editor (e.g. `"h1"`, `"body-lg"`).
|
|
65
|
+
* Overrides the default font.
|
|
66
|
+
*/
|
|
67
|
+
textStyle?: InputEditTextStyle;
|
|
68
|
+
/**
|
|
69
|
+
* Enable rich-text editing (web only). The field becomes a `contenteditable` surface
|
|
70
|
+
* and `value` / `onChangeText` / `onConfirm` carry HTML. Wire a toolbar via `tools`
|
|
71
|
+
* using `document.execCommand`. Falls back to a plain text field on React Native.
|
|
72
|
+
* The HTML is not sanitized — sanitize before rendering untrusted values.
|
|
73
|
+
*/
|
|
74
|
+
richText?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Disable the control.
|
|
77
|
+
*/
|
|
78
|
+
disabled?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Highlight the control as invalid.
|
|
81
|
+
*/
|
|
82
|
+
error?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Error message displayed below the field (implies `error`).
|
|
85
|
+
*/
|
|
86
|
+
errorMessage?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Unique identifier for the input element. Used for accessibility linking.
|
|
89
|
+
*/
|
|
90
|
+
id?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Accessible label for screen readers (use when no visible label is present).
|
|
93
|
+
*/
|
|
94
|
+
"aria-label"?: string;
|
|
95
|
+
/**
|
|
96
|
+
* ID of an external element that labels the field (e.g. a heading placed above it).
|
|
97
|
+
*/
|
|
98
|
+
"aria-labelledby"?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Test identifier.
|
|
101
|
+
*/
|
|
102
|
+
testID?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Optional class name applied to the root element (web).
|
|
105
|
+
*/
|
|
106
|
+
className?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A multi-line text field for editing freeform text. In `"inline"` mode it rests as
|
|
110
|
+
* plain text and reveals a highlighted editor on click; in `"form"` mode it is always
|
|
111
|
+
* an open, editable field. `Enter` inserts a new line; confirm via the floating control
|
|
112
|
+
* or `Cmd`/`Ctrl` + `Enter`; clicking outside also commits. `Escape` cancels and
|
|
113
|
+
* reverts.
|
|
114
|
+
*/
|
|
115
|
+
declare const InputEdit: React.ForwardRefExoticComponent<InputEditProps & React.RefAttributes<HTMLTextAreaElement | HTMLDivElement>>;
|
|
116
|
+
|
|
117
|
+
export { InputEdit, type InputEditProps };
|