Skip to content

Commit d171e1f

Browse files
authored
feat(web): add basic setup (#491)
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please follow the template so that the reviewers can easily understand what the code changes affect --> # Summary Explain the **motivation** for making this change: here are some points to help you: - Basic web setup for the library and example app, intended for internal use only for now - **Currently the code related to web is not exported to npm** - Added web entry + minimal web `EnrichedTextInput` stub to unblock web integration work. - Wired example-web app to the `EnrichedTextInput` for the web - `index.tsx` entrypoint for android/ios, `index.web.tsx` entrypoint for the web ## Test Plan - Run example-web app, make sure a textarea with "Hello from web!" appears. - Check that example app still builds for android and ios ## Screenshots / Videos <img width="679" height="271" alt="image" src="https://github.com/user-attachments/assets/338f075e-6816-470d-b1ef-4e4a48b4817e" /> ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | Android | ✅ | | Web | ✅ |
1 parent 762c5af commit d171e1f

9 files changed

Lines changed: 387 additions & 137 deletions

File tree

apps/example-web/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import './App.css';
2+
import { EnrichedTextInput } from 'react-native-enriched';
23

34
function App() {
45
return (
56
<div className="container">
6-
<div>Text input</div>
7-
<input type="text" placeholder="Type something..." />
7+
<EnrichedTextInput defaultValue="Hello from web!" />
88
</div>
99
);
1010
}

apps/example-web/tsconfig.app.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
"compilerOptions": {
44
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
55
"types": ["vite/client"],
6+
"baseUrl": ".",
7+
"paths": {
8+
"react-native-enriched": ["../../src/index.web.tsx"]
9+
}
610
},
711
"include": ["src"]
812
}

apps/example-web/vite.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1+
import path from 'node:path';
12
import { defineConfig } from 'vite';
23
import react from '@vitejs/plugin-react';
34

45
// https://vite.dev/config/
56
export default defineConfig({
67
plugins: [react()],
8+
resolve: {
9+
alias: {
10+
'react-native-enriched': path.resolve(
11+
__dirname,
12+
'../../src/index.web.tsx'
13+
),
14+
},
15+
},
716
});

package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
"source": "./src/index.tsx",
66
"main": "./lib/module/index.js",
77
"types": "./lib/typescript/src/index.d.ts",
8+
"sideEffects": false,
89
"exports": {
910
".": {
11+
"react-native": {
12+
"types": "./lib/typescript/src/index.d.ts",
13+
"default": "./lib/module/index.js"
14+
},
1015
"types": "./lib/typescript/src/index.d.ts",
1116
"default": "./lib/module/index.js"
1217
},
@@ -20,6 +25,14 @@
2025
"cpp",
2126
"*.podspec",
2227
"react-native.config.js",
28+
"!src/web",
29+
"!src/index.web.tsx",
30+
"!lib/module/web",
31+
"!lib/module/index.web.js",
32+
"!lib/module/index.web.js.map",
33+
"!lib/typescript/src/web",
34+
"!lib/typescript/src/index.web.d.ts",
35+
"!lib/typescript/src/index.web.d.ts.map",
2336
"!ios/build",
2437
"!android/build",
2538
"!android/gradle",

src/index.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
export * from './EnrichedTextInput';
1+
export { EnrichedTextInput } from './native/EnrichedTextInput';
22
export type {
3+
EnrichedTextInputProps,
34
OnChangeTextEvent,
45
OnChangeHtmlEvent,
56
OnChangeStateEvent,
@@ -9,5 +10,11 @@ export type {
910
OnKeyPressEvent,
1011
OnPasteImagesEvent,
1112
OnSubmitEditing,
12-
} from './spec/EnrichedTextInputNativeComponent';
13-
export type { HtmlStyle, MentionStyleProperties } from './types';
13+
HtmlStyle,
14+
MentionStyleProperties,
15+
FocusEvent,
16+
BlurEvent,
17+
EnrichedTextInputInstance,
18+
ContextMenuItem,
19+
OnChangeMentionEvent,
20+
} from './types';

src/index.web.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export { EnrichedTextInput } from './web/EnrichedTextInput';
2+
export type {
3+
EnrichedTextInputProps,
4+
OnChangeTextEvent,
5+
OnChangeHtmlEvent,
6+
OnChangeStateEvent,
7+
OnLinkDetected,
8+
OnMentionDetected,
9+
OnChangeSelectionEvent,
10+
OnKeyPressEvent,
11+
OnPasteImagesEvent,
12+
OnSubmitEditing,
13+
HtmlStyle,
14+
MentionStyleProperties,
15+
FocusEvent,
16+
BlurEvent,
17+
EnrichedTextInputInstance,
18+
ContextMenuItem,
19+
OnChangeMentionEvent,
20+
} from './types';
Lines changed: 15 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
type Component,
3-
type RefObject,
43
useEffect,
54
useImperativeHandle,
65
useMemo,
@@ -10,147 +9,28 @@ import { useCallback } from 'react';
109
import EnrichedTextInputNativeComponent, {
1110
Commands,
1211
type NativeProps,
13-
type OnChangeHtmlEvent,
14-
type OnChangeSelectionEvent,
15-
type OnChangeStateEvent,
16-
type OnChangeTextEvent,
1712
type OnContextMenuItemPressEvent,
18-
type OnLinkDetected,
1913
type OnMentionEvent,
20-
type OnMentionDetected,
2114
type OnMentionDetectedInternal,
2215
type OnRequestHtmlResultEvent,
23-
type OnKeyPressEvent,
24-
type OnPasteImagesEvent,
25-
type OnSubmitEditing,
26-
} from './spec/EnrichedTextInputNativeComponent';
16+
} from '../spec/EnrichedTextInputNativeComponent';
2717
import type {
28-
ColorValue,
2918
HostInstance,
3019
MeasureInWindowOnSuccessCallback,
3120
MeasureLayoutOnSuccessCallback,
3221
MeasureOnSuccessCallback,
3322
NativeMethods,
3423
NativeSyntheticEvent,
35-
ReturnKeyTypeOptions,
36-
TargetedEvent,
37-
TextStyle,
38-
ViewProps,
39-
ViewStyle,
4024
} from 'react-native';
41-
import { normalizeHtmlStyle } from './utils/normalizeHtmlStyle';
42-
import { toNativeRegexConfig } from './utils/regexParser';
43-
import { nullthrows } from './utils/nullthrows';
44-
import type { HtmlStyle } from './types';
45-
46-
export type FocusEvent = NativeSyntheticEvent<TargetedEvent>;
47-
export type BlurEvent = NativeSyntheticEvent<TargetedEvent>;
48-
49-
export interface EnrichedTextInputInstance extends NativeMethods {
50-
// General commands
51-
focus: () => void;
52-
blur: () => void;
53-
setValue: (value: string) => void;
54-
setSelection: (start: number, end: number) => void;
55-
getHTML: () => Promise<string>;
56-
57-
// Text formatting commands
58-
toggleBold: () => void;
59-
toggleItalic: () => void;
60-
toggleUnderline: () => void;
61-
toggleStrikeThrough: () => void;
62-
toggleInlineCode: () => void;
63-
toggleH1: () => void;
64-
toggleH2: () => void;
65-
toggleH3: () => void;
66-
toggleH4: () => void;
67-
toggleH5: () => void;
68-
toggleH6: () => void;
69-
toggleCodeBlock: () => void;
70-
toggleBlockQuote: () => void;
71-
toggleOrderedList: () => void;
72-
toggleUnorderedList: () => void;
73-
toggleCheckboxList: (checked: boolean) => void;
74-
setLink: (start: number, end: number, text: string, url: string) => void;
75-
removeLink: (start: number, end: number) => void;
76-
setImage: (src: string, width: number, height: number) => void;
77-
startMention: (indicator: string) => void;
78-
setMention: (
79-
indicator: string,
80-
text: string,
81-
attributes?: Record<string, string>
82-
) => void;
83-
}
84-
85-
export interface ContextMenuItem {
86-
text: string;
87-
onPress: ({
88-
text,
89-
selection,
90-
styleState,
91-
}: {
92-
text: string;
93-
selection: { start: number; end: number };
94-
styleState: OnChangeStateEvent;
95-
}) => void;
96-
visible?: boolean;
97-
}
98-
99-
export interface OnChangeMentionEvent {
100-
indicator: string;
101-
text: string;
102-
}
103-
104-
export interface EnrichedTextInputProps extends Omit<ViewProps, 'children'> {
105-
ref?: RefObject<EnrichedTextInputInstance | null>;
106-
autoFocus?: boolean;
107-
editable?: boolean;
108-
mentionIndicators?: string[];
109-
defaultValue?: string;
110-
placeholder?: string;
111-
placeholderTextColor?: ColorValue;
112-
cursorColor?: ColorValue;
113-
selectionColor?: ColorValue;
114-
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
115-
htmlStyle?: HtmlStyle;
116-
style?: ViewStyle | TextStyle;
117-
scrollEnabled?: boolean;
118-
linkRegex?: RegExp | null;
119-
returnKeyType?: ReturnKeyTypeOptions;
120-
returnKeyLabel?: string;
121-
submitBehavior?: 'submit' | 'blurAndSubmit' | 'newline';
122-
onFocus?: (e: FocusEvent) => void;
123-
onBlur?: (e: BlurEvent) => void;
124-
onChangeText?: (e: NativeSyntheticEvent<OnChangeTextEvent>) => void;
125-
onChangeHtml?: (e: NativeSyntheticEvent<OnChangeHtmlEvent>) => void;
126-
onChangeState?: (e: NativeSyntheticEvent<OnChangeStateEvent>) => void;
127-
onLinkDetected?: (e: OnLinkDetected) => void;
128-
onMentionDetected?: (e: OnMentionDetected) => void;
129-
onStartMention?: (indicator: string) => void;
130-
onChangeMention?: (e: OnChangeMentionEvent) => void;
131-
onEndMention?: (indicator: string) => void;
132-
onChangeSelection?: (e: NativeSyntheticEvent<OnChangeSelectionEvent>) => void;
133-
onKeyPress?: (e: NativeSyntheticEvent<OnKeyPressEvent>) => void;
134-
onSubmitEditing?: (e: NativeSyntheticEvent<OnSubmitEditing>) => void;
135-
onPasteImages?: (e: NativeSyntheticEvent<OnPasteImagesEvent>) => void;
136-
contextMenuItems?: ContextMenuItem[];
137-
/**
138-
* If true, Android will use experimental synchronous events.
139-
* This will prevent from input flickering when updating component size.
140-
* However, this is an experimental feature, which has not been thoroughly tested.
141-
* We may decide to enable it by default in a future release.
142-
* Disabled by default.
143-
*/
144-
androidExperimentalSynchronousEvents?: boolean;
145-
/**
146-
* If true, external HTML (e.g. from Google Docs, Word, web pages) will be
147-
* normalized through the HTML normalizer before being applied.
148-
* This converts arbitrary HTML into the canonical tag subset that the enriched
149-
* parser understands.
150-
* Disabled by default.
151-
*/
152-
useHtmlNormalizer?: boolean;
153-
}
25+
import { normalizeHtmlStyle } from '../utils/normalizeHtmlStyle';
26+
import { toNativeRegexConfig } from '../utils/regexParser';
27+
import { nullthrows } from '../utils/nullthrows';
28+
import type {
29+
ContextMenuItem,
30+
EnrichedTextInputProps,
31+
OnLinkDetected,
32+
OnMentionDetected,
33+
} from '../types';
15434

15535
const warnMentionIndicators = (indicator: string) => {
15636
console.warn(
@@ -416,7 +296,11 @@ export const EnrichedTextInput = ({
416296
) => {
417297
const { text, indicator, payload } = e.nativeEvent;
418298
const attributes = JSON.parse(payload) as Record<string, string>;
419-
onMentionDetected?.({ text, indicator, attributes });
299+
onMentionDetected?.({
300+
text,
301+
indicator,
302+
attributes,
303+
} satisfies OnMentionDetected);
420304
};
421305

422306
const handleRequestHtmlResult = (

0 commit comments

Comments
 (0)