@herca/rn-kit 0.1.26 → 0.1.27

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.
Files changed (54) hide show
  1. package/lib/module/BottomSheet/BottomSheet.js +4 -1
  2. package/lib/module/BottomSheet/BottomSheet.js.map +1 -1
  3. package/lib/module/Button/tests/Button.test.js +70 -0
  4. package/lib/module/Button/tests/Button.test.js.map +1 -0
  5. package/lib/module/Button/type.js +1 -1
  6. package/lib/module/DatePicker/TimePicker.js +84 -39
  7. package/lib/module/DatePicker/TimePicker.js.map +1 -1
  8. package/lib/module/Icon/Icon.js +7 -1
  9. package/lib/module/Icon/Icon.js.map +1 -1
  10. package/lib/module/Icon/README.md +6 -0
  11. package/lib/module/Icon/icons/Bold.js +25 -0
  12. package/lib/module/Icon/icons/Bold.js.map +1 -0
  13. package/lib/module/Icon/icons/Italic.js +25 -0
  14. package/lib/module/Icon/icons/Italic.js.map +1 -0
  15. package/lib/module/Icon/icons/UnderLine.js +25 -0
  16. package/lib/module/Icon/icons/UnderLine.js.map +1 -0
  17. package/lib/module/Input/InputOtp.js +7 -5
  18. package/lib/module/Input/InputOtp.js.map +1 -1
  19. package/lib/module/Input/README_INPUT_OTP.md +8 -7
  20. package/lib/module/TextEditor/TextEditor.js +206 -4
  21. package/lib/module/TextEditor/TextEditor.js.map +1 -1
  22. package/lib/typescript/src/Button/tests/Button.test.d.ts +2 -0
  23. package/lib/typescript/src/Button/tests/Button.test.d.ts.map +1 -0
  24. package/lib/typescript/src/DatePicker/TimePicker.d.ts.map +1 -1
  25. package/lib/typescript/src/Icon/Icon.d.ts.map +1 -1
  26. package/lib/typescript/src/Icon/icons/Bold.d.ts +9 -0
  27. package/lib/typescript/src/Icon/icons/Bold.d.ts.map +1 -0
  28. package/lib/typescript/src/Icon/icons/Italic.d.ts +9 -0
  29. package/lib/typescript/src/Icon/icons/Italic.d.ts.map +1 -0
  30. package/lib/typescript/src/Icon/icons/UnderLine.d.ts +9 -0
  31. package/lib/typescript/src/Icon/icons/UnderLine.d.ts.map +1 -0
  32. package/lib/typescript/src/Icon/type.d.ts +1 -1
  33. package/lib/typescript/src/Icon/type.d.ts.map +1 -1
  34. package/lib/typescript/src/Input/InputOtp.d.ts +1 -0
  35. package/lib/typescript/src/Input/InputOtp.d.ts.map +1 -1
  36. package/lib/typescript/src/TextEditor/TextEditor.d.ts +2 -1
  37. package/lib/typescript/src/TextEditor/TextEditor.d.ts.map +1 -1
  38. package/lib/typescript/src/TextEditor/types.d.ts +7 -0
  39. package/lib/typescript/src/TextEditor/types.d.ts.map +1 -1
  40. package/package.json +2 -2
  41. package/src/BottomSheet/BottomSheet.tsx +4 -1
  42. package/src/Button/tests/Button.test.tsx +49 -0
  43. package/src/Button/type.tsx +1 -1
  44. package/src/DatePicker/TimePicker.tsx +116 -52
  45. package/src/Icon/Icon.tsx +6 -0
  46. package/src/Icon/README.md +6 -0
  47. package/src/Icon/icons/Bold.tsx +22 -0
  48. package/src/Icon/icons/Italic.tsx +22 -0
  49. package/src/Icon/icons/UnderLine.tsx +26 -0
  50. package/src/Icon/type.tsx +3 -0
  51. package/src/Input/InputOtp.tsx +7 -3
  52. package/src/Input/README_INPUT_OTP.md +8 -7
  53. package/src/TextEditor/TextEditor.tsx +222 -3
  54. package/src/TextEditor/types.tsx +7 -0
@@ -1,5 +1,224 @@
1
- import { View } from 'react-native';
1
+ import { useRef, useState, useEffect } from 'react';
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+ Platform,
6
+ KeyboardAvoidingView,
7
+ TouchableOpacity,
8
+ Keyboard,
9
+ type KeyboardEvent,
10
+ } from 'react-native';
11
+ import { WebView } from 'react-native-webview';
12
+ import Color from '../Color/Color';
13
+ import type { TextEditorType } from './types';
14
+ import LabelForm from '../LabelForm/LabelForm';
15
+ import Typography from '../Typography/Typography';
16
+ import Icon from '../Icon';
2
17
 
3
- export default function TextEditor() {
4
- return <View></View>;
18
+ export default function TextEditor({
19
+ label,
20
+ hasError,
21
+ hint,
22
+ onChange,
23
+ height = 300,
24
+ }: TextEditorType) {
25
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
26
+ const [showToolbar, setShowToolbar] = useState<boolean>(false);
27
+ const webviewRef = useRef<WebView>(null);
28
+ const handleOnChange = (data: string) => {
29
+ onChange?.(data);
30
+ };
31
+
32
+ const htmlEditor = `
33
+ <html>
34
+ <head>
35
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
36
+ <style>
37
+ *,
38
+ *::before,
39
+ *::after {
40
+ box-sizing: border-box;
41
+ }
42
+ html, body {
43
+ margin: 0;
44
+ padding: 0;
45
+ width: 99.9%;
46
+ height: 100%;
47
+ overflow-x: hidden;
48
+ }
49
+ #editor {
50
+ min-height: ${height};
51
+ width: 100%;
52
+ max-width: 100%;
53
+ padding: 12px;
54
+ font-size: 12px;
55
+ border: 1px solid ${Color.gray[200]};
56
+ border-radius: 6px;
57
+ outline: none;
58
+ transition: border 0.2s;
59
+ overflow-wrap: break-word;
60
+ font-weight: 500;
61
+ word-wrap: break-word;
62
+ word-break: break-word;
63
+ }
64
+ #editor.focused {
65
+ border-color: ${Color.gray[200]}
66
+ }
67
+ </style>
68
+ </head>
69
+ <body>
70
+ <div id="editor" contenteditable="true">Tulis sesuatu di sini...</div>
71
+ <script>
72
+ const editor = document.getElementById("editor");
73
+
74
+ editor.addEventListener("focus", () => {
75
+ editor.classList.add("focused");
76
+ });
77
+
78
+ editor.addEventListener("blur", () => {
79
+ editor.classList.remove("focused");
80
+ });
81
+ const handleMessage = (command) => {
82
+ if (command === 'getContent') {
83
+ window.ReactNativeWebView.postMessage(editor.innerHTML);
84
+ } else {
85
+ editor.focus();
86
+ document.execCommand(command, false, '');
87
+ }
88
+ };
89
+
90
+ // ini untuk Android
91
+ document.addEventListener("message", (event) => {
92
+ handleMessage(event.data);
93
+ });
94
+
95
+ // ini untuk iOS
96
+ window.addEventListener("message", (event) => {
97
+ handleMessage(event.data);
98
+ });
99
+ </script>
100
+ </body>
101
+ </html>
102
+ `;
103
+
104
+ const formatText = (command: string) => {
105
+ if (webviewRef.current) {
106
+ if (Platform.OS === 'ios') {
107
+ webviewRef.current.injectJavaScript(`
108
+ document.execCommand('${command}', false, '');
109
+ true; //di ios harus biar berfunsi boldnya
110
+ `);
111
+ } else {
112
+ webviewRef.current.postMessage(command);
113
+ }
114
+ }
115
+ };
116
+
117
+ useEffect(() => {
118
+ const showSub = Keyboard.addListener(
119
+ 'keyboardDidShow',
120
+ (e: KeyboardEvent) => {
121
+ if (Platform.OS === 'ios')
122
+ setKeyboardHeight(e.endCoordinates.height - 35);
123
+ setShowToolbar(true);
124
+ }
125
+ );
126
+ const hideSub = Keyboard.addListener('keyboardDidHide', () => {
127
+ if (Platform.OS === 'ios') setKeyboardHeight(0);
128
+ setShowToolbar(false);
129
+ });
130
+
131
+ return () => {
132
+ showSub.remove();
133
+ hideSub.remove();
134
+ };
135
+ }, []);
136
+
137
+ return (
138
+ <View style={styles.container}>
139
+ {label ? <LabelForm title={label} /> : null}
140
+ <KeyboardAvoidingView
141
+ style={(styles.editorWrapper, [{ minHeight: height }])}
142
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}
143
+ >
144
+ <WebView
145
+ ref={webviewRef}
146
+ originWhitelist={['*']}
147
+ source={{ html: htmlEditor }}
148
+ onMessage={(event) => {
149
+ handleOnChange(event.nativeEvent.data);
150
+ }}
151
+ style={styles.webview}
152
+ javaScriptEnabled={true}
153
+ domStorageEnabled={true}
154
+ mixedContentMode="always"
155
+ allowUniversalAccessFromFileURLs={true}
156
+ allowFileAccess={true}
157
+ />
158
+ </KeyboardAvoidingView>
159
+
160
+ {hint ? (
161
+ <Typography
162
+ color={hasError ? Color.danger[500] : Color.gray[700]}
163
+ variant="t3"
164
+ weight="medium"
165
+ >
166
+ {hint}
167
+ </Typography>
168
+ ) : null}
169
+
170
+ {showToolbar ? (
171
+ <View style={[styles.toolbar, { bottom: keyboardHeight }]}>
172
+ <TouchableOpacity
173
+ onPress={() => formatText('bold')}
174
+ style={styles.toolButton}
175
+ >
176
+ <Icon name="Bold" />
177
+ </TouchableOpacity>
178
+ <TouchableOpacity
179
+ onPress={() => formatText('italic')}
180
+ style={styles.toolButton}
181
+ >
182
+ <Icon name="Italic" />
183
+ </TouchableOpacity>
184
+ <TouchableOpacity
185
+ onPress={() => formatText('underline')}
186
+ style={styles.toolButton}
187
+ >
188
+ <Icon name="UnderLine" />
189
+ </TouchableOpacity>
190
+ </View>
191
+ ) : null}
192
+ </View>
193
+ );
5
194
  }
195
+
196
+ const styles = StyleSheet.create({
197
+ container: {
198
+ backgroundColor: '#fff',
199
+ gap: 4,
200
+ position: 'relative',
201
+ },
202
+ editorWrapper: { flex: 1 },
203
+ toolbar: {
204
+ flexDirection: 'row',
205
+ borderTopWidth: 1,
206
+ borderColor: Color.gray[200],
207
+ paddingVertical: 8,
208
+ paddingHorizontal: 10,
209
+ position: 'absolute',
210
+ bottom: 0,
211
+ left: 0,
212
+ width: '100%',
213
+ alignItems: 'center',
214
+ },
215
+ toolButton: {
216
+ padding: 8,
217
+ marginHorizontal: 4,
218
+ borderRadius: 6,
219
+ },
220
+ toolText: {
221
+ fontWeight: 'bold',
222
+ },
223
+ webview: { flex: 1 },
224
+ });
@@ -0,0 +1,7 @@
1
+ export interface TextEditorType {
2
+ label?: string;
3
+ hint?: string;
4
+ hasError?: boolean;
5
+ onChange?: (data: string) => void;
6
+ height?: number;
7
+ }