├── .watchmanconfig ├── .gitattributes ├── .babelrc ├── android ├── settings.gradle ├── app │ ├── src │ │ └── main │ │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ └── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── java │ │ │ └── com │ │ │ │ └── reactnativemarkdowneditor │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── AndroidManifest.xml │ ├── BUCK │ ├── proguard-rules.pro │ └── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── keystores │ ├── debug.keystore.properties │ └── BUCK ├── build.gradle ├── gradle.properties ├── gradlew.bat └── gradlew ├── app.json ├── lib ├── static │ └── visibility.png ├── src │ ├── placeholderStrings.js │ ├── utils.js │ ├── applyWrapFormat.js │ ├── renderButtons.js │ ├── applyListFormat.js │ ├── applyWebLinkFormat.js │ ├── applyWrapFormatNewLines.js │ ├── __tests__ │ │ ├── urlTestCases.json │ │ └── preview-tests.js │ ├── Formats.js │ ├── MarkdownEditor.js │ └── webLinkValidator.js ├── main.js ├── package.json └── README.md ├── .buckconfig ├── .travis.yml ├── index.ios.js ├── ios ├── reactnativemarkdowneditor │ ├── AppDelegate.h │ ├── main.m │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.m │ ├── Info.plist │ └── Base.lproj │ │ └── LaunchScreen.xib ├── reactnativemarkdowneditorTests │ ├── Info.plist │ └── reactnativemarkdowneditorTests.m ├── reactnativemarkdowneditor-tvOSTests │ └── Info.plist ├── reactnativemarkdowneditor-tvOS │ └── Info.plist └── reactnativemarkdowneditor.xcodeproj │ ├── xcshareddata │ └── xcschemes │ │ ├── reactnativemarkdowneditor.xcscheme │ │ └── reactnativemarkdowneditor-tvOS.xcscheme │ └── project.pbxproj ├── .gitignore ├── package.json ├── LICENSE.md ├── index.android.js ├── .flowconfig └── README.md /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } 4 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'reactnativemarkdowneditor' 2 | 3 | include ':app' 4 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactnativemarkdowneditor", 3 | "displayName": "Markdown Editor" 4 | } 5 | -------------------------------------------------------------------------------- /lib/static/visibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunall17/react-native-markdown-editor/HEAD/lib/static/visibility.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | reactnativemarkdowneditor 3 | 4 | -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /lib/src/placeholderStrings.js: -------------------------------------------------------------------------------- 1 | export const writeUrlTextHere = 'https://example.com'; 2 | export const writeTextHereString = 'Write some text here'; 3 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunall17/react-native-markdown-editor/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | dist: precise 3 | node_js: 4 | - 6.0 5 | 6 | before_install: 7 | - npm install 8 | install: 9 | npm run test 10 | -------------------------------------------------------------------------------- /android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunall17/react-native-markdown-editor/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunall17/react-native-markdown-editor/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunall17/react-native-markdown-editor/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunall17/react-native-markdown-editor/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 6 | -------------------------------------------------------------------------------- /lib/src/utils.js: -------------------------------------------------------------------------------- 1 | import regexValidator from './webLinkValidator'; 2 | 3 | export const replaceBetween = (text: string, selection: Object, what: string) => 4 | text.substring(0, selection.start) + what + text.substring(selection.end); 5 | 6 | export const isStringWebLink = (text: string): boolean => { 7 | const pattern = regexValidator; 8 | return pattern.test(text); 9 | }; 10 | -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Platform, StyleSheet, Text, View, AppRegistry } from 'react-native'; 3 | import { MarkdownEditor } from './lib/main'; 4 | 5 | 6 | export default class App extends Component { 7 | render() { 8 | return ; 9 | } 10 | } 11 | 12 | 13 | AppRegistry.registerComponent('reactnativemarkdowneditor', () => App); 14 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | import applyWrapFormat from './src/applyWrapFormat'; 2 | import applyWrapFormatNewLines from './src/applyWrapFormatNewLines'; 3 | import applyListFormat from './src/applyListFormat'; 4 | import applyWebLinkFormat from './src/applyWebLinkFormat'; 5 | import MarkdownEditor from './src/MarkdownEditor'; 6 | 7 | module.exports = { 8 | MarkdownEditor, 9 | applyWrapFormat, 10 | applyWrapFormatNewLines, 11 | applyListFormat, 12 | applyWebLinkFormat, 13 | }; 14 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/reactnativemarkdowneditor/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reactnativemarkdowneditor; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "reactnativemarkdowneditor"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditor/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditor/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditor/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /lib/src/applyWrapFormat.js: -------------------------------------------------------------------------------- 1 | import { replaceBetween } from './utils'; 2 | 3 | export default ({ getState, item, setState }) => { 4 | const { text, selection } = getState(); 5 | const newText = replaceBetween( 6 | text, 7 | selection, 8 | item.wrapper.concat(text.substring(selection.start, selection.end), item.wrapper), 9 | ); 10 | let newPosition; 11 | if (selection.start === selection.end) { 12 | newPosition = selection.end + item.wrapper.length; 13 | } else { 14 | newPosition = selection.end + item.wrapper.length * 2; 15 | } 16 | const extra = { 17 | selection: { 18 | start: newPosition, 19 | end: newPosition, 20 | }, 21 | }; 22 | setState({ text: newText }, () => { 23 | setTimeout(() => { 24 | setState({ ...extra }); 25 | }, 25); 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-markdown-editor", 3 | "version": "1.0.1", 4 | "main": "main.js", 5 | "description": "A markdown editor for react native", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/kunall17/react-native-markdown-editor" 9 | }, 10 | "scripts": { 11 | "test": "jest" 12 | }, 13 | "dependencies": { 14 | "react-native-markdown-view": "^1.0.0" 15 | }, 16 | "devDependencies": { 17 | "jest": "20.0.4" 18 | }, 19 | "jest": { 20 | "preset": "react-native" 21 | }, 22 | "keywords": ["markdown", "editor", "react-native"], 23 | "author": "kunall17", 24 | "bugs": { 25 | "url": "https://github.com/kunall17/react-native-markdown-editor/issues" 26 | }, 27 | "homepage": "https://github.com/kunall17/react-native-markdown-editor" 28 | } 29 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditor-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 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: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 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 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 50 | 51 | fastlane/report.xml 52 | fastlane/Preview.html 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-markdown-editor", 3 | "version": "1.0.4", 4 | "main": "lib/main.js", 5 | "scripts": { 6 | "test": "npm run test:prettier && jest", 7 | "prettier-all": "prettier --write --single-quote --jsx-bracket-same-line --trailing-comma all --print-width 100 `find lib -name \"*.js\"`", 8 | "prettier": "prettier --write --single-quote --jsx-bracket-same-line --trailing-comma all --print-width 100", 9 | "test:prettier": "prettier-check --single-quote --jsx-bracket-same-line --trailing-comma all --print-width 100 `find lib -name \"*.js\"`" 10 | }, 11 | "dependencies": { 12 | "react-native-markdown-view": "^1.0.0" 13 | }, 14 | "peerDependencies": { 15 | "react": "16.0.0-alpha.12", 16 | "react-native": "0.47.1" 17 | }, 18 | "devDependencies": { 19 | "babel-jest": "20.0.3", 20 | "babel-preset-react-native": "2.1.0", 21 | "deep-freeze": "^0.0.1", 22 | "jest": "20.0.4", 23 | "prettier": "^1.5.3", 24 | "prettier-check": "^1.0.0" 25 | }, 26 | "jest": { 27 | "preset": "react-native" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/renderButtons.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FlatList, TouchableOpacity, Text } from 'react-native'; 3 | 4 | import Formats from './Formats'; 5 | 6 | const FOREGROUND_COLOR = 'rgba(82, 194, 175, 1)'; 7 | const defaultStyles = { padding: 8, color: FOREGROUND_COLOR, fontSize: 16 }; 8 | 9 | const defaultMarkdownButton = ({ item, getState, setState }) => { 10 | return ( 11 | item.onPress({ getState, setState, item })}> 12 | 13 | {item.title} 14 | 15 | 16 | ); 17 | }; 18 | 19 | export const renderFormatButtons = ({ getState, setState }, formats, markdownButton) => { 20 | const list = ( 21 | 25 | markdownButton 26 | ? markdownButton({ item, getState, setState }) 27 | : defaultMarkdownButton({ item, getState, setState })} 28 | horizontal 29 | /> 30 | ); 31 | return list; 32 | }; 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Kunal Gupta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/src/applyListFormat.js: -------------------------------------------------------------------------------- 1 | import { replaceBetween } from './utils'; 2 | 3 | export default ({ getState, item, setState }) => { 4 | let { text } = getState(); 5 | const { selection } = getState(); 6 | text = text || ''; 7 | let newText; 8 | let newSelection; 9 | if (selection.start !== selection.end) { 10 | newText = replaceBetween( 11 | text, 12 | selection, 13 | `${item.prefix} ${text.substring(selection.start, selection.end)}\n`, 14 | ); 15 | newSelection = { start: selection.end + 3, end: selection.end + 3 }; 16 | } else if ( 17 | selection.start === selection.end && 18 | text.substring(selection.end - 1, selection.end) === '\n' 19 | ) { 20 | newText = replaceBetween(text, selection, `${item.prefix} `); 21 | newSelection = { start: selection.start + 2, end: selection.start + 2 }; 22 | } else { 23 | newText = replaceBetween(text, selection, `\n${item.prefix} `); 24 | newSelection = { start: selection.start + 3, end: selection.start + 3 }; 25 | } 26 | 27 | setState({ text: newText }, () => { 28 | setTimeout(() => { 29 | setState({ selection: newSelection }); 30 | }, 300); 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/reactnativemarkdowneditor/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.reactnativemarkdowneditor; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.shell.MainReactPackage; 9 | import com.facebook.soloader.SoLoader; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() { 19 | return BuildConfig.DEBUG; 20 | } 21 | 22 | @Override 23 | protected List getPackages() { 24 | return Arrays.asList( 25 | new MainReactPackage() 26 | ); 27 | } 28 | }; 29 | 30 | @Override 31 | public ReactNativeHost getReactNativeHost() { 32 | return mReactNativeHost; 33 | } 34 | 35 | @Override 36 | public void onCreate() { 37 | super.onCreate(); 38 | SoLoader.init(this, /* native exopackage */ false); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /index.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | AppRegistry, 10 | StyleSheet, 11 | Text, 12 | View 13 | } from 'react-native'; 14 | 15 | export default class reactnativemarkdowneditor extends Component { 16 | render() { 17 | return ( 18 | 19 | 20 | Welcome to React Native! 21 | 22 | 23 | To get started, edit index.android.js 24 | 25 | 26 | Double tap R on your keyboard to reload,{'\n'} 27 | Shake or press menu button for dev menu 28 | 29 | 30 | ); 31 | } 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | container: { 36 | flex: 1, 37 | justifyContent: 'center', 38 | alignItems: 'center', 39 | backgroundColor: '#F5FCFF', 40 | }, 41 | welcome: { 42 | fontSize: 20, 43 | textAlign: 'center', 44 | margin: 10, 45 | }, 46 | instructions: { 47 | textAlign: 'center', 48 | color: '#333333', 49 | marginBottom: 5, 50 | }, 51 | }); 52 | 53 | AppRegistry.registerComponent('reactnativemarkdowneditor', () => reactnativemarkdowneditor); 54 | -------------------------------------------------------------------------------- /lib/src/applyWebLinkFormat.js: -------------------------------------------------------------------------------- 1 | import { isStringWebLink, replaceBetween } from './utils'; 2 | import { writeTextHereString, writeUrlTextHere } from './placeholderStrings'; 3 | 4 | export default ({ getState, item, setState }) => { 5 | const { selection, text } = getState(); 6 | let newText; 7 | let newSelection; 8 | const selectedText = text.substring(selection.start, selection.end); 9 | if (selection.start !== selection.end) { 10 | if (isStringWebLink(selectedText)) { 11 | newText = replaceBetween(text, selection, `[${writeTextHereString}](${selectedText})`); 12 | newSelection = { 13 | start: selection.start + 1, 14 | end: selection.start + 1 + writeTextHereString.length, 15 | }; 16 | } else { 17 | newText = replaceBetween(text, selection, `[${selectedText}](${writeUrlTextHere})`); 18 | newSelection = { 19 | start: selection.end + 3, 20 | end: selection.end + 3 + writeUrlTextHere.length, 21 | }; 22 | } 23 | } else { 24 | newText = replaceBetween(text, selection, `[${writeTextHereString}](${writeUrlTextHere})`); 25 | newSelection = { 26 | start: selection.start + 1, 27 | end: selection.start + 1 + writeTextHereString.length, 28 | }; 29 | } 30 | setState({ text: newText }, () => { 31 | setTimeout(() => { 32 | setState({ selection: newSelection }); 33 | }, 25); 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /lib/src/applyWrapFormatNewLines.js: -------------------------------------------------------------------------------- 1 | import { replaceBetween } from './utils'; 2 | 3 | export default ({ getState, item, setState }) => { 4 | const { text, selection } = getState(); 5 | let newText = replaceBetween( 6 | text, 7 | selection, 8 | `\n${item.wrapper.concat( 9 | '\n', 10 | text.substring(selection.start, selection.end), 11 | '\n', 12 | item.wrapper, 13 | '\n', 14 | )}`, 15 | ); 16 | let newPosition; 17 | if (selection.start === selection.end) { 18 | newPosition = selection.end + item.wrapper.length + 2; // +2 For two new lines 19 | newText = replaceBetween( 20 | text, 21 | selection, 22 | `\n${item.wrapper.concat( 23 | '\n', 24 | text.substring(selection.start, selection.end), 25 | '\n', 26 | item.wrapper, 27 | '\n', 28 | )}`, 29 | ); 30 | } else { 31 | newPosition = selection.end + item.wrapper.length * 2 + 3; // +3 For three new lines 32 | newText = replaceBetween( 33 | text, 34 | selection, 35 | `${item.wrapper.concat( 36 | '\n', 37 | text.substring(selection.start, selection.end), 38 | '\n', 39 | item.wrapper, 40 | '\n', 41 | )}`, 42 | ); 43 | } 44 | const extra = { 45 | selection: { 46 | start: newPosition, 47 | end: newPosition, 48 | }, 49 | }; 50 | setState({ text: newText }, () => { 51 | setTimeout(() => { 52 | setState({ ...extra }); 53 | }, 25); 54 | }); 55 | }; 56 | -------------------------------------------------------------------------------- /lib/src/__tests__/urlTestCases.json: -------------------------------------------------------------------------------- 1 | [ 2 | "http://www.example.com:8800", 3 | "http://www.example.com/a/b/c/d/e/f/g/h/i.html", 4 | "http://www.test.com?pageid=123&testid=1524", 5 | "http://www.test.com/do.html#A", 6 | "http://foo.com/blah_blah", 7 | "http://foo.com/blah_blah/", 8 | "http://foo.com/blah_blah_(wikipedia)", 9 | "http://foo.com/blah_blah_(wikipedia)_(again)", 10 | "http://www.example.com/wpstyle/?p=364", 11 | "https://www.example.com/foo/?bar=new&new=42&new", 12 | "http://✪df.ws/123", 13 | "http://userid:password@example.com:8080", 14 | "http://userid:password@example.com:8080/", 15 | "http://userid@example.com", 16 | "http://userid@example.com/", 17 | "http://userid@example.com:8080", 18 | "http://userid@example.com:8080/", 19 | "http://userid:password@example.com", 20 | "http://userid:password@example.com/", 21 | "http://142.42.1.1/", 22 | "http://142.42.1.1:8080/", 23 | "http://➡.ws/䨹", 24 | "http://⌘.ws", 25 | "http://⌘.ws/", 26 | "http://foo.com/blah_(wikipedia)#cite-1", 27 | "http://foo.com/blah_(wikipedia)_blah#cite-1", 28 | "http://foo.com/unicode_(✪)_in_parens", 29 | "http://foo.com/(something)?after=parens", 30 | "http://☺.damowmow.com/", 31 | "http://code.google.com/events/#&product=browser", 32 | "http://j.mp", 33 | "ftp://foo.bar/new", 34 | "http://foo.bar/?q=Test%20URL-encoded%20stuff", 35 | "http://مثال.إختبار", 36 | "http://例子.测试", 37 | "http://उदाहरण.परीक्षा", 38 | "http://1337.net", 39 | "http://a.b-c.de", 40 | "http://223.255.255.254" 41 | ] 42 | -------------------------------------------------------------------------------- /lib/src/Formats.js: -------------------------------------------------------------------------------- 1 | import applyWrapFormat from './applyWrapFormat'; 2 | import applyWrapFormatNewLines from './applyWrapFormatNewLines'; 3 | import applyListFormat from './applyListFormat'; 4 | import applyWebLinkFormat from './applyWebLinkFormat'; 5 | 6 | export default [ 7 | { key: 'B', title: 'B', wrapper: '**', onPress: applyWrapFormat, style: { fontWeight: 'bold' } }, 8 | { key: 'I', title: 'I', wrapper: '*', onPress: applyWrapFormat, style: { fontStyle: 'italic' } }, 9 | { 10 | key: 'U', 11 | title: 'U', 12 | wrapper: '__', 13 | onPress: applyWrapFormat, 14 | style: { textDecorationLine: 'underline' }, 15 | }, 16 | { 17 | key: 'S', 18 | title: 'S', 19 | wrapper: '~~', 20 | onPress: applyWrapFormat, 21 | style: { textDecorationLine: 'line-through' }, 22 | }, 23 | { key: 'C', title: 'C', wrapper: '`', onPress: applyWrapFormat }, 24 | { key: 'CC', title: 'CC', wrapper: '```', onPress: applyWrapFormatNewLines }, 25 | { key: 'L', title: 'L', prefix: '-', onPress: applyListFormat }, 26 | { key: 'WEB', title: 'WEB', onPress: applyWebLinkFormat }, 27 | { key: 'H1', title: 'H1', prefix: '#', onPress: applyListFormat }, 28 | { key: 'H2', title: 'H2', prefix: '##', onPress: applyListFormat }, 29 | { key: 'H3', title: 'H3', prefix: '###', onPress: applyListFormat }, 30 | { key: 'H4', title: 'H4', prefix: '####', onPress: applyListFormat }, 31 | { key: 'H5', title: 'H5', prefix: '#####', onPress: applyListFormat }, 32 | { key: 'H6', title: 'H6', prefix: '######', onPress: applyListFormat }, 33 | ]; 34 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | .*/Libraries/react-native/ReactNative.js 16 | 17 | [include] 18 | 19 | [libs] 20 | node_modules/react-native/Libraries/react-native/react-native-interface.js 21 | node_modules/react-native/flow 22 | flow/ 23 | 24 | [options] 25 | emoji=true 26 | 27 | module.system=haste 28 | 29 | munge_underscores=true 30 | 31 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 32 | 33 | suppress_type=$FlowIssue 34 | suppress_type=$FlowFixMe 35 | suppress_type=$FixMe 36 | 37 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-9]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 38 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-9]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 39 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 40 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 41 | 42 | unsafe.enable_getters_and_setters=true 43 | 44 | [version] 45 | ^0.49.1 46 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditor/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import 13 | #import 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"reactnativemarkdowneditor" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditor-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.reactnativemarkdowneditor", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.reactnativemarkdowneditor", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditor/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | reactnativemarkdowneditor 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | NSLocationWhenInUseUsageDescription 42 | 43 | NSAppTransportSecurity 44 | 45 | 46 | NSExceptionDomains 47 | 48 | localhost 49 | 50 | NSExceptionAllowsInsecureHTTPLoads 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditorTests/reactnativemarkdowneditorTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface reactnativemarkdowneditorTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation reactnativemarkdowneditorTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # TextLayoutBuilder uses a non-public Android constructor within StaticLayout. 54 | # See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. 55 | -dontwarn android.text.StaticLayout 56 | 57 | # okhttp 58 | 59 | -keepattributes Signature 60 | -keepattributes *Annotation* 61 | -keep class okhttp3.** { *; } 62 | -keep interface okhttp3.** { *; } 63 | -dontwarn okhttp3.** 64 | 65 | # okio 66 | 67 | -keep class sun.misc.Unsafe { *; } 68 | -dontwarn java.nio.file.* 69 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 70 | -dontwarn okio.** 71 | -------------------------------------------------------------------------------- /ios/reactnativemarkdowneditor/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /lib/src/MarkdownEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | View, 4 | StyleSheet, 5 | TextInput, 6 | Platform, 7 | KeyboardAvoidingView, 8 | TouchableOpacity, 9 | Image, 10 | ScrollView, 11 | } from 'react-native'; 12 | import { MarkdownView } from 'react-native-markdown-view'; 13 | 14 | import { renderFormatButtons } from './renderButtons'; 15 | 16 | const FOREGROUND_COLOR = 'rgba(82, 194, 175, 1)'; 17 | const styles = StyleSheet.create({ 18 | composeText: { 19 | borderColor: FOREGROUND_COLOR, 20 | borderWidth: 1, 21 | flexDirection: 'column', 22 | flex: 1, 23 | padding: 4, 24 | paddingLeft: 8, 25 | fontSize: 16, 26 | }, 27 | buttonContainer: { 28 | flex: 0, 29 | flexDirection: 'row', 30 | }, 31 | inlinePadding: { 32 | padding: 8, 33 | }, 34 | preview: { 35 | flex: 0.2, 36 | padding: 5, 37 | borderWidth: 1, 38 | borderColor: FOREGROUND_COLOR, 39 | }, 40 | screen: { 41 | flex: 1, 42 | flexDirection: 'column', 43 | alignItems: 'stretch', 44 | backgroundColor: 'white', 45 | }, 46 | }); 47 | 48 | const markdownStyles = { 49 | heading1: { 50 | fontSize: 24, 51 | color: 'purple', 52 | }, 53 | link: { 54 | color: 'pink', 55 | }, 56 | mailTo: { 57 | color: 'orange', 58 | }, 59 | text: { 60 | color: '#555555', 61 | }, 62 | }; 63 | 64 | export default class MarkdownEditor extends React.Component { 65 | constructor(props) { 66 | super(props); 67 | this.state = { 68 | text: props.defaultValue || '', 69 | selection: { start: 0, end: 0 }, 70 | showPreview: props.showPreview ? props.showPreview : false, 71 | }; 72 | } 73 | textInput: TextInput; 74 | 75 | changeText = (input: string) => { 76 | this.setState({ text: input }); 77 | if (this.props.onMarkdownChange) this.props.onMarkdownChange(input); 78 | }; 79 | 80 | onSelectionChange = event => { 81 | this.setState({ 82 | selection: event.nativeEvent.selection, 83 | }); 84 | }; 85 | 86 | componentDidMount() { 87 | this.textInput.focus(); 88 | } 89 | 90 | getState = () => { 91 | this.setState({ 92 | selection: { 93 | start: 1, 94 | end: 1, 95 | }, 96 | }); 97 | return this.state; 98 | }; 99 | 100 | convertMarkdown = () => { 101 | this.setState({ showPreview: !this.state.showPreview }); 102 | }; 103 | 104 | renderPreview = () => { 105 | return ( 106 | 107 | 108 | 109 | {this.state.text === '' ? 'Markdown preview here' : this.state.text} 110 | 111 | 112 | 113 | ); 114 | }; 115 | 116 | render() { 117 | const WrapperView = Platform.OS === 'ios' ? KeyboardAvoidingView : View; 118 | const { Formats, markdownButton } = this.props; 119 | const { text, selection, showPreview } = this.state; 120 | return ( 121 | 122 | (this.textInput = textInput)} 131 | selection={selection} 132 | /> 133 | {showPreview ? this.renderPreview() : null} 134 | 135 | 138 | 143 | 144 | {renderFormatButtons( 145 | { 146 | getState: this.getState, 147 | setState: (state, callback) => { 148 | this.textInput.focus(); 149 | this.setState(state, callback); 150 | }, 151 | }, 152 | Formats, 153 | markdownButton, 154 | )} 155 | 156 | 157 | ); 158 | } 159 | } 160 | 161 | MarkdownEditor.defaultValue = { 162 | defaultValue: '', 163 | }; 164 | -------------------------------------------------------------------------------- /lib/src/webLinkValidator.js: -------------------------------------------------------------------------------- 1 | // prettier-ignore 2 | /* eslint-disable */ 3 | 4 | // 5 | // Regular Expression for URL validation 6 | // 7 | // Author: Diego Perini 8 | // Updated: 2010/12/05 9 | // License: MIT 10 | // 11 | // Copyright (c) 2010-2013 Diego Perini (http://www.iport.it) 12 | // 13 | // Permission is hereby granted, free of charge, to any person 14 | // obtaining a copy of this software and associated documentation 15 | // files (the "Software"), to deal in the Software without 16 | // restriction, including without limitation the rights to use, 17 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the 19 | // Software is furnished to do so, subject to the following 20 | // conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be 23 | // included in all copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | // OTHER DEALINGS IN THE SOFTWARE. 33 | // 34 | // the regular expression composed & commented 35 | // could be easily tweaked for RFC compliance, 36 | // it was expressly modified to fit & satisfy 37 | // these test for an URL shortener: 38 | // 39 | // http://mathiasbynens.be/demo/url-regex 40 | // 41 | // Notes on possible differences from a standard/generic validation: 42 | // 43 | // - utf-8 char class take in consideration the full Unicode range 44 | // - TLDs have been made mandatory so single names like "localhost" fails 45 | // - protocols have been restricted to ftp, http and https only as requested 46 | // 47 | // Changes: 48 | // 49 | // - IP address dotted notation validation, range: 1.0.0.0 - 223.255.255.255 50 | // first and last IP address of each class is considered invalid 51 | // (since they are broadcast/network addresses) 52 | // 53 | // - Added exclusion of private, reserved and/or local networks ranges 54 | // 55 | // - Made starting path slash optional (http://example.com?foo=bar) 56 | // 57 | // - Allow a dot (.) at the end of hostnames (http://example.com.) 58 | // 59 | // Compressed one-line versions: 60 | // 61 | // Javascript version 62 | // 63 | // /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i 64 | // 65 | // PHP version 66 | // 67 | // _^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$_iuS 68 | // 69 | export default new RegExp( 70 | "^" + 71 | // protocol identifier 72 | "(?:(?:https?|ftp)://)" + 73 | // user:pass authentication 74 | "(?:\\S+(?::\\S*)?@)?" + 75 | "(?:" + 76 | // IP address exclusion 77 | // private & local networks 78 | "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + 79 | "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + 80 | "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" + 81 | // IP address dotted notation octets 82 | // excludes loopback network 0.0.0.0 83 | // excludes reserved space >= 224.0.0.0 84 | // excludes network & broacast addresses 85 | // (first & last IP address of each class) 86 | "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + 87 | "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + 88 | "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + 89 | "|" + 90 | // host name 91 | "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" + 92 | // domain name 93 | "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" + 94 | // TLD identifier 95 | "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" + 96 | // TLD may end with dot 97 | "\\.?" + 98 | ")" + 99 | // port number 100 | "(?::\\d{2,5})?" + 101 | // resource path 102 | "(?:[/?#]\\S*)?" + 103 | "$", "i" 104 | ); 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-markdown-editor 2 | 3 | [![npm version](https://badge.fury.io/js/react-native-markdown-editor.svg)](https://badge.fury.io/js/react-native-markdown-editor) 4 | [![Build Status](https://travis-ci.org/kunall17/react-native-markdown-editor.svg?branch=master)](https://travis-ci.org/kunall17/react-native-markdown-editor) 5 | 6 | This is a library for rendering a markdown editor for the markdown with helper buttons to easily write markdown. With live preview markdown as well (thanks to https://github.com/Benjamin-Dobell/react-native-markdown-view) 7 | 8 | ## Index 9 | 10 | * [Getting started](#getting-started) 11 | * [Screenshots](#screenshots) 12 | * [Features](#features) 13 | * [Usage](#usage) 14 | * [Customizing](#Customizing) 15 | * [Your own custom formats](#your-own-custom-formats) 16 | * [Customize the helper button](#customize-the-helper-button) 17 | * [Customize the markdownButton styles](#customize-the-markdownbutton-styles) 18 | * [Props for the Markdown Editor](#props) 19 | * [Contributing](#contributing) 20 | 21 | ## Getting Started 22 | 23 | Install the node module: 24 | 25 | yarn add react-native-markdown-editor 26 | 27 | or with npm: 28 | 29 | npm install --save react-native-markdown-editor 30 | 31 | Then see [Usage](#usage) for futher details 32 | 33 | ## Screenshots 34 | 35 | Screenshot: 36 | 37 | 38 | 39 | ## Features 40 | 41 | * Multiline textinput for writing markdown 42 | * Live preview of the markdown written (can be hidden) 43 | * Helper buttons to write the syntax for the markdown (like github) 44 | 45 | 46 |
47 | Markdown where editor helps (in order for the default format) 48 |

49 | 50 | **Bold Text** 51 | 52 | *Italic Text* 53 | 54 | __Underline text__ 55 | 56 | ~~Strikethrough~~ 57 | 58 | `Inline code` 59 | 60 | * Item 1 61 | * Item 2 62 | 63 | Url Links: 64 | 65 | [GitHub](http://github.com) 66 | 67 | ``` 68 | function codeExample(arg) { 69 | } 70 | ``` 71 | 72 | # This is an < h1 > tag 73 | 74 |

75 |
76 | 77 | ## Usage 78 | 79 | Import the editor through 80 | 81 | `import { MarkdownEditor } from 'react-native-markdown-editor';` 82 | 83 | And use like this this in the jsx 84 | 85 | `` 86 | 87 | And pass a function `onMarkdownChange` which will be callback when markdown is changed 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | It can be used with a toolbar and have a submit menu option there! 94 | 95 | ## Customizing 96 | 97 | 98 | ##### Your own custom formats 99 | You can give a custom list of buttons you want to give the default is [Formats](https://github.com/kunall17/react-native-markdown-editor/blob/master/lib/src/Formats.js) by speicifying 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | #### Customize the helper button 106 | 107 | You can customize the helper button using the `markdownButton` prop. 108 | The buttons are rendered using a `FlatList` and the data passed is the [Formats](https://github.com/kunall17/react-native-markdown-editor/blob/master/lib/src/Formats.js) 109 | 110 | markdownButton can be method like 111 | 112 | ``` 113 | const defaultMarkdownButton = ({ item, getState, setState }) => 114 |