paint_box_react_native 0.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/LICENSE +20 -0
- package/PaintBoxReactNative.podspec +20 -0
- package/README.md +161 -0
- package/android/build.gradle +70 -0
- package/android/gradle.properties +45 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/paintboxreactnative/NativeViewHandler.kt +26 -0
- package/android/src/main/java/com/paintboxreactnative/PaintBoxNativeView.kt +61 -0
- package/android/src/main/java/com/paintboxreactnative/PaintBoxNativeViewManager.kt +105 -0
- package/android/src/main/java/com/paintboxreactnative/PaintBoxReactNativeModule.kt +74 -0
- package/android/src/main/java/com/paintboxreactnative/PaintBoxReactNativePackage.kt +41 -0
- package/android/src/main/java/com/paintboxreactnative/dto/ColorDTO.kt +37 -0
- package/ios/PaintBoxReactNative.h +5 -0
- package/ios/PaintBoxReactNative.mm +21 -0
- package/lib/module/IPaintEditor.js +7 -0
- package/lib/module/IPaintEditor.js.map +1 -0
- package/lib/module/NativePaintBoxReactNative.js +5 -0
- package/lib/module/NativePaintBoxReactNative.js.map +1 -0
- package/lib/module/PaintBoxContext.js +50 -0
- package/lib/module/PaintBoxContext.js.map +1 -0
- package/lib/module/PaintBoxRNView.js +90 -0
- package/lib/module/PaintBoxRNView.js.map +1 -0
- package/lib/module/PaintBoxViewNativeComponent.ts +63 -0
- package/lib/module/PaintEditor.js +121 -0
- package/lib/module/PaintEditor.js.map +1 -0
- package/lib/module/dto/BaseDTO.js +8 -0
- package/lib/module/dto/BaseDTO.js.map +1 -0
- package/lib/module/dto/ColorDTO.js +33 -0
- package/lib/module/dto/ColorDTO.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +2 -0
- package/lib/module/model/Color.js +12 -0
- package/lib/module/model/Color.js.map +1 -0
- package/lib/module/model/MimeType.js +12 -0
- package/lib/module/model/MimeType.js.map +1 -0
- package/lib/module/model/PaintMode.js +11 -0
- package/lib/module/model/PaintMode.js.map +1 -0
- package/lib/module/model/index.js +6 -0
- package/lib/module/model/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/IPaintEditor.d.ts +18 -0
- package/lib/typescript/src/IPaintEditor.d.ts.map +1 -0
- package/lib/typescript/src/NativePaintBoxReactNative.d.ts +11 -0
- package/lib/typescript/src/NativePaintBoxReactNative.d.ts.map +1 -0
- package/lib/typescript/src/PaintBoxContext.d.ts +18 -0
- package/lib/typescript/src/PaintBoxContext.d.ts.map +1 -0
- package/lib/typescript/src/PaintBoxRNView.d.ts +11 -0
- package/lib/typescript/src/PaintBoxRNView.d.ts.map +1 -0
- package/lib/typescript/src/PaintBoxViewNativeComponent.d.ts +24 -0
- package/lib/typescript/src/PaintBoxViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/PaintEditor.d.ts +22 -0
- package/lib/typescript/src/PaintEditor.d.ts.map +1 -0
- package/lib/typescript/src/dto/BaseDTO.d.ts +5 -0
- package/lib/typescript/src/dto/BaseDTO.d.ts.map +1 -0
- package/lib/typescript/src/dto/ColorDTO.d.ts +19 -0
- package/lib/typescript/src/dto/ColorDTO.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/model/Color.d.ts +8 -0
- package/lib/typescript/src/model/Color.d.ts.map +1 -0
- package/lib/typescript/src/model/MimeType.d.ts +9 -0
- package/lib/typescript/src/model/MimeType.d.ts.map +1 -0
- package/lib/typescript/src/model/PaintMode.d.ts +8 -0
- package/lib/typescript/src/model/PaintMode.d.ts.map +1 -0
- package/lib/typescript/src/model/index.d.ts +4 -0
- package/lib/typescript/src/model/index.d.ts.map +1 -0
- package/package.json +175 -0
- package/src/IPaintEditor.ts +32 -0
- package/src/NativePaintBoxReactNative.ts +20 -0
- package/src/PaintBoxContext.ts +67 -0
- package/src/PaintBoxRNView.tsx +98 -0
- package/src/PaintBoxViewNativeComponent.ts +63 -0
- package/src/PaintEditor.ts +152 -0
- package/src/dto/BaseDTO.ts +7 -0
- package/src/dto/ColorDTO.ts +44 -0
- package/src/index.ts +5 -0
- package/src/index.tsx +3 -0
- package/src/model/Color.ts +13 -0
- package/src/model/MimeType.ts +8 -0
- package/src/model/PaintMode.ts +7 -0
- package/src/model/index.ts +3 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sinem.avci
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "PaintBoxReactNative"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/sinemavci/paint_box_react_native.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
|
+
s.private_header_files = "ios/**/*.h"
|
|
18
|
+
|
|
19
|
+
install_modules_dependencies(s)
|
|
20
|
+
end
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# 🎨 paint_box_react_native
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="./assets/demo.gif" width="200" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
paint_box_react_native is a lightweight drawing component for React Native that allows users to draw on a canvas with customizable brush settings.
|
|
8
|
+
|
|
9
|
+
## ⚙️ Installation
|
|
10
|
+
```sh
|
|
11
|
+
npm install paint_box_react_native
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 🚀 Usage
|
|
15
|
+
```js
|
|
16
|
+
import { PaintBoxRNView, PaintEditor } from 'paint_box_react_native';
|
|
17
|
+
|
|
18
|
+
// ...
|
|
19
|
+
|
|
20
|
+
<PaintBoxRNView
|
|
21
|
+
paintEditor={paintEditor1}
|
|
22
|
+
onPaintBoxReady={async () => {
|
|
23
|
+
paintEditor1.isEnable().then((val) => {
|
|
24
|
+
if (val) {
|
|
25
|
+
paintEditor1.getStrokeSize().then((size) => {
|
|
26
|
+
setStrokeWidth1(size);
|
|
27
|
+
});
|
|
28
|
+
paintEditor1.getPaintMode().then((mode) => {
|
|
29
|
+
setPaintMode1(mode);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Api Reference
|
|
39
|
+
|
|
40
|
+
#### 🧠 Core Concepts
|
|
41
|
+
PaintEditor
|
|
42
|
+
|
|
43
|
+
PaintEditor is the controller object used to interact with a specific PaintBoxRNView instance.
|
|
44
|
+
|
|
45
|
+
Each PaintBoxRNView must have its own PaintEditor instance.
|
|
46
|
+
|
|
47
|
+
#### ⚠️ Multiple PaintBox Support
|
|
48
|
+
You can use multiple paint boxes independently:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
const paintEditor1 = new PaintEditor();
|
|
52
|
+
const paintEditor2 = new PaintEditor();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
<PaintBoxRNView paintEditor={paintEditor1} />
|
|
57
|
+
<PaintBoxRNView paintEditor={paintEditor2} />
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Undo
|
|
61
|
+
Reverts the last drawing action.
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
paintEditor.undo();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Redo
|
|
68
|
+
Restores the last undone action.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
paintEditor.redo();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Reset
|
|
75
|
+
Clears the entire canvas.
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
paintEditor.reset();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### Import
|
|
82
|
+
| Param | Type | Required | Description |
|
|
83
|
+
| ------ | ------ | -------- | --------------- |
|
|
84
|
+
| path | string | ✅ | Image file path |
|
|
85
|
+
| width | number | ❌ | Target width |
|
|
86
|
+
| height | number | ❌ | Target height |
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
import(path: string, width?: number, height?: number): Promise<void>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Export
|
|
93
|
+
```js
|
|
94
|
+
export(path: string, fileName: string, mimeType: MimeType): Promise<string>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
type MimeType = "jpeg" | "png" | "gif" | "tif" | "bmp" | "pdf";
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Param | Type | Required | Description |
|
|
102
|
+
| -------- | -------- | -------- | --------------------------- |
|
|
103
|
+
| path | string | ✅ | Output directory path |
|
|
104
|
+
| fileName | string | ✅ | File name without extension |
|
|
105
|
+
| mimeType | MimeType | ✅ | Output format |
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
#### Enable
|
|
109
|
+
```js
|
|
110
|
+
isEnable(): Promise<boolean>
|
|
111
|
+
setEnable(value: boolean): void
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
#### 🎨 Paint Mode
|
|
116
|
+
```js
|
|
117
|
+
enum PaintMode {
|
|
118
|
+
PEN = "Pen",
|
|
119
|
+
MARKER = "Marker",
|
|
120
|
+
BUCKET = "Bucket",
|
|
121
|
+
BRUSH = "Brush",
|
|
122
|
+
ERASER = "Eraser"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
getPaintMode(): Promise<PaintMode>
|
|
128
|
+
setPaintMode(mode: PaintMode): void
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### 🌈 Stroke Color
|
|
132
|
+
```js
|
|
133
|
+
getStrokeColor(): Promise<Color>
|
|
134
|
+
setStrokeColor(color: Color): void
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Color Definition
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
class Color {
|
|
141
|
+
constructor(r: number, g: number, b: number)
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Stroke Size
|
|
146
|
+
```js
|
|
147
|
+
getStrokeSize(): Promise<number>
|
|
148
|
+
setStrokeSize(size: number): void
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## ⚙️ Component Props
|
|
152
|
+
| Prop | Type | Required | Description |
|
|
153
|
+
| --------------- | ----------- | -------- | --------------------------- |
|
|
154
|
+
| paintEditor | PaintEditor | ✅ | Controller instance |
|
|
155
|
+
| onPaintBoxReady | () => void | ❌ | Called when canvas is ready |
|
|
156
|
+
|
|
157
|
+
## Best Practices
|
|
158
|
+
- Always create one PaintEditor per PaintBox
|
|
159
|
+
- Wait for onPaintBoxReady before calling methods
|
|
160
|
+
- Avoid very large stroke sizes (performance impact)
|
|
161
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.PaintBoxReactNative = [
|
|
3
|
+
kotlinVersion: "2.0.21",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36,
|
|
6
|
+
targetSdkVersion: 36
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
ext.getExtOrDefault = { prop ->
|
|
10
|
+
if (rootProject.ext.has(prop)) {
|
|
11
|
+
return rootProject.ext.get(prop)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return PaintBoxReactNative[prop]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
repositories {
|
|
18
|
+
google()
|
|
19
|
+
mavenCentral()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
dependencies {
|
|
23
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
24
|
+
// noinspection DifferentKotlinGradleVersion
|
|
25
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
apply plugin: "com.android.library"
|
|
31
|
+
apply plugin: "kotlin-android"
|
|
32
|
+
|
|
33
|
+
apply plugin: "com.facebook.react"
|
|
34
|
+
|
|
35
|
+
android {
|
|
36
|
+
namespace "com.paintboxreactnative"
|
|
37
|
+
|
|
38
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
39
|
+
|
|
40
|
+
defaultConfig {
|
|
41
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
42
|
+
targetSdkVersion getExtOrDefault("targetSdkVersion")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
buildFeatures {
|
|
46
|
+
buildConfig true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
buildTypes {
|
|
50
|
+
release {
|
|
51
|
+
minifyEnabled false
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
lint {
|
|
56
|
+
disable "GradleCompatible"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
compileOptions {
|
|
60
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
61
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
dependencies {
|
|
66
|
+
implementation(project(":nativebox"))
|
|
67
|
+
implementation("com.google.code.gson:gson:2.13.2")
|
|
68
|
+
implementation "com.facebook.react:react-android"
|
|
69
|
+
implementation("io.github.beyka:Android-TiffBitmapFactory:0.9.9.1")
|
|
70
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Project-wide Gradle settings.
|
|
2
|
+
|
|
3
|
+
# IDE (e.g. Android Studio) users:
|
|
4
|
+
# Gradle settings configured through the IDE *will override*
|
|
5
|
+
# any settings specified in this file.
|
|
6
|
+
|
|
7
|
+
# For more details on how to configure your build environment visit
|
|
8
|
+
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
9
|
+
|
|
10
|
+
# Specifies the JVM arguments used for the daemon process.
|
|
11
|
+
# The setting is particularly useful for tweaking memory settings.
|
|
12
|
+
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
|
|
13
|
+
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
|
14
|
+
|
|
15
|
+
# When configured, Gradle will run in incubating parallel mode.
|
|
16
|
+
# This option should only be used with decoupled projects. More details, visit
|
|
17
|
+
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
|
18
|
+
# org.gradle.parallel=true
|
|
19
|
+
|
|
20
|
+
# AndroidX package structure to make it clearer which packages are bundled with the
|
|
21
|
+
# Android operating system, and which are packaged with your app's APK
|
|
22
|
+
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
|
23
|
+
android.useAndroidX=true
|
|
24
|
+
|
|
25
|
+
# Use this property to specify which architecture you want to build.
|
|
26
|
+
# You can also override it from the CLI using
|
|
27
|
+
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
|
28
|
+
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
|
29
|
+
|
|
30
|
+
# Use this property to enable support to the new architecture.
|
|
31
|
+
# This will allow you to use TurboModules and the Fabric render in
|
|
32
|
+
# your application. You should enable this flag either if you want
|
|
33
|
+
# to write custom TurboModules/Fabric components OR use libraries that
|
|
34
|
+
# are providing them.
|
|
35
|
+
newArchEnabled=true
|
|
36
|
+
|
|
37
|
+
# Use this property to enable or disable the Hermes JS engine.
|
|
38
|
+
# If set to false, you will be using JSC instead.
|
|
39
|
+
hermesEnabled=true
|
|
40
|
+
|
|
41
|
+
# Use this property to enable edge-to-edge display support.
|
|
42
|
+
# This allows your app to draw behind system bars for an immersive UI.
|
|
43
|
+
# Note: Only works with ReactActivity and should not be used with custom Activity.
|
|
44
|
+
edgeToEdgeEnabled=false
|
|
45
|
+
NODE_BINARY=/Users/sinemavci/.nvm/versions/node/v20.19.0/bin/node
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package com.paintboxreactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactContext
|
|
4
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
5
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
6
|
+
import com.facebook.react.uimanager.UIManagerModule
|
|
7
|
+
import com.facebook.react.uimanager.common.UIManagerType
|
|
8
|
+
|
|
9
|
+
object NativeViewHandler {
|
|
10
|
+
fun resolve(
|
|
11
|
+
reactContext: ReactContext,
|
|
12
|
+
viewTag: Int,
|
|
13
|
+
callback: (view: PaintBoxNativeView) -> Unit
|
|
14
|
+
) {
|
|
15
|
+
UiThreadUtil.runOnUiThread {
|
|
16
|
+
|
|
17
|
+
val uiManager = UIManagerHelper.getUIManager(
|
|
18
|
+
reactContext,
|
|
19
|
+
UIManagerType.FABRIC
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
val paintBoxView: PaintBoxNativeView = uiManager?.resolveView(viewTag) as PaintBoxNativeView
|
|
23
|
+
callback(paintBoxView)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
package com.paintboxreactnative
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import android.widget.FrameLayout
|
|
6
|
+
import com.facebook.react.bridge.Arguments
|
|
7
|
+
import com.facebook.react.bridge.ReactContext
|
|
8
|
+
import com.facebook.react.bridge.WritableMap
|
|
9
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
10
|
+
import com.facebook.react.uimanager.events.Event
|
|
11
|
+
import com.kotlin.native_drawing_plugin.PaintBoxView
|
|
12
|
+
|
|
13
|
+
class ViewAttachedEvent(surfaceId: Int, viewId: Int, val payload: WritableMap) : Event<ViewAttachedEvent>(surfaceId, viewId) {
|
|
14
|
+
override fun getEventName(): String {
|
|
15
|
+
return EVENT_NAME
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override fun getEventData(): WritableMap {
|
|
19
|
+
return payload
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
companion object {
|
|
23
|
+
const val EVENT_NAME: String = "onPaintBoxReady"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@SuppressLint("ViewConstructor")
|
|
28
|
+
class PaintBoxNativeView(context: ReactContext): FrameLayout(context) {
|
|
29
|
+
val paintBox: PaintBoxView = PaintBoxView(context)
|
|
30
|
+
private var hasDispatchedReady = false
|
|
31
|
+
|
|
32
|
+
init {
|
|
33
|
+
this.addView(paintBox)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun dispatchEvent() {
|
|
37
|
+
if (!hasDispatchedReady) {
|
|
38
|
+
post {
|
|
39
|
+
if (id == -1) {
|
|
40
|
+
post { dispatchEvent() }
|
|
41
|
+
return@post
|
|
42
|
+
} else {
|
|
43
|
+
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
44
|
+
val eventDispatcher =
|
|
45
|
+
UIManagerHelper.getEventDispatcher(context as ReactContext, id)
|
|
46
|
+
val payload = Arguments.createMap().apply {
|
|
47
|
+
putBoolean("result", true)
|
|
48
|
+
}
|
|
49
|
+
val event = ViewAttachedEvent(surfaceId, id, payload)
|
|
50
|
+
eventDispatcher?.dispatchEvent(event)
|
|
51
|
+
hasDispatchedReady = true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override fun onAttachedToWindow() {
|
|
58
|
+
super.onAttachedToWindow()
|
|
59
|
+
dispatchEvent()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
package com.paintboxreactnative
|
|
2
|
+
|
|
3
|
+
import android.os.Build
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import android.widget.FrameLayout
|
|
6
|
+
import androidx.annotation.RequiresApi
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
9
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
10
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
11
|
+
import com.facebook.react.viewmanagers.PaintBoxViewManagerDelegate
|
|
12
|
+
import com.facebook.react.viewmanagers.PaintBoxViewManagerInterface
|
|
13
|
+
import com.google.gson.Gson
|
|
14
|
+
import com.kotlin.native_drawing_plugin.PaintMode
|
|
15
|
+
import com.paintboxreactnative.dto.ColorDTO
|
|
16
|
+
|
|
17
|
+
class PaintBoxNativeViewManager(private val callerContext: ReactApplicationContext) :
|
|
18
|
+
SimpleViewManager<PaintBoxNativeView>(), PaintBoxViewManagerInterface<PaintBoxNativeView> {
|
|
19
|
+
|
|
20
|
+
override fun getName() = REACT_CLASS
|
|
21
|
+
|
|
22
|
+
override fun createViewInstance(context: ThemedReactContext): PaintBoxNativeView {
|
|
23
|
+
return PaintBoxNativeView(context).apply {
|
|
24
|
+
isClickable = true
|
|
25
|
+
isFocusable = true
|
|
26
|
+
layoutParams = FrameLayout.LayoutParams(
|
|
27
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
28
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override fun getCommandsMap() = mapOf(
|
|
34
|
+
"setViewMode" to COMMAND_SET_VIEW_MODE,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
override fun getDelegate(): ViewManagerDelegate<PaintBoxNativeView> {
|
|
38
|
+
return PaintBoxViewManagerDelegate(this)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
|
|
42
|
+
override fun undo(view: PaintBoxNativeView?) {
|
|
43
|
+
view?.paintBox?.paintEditor?.undo()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override fun redo(view: PaintBoxNativeView?) {
|
|
47
|
+
view?.paintBox?.paintEditor?.redo()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
override fun reset(view: PaintBoxNativeView?) {
|
|
51
|
+
view?.paintBox?.paintEditor?.reset()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override fun importImage(
|
|
55
|
+
view: PaintBoxNativeView?,
|
|
56
|
+
path: String?,
|
|
57
|
+
width: Double,
|
|
58
|
+
height: Double
|
|
59
|
+
) {
|
|
60
|
+
if (path != null) {
|
|
61
|
+
Log.e("import image path exist", "import image path exist: ${path}")
|
|
62
|
+
val _width = if (width != -1.0) {
|
|
63
|
+
width
|
|
64
|
+
} else {
|
|
65
|
+
null
|
|
66
|
+
}
|
|
67
|
+
val _height = if (height != -1.0) {
|
|
68
|
+
height
|
|
69
|
+
} else {
|
|
70
|
+
null
|
|
71
|
+
}
|
|
72
|
+
Log.e("import image path exist", "import image path exist: ${_width} ${_height}")
|
|
73
|
+
view?.paintBox?.paintEditor?.import(path, _width, _height)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override fun setEnable(view: PaintBoxNativeView, enable: Boolean) {
|
|
78
|
+
view.paintBox.paintEditor.setEnable(enable)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
override fun setPaintMode(view: PaintBoxNativeView?, paintMode: String?) {
|
|
83
|
+
if(paintMode != null) {
|
|
84
|
+
view?.paintBox?.paintEditor?.setPaintMode(PaintMode.valueOf(paintMode))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
override fun setStrokeColor(view: PaintBoxNativeView?, color: String?) {
|
|
89
|
+
if (color != null) {
|
|
90
|
+
val colorDTO = Gson().fromJson(color, ColorDTO::class.java)
|
|
91
|
+
val _color = colorDTO.toDataModel()
|
|
92
|
+
Log.e("color: ", "setStrokeColor: ${_color.alpha()}")
|
|
93
|
+
view?.paintBox?.paintEditor?.setStrokeColor(_color)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
override fun setStrokeSize(view: PaintBoxNativeView?, size: Double) {
|
|
98
|
+
view?.paintBox?.paintEditor?.setStrokeWidth(size)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
companion object {
|
|
102
|
+
private const val REACT_CLASS = "PaintBoxView"
|
|
103
|
+
private const val COMMAND_SET_VIEW_MODE = 1
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
package com.paintboxreactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.google.gson.Gson
|
|
6
|
+
import com.google.gson.GsonBuilder
|
|
7
|
+
import com.kotlin.native_drawing_plugin.MimeType
|
|
8
|
+
import com.paintboxreactnative.dto.ColorDTO
|
|
9
|
+
|
|
10
|
+
class PaintBoxReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
11
|
+
NativePaintBoxReactNativeSpec(reactContext) {
|
|
12
|
+
val converter: Gson = GsonBuilder()
|
|
13
|
+
.create()
|
|
14
|
+
|
|
15
|
+
override fun export(viewTag: Double?, path: String?, fileName: String?, mimeType: String?, promise: Promise) {
|
|
16
|
+
if (viewTag != null) {
|
|
17
|
+
|
|
18
|
+
NativeViewHandler.resolve(reactContext, viewTag.toInt(), { paintBoxView: PaintBoxNativeView ->
|
|
19
|
+
if (path != null && mimeType != null) {
|
|
20
|
+
paintBoxView.paintBox.paintEditor.export(path, MimeType.fromValue(mimeType), fileName)
|
|
21
|
+
} else {
|
|
22
|
+
promise.reject(Throwable("Path or file Name is not valid"))
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override fun isEnable(viewTag: Double?, promise: Promise) {
|
|
29
|
+
if(viewTag != null) {
|
|
30
|
+
NativeViewHandler.resolve(reactContext, viewTag.toInt(), { paintBoxView: PaintBoxNativeView ->
|
|
31
|
+
val isEnabled = paintBoxView.paintBox.isEnabled
|
|
32
|
+
promise.resolve(isEnabled)
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
override fun getPaintMode(viewTag: Double?, promise: Promise) {
|
|
39
|
+
if(viewTag != null) {
|
|
40
|
+
NativeViewHandler.resolve(reactContext, viewTag.toInt(), { paintBoxView: PaintBoxNativeView ->
|
|
41
|
+
val paintMode = paintBoxView.paintBox.paintEditor.getPaintMode()
|
|
42
|
+
promise.resolve(paintMode.name)
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
override fun getStrokeColor(viewTag: Double?, promise: Promise) {
|
|
48
|
+
if (viewTag != null) {
|
|
49
|
+
NativeViewHandler.resolve(reactContext, viewTag.toInt(), { paintBoxView: PaintBoxNativeView ->
|
|
50
|
+
try {
|
|
51
|
+
val strokeColor = paintBoxView.paintBox.paintEditor.getStrokeColor()
|
|
52
|
+
var strokeColorDTO: ColorDTO?
|
|
53
|
+
strokeColorDTO = ColorDTO.fromDataModel(strokeColor)
|
|
54
|
+
promise.resolve(converter.toJson(strokeColorDTO))
|
|
55
|
+
} catch (error: Error) {
|
|
56
|
+
promise.reject(Throwable(error))
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override fun getStrokeSize(viewTag: Double?, promise: Promise) {
|
|
63
|
+
if (viewTag != null) {
|
|
64
|
+
NativeViewHandler.resolve(reactContext, viewTag.toInt(), { paintBoxView: PaintBoxNativeView ->
|
|
65
|
+
val size = paintBoxView.paintBox.paintEditor.getStrokeWidth()
|
|
66
|
+
promise.resolve(size)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
companion object {
|
|
72
|
+
const val NAME = NativePaintBoxReactNativeSpec.NAME
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package com.paintboxreactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager
|
|
9
|
+
import java.util.HashMap
|
|
10
|
+
|
|
11
|
+
class PaintBoxReactNativePackage : BaseReactPackage() {
|
|
12
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
13
|
+
return (if (name == PaintBoxReactNativeModule.NAME) {
|
|
14
|
+
PaintBoxReactNativeModule(reactContext)
|
|
15
|
+
} else {
|
|
16
|
+
null
|
|
17
|
+
}) as NativeModule?
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
21
|
+
return ReactModuleInfoProvider {
|
|
22
|
+
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
|
23
|
+
moduleInfos[PaintBoxReactNativeModule.NAME] = ReactModuleInfo(
|
|
24
|
+
PaintBoxReactNativeModule.NAME,
|
|
25
|
+
PaintBoxReactNativeModule.NAME,
|
|
26
|
+
false, // canOverrideExistingModule
|
|
27
|
+
false, // needsEagerInit
|
|
28
|
+
true, // isCxxModule
|
|
29
|
+
false,
|
|
30
|
+
true,// isTurboModule
|
|
31
|
+
)
|
|
32
|
+
moduleInfos
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<in Nothing, in Nothing>> {
|
|
37
|
+
return listOf(
|
|
38
|
+
PaintBoxNativeViewManager(reactContext)
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package com.paintboxreactnative.dto
|
|
2
|
+
|
|
3
|
+
import android.annotation.TargetApi
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import androidx.annotation.RequiresApi
|
|
7
|
+
|
|
8
|
+
data class ColorDTO(
|
|
9
|
+
val red: Double,
|
|
10
|
+
val green: Double,
|
|
11
|
+
val blue: Double,
|
|
12
|
+
val alpha: Double = 255.0,
|
|
13
|
+
) {
|
|
14
|
+
companion object {
|
|
15
|
+
@TargetApi(Build.VERSION_CODES.O)
|
|
16
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
17
|
+
fun fromDataModel(color: Color): ColorDTO {
|
|
18
|
+
return ColorDTO(
|
|
19
|
+
red = color.red().toDouble(),
|
|
20
|
+
green = color.green().toDouble(),
|
|
21
|
+
blue = color.blue().toDouble(),
|
|
22
|
+
alpha = color.alpha().toDouble()
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@TargetApi(Build.VERSION_CODES.O)
|
|
28
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
29
|
+
fun toDataModel(): Color {
|
|
30
|
+
return Color.valueOf(
|
|
31
|
+
(red / 255.0).toFloat(),
|
|
32
|
+
(green / 255.0).toFloat(),
|
|
33
|
+
(blue.toFloat() / 255.0).toFloat(),
|
|
34
|
+
(alpha.toFloat() / 255.0).toFloat(),
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#import "PaintBoxReactNative.h"
|
|
2
|
+
|
|
3
|
+
@implementation PaintBoxReactNative
|
|
4
|
+
- (NSNumber *)multiply:(double)a b:(double)b {
|
|
5
|
+
NSNumber *result = @(a * b);
|
|
6
|
+
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
11
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
12
|
+
{
|
|
13
|
+
return std::make_shared<facebook::react::NativePaintBoxReactNativeSpecJSI>(params);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
+ (NSString *)moduleName
|
|
17
|
+
{
|
|
18
|
+
return @"PaintBoxReactNative";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@end
|