mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
515e15242c | ||
|
|
374307aa99 | ||
|
|
5150fa1071 | ||
|
|
62b46efe10 | ||
|
|
ab21e6baab | ||
|
|
03c1688ce6 | ||
|
|
48d6492448 | ||
|
|
ae799add4b | ||
|
|
82c9924411 | ||
|
|
3d8e2fe41e | ||
|
|
f933154185 | ||
|
|
30fcc7602b | ||
|
|
26c0d79c06 | ||
|
|
d5fa599960 | ||
|
|
78b66f046a | ||
|
|
013e715934 | ||
|
|
afe78f6935 | ||
|
|
917c0391ee | ||
|
|
6d25667654 | ||
|
|
33dde47ab5 | ||
|
|
c59475415c | ||
|
|
d457a18766 | ||
|
|
e8854b771e |
@@ -25,6 +25,7 @@
|
||||
var React = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
@@ -425,16 +426,18 @@ exports.examples = [
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.leftMargin}>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Center
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.center}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
{ Platform.OS === 'android' ?
|
||||
<View style={styles.leftMargin}>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Center
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.center}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
: null }
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
Slider,
|
||||
Text,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var SliderExample = React.createClass({
|
||||
getDefaultProps() {
|
||||
return {
|
||||
value: 0,
|
||||
}
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
value: this.props.value,
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={styles.text} >
|
||||
{this.state.value && +this.state.value.toFixed(3)}
|
||||
</Text>
|
||||
<Slider
|
||||
{...this.props}
|
||||
onValueChange={(value) => this.setState({value: value})} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var SlidingCompleteExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
slideCompletionValue: 0,
|
||||
slideCompletionCount: 0,
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<SliderExample
|
||||
{...this.props}
|
||||
onSlidingComplete={(value) => this.setState({
|
||||
slideCompletionValue: value,
|
||||
slideCompletionCount: this.state.slideCompletionCount + 1})} />
|
||||
<Text>
|
||||
Completions: {this.state.slideCompletionCount} Value: {this.state.slideCompletionValue}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
slider: {
|
||||
height: 10,
|
||||
margin: 10,
|
||||
},
|
||||
text: {
|
||||
fontSize: 14,
|
||||
textAlign: 'center',
|
||||
fontWeight: '500',
|
||||
margin: 10,
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = '<Slider>';
|
||||
exports.displayName = 'SliderExample';
|
||||
exports.description = 'Slider input for numeric values';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Default settings',
|
||||
render(): ReactElement {
|
||||
return <SliderExample />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Initial value: 0.5',
|
||||
render(): ReactElement {
|
||||
return <SliderExample value={0.5} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'minimumValue: -1, maximumValue: 2',
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<SliderExample
|
||||
minimumValue={-1}
|
||||
maximumValue={2}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'step: 0.25',
|
||||
render(): ReactElement {
|
||||
return <SliderExample step={0.25} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'onSlidingComplete',
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<SlidingCompleteExample />
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom min/max track tint color',
|
||||
platform: 'ios',
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<SliderExample
|
||||
minimumTrackTintColor={'red'}
|
||||
maximumTrackTintColor={'green'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom thumb image',
|
||||
platform: 'ios',
|
||||
render(): ReactElement {
|
||||
return <SliderExample thumbImage={require('./uie_thumb_big.png')} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom track image',
|
||||
platform: 'ios',
|
||||
render(): ReactElement {
|
||||
return <SliderExample trackImage={require('./slider.png')} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom min/max track image',
|
||||
platform: 'ios',
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<SliderExample
|
||||
minimumTrackImage={require('./slider-left.png')}
|
||||
maximumTrackImage={require('./slider-right.png')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
||||
@@ -23,6 +23,10 @@ export type UIExplorerExample = {
|
||||
};
|
||||
|
||||
var ComponentExamples: Array<UIExplorerExample> = [
|
||||
{
|
||||
key: 'SliderExample',
|
||||
module: require('./SliderExample'),
|
||||
},
|
||||
{
|
||||
key: 'ImageExample',
|
||||
module: require('./ImageExample'),
|
||||
|
||||
@@ -50,8 +50,8 @@ var ComponentExamples: Array<UIExplorerExample> = [
|
||||
module: require('./ListViewPagingExample'),
|
||||
},
|
||||
{
|
||||
key: 'MapViewExample',
|
||||
module: require('./MapViewExample'),
|
||||
key: 'MapViewExample',
|
||||
module: require('./MapViewExample'),
|
||||
},
|
||||
{
|
||||
key: 'ModalExample',
|
||||
@@ -90,8 +90,8 @@ var ComponentExamples: Array<UIExplorerExample> = [
|
||||
module: require('./SegmentedControlIOSExample'),
|
||||
},
|
||||
{
|
||||
key: 'SliderIOSExample',
|
||||
module: require('./SliderIOSExample'),
|
||||
key: 'SliderExample',
|
||||
module: require('./SliderExample'),
|
||||
},
|
||||
{
|
||||
key: 'StatusBarExample',
|
||||
|
||||
@@ -133,7 +133,10 @@ const RefreshControl = React.createClass({
|
||||
|
||||
_onRefresh() {
|
||||
this.props.onRefresh && this.props.onRefresh();
|
||||
this._nativeRef.setNativeProps({refreshing: this.props.refreshing});
|
||||
|
||||
if (this._nativeRef) {
|
||||
this._nativeRef.setNativeProps({refreshing: this.props.refreshing});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -202,7 +202,6 @@ var ScrollResponderMixin = {
|
||||
* a touch has already started.
|
||||
*/
|
||||
scrollResponderHandleResponderReject: function() {
|
||||
warning(false, "ScrollView doesn't take rejection well - scrolls anyway");
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule Slider
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Image = require('Image');
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var Platform = require('Platform');
|
||||
var PropTypes = require('ReactPropTypes');
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
type Event = Object;
|
||||
|
||||
/**
|
||||
* A component used to select a single value from a range of values.
|
||||
*/
|
||||
var Slider = React.createClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
propTypes: {
|
||||
...View.propTypes,
|
||||
|
||||
/**
|
||||
* Used to style and layout the `Slider`. See `StyleSheet.js` and
|
||||
* `ViewStylePropTypes.js` for more info.
|
||||
*/
|
||||
style: View.propTypes.style,
|
||||
|
||||
/**
|
||||
* Initial value of the slider. The value should be between minimumValue
|
||||
* and maximumValue, which default to 0 and 1 respectively.
|
||||
* Default value is 0.
|
||||
*
|
||||
* *This is not a controlled component*, you don't need to update the
|
||||
* value during dragging.
|
||||
*/
|
||||
value: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Step value of the slider. The value should be
|
||||
* between 0 and (maximumValue - minimumValue).
|
||||
* Default value is 0.
|
||||
*/
|
||||
step: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Initial minimum value of the slider. Default value is 0.
|
||||
*/
|
||||
minimumValue: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Initial maximum value of the slider. Default value is 1.
|
||||
*/
|
||||
maximumValue: PropTypes.number,
|
||||
|
||||
/**
|
||||
* The color used for the track to the left of the button. Overrides the
|
||||
* default blue gradient image.
|
||||
* @platform ios
|
||||
*/
|
||||
minimumTrackTintColor: PropTypes.string,
|
||||
|
||||
/**
|
||||
* The color used for the track to the right of the button. Overrides the
|
||||
* default blue gradient image.
|
||||
* @platform ios
|
||||
*/
|
||||
maximumTrackTintColor: PropTypes.string,
|
||||
|
||||
/**
|
||||
* If true the user won't be able to move the slider.
|
||||
* Default value is false.
|
||||
*/
|
||||
disabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Assigns a single image for the track. Only static images are supported.
|
||||
* The center pixel of the image will be stretched to fill the track.
|
||||
* @platform ios
|
||||
*/
|
||||
trackImage: Image.propTypes.source,
|
||||
|
||||
/**
|
||||
* Assigns a minimum track image. Only static images are supported. The
|
||||
* rightmost pixel of the image will be stretched to fill the track.
|
||||
* @platform ios
|
||||
*/
|
||||
minimumTrackImage: Image.propTypes.source,
|
||||
|
||||
/**
|
||||
* Assigns a maximum track image. Only static images are supported. The
|
||||
* leftmost pixel of the image will be stretched to fill the track.
|
||||
* @platform ios
|
||||
*/
|
||||
maximumTrackImage: Image.propTypes.source,
|
||||
|
||||
/**
|
||||
* Sets an image for the thumb. Only static images are supported.
|
||||
* @platform ios
|
||||
*/
|
||||
thumbImage: Image.propTypes.source,
|
||||
|
||||
/**
|
||||
* Callback continuously called while the user is dragging the slider.
|
||||
*/
|
||||
onValueChange: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Callback called when the user finishes changing the value (e.g. when
|
||||
* the slider is released).
|
||||
*/
|
||||
onSlidingComplete: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Used to locate this view in UI automation tests.
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() : any {
|
||||
return {
|
||||
disabled: false,
|
||||
value: 0,
|
||||
minimumValue: 0,
|
||||
maximumValue: 1,
|
||||
step: 0
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let {style, onValueChange, onSlidingComplete, ...props} = this.props;
|
||||
props.style = [styles.slider, style];
|
||||
|
||||
props.onValueChange = onValueChange && ((event: Event) => {
|
||||
let userEvent = true;
|
||||
if (Platform.OS === 'android') {
|
||||
// On Android there's a special flag telling us the user is
|
||||
// dragging the slider.
|
||||
userEvent = event.nativeEvent.fromUser;
|
||||
}
|
||||
onValueChange && userEvent && onValueChange(event.nativeEvent.value);
|
||||
});
|
||||
|
||||
props.onChange = props.onValueChange;
|
||||
|
||||
props.onSlidingComplete = onSlidingComplete && ((event: Event) => {
|
||||
onSlidingComplete && onSlidingComplete(event.nativeEvent.value);
|
||||
});
|
||||
|
||||
return <RCTSlider
|
||||
{...props}
|
||||
enabled={!this.props.disabled}
|
||||
onStartShouldSetResponder={() => true}
|
||||
onResponderTerminationRequest={() => false}
|
||||
/>;
|
||||
}
|
||||
});
|
||||
|
||||
let styles;
|
||||
if (Platform.OS === 'ios') {
|
||||
styles = StyleSheet.create({
|
||||
slider: {
|
||||
height: 40,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
styles = StyleSheet.create({
|
||||
slider: {},
|
||||
});
|
||||
}
|
||||
|
||||
let options = {};
|
||||
if (Platform.OS === 'android') {
|
||||
options = {
|
||||
nativeOnly: {
|
||||
enabled: true,
|
||||
}
|
||||
};
|
||||
}
|
||||
const RCTSlider = requireNativeComponent('RCTSlider', Slider, options);
|
||||
|
||||
module.exports = Slider;
|
||||
@@ -22,6 +22,12 @@ var requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
type Event = Object;
|
||||
|
||||
/**
|
||||
* **Note:** SliderIOS is deprecated and will be removed in the future. Use the cross-platform
|
||||
* Slider as a drop-in replacement with the same API.
|
||||
*
|
||||
* An iOS-specific component used to select a single value from a range of values.
|
||||
*/
|
||||
var SliderIOS = React.createClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
@@ -120,6 +126,11 @@ var SliderIOS = React.createClass({
|
||||
},
|
||||
|
||||
render: function() {
|
||||
console.warn(
|
||||
'SliderIOS is deprecated and will be removed in ' +
|
||||
'future versions of React Native. Use the cross-platform Slider ' +
|
||||
'as a drop-in replacement.');
|
||||
|
||||
let {style, onValueChange, onSlidingComplete, ...props} = this.props;
|
||||
props.style = [styles.slider, style];
|
||||
|
||||
|
||||
@@ -13,7 +13,12 @@
|
||||
|
||||
var RCTNetworking = require('RCTNetworking');
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
const utf8 = require('utf8');
|
||||
const warning = require('fbjs/lib/warning');
|
||||
|
||||
type ResponseType = '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text';
|
||||
type Response = ?Object | string;
|
||||
|
||||
const UNSENT = 0;
|
||||
const OPENED = 1;
|
||||
@@ -21,6 +26,15 @@ const HEADERS_RECEIVED = 2;
|
||||
const LOADING = 3;
|
||||
const DONE = 4;
|
||||
|
||||
const SUPPORTED_RESPONSE_TYPES = {
|
||||
arraybuffer: typeof global.ArrayBuffer === 'function',
|
||||
blob: typeof global.Blob === 'function',
|
||||
document: false,
|
||||
json: true,
|
||||
text: true,
|
||||
'': true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Shared base for platform-specific XMLHttpRequest implementations.
|
||||
*/
|
||||
@@ -43,9 +57,7 @@ class XMLHttpRequestBase {
|
||||
upload: any;
|
||||
readyState: number;
|
||||
responseHeaders: ?Object;
|
||||
responseText: ?string;
|
||||
response: ?string;
|
||||
responseType: '' | 'text';
|
||||
responseText: string;
|
||||
status: number;
|
||||
timeout: number;
|
||||
responseURL: ?string;
|
||||
@@ -57,12 +69,16 @@ class XMLHttpRequestBase {
|
||||
_requestId: ?number;
|
||||
_subscriptions: [any];
|
||||
|
||||
_method: ?string;
|
||||
_url: ?string;
|
||||
_headers: Object;
|
||||
_sent: boolean;
|
||||
_aborted: boolean;
|
||||
_cachedResponse: Response;
|
||||
_hasError: boolean;
|
||||
_headers: Object;
|
||||
_lowerCaseResponseHeaders: Object;
|
||||
_method: ?string;
|
||||
_response: string | ?Object;
|
||||
_responseType: ResponseType;
|
||||
_sent: boolean;
|
||||
_url: ?string;
|
||||
|
||||
constructor() {
|
||||
this.UNSENT = UNSENT;
|
||||
@@ -82,24 +98,101 @@ class XMLHttpRequestBase {
|
||||
this._aborted = false;
|
||||
}
|
||||
|
||||
_reset() {
|
||||
_reset(): void {
|
||||
this.readyState = this.UNSENT;
|
||||
this.responseHeaders = undefined;
|
||||
this.responseText = '';
|
||||
this.response = null;
|
||||
this.responseType = '';
|
||||
this.status = 0;
|
||||
delete this.responseURL;
|
||||
|
||||
this._requestId = null;
|
||||
|
||||
this._cachedResponse = undefined;
|
||||
this._hasError = false;
|
||||
this._headers = {};
|
||||
this._responseType = '';
|
||||
this._sent = false;
|
||||
this._lowerCaseResponseHeaders = {};
|
||||
|
||||
this._clearSubscriptions();
|
||||
}
|
||||
|
||||
// $FlowIssue #10784535
|
||||
get responseType(): ResponseType {
|
||||
return this._responseType;
|
||||
}
|
||||
|
||||
// $FlowIssue #10784535
|
||||
set responseType(responseType: ResponseType): void {
|
||||
if (this.readyState > HEADERS_RECEIVED) {
|
||||
throw new Error(
|
||||
"Failed to set the 'responseType' property on 'XMLHttpRequest': The " +
|
||||
"response type cannot be set if the object's state is LOADING or DONE"
|
||||
);
|
||||
}
|
||||
if (!SUPPORTED_RESPONSE_TYPES.hasOwnProperty(responseType)) {
|
||||
warning(
|
||||
`The provided value '${responseType}' is not a valid 'responseType'.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// redboxes early, e.g. for 'arraybuffer' on ios 7
|
||||
invariant(
|
||||
SUPPORTED_RESPONSE_TYPES[responseType] || responseType === 'document',
|
||||
`The provided value '${responseType}' is unsupported in this environment.`
|
||||
);
|
||||
this._responseType = responseType;
|
||||
}
|
||||
|
||||
// $FlowIssue #10784535
|
||||
get response(): Response {
|
||||
const {responseType} = this;
|
||||
if (responseType === '' || responseType === 'text') {
|
||||
return this.readyState < LOADING || this._hasError
|
||||
? ''
|
||||
: this.responseText;
|
||||
}
|
||||
|
||||
if (this.readyState !== DONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this._cachedResponse !== undefined) {
|
||||
return this._cachedResponse;
|
||||
}
|
||||
|
||||
switch (this.responseType) {
|
||||
case 'document':
|
||||
this._cachedResponse = null;
|
||||
break;
|
||||
|
||||
case 'arraybuffer':
|
||||
this._cachedResponse = toArrayBuffer(
|
||||
this.responseText, this.getResponseHeader('content-type') || '');
|
||||
break;
|
||||
|
||||
case 'blob':
|
||||
this._cachedResponse = new global.Blob(
|
||||
[this.responseText],
|
||||
{type: this.getResponseHeader('content-type') || ''}
|
||||
);
|
||||
break;
|
||||
|
||||
case 'json':
|
||||
try {
|
||||
this._cachedResponse = JSON.parse(this.responseText);
|
||||
} catch (_) {
|
||||
this._cachedResponse = null;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
this._cachedResponse = null;
|
||||
}
|
||||
|
||||
return this._cachedResponse;
|
||||
}
|
||||
|
||||
didCreateRequest(requestId: number): void {
|
||||
this._requestId = requestId;
|
||||
this._subscriptions.push(RCTDeviceEventEmitter.addListener(
|
||||
@@ -151,22 +244,7 @@ class XMLHttpRequestBase {
|
||||
} else {
|
||||
this.responseText += responseText;
|
||||
}
|
||||
switch(this.responseType) {
|
||||
case '':
|
||||
case 'text':
|
||||
this.response = this.responseText;
|
||||
break;
|
||||
case 'blob': // whatwg-fetch sets this in Chrome
|
||||
/* global Blob: true */
|
||||
invariant(
|
||||
typeof Blob === 'function',
|
||||
`responseType "blob" is only supported on platforms with native Blob support`
|
||||
);
|
||||
this.response = new Blob([this.responseText]);
|
||||
break;
|
||||
default: //TODO: Support other types, eg: document, arraybuffer, json
|
||||
invariant(false, `responseType "${this.responseType}" is unsupported`);
|
||||
}
|
||||
this._cachedResponse = undefined; // force lazy recomputation
|
||||
this.setReadyState(this.LOADING);
|
||||
}
|
||||
}
|
||||
@@ -175,6 +253,7 @@ class XMLHttpRequestBase {
|
||||
if (requestId === this._requestId) {
|
||||
if (error) {
|
||||
this.responseText = error;
|
||||
this._hasError = true;
|
||||
}
|
||||
this._clearSubscriptions();
|
||||
this._requestId = null;
|
||||
@@ -304,4 +383,24 @@ XMLHttpRequestBase.HEADERS_RECEIVED = HEADERS_RECEIVED;
|
||||
XMLHttpRequestBase.LOADING = LOADING;
|
||||
XMLHttpRequestBase.DONE = DONE;
|
||||
|
||||
function toArrayBuffer(text: string, contentType: string): ArrayBuffer {
|
||||
const {length} = text;
|
||||
if (length === 0) {
|
||||
return new ArrayBuffer(0);
|
||||
}
|
||||
|
||||
const charsetMatch = contentType.match(/;\s*charset=([^;]*)/i);
|
||||
const charset = charsetMatch ? charsetMatch[1].trim() : 'utf-8';
|
||||
|
||||
if (/^utf-?8$/i.test(charset)) {
|
||||
return utf8.encode(text);
|
||||
} else { //TODO: utf16 / ucs2 / utf32
|
||||
const array = new Uint8Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = text.charCodeAt(i); // Uint8Array automatically masks with 0xff
|
||||
}
|
||||
return array.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = XMLHttpRequestBase;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
jest.autoMockOff();
|
||||
|
||||
const {encode} = require('../utf8');
|
||||
|
||||
describe('UTF-8 encoding:', () => {
|
||||
it('can encode code points < U+80', () => {
|
||||
const arrayBuffer = encode('\u0000abcDEF\u007f');
|
||||
expect(new Uint8Array(arrayBuffer)).toEqual(
|
||||
new Uint8Array([0x00, 0x61, 0x62, 0x63, 0x44, 0x45, 0x46, 0x7f]));
|
||||
});
|
||||
|
||||
it('can encode code points < U+800', () => {
|
||||
const arrayBuffer = encode('\u0080\u0548\u07ff');
|
||||
expect(new Uint8Array(arrayBuffer)).toEqual(
|
||||
new Uint8Array([0xc2, 0x80, 0xd5, 0x88, 0xdf, 0xbf]));
|
||||
});
|
||||
|
||||
it('can encode code points < U+10000', () => {
|
||||
const arrayBuffer = encode('\u0800\uac48\uffff');
|
||||
expect(new Uint8Array(arrayBuffer)).toEqual(
|
||||
new Uint8Array([0xe0, 0xa0, 0x80, 0xea, 0xb1, 0x88, 0xef, 0xbf, 0xbf]));
|
||||
});
|
||||
|
||||
it('can encode code points in the Supplementary Planes (surrogate pairs)', () => {
|
||||
const arrayBuffer = encode([
|
||||
'\ud800\udc00',
|
||||
'\ud800\ude89',
|
||||
'\ud83d\ude3b',
|
||||
'\udbff\udfff'
|
||||
].join(''));
|
||||
expect(new Uint8Array(arrayBuffer)).toEqual(
|
||||
new Uint8Array([
|
||||
0xf0, 0x90, 0x80, 0x80,
|
||||
0xf0, 0x90, 0x8a, 0x89,
|
||||
0xf0, 0x9f, 0x98, 0xbb,
|
||||
0xf4, 0x8f, 0xbf, 0xbf,
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it('allows for stray high surrogates', () => {
|
||||
const arrayBuffer = encode('a\ud8c6b');
|
||||
expect(new Uint8Array(arrayBuffer)).toEqual(
|
||||
new Uint8Array([0x61, 0xed, 0xa3, 0x86, 0x62]));
|
||||
});
|
||||
|
||||
it('allows for stray low surrogates', () => {
|
||||
const arrayBuffer = encode('a\ude19b');
|
||||
expect(new Uint8Array(arrayBuffer)).toEqual(
|
||||
new Uint8Array([0x61, 0xed, 0xb8, 0x99, 0x62]));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule utf8
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
class ByteVector {
|
||||
_storage: Uint8Array;
|
||||
_sizeWritten: number;
|
||||
|
||||
constructor(size) {
|
||||
this._storage = new Uint8Array(size);
|
||||
this._sizeWritten = 0;
|
||||
}
|
||||
|
||||
push(value: number): ByteVector {
|
||||
const i = this._sizeWritten;
|
||||
if (i === this._storage.length) {
|
||||
this._realloc();
|
||||
}
|
||||
this._storage[i] = value;
|
||||
this._sizeWritten = i + 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
getBuffer(): ArrayBuffer {
|
||||
return this._storage.buffer.slice(0, this._sizeWritten);
|
||||
}
|
||||
|
||||
_realloc() {
|
||||
const storage = this._storage;
|
||||
this._storage = new Uint8Array(align(storage.length * 1.5));
|
||||
this._storage.set(storage);
|
||||
}
|
||||
}
|
||||
|
||||
/*eslint-disable no-bitwise */
|
||||
exports.encode = (string: string): ArrayBuffer => {
|
||||
const {length} = string;
|
||||
const bytes = new ByteVector(length);
|
||||
|
||||
// each character / char code is assumed to represent an UTF-16 wchar.
|
||||
// With the notable exception of surrogate pairs, each wchar represents the
|
||||
// corresponding unicode code point.
|
||||
// For an explanation of UTF-8 encoding, read [1]
|
||||
// For an explanation of UTF-16 surrogate pairs, read [2]
|
||||
//
|
||||
// [1] https://en.wikipedia.org/wiki/UTF-8#Description
|
||||
// [2] https://en.wikipedia.org/wiki/UTF-16#U.2B10000_to_U.2B10FFFF
|
||||
let nextCodePoint = string.charCodeAt(0);
|
||||
for (let i = 0; i < length; i++) {
|
||||
let codePoint = nextCodePoint;
|
||||
nextCodePoint = string.charCodeAt(i + 1);
|
||||
|
||||
if (codePoint < 0x80) {
|
||||
bytes.push(codePoint);
|
||||
} else if (codePoint < 0x800) {
|
||||
bytes
|
||||
.push(0xc0 | codePoint >>> 6)
|
||||
.push(0x80 | codePoint & 0x3f);
|
||||
} else if (codePoint >>> 10 === 0x36 && nextCodePoint >>> 10 === 0x37) { // high surrogate & low surrogate
|
||||
codePoint = 0x10000 + (((codePoint & 0x3ff) << 10) | (nextCodePoint & 0x3ff));
|
||||
bytes
|
||||
.push(0xf0 | codePoint >>> 18 & 0x7)
|
||||
.push(0x80 | codePoint >>> 12 & 0x3f)
|
||||
.push(0x80 | codePoint >>> 6 & 0x3f)
|
||||
.push(0x80 | codePoint & 0x3f);
|
||||
|
||||
i += 1;
|
||||
nextCodePoint = string.charCodeAt(i + 1);
|
||||
} else {
|
||||
bytes
|
||||
.push(0xe0 | codePoint >>> 12)
|
||||
.push(0x80 | codePoint >>> 6 & 0x3f)
|
||||
.push(0x80 | codePoint & 0x3f);
|
||||
}
|
||||
}
|
||||
return bytes.getBuffer();
|
||||
};
|
||||
|
||||
// align to multiples of 8 bytes
|
||||
function align(size: number): number {
|
||||
return size % 8 ? (Math.floor(size / 8) + 1) << 3 : size;
|
||||
}
|
||||
+1
@@ -31,6 +31,7 @@ var ReactNative = {
|
||||
get ProgressViewIOS() { return require('ProgressViewIOS'); },
|
||||
get ScrollView() { return require('ScrollView'); },
|
||||
get SegmentedControlIOS() { return require('SegmentedControlIOS'); },
|
||||
get Slider() { return require('Slider'); },
|
||||
get SliderIOS() { return require('SliderIOS'); },
|
||||
get SnapshotViewIOS() { return require('SnapshotViewIOS'); },
|
||||
get Switch() { return require('Switch'); },
|
||||
|
||||
@@ -44,6 +44,7 @@ var ReactNative = Object.assign(Object.create(require('react')), {
|
||||
ScrollView: require('ScrollView'),
|
||||
SegmentedControlIOS: require('SegmentedControlIOS'),
|
||||
SliderIOS: require('SliderIOS'),
|
||||
Slider: require('Slider'),
|
||||
SnapshotViewIOS: require('SnapshotViewIOS'),
|
||||
StatusBar: require('StatusBar'),
|
||||
Switch: require('Switch'),
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "React"
|
||||
s.version = package['version']
|
||||
s.version = "0.24.1"
|
||||
s.summary = package['description']
|
||||
s.description = <<-DESC
|
||||
React Native apps are built using the React JS
|
||||
|
||||
@@ -39,9 +39,12 @@ static NSNumber *RCTGetEventID(id<RCTEvent> event)
|
||||
|
||||
@implementation RCTEventDispatcher
|
||||
{
|
||||
// We need this lock to protect access to _eventQueue and __eventsDispatchScheduled. It's filled in on main thread and consumed on js thread.
|
||||
// We need this lock to protect access to _events, _eventQueue and _eventsDispatchScheduled. It's filled in on main thread and consumed on js thread.
|
||||
NSLock *_eventQueueLock;
|
||||
NSMutableDictionary *_eventQueue;
|
||||
// We have this id -> event mapping so we coalesce effectively.
|
||||
NSMutableDictionary<NSNumber *, id<RCTEvent>> *_events;
|
||||
// This array contains ids of events in order they come in, so we can emit them to JS in the exact same order.
|
||||
NSMutableArray<NSNumber *> *_eventQueue;
|
||||
BOOL _eventsDispatchScheduled;
|
||||
}
|
||||
|
||||
@@ -52,7 +55,8 @@ RCT_EXPORT_MODULE()
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
_eventQueue = [NSMutableDictionary new];
|
||||
_events = [NSMutableDictionary new];
|
||||
_eventQueue = [NSMutableArray new];
|
||||
_eventQueueLock = [NSLock new];
|
||||
_eventsDispatchScheduled = NO;
|
||||
}
|
||||
@@ -131,12 +135,14 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
NSNumber *eventID = RCTGetEventID(event);
|
||||
|
||||
id<RCTEvent> previousEvent = _eventQueue[eventID];
|
||||
id<RCTEvent> previousEvent = _events[eventID];
|
||||
if (previousEvent) {
|
||||
RCTAssert([event canCoalesce], @"Got event %@ which cannot be coalesced, but has the same eventID %@ as the previous event %@", event, eventID, previousEvent);
|
||||
event = [previousEvent coalesceWithEvent:event];
|
||||
} else {
|
||||
[_eventQueue addObject:eventID];
|
||||
}
|
||||
_eventQueue[eventID] = event;
|
||||
_events[eventID] = event;
|
||||
|
||||
BOOL scheduleEventsDispatch = NO;
|
||||
if (!_eventsDispatchScheduled) {
|
||||
@@ -170,13 +176,15 @@ RCT_EXPORT_MODULE()
|
||||
- (void)flushEventsQueue
|
||||
{
|
||||
[_eventQueueLock lock];
|
||||
NSDictionary *eventQueue = _eventQueue;
|
||||
_eventQueue = [NSMutableDictionary new];
|
||||
NSDictionary *events = _events;
|
||||
_events = [NSMutableDictionary new];
|
||||
NSMutableArray *eventQueue = _eventQueue;
|
||||
_eventQueue = [NSMutableArray new];
|
||||
_eventsDispatchScheduled = NO;
|
||||
[_eventQueueLock unlock];
|
||||
|
||||
for (id<RCTEvent> event in eventQueue.allValues) {
|
||||
[self dispatchEvent:event];
|
||||
for (NSNumber *eventId in eventQueue) {
|
||||
[self dispatchEvent:events[eventId]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +234,6 @@ static BOOL RCTAnyTouchesChanged(NSSet<UITouch *> *touches)
|
||||
{
|
||||
// If gesture just recognized, send all touches to JS as if they just began.
|
||||
if (self.state == UIGestureRecognizerStateBegan) {
|
||||
_coalescingKey++;
|
||||
[self _updateAndDispatchTouches:_nativeTouches.set eventName:@"topTouchStart" originatingTime:0];
|
||||
|
||||
// We store this flag separately from `state` because after a gesture is
|
||||
@@ -253,6 +252,7 @@ static BOOL RCTAnyTouchesChanged(NSSet<UITouch *> *touches)
|
||||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
|
||||
_coalescingKey++;
|
||||
// "start" has to record new touches before extracting the event.
|
||||
// "end"/"cancel" needs to remove the touch *after* extracting the event.
|
||||
[self _recordNewTouches:touches];
|
||||
@@ -278,6 +278,7 @@ static BOOL RCTAnyTouchesChanged(NSSet<UITouch *> *touches)
|
||||
{
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
|
||||
_coalescingKey++;
|
||||
if (_dispatchedInitialTouches) {
|
||||
[self _updateAndDispatchTouches:touches eventName:@"touchEnd" originatingTime:event.timestamp];
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=0.0.1-master
|
||||
VERSION_NAME=0.24.1
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -17,6 +17,7 @@ android_library(
|
||||
react_native_target('java/com/facebook/react/views/progressbar:progressbar'),
|
||||
react_native_target('java/com/facebook/react/views/recyclerview:recyclerview'),
|
||||
react_native_target('java/com/facebook/react/views/scroll:scroll'),
|
||||
react_native_target('java/com/facebook/react/views/slider:slider'),
|
||||
react_native_target('java/com/facebook/react/views/swiperefresh:swiperefresh'),
|
||||
react_native_target('java/com/facebook/react/views/switchview:switchview'),
|
||||
react_native_target('java/com/facebook/react/views/text:text'),
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.facebook.react.views.progressbar.ReactProgressBarViewManager;
|
||||
import com.facebook.react.views.recyclerview.RecyclerViewBackedScrollViewManager;
|
||||
import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager;
|
||||
import com.facebook.react.views.scroll.ReactScrollViewManager;
|
||||
import com.facebook.react.views.slider.ReactSliderManager;
|
||||
import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager;
|
||||
import com.facebook.react.views.switchview.ReactSwitchManager;
|
||||
import com.facebook.react.views.text.ReactRawTextManager;
|
||||
@@ -109,6 +110,7 @@ public class MainReactPackage implements ReactPackage {
|
||||
new ReactProgressBarViewManager(),
|
||||
new ReactRawTextManager(),
|
||||
new ReactScrollViewManager(),
|
||||
new ReactSliderManager(),
|
||||
new ReactSwitchManager(),
|
||||
new FrescoBasedReactTextInlineImageViewManager(),
|
||||
new ReactTextInputManager(),
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
include_defs('//ReactAndroid/DEFS')
|
||||
|
||||
android_library(
|
||||
name = 'slider',
|
||||
srcs = glob(['*.java']),
|
||||
deps = [
|
||||
react_native_target('java/com/facebook/react/bridge:bridge'),
|
||||
react_native_target('java/com/facebook/react/common:common'),
|
||||
react_native_target('java/com/facebook/csslayout:csslayout'),
|
||||
react_native_target('java/com/facebook/react/uimanager:uimanager'),
|
||||
react_native_target('java/com/facebook/react/uimanager/annotations:annotations'),
|
||||
react_native_dep('android_res/android/support/v7/appcompat-orig:res-for-react-native'),
|
||||
react_native_dep('third-party/android/support/v7/appcompat-orig:appcompat'),
|
||||
react_native_dep('third-party/java/jsr-305:jsr-305'),
|
||||
],
|
||||
visibility = [
|
||||
'PUBLIC',
|
||||
],
|
||||
)
|
||||
|
||||
project_config(
|
||||
src_target = ':slider',
|
||||
)
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.slider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Slider that behaves more like the iOS one, for consistency.
|
||||
*
|
||||
* On iOS, the value is 0..1. Android SeekBar only supports integer values.
|
||||
* For consistency, we pretend in JS that the value is 0..1 but set the
|
||||
* SeekBar value to 0..100.
|
||||
*
|
||||
* Note that the slider is _not_ a controlled component (setValue isn't called
|
||||
* during dragging).
|
||||
*/
|
||||
public class ReactSlider extends SeekBar {
|
||||
|
||||
/**
|
||||
* If step is 0 (unset) we default to this total number of steps.
|
||||
* Don't use 100 which leads to rounding errors (0.200000000001).
|
||||
*/
|
||||
private static int DEFAULT_TOTAL_STEPS = 128;
|
||||
|
||||
/**
|
||||
* We want custom min..max range.
|
||||
* Android only supports 0..max range so we implement this ourselves.
|
||||
*/
|
||||
private double mMinValue = 0;
|
||||
private double mMaxValue = 0;
|
||||
|
||||
/**
|
||||
* Value sent from JS (setState).
|
||||
* Doesn't get updated during drag (slider is not a controlled component).
|
||||
*/
|
||||
private double mValue = 0;
|
||||
|
||||
/**
|
||||
* If zero it's determined automatically.
|
||||
*/
|
||||
private double mStep = 0;
|
||||
|
||||
public ReactSlider(Context context, @Nullable AttributeSet attrs, int style) {
|
||||
super(context, attrs, style);
|
||||
}
|
||||
|
||||
/* package */ void setMaxValue(double max) {
|
||||
mMaxValue = max;
|
||||
updateAll();
|
||||
}
|
||||
|
||||
/* package */ void setMinValue(double min) {
|
||||
mMinValue = min;
|
||||
updateAll();
|
||||
}
|
||||
|
||||
/* package */ void setValue(double value) {
|
||||
mValue = value;
|
||||
updateValue();
|
||||
}
|
||||
|
||||
/* package */ void setStep(double step) {
|
||||
mStep = step;
|
||||
updateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert SeekBar's native progress value (e.g. 0..100) to a value
|
||||
* passed to JS (e.g. -1.0..2.5).
|
||||
*/
|
||||
public double toRealProgress(int seekBarProgress) {
|
||||
if (seekBarProgress == getMax()) {
|
||||
return mMaxValue;
|
||||
}
|
||||
return seekBarProgress * mStep + mMinValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update underlying native SeekBar's values.
|
||||
*/
|
||||
private void updateAll() {
|
||||
if (mStep == 0) {
|
||||
mStep = (mMaxValue - mMinValue) / (double) DEFAULT_TOTAL_STEPS;
|
||||
}
|
||||
setMax(getTotalSteps());
|
||||
updateValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update value only (optimization in case only value is set).
|
||||
*/
|
||||
private void updateValue() {
|
||||
setProgress((int) Math.round(
|
||||
(mValue - mMinValue) / (mMaxValue - mMinValue) * getTotalSteps()));
|
||||
}
|
||||
|
||||
private int getTotalSteps() {
|
||||
return (int) Math.ceil((mMaxValue - mMinValue) / mStep);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.slider;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
/**
|
||||
* Event emitted by a ReactSliderManager when user changes slider position.
|
||||
*/
|
||||
public class ReactSliderEvent extends Event<ReactSliderEvent> {
|
||||
|
||||
public static final String EVENT_NAME = "topChange";
|
||||
|
||||
private final double mValue;
|
||||
private final boolean mFromUser;
|
||||
|
||||
public ReactSliderEvent(int viewId, long timestampMs, double value, boolean fromUser) {
|
||||
super(viewId, timestampMs);
|
||||
mValue = value;
|
||||
mFromUser = fromUser;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public boolean isFromUser() {
|
||||
return mFromUser;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return EVENT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCoalescingKey() {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
|
||||
}
|
||||
|
||||
private WritableMap serializeEventData() {
|
||||
WritableMap eventData = Arguments.createMap();
|
||||
eventData.putInt("target", getViewTag());
|
||||
eventData.putDouble("value", getValue());
|
||||
eventData.putBoolean("fromUser", isFromUser());
|
||||
return eventData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.slider;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import com.facebook.csslayout.CSSNode;
|
||||
import com.facebook.csslayout.MeasureOutput;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.common.SystemClock;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
import com.facebook.react.uimanager.SimpleViewManager;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.ViewProps;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
/**
|
||||
* Manages instances of {@code ReactSlider}.
|
||||
*
|
||||
* Note that the slider is _not_ a controlled component.
|
||||
*/
|
||||
public class ReactSliderManager extends SimpleViewManager<ReactSlider> {
|
||||
|
||||
private static final int STYLE = android.R.attr.seekBarStyle;
|
||||
|
||||
private static final String REACT_CLASS = "RCTSlider";
|
||||
|
||||
static class ReactSliderShadowNode extends LayoutShadowNode implements
|
||||
CSSNode.MeasureFunction {
|
||||
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
private boolean mMeasured;
|
||||
|
||||
private ReactSliderShadowNode() {
|
||||
setMeasureFunction(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) {
|
||||
if (!mMeasured) {
|
||||
SeekBar reactSlider = new ReactSlider(getThemedContext(), null, STYLE);
|
||||
final int spec = View.MeasureSpec.makeMeasureSpec(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
View.MeasureSpec.UNSPECIFIED);
|
||||
reactSlider.measure(spec, spec);
|
||||
mWidth = reactSlider.getMeasuredWidth();
|
||||
mHeight = reactSlider.getMeasuredHeight();
|
||||
mMeasured = true;
|
||||
}
|
||||
measureOutput.width = mWidth;
|
||||
measureOutput.height = mHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private static final SeekBar.OnSeekBarChangeListener ON_CHANGE_LISTENER =
|
||||
new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) {
|
||||
ReactContext reactContext = (ReactContext) seekbar.getContext();
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(
|
||||
new ReactSliderEvent(
|
||||
seekbar.getId(),
|
||||
SystemClock.nanoTime(),
|
||||
((ReactSlider)seekbar).toRealProgress(progress),
|
||||
fromUser));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekbar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekbar) {
|
||||
ReactContext reactContext = (ReactContext) seekbar.getContext();
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(
|
||||
new ReactSlidingCompleteEvent(
|
||||
seekbar.getId(),
|
||||
SystemClock.nanoTime(),
|
||||
((ReactSlider)seekbar).toRealProgress(seekbar.getProgress())));
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return REACT_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutShadowNode createShadowNodeInstance() {
|
||||
return new ReactSliderShadowNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getShadowNodeClass() {
|
||||
return ReactSliderShadowNode.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactSlider createViewInstance(ThemedReactContext context) {
|
||||
return new ReactSlider(context, null, STYLE);
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.ENABLED, defaultBoolean = true)
|
||||
public void setEnabled(ReactSlider view, boolean enabled) {
|
||||
view.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@ReactProp(name = "value", defaultDouble = 0d)
|
||||
public void setValue(ReactSlider view, double value) {
|
||||
view.setOnSeekBarChangeListener(null);
|
||||
view.setValue(value);
|
||||
view.setOnSeekBarChangeListener(ON_CHANGE_LISTENER);
|
||||
}
|
||||
|
||||
@ReactProp(name = "minimumValue", defaultDouble = 0d)
|
||||
public void setMinimumValue(ReactSlider view, double value) {
|
||||
view.setMinValue(value);
|
||||
}
|
||||
|
||||
@ReactProp(name = "maximumValue", defaultDouble = 1d)
|
||||
public void setMaximumValue(ReactSlider view, double value) {
|
||||
view.setMaxValue(value);
|
||||
}
|
||||
|
||||
@ReactProp(name = "step", defaultDouble = 0d)
|
||||
public void setStep(ReactSlider view, double value) {
|
||||
view.setStep(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addEventEmitters(final ThemedReactContext reactContext, final ReactSlider view) {
|
||||
view.setOnSeekBarChangeListener(ON_CHANGE_LISTENER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getExportedCustomDirectEventTypeConstants() {
|
||||
return MapBuilder.of(
|
||||
ReactSlidingCompleteEvent.EVENT_NAME,
|
||||
MapBuilder.of("registrationName", "onSlidingComplete"));
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.slider;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
/**
|
||||
* Event emitted when the user finishes dragging the slider.
|
||||
*/
|
||||
public class ReactSlidingCompleteEvent extends Event<ReactSlidingCompleteEvent> {
|
||||
|
||||
public static final String EVENT_NAME = "topSlidingComplete";
|
||||
|
||||
private final double mValue;
|
||||
|
||||
public ReactSlidingCompleteEvent(int viewId, long timestampMs, double value) {
|
||||
super(viewId, timestampMs);
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return EVENT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCoalescingKey() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCoalesce() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
|
||||
}
|
||||
|
||||
private WritableMap serializeEventData() {
|
||||
WritableMap eventData = Arguments.createMap();
|
||||
eventData.putInt("target", getViewTag());
|
||||
eventData.putDouble("value", getValue());
|
||||
return eventData;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,12 @@ module.exports = function(options, filename) {
|
||||
var transform = filename
|
||||
? './' + path.relative(path.dirname(filename), transformPath) // packager can't handle absolute paths
|
||||
: hmrTransform;
|
||||
|
||||
// Fix the module path to use '/' on Windows.
|
||||
if (path.sep === '\\') {
|
||||
transform = transform.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
return {
|
||||
plugins: resolvePlugins([
|
||||
[
|
||||
|
||||
@@ -96,6 +96,17 @@ module.exports = yeoman.generators.NamedBase.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
this.npmInstall('react', { '--save': true });
|
||||
var reactNativePackageJson = require('../../package.json');
|
||||
var { peerDependencies } = reactNativePackageJson;
|
||||
if (!peerDependencies) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reactVersion = peerDependencies.react;
|
||||
if (!reactVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.npmInstall(`react@${reactVersion}`, { '--save': true });
|
||||
}
|
||||
});
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "1000.0.0",
|
||||
"version": "0.24.1",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
@@ -134,7 +134,7 @@
|
||||
"babel-core": "^6.6.4",
|
||||
"babel-plugin-external-helpers": "^6.5.0",
|
||||
"babel-polyfill": "^6.6.1",
|
||||
"babel-preset-react-native": "1.5.2",
|
||||
"babel-preset-react-native": "^1.5.2",
|
||||
"babel-register": "^6.6.0",
|
||||
"babel-types": "^6.6.4",
|
||||
"babylon": "^6.6.4",
|
||||
@@ -148,7 +148,7 @@
|
||||
"fbjs-scripts": "^0.4.0",
|
||||
"graceful-fs": "^4.1.2",
|
||||
"image-size": "^0.3.5",
|
||||
"immutable": "^3.7.5",
|
||||
"immutable": "~3.7.6",
|
||||
"joi": "^6.6.1",
|
||||
"json-stable-stringify": "^1.0.1",
|
||||
"json5": "^0.4.0",
|
||||
@@ -157,7 +157,7 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"module-deps": "^3.9.1",
|
||||
"node-fetch": "^1.3.3",
|
||||
"node-haste": "~2.9.4",
|
||||
"node-haste": "~2.9.6",
|
||||
"opn": "^3.0.2",
|
||||
"optimist": "^0.6.1",
|
||||
"progress": "^1.1.8",
|
||||
@@ -190,4 +190,4 @@
|
||||
"react": "^0.14.5",
|
||||
"shelljs": "0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-12
@@ -189,23 +189,24 @@ class Bundler {
|
||||
);
|
||||
}
|
||||
|
||||
_hmrURL(prefix, platform, extensionOverride, path) {
|
||||
const matchingRoot = this._projectRoots.find(root => path.startsWith(root));
|
||||
_hmrURL(prefix, platform, extensionOverride, filePath) {
|
||||
const matchingRoot = this._projectRoots.find(root => filePath.startsWith(root));
|
||||
|
||||
if (!matchingRoot) {
|
||||
throw new Error('No matching project root for ', path);
|
||||
throw new Error('No matching project root for ', filePath);
|
||||
}
|
||||
|
||||
const extensionStart = path.lastIndexOf('.');
|
||||
let resource = path.substring(
|
||||
// Replaces '\' with '/' for Windows paths.
|
||||
if (path.sep === '\\') {
|
||||
filePath = filePath.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
const extensionStart = filePath.lastIndexOf('.');
|
||||
let resource = filePath.substring(
|
||||
matchingRoot.length,
|
||||
extensionStart !== -1 ? extensionStart : undefined,
|
||||
);
|
||||
|
||||
const extension = extensionStart !== -1
|
||||
? path.substring(extensionStart + 1)
|
||||
: null;
|
||||
|
||||
return (
|
||||
prefix + resource +
|
||||
'.' + extensionOverride + '?' +
|
||||
@@ -611,10 +612,11 @@ class Bundler {
|
||||
};
|
||||
|
||||
const json = JSON.stringify(asset);
|
||||
const assetRegistryPath = 'react-native/Libraries/Image/AssetRegistry';
|
||||
const code =
|
||||
`module.exports = require('AssetRegistry').registerAsset(${json});`;
|
||||
const dependencies = ['AssetRegistry'];
|
||||
const dependencyOffsets = [code.indexOf('AssetRegistry') - 1];
|
||||
`module.exports = require(${JSON.stringify(assetRegistryPath)}).registerAsset(${json});`;
|
||||
const dependencies = [assetRegistryPath];
|
||||
const dependencyOffsets = [code.indexOf(assetRegistryPath) - 1];
|
||||
|
||||
return {
|
||||
asset,
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ class Resolver {
|
||||
cache: opts.cache,
|
||||
shouldThrowOnUnresolvedErrors: (_, platform) => platform === 'ios',
|
||||
transformCode: opts.transformCode,
|
||||
assetDependencies: ['AssetRegistry'],
|
||||
assetDependencies: ['react-native/Libraries/Image/AssetRegistry'],
|
||||
});
|
||||
|
||||
this._getModuleId = options.getModuleId;
|
||||
|
||||
@@ -96,7 +96,7 @@ if (!CI_PULL_REQUEST && CIRCLE_PROJECT_USERNAME === `facebook`) {
|
||||
if (currentCommit === latestTagCommit) {
|
||||
echo(`------------ DEPLOYING latest`);
|
||||
// leave only releases and blog folder
|
||||
rm(`-rf`, ls(`*`).filter(name => (name !== 'releases') || (name !== 'blog')));
|
||||
rm(`-rf`, ls(`*`).filter(name => (name !== 'releases') && (name !== 'blog')));
|
||||
cd(`../..`);
|
||||
if (exec(`RN_VERSION=${version} RN_LATEST_VERSION=${latestVersion} \
|
||||
RN_AVAILABLE_DOCS_VERSIONS=${versions} node server/generate.js`).code !== 0) {
|
||||
|
||||
@@ -209,6 +209,7 @@ var components = [
|
||||
'../Libraries/Components/RefreshControl/RefreshControl.js',
|
||||
'../Libraries/Components/ScrollView/ScrollView.js',
|
||||
'../Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.ios.js',
|
||||
'../Libraries/Components/Slider/Slider.js',
|
||||
'../Libraries/Components/SliderIOS/SliderIOS.ios.js',
|
||||
'../Libraries/Components/StatusBar/StatusBar.js',
|
||||
'../Libraries/Components/Switch/Switch.js',
|
||||
|
||||
Reference in New Issue
Block a user