react-native-material-kit icon indicating copy to clipboard operation
react-native-material-kit copied to clipboard

[WIP] Disabled controls

Open Crash-- opened this issue 9 years ago • 12 comments

This PR is a Work In Progress (only few components and only tested on iOS)

Currently supported controls :

  • [x] Button
  • [x] Checkbox
  • [x] Slider
  • [x] RangeSlider
  • [x] RadioButton
  • [ ] Switch

You can add enabled={true|false} to each controls. If the component has some props to stylish it, you can also stylish the disabled state.

// Color of the border (outer circle), when checked
    borderOnColor: PropTypes.string,

    // Color of the border (outer circle), when unchecked
    borderOffColor: PropTypes.string,

    // Color of the inner circle, when checked
    fillColor: PropTypes.string,

    // Color of the border (outer circle), when checked and disabled
    disabledBorderOnColor: PropTypes.string,

    // Color of the border (outer circle), when unchecked and disabled
    disabledBorderOffColor: PropTypes.string,

    // Color of the inner circle, when checked and disabled
    disabledFillColor: PropTypes.string,

Any feedbacks on that functionality?

Crash-- avatar Jul 12 '16 13:07 Crash--

Does this mean that I have to have enable on all controls by default?

Btw, enable is the same as enable={true}.

dansjensen avatar Jul 21 '16 12:07 dansjensen

I recommend to update wording. enabled, disabled and editable represent state. But enable represent action.

However, I am more concern if we have to include true as default property. It is the opposite of the general rule of using disabled on DOM properties.

dansjensen avatar Jul 21 '16 12:07 dansjensen

I though I had choose this word by taking a look at material-ui (having a closer API can not be bad), but seems it's not the case. So I'll change enabled (typo in the desc...) to disabled

But don't worry, by default enabled is set to true so no need to add true property everywhere.

Crash-- avatar Jul 21 '16 12:07 Crash--

@Crash-- Awesome. Thanks for great work.

dansjensen avatar Jul 21 '16 12:07 dansjensen

What's remaining to get this merged?

mrshll avatar Aug 17 '16 13:08 mrshll

Not so much, I'll try to get it done within a few days. Thanks for the reminder ;)

Crash-- avatar Aug 17 '16 14:08 Crash--

Awesome! I'm happy to help get it across the line or do some testing. Let me know On Wed, Aug 17, 2016 at 9:05 AM Quentin Valmori [email protected] wrote:

Not so much, I'll try to get it done within a few days. Thanks for the reminder ;)

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/xinthink/react-native-material-kit/pull/199#issuecomment-240421599, or mute the thread https://github.com/notifications/unsubscribe-auth/AAUwHD0heUVQj51BYGHqCvP4ShrtUbf6ks5qgxVGgaJpZM4JKa82 .

mrshll avatar Aug 17 '16 14:08 mrshll

Any hope for this getting merged?

jimmyhmiller avatar Oct 26 '16 12:10 jimmyhmiller

Will it be merged?

sriraman avatar Jan 23 '17 18:01 sriraman

If anyone looking for disabled switch

<MKSwitch style={styles.appleSwitch}
             checked = {this.state.suspendSwitch}
             onCheckedChange={(e) => {
             this.setState({suspendSwitch: this.state.suspendSwitch})
         } }
     />

santhosh77h avatar Feb 08 '17 14:02 santhosh77h

When this will be merged to master branch?

binzailani3136 avatar Mar 09 '17 14:03 binzailani3136

for switch I added a disabled props like the following code, please have a look and update it, I just needed to update _onPress, _onPressIn, _onPressOut and componentWillRecieveProps function

/* eslint react/no-multi-comp:0, react/prop-types:0 */
//
// MDL style switch component.
//
// <image src="http://bit.ly/1OF6Z96" width="400"/>
//
// - @see [MDL Switch](http://bit.ly/1IcHMPo)
// - [Props](#props)
// - [Defaults](#defaults)
//
// Created by ywu on 15/7/28.
//

import React, {
  Component,
  PropTypes,
} from 'react';

import {
  Animated,
  TouchableWithoutFeedback,
  View,
} from 'react-native';

import MKColor from '../MKColor';
import * as utils from '../utils';
import { getTheme } from '../theme';

// ## <section id='thumb'>Thumb</section>
// `Thumb` component of the [`Switch`](#switch).
// Which is displayed as a circle with shadow and ripple effect.
class Thumb extends Component {
  static propTypes = {
    // [View Props](https://facebook.github.io/react-native/docs/view.html#props)...
    ...View.propTypes,
    checked: PropTypes.bool,
    onColor: PropTypes.string,
    offColor: PropTypes.string,
    rippleColor: PropTypes.string,
    rippleAniDuration: PropTypes.number,
    rippleRadius: PropTypes.number,
    thumbStyle: PropTypes.any,
  };

  // Default props of `Thumb`
  static defaultProps = {
    pointerEvents: 'none',
    style: {
      shadowColor: 'black',
      shadowRadius: 1,
      shadowOpacity: 0.7,
      shadowOffset: { width: 0, height: 1 },
    },
  };

  constructor(props) {
    super(props);
    this._animatedRippleScale = new Animated.Value(0);
    this._animatedRippleAlpha = new Animated.Value(0);
    this.state = {
      checked: false,
    };
  }

  componentWillMount() {
    this.setState({ checked: this.props.checked });
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.checked !== this.props.checked) {
      this.setState({ checked: nextProps.checked });
    }
  }

  // When a toggle action started.
  startToggle() {
    this.showRipple();
  }

  // When a toggle action (from the given state) is confirmed.
  // - {`boolean`} `fromState` the previous state
  confirmToggle(fromState) {
    this.setState(
      {
        checked: !fromState,
      }
    );
  }

  // When a toggle action is finished (confirmed or canceled).
  endToggle() {
    this.hideRipple();
  }

  // Start the ripple effect
  showRipple() {
    // scaling up the ripple layer
    this._rippleAni = Animated.parallel([
      Animated.timing(this._animatedRippleAlpha, {
        toValue: 1,
        duration: this.props.rippleAniDuration || 250,
      }),
      Animated.timing(this._animatedRippleScale, {
        toValue: 1,
        duration: this.props.rippleAniDuration || 250,
      }),
    ]);

    this._rippleAni.start(() => {
      this._rippleAni = undefined;

      // if any pending animation, do it
      if (this._pendingRippleAni) {
        this._pendingRippleAni();
      }
    });
  }

  // Stop the ripple effect
  hideRipple() {
    this._pendingRippleAni = () => {
      Animated.parallel([
        Animated.timing(this._animatedRippleScale, {
          toValue: 0,
          duration: this.props.rippleAniDuration || 250,
        }),
        Animated.timing(this._animatedRippleAlpha, {
          toValue: 0,
          duration: this.props.rippleAniDuration || 250,
        }),
      ]).start();

      this._pendingRippleAni = undefined;
    };

    if (!this._rippleAni) {
      // previous ripple animation is done, good to go
      this._pendingRippleAni();
    }
  }

  _getBgColor() {
    return this.state.checked ? this.props.onColor : this.props.offColor;
  }

  // Rendering the `Thumb`
  render() {
    const rippleSize = this.props.rippleRadius * 2;

    return (
      <View ref="container"
        style={[this.props.style, {
          position: 'absolute',
          width: rippleSize,
          height: rippleSize,
          backgroundColor: MKColor.Transparent,
        }]}
      >
        <View  // the circle
          style={[
            Thumb.defaultProps.style,
            this.props.thumbStyle,
            { backgroundColor: this._getBgColor() },
          ]}
        />
        <Animated.View  // the ripple layer
          style={{
            position: 'absolute',
            opacity: this._animatedRippleAlpha,
            backgroundColor: this.props.rippleColor,
            width: rippleSize,
            height: rippleSize,
            borderRadius: this.props.rippleRadius,
            top: 0,
            left: 0,
            transform: [
              { scale: this._animatedRippleScale },
            ],
          }}
        />
      </View>
    );
  }
}

// Enable animations on `Thumb`
const AnimatedThumb = Animated.createAnimatedComponent(Thumb);


// ## <section id='switch'>Switch</section>
// The `Switch` component. Which is made up of a `Track` and a [`Thumb`](#thumb).
class Switch extends Component {
  constructor(props) {
    super(props);
    this.theme = getTheme();
    this._animatedThumbLeft = new Animated.Value(0);
    this.state = {
      trackSize: 0,
      trackLength: 0,
      trackRadii: 0,
      trackMargin: 0,
      thumbFrame: { x: 0, padding: 0, r: 0, rippleRadii: 0 },
      checked: false,
    };
  }

  componentWillMount() {
    const nextState = this._layoutThumb(this.props.checked,
      this.props.thumbRadius,
      this.props.trackLength,
      this.props.trackSize);
    this.setState(Object.assign(nextState, { checked: this.props.checked, disabled:this.props.disabled }));
  }

  componentWillReceiveProps(nextProps) {
    const nextState = this._layoutThumb(nextProps.checked,
      nextProps.thumbRadius,
      nextProps.trackLength,
      nextProps.trackSize);
    this.setState(Object.assign(nextState, { checked: nextProps.checked, disabled:nextProps.disabled }));
  }

  // Un-boxing the `Thumb` node from `AnimatedComponent`,
  // in order to access the component functions defined in `Thumb`
  getThumb() {
    if (typeof this.refs.thumb.refs.node !== 'undefined') {
      return this.refs.thumb.refs.node;
    }
    return this.refs.thumb._component;
  }

  // property initializers begin
  _onPress = () => {
    if(this.props.disabled) return true;
    this.confirmToggle();
    if (this.props.onPress) {
      this.props.onPress();
    }
  };

  _onPressIn = () => {
     if(this.props.disabled) return true;
    this.startToggle();
    if (this.props.onPressIn) {
      this.props.onPressIn();
    }
  };

  _onPressOut = () => {
    if(this.props.disabled) return true;
    this.endToggle();
    if (this.props.onPressOut) {
      this.props.onPressOut();
    }
  };
  // property initializers end

  // Layout the thumb according to the size of the track
  _layoutThumb(checked, thumbRadius, trackLength, trackSize) {
    const trackRadii = trackSize / 2;
    const thumbRadii = thumbRadius;
    const rippleRadii = trackLength - trackSize;
    const trackMargin = rippleRadii - trackRadii;  // make room for ripple
    const thumbLeft = checked ? trackMargin + trackRadii : 0;
    this._animatedThumbLeft.setValue(thumbLeft);

    return {
      trackSize,
      trackLength,
      trackRadii,
      trackMargin,
      thumbFrame: {
        rippleRadii,
        x: thumbLeft,
        r: thumbRadii,
        padding: rippleRadii - thumbRadii,
      },
    };
  }

  // Move the thumb left or right according to the current state
  _translateThumb() {
    this._animatedThumbLeft.setValue(this.state.thumbFrame.x);
    const newX = this._computeThumbX(this.state.checked);
    Animated.timing(this._animatedThumbLeft, {
      toValue: newX,
      duration: this.props.thumbAniDuration || 300,
    }).start(() => {
      this.state.thumbFrame.x = newX;
    });
  }

  // Calc the next position (x-axis) of the thumb
  _computeThumbX(toChecked) {
    if (!this.state.thumbFrame.r) {
      return 0;
    }

    const { trackLength, trackSize } = this.state;
    const dx = (toChecked ? 1 : -1) * (trackLength - trackSize);
    return this.state.thumbFrame.x + dx;
  }

  // When a toggle action started.
  startToggle() {
    this.getThumb().startToggle();
  }

  // When a toggle action is confirmed.
  confirmToggle() {
    const prevState = this.state.checked;
    this.setState({ checked: !prevState }, () => {
      this.getThumb().confirmToggle(prevState);
      this._translateThumb();

      if (this.props.onCheckedChange) {
        this.props.onCheckedChange({ checked: this.state.checked });
      }
    });
  }

  // When a toggle action is finished (confirmed or canceled).
  endToggle() {
    this.getThumb().endToggle();
  }

  _getBgColor(theme) {
    const onColor = this.props.onColor || theme.onColor;
    const offColor = this.props.offColor || theme.offColor;
    return this.state.checked ? onColor : offColor;
  }

  // Rendering the `Switch`
  render() {
    const touchProps = {
      delayPressIn: this.props.delayPressIn,
      delayPressOut: this.props.delayPressOut,
      delayLongPress: this.props.delayLongPress,
      onLongPress: this.props.onLongPress,
    };

    const mergedStyle = Object.assign({}, this.theme.switchStyle, utils.compact({
      onColor: this.props.onColor,
      offColor: this.props.offColor,
      thumbOnColor: this.props.thumbOnColor,
      thumbOffColor: this.props.thumbOffColor,
      rippleColor: this.props.rippleColor,
    }));

    const thumbFrame = this.state.thumbFrame;
    const thumbProps = {
      checked: this.state.checked,
      onColor: mergedStyle.thumbOnColor,
      offColor: mergedStyle.thumbOffColor,
      rippleColor: mergedStyle.rippleColor,
      rippleRadius: thumbFrame.rippleRadii,
      rippleAniDuration: this.props.rippleAniDuration,
      radius: this.props.thumbRadius,
      style: {
        left: this._animatedThumbLeft,
        top: 0,
      },
      thumbStyle: {
        width: this.props.thumbRadius * 2,
        height: this.props.thumbRadius * 2,
        borderRadius: this.props.thumbRadius,
        top: thumbFrame.padding,
        left: thumbFrame.padding,
      },
    };

    return (
      <TouchableWithoutFeedback
        {...touchProps}
        onPress={this._onPress}
        onPressIn={this._onPressIn}
        onPressOut={this._onPressOut}
      >
        <View ref="container"
          pointerEvents="box-only"
          style={this.props.style}
        >
          <View ref="track"  // the 'track' part
            style={{
              width: this.props.trackLength,
              height: this.props.trackSize,
              backgroundColor: this._getBgColor(mergedStyle),
              borderRadius: this.state.trackRadii,
              margin: this.state.trackMargin,
            }}
          />
          <AnimatedThumb ref="thumb"  // the 'thumb' part
            {...thumbProps}
          />
        </View>
      </TouchableWithoutFeedback>
    );
  }
}

// ## <section id='props'>Props</section>
Switch.propTypes = {
  // Touchable...
  ...TouchableWithoutFeedback.propTypes,

  // Toggle status of the `Switch`
  checked: PropTypes.bool,

  // Callback when the toggle status is changed.
  onCheckedChange: PropTypes.func,

  // Color of the track, when switch is checked
  onColor: PropTypes.string,

  // Color of the track, when switch is off
  offColor: PropTypes.string,

  // The thickness of the Switch track
  trackSize: PropTypes.number,

  // The length of the Switch track
  trackLength: PropTypes.number,

  // Radius of the thumb button
  thumbRadius: PropTypes.number,

  // Color of the thumb, when switch is checked
  thumbOnColor: PropTypes.string,

  // Color of the thumb, when switch is off
  thumbOffColor: PropTypes.string,

  // Duration of the thumb sliding animation, in milliseconds
  thumbAniDuration: PropTypes.number,

  // Color of the ripple layer
  rippleColor: PropTypes.string,

  // Duration of the ripple effect, in milliseconds
  rippleAniDuration: PropTypes.number,

  //swicth will not be clickable anymore
  disabled:PropTypes.bool,
};

// ## <section id='defaults'>Defaults</section>
Switch.defaultProps = {
  disabled:false,
  checked: false,
  trackLength: 48,
  trackSize: 20,
  thumbRadius: 14,
};


// ## Public interface
module.exports = Switch;

manjeets12 avatar Jun 27 '17 10:06 manjeets12