<commit_before>import * as React from 'react'
import { structEq } from './../utils'
import { Atom } from './../atom'
import { ObservableReactHTMLProps } from './observablePropTypes'
import { warning, getReactComponentName, DEV_ENV } from './../utils'
import { Observable, ObservableInput } from 'rxjs/Observable'
import { Subscription as RxSubscription } from 'rxjs/Subscription'
import 'rxjs/add/operator/scan'
import 'rxjs/add/operator/map'
import 'rxjs/add/observable/combineLatest'

// a hack required to support the declaration merging of the Atom type and
// Atom namespace in atom/index.ts
//
// if we don't do it, we get the "cannot be named" compiler error.
import { Atom as _Atom } from './../atom/base' // tslint:disable-line no-unused-vars

export interface Subscription {
  unsubscribe(): void
}

export type Lifted<T> = {
  [K in keyof T]: T[K] | Observable<T[K]>
}

export interface LiftWrapperProps<TProps> {
  component: React.Component<TProps, any>
    | React.StatelessComponent<TProps>
    | React.ComponentClass<TProps>
    | keyof React.ReactHTML
  props: Lifted<TProps>
}

export interface LiftWrapperState {
  renderCache?: React.DOMElement<any, any> | null
  subscription?: Subscription | null
}

/**
 * A wrapper component that allows to use observables for prop values of a
 * given component.
 */
class LiftWrapper<TProps>
    extends React.Component<LiftWrapperProps<TProps>, LiftWrapperState> {
  constructor(props: LiftWrapperProps<TProps>) {
    super(props, LiftWrapper._initState)
  }

  static _initState: LiftWrapperState = {
    renderCache: null,
    subscription: null
  }

  static _endState: LiftWrapperState = {
    subscription: null
  }

  render() {
    return this.state.renderCache || null
  }

  private _subscribe(newProps: LiftWrapperProps<TProps>) {
    const { props, component } = newProps

    let n = 0
    walkObservables(props, () => n += 1)

    switch (n) {
      case 0:
        this.setState({
          subscription: null,
          renderCache: render(component, props)
        })
        break

      // @NOTE original Calmm code below
      // The created object is never used and it looks like that
      // the useful work is done in the constructor.
      // Could this be replaced by a regular closure? Perhaps using
      // a class is an optimization?
      case 1:
        new RenderOne(this, newProps) // tslint:disable-line
        break
      default:
        new RenderMany(this, newProps, n) // tslint:disable-line
        break
    }
  }

  private _unsubscribe() {
    const subscription = this.state ? this.state.subscription : null
    if (subscription)
      subscription.unsubscribe()
  }

  componentWillReceiveProps(newProps: LiftWrapperProps<TProps>) {
    this._unsubscribe()
    this._subscribe(newProps)
  }

  componentWillMount() {
    this._unsubscribe()
    this._subscribe(this.props)
  }

  componentWillUnmount() {
    this._unsubscribe()
    this.setState(LiftWrapper._initState)
  }

  shouldComponentUpdate(
    _newProps: Readonly<LiftWrapperProps<TProps>>,
    newState: Readonly<LiftWrapperState>,
    _newContext: any
  ) {
    return newState.renderCache !== this.state.renderCache
  }
}

// here we only say TProps, but a lifted component
// will also accept a value of Observable<T> for any prop of
// type T.
export type LiftedComponentProps<TProps> = Lifted<TProps> & {
  mount?(el: HTMLElement): void
}

/**
 * 'Lift' a component so it can accept observable values for props.
 *
 * A lifted component will accept a value of Observable<T> for any prop of
 * type T. However, this is not registered in the lifted component prop types
 * in any way (not possible right now), so casting observable values to `any` is
 * required.
 *
 *
 * @example
 * import { lift } from '@grammarly/focal';
 *
 * type HelloComponentProps = { name: string };
 *
 * const HelloComponent = ({ name }: HelloComponentProps) => <div>Hello, {name}.</div>;
 *
 * // normal usage
 * <HelloComponent name='world' />
 *
 * // lifted version of the same component
 * const LiftedHelloComponent = lift(HelloComponent);
 *
 * const observableValue = Observable.of('world'));
 *
 * // using an observable value for prop
 * <LiftedHelloComponent name={observableValue as any} />
 */
export function lift<TProps>(
  component: React.ComponentClass<TProps> | React.StatelessComponent<TProps>
) {
  return (props: LiftedComponentProps<TProps>) =>
    React.createElement<LiftWrapperProps<TProps>>(
      LiftWrapper,
      { component: component, props: props }
    )
}

export interface LiftedIntrinsicComponentProps<E> extends ObservableReactHTMLProps<E> {
  mount?(el: E): void
}

export function liftIntrinsic<E extends Element>(intrinsicClassName: keyof React.ReactHTML) {
  return (props: LiftedIntrinsicComponentProps<E>) =>
    React.createElement<LiftWrapperProps<ObservableReactHTMLProps<E>>>(
      LiftWrapper,
      { component: intrinsicClassName, props: props }) as
        any as React.DOMElement<LiftedIntrinsicComponentProps<E>, E>
}<commit_msg>Use functional component type for intrinsics as is<commit_after>import * as React from 'react'
import { structEq } from './../utils'
import { Atom } from './../atom'
import { ObservableReactHTMLProps } from './observablePropTypes'
import { warning, getReactComponentName, DEV_ENV } from './../utils'
import { Observable, ObservableInput } from 'rxjs/Observable'
import { Subscription as RxSubscription } from 'rxjs/Subscription'
import 'rxjs/add/operator/scan'
import 'rxjs/add/operator/map'
import 'rxjs/add/observable/combineLatest'

// a hack required to support the declaration merging of the Atom type and
// Atom namespace in atom/index.ts
//
// if we don't do it, we get the "cannot be named" compiler error.
import { Atom as _Atom } from './../atom/base' // tslint:disable-line no-unused-vars

export interface Subscription {
  unsubscribe(): void
}

export type Lifted<T> = {
  [K in keyof T]: T[K] | Observable<T[K]>
}

export interface LiftWrapperProps<TProps> {
  component: React.Component<TProps, any>
    | React.StatelessComponent<TProps>
    | React.ComponentClass<TProps>
    | keyof React.ReactHTML
  props: Lifted<TProps>
}

export interface LiftWrapperState {
  renderCache?: React.DOMElement<any, any> | null
  subscription?: Subscription | null
}

/**
 * A wrapper component that allows to use observables for prop values of a
 * given component.
 */
class LiftWrapper<TProps>
    extends React.Component<LiftWrapperProps<TProps>, LiftWrapperState> {
  constructor(props: LiftWrapperProps<TProps>) {
    super(props, LiftWrapper._initState)
  }

  static _initState: LiftWrapperState = {
    renderCache: null,
    subscription: null
  }

  static _endState: LiftWrapperState = {
    subscription: null
  }

  render() {
    return this.state.renderCache || null
  }

  private _subscribe(newProps: LiftWrapperProps<TProps>) {
    const { props, component } = newProps

    let n = 0
    walkObservables(props, () => n += 1)

    switch (n) {
      case 0:
        this.setState({
          subscription: null,
          renderCache: render(component, props)
        })
        break

      // @NOTE original Calmm code below
      // The created object is never used and it looks like that
      // the useful work is done in the constructor.
      // Could this be replaced by a regular closure? Perhaps using
      // a class is an optimization?
      case 1:
        new RenderOne(this, newProps) // tslint:disable-line
        break
      default:
        new RenderMany(this, newProps, n) // tslint:disable-line
        break
    }
  }

  private _unsubscribe() {
    const subscription = this.state ? this.state.subscription : null
    if (subscription)
      subscription.unsubscribe()
  }

  componentWillReceiveProps(newProps: LiftWrapperProps<TProps>) {
    this._unsubscribe()
    this._subscribe(newProps)
  }

  componentWillMount() {
    this._unsubscribe()
    this._subscribe(this.props)
  }

  componentWillUnmount() {
    this._unsubscribe()
    this.setState(LiftWrapper._initState)
  }

  shouldComponentUpdate(
    _newProps: Readonly<LiftWrapperProps<TProps>>,
    newState: Readonly<LiftWrapperState>,
    _newContext: any
  ) {
    return newState.renderCache !== this.state.renderCache
  }
}

// here we only say TProps, but a lifted component
// will also accept a value of Observable<T> for any prop of
// type T.
export type LiftedComponentProps<TProps> = Lifted<TProps> & {
  mount?(el: HTMLElement): void
}

/**
 * 'Lift' a component so it can accept observable values for props.
 *
 * A lifted component will accept a value of Observable<T> for any prop of
 * type T. However, this is not registered in the lifted component prop types
 * in any way (not possible right now), so casting observable values to `any` is
 * required.
 *
 *
 * @example
 * import { lift } from '@grammarly/focal';
 *
 * type HelloComponentProps = { name: string };
 *
 * const HelloComponent = ({ name }: HelloComponentProps) => <div>Hello, {name}.</div>;
 *
 * // normal usage
 * <HelloComponent name='world' />
 *
 * // lifted version of the same component
 * const LiftedHelloComponent = lift(HelloComponent);
 *
 * const observableValue = Observable.of('world'));
 *
 * // using an observable value for prop
 * <LiftedHelloComponent name={observableValue as any} />
 */
export function lift<TProps>(
  component: React.ComponentClass<TProps> | React.StatelessComponent<TProps>
) {
  return (props: LiftedComponentProps<TProps>) =>
    React.createElement<LiftWrapperProps<TProps>>(
      LiftWrapper,
      { component: component, props: props }
    )
}

export interface LiftedIntrinsicComponentProps<E> extends ObservableReactHTMLProps<E> {
  mount?(el: E): void
}

export function liftIntrinsic<E extends Element>(intrinsicClassName: keyof React.ReactHTML) {
  return (props: LiftedIntrinsicComponentProps<E>) =>
    React.createElement<LiftWrapperProps<ObservableReactHTMLProps<E>>>(
      LiftWrapper,
      { component: intrinsicClassName, props: props }
    )
}
{'fraction_non_alphanumeric': 0.10858736800159394, 'fraction_numerical': 0.0007969715082685794, 'mean_word_length': 3.586112380082229, 'pattern_counts': {'":': 0, '<': 81, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3782695', 'n_tokens_mistral': 3005, 'n_tokens_neox': 2984, 'n_words': 858}