"use strict";

import * as React from 'react';
import { Animated, TextInput as NativeTextInput } from 'react-native';
import TextFieldAffix from './TextField/Adornment/TextFieldAffix';
import TextFieldIcon from './TextField/Adornment/TextFieldIcon';
import TextFieldFlat from './TextField/TextFieldFlat';
import TextFieldOutlined from './TextField/TextFieldOutlined';
import { useInternalTheme } from './theming';
import { forwardRef } from './utils/forwardRef';
import { roundLayoutSize } from './utils/roundLayoutSize';
import { jsx as _jsx } from "react/jsx-runtime";
const BLUR_ANIMATION_DURATION = 180;
const FOCUS_ANIMATION_DURATION = 150;
const DefaultRenderer = props => /*#__PURE__*/_jsx(NativeTextInput, {
  ...props
});

/**
 * A component to allow users to input text.
 *
 * ## Usage
 * ```js
 * import * as React from 'react';
 * import { TextField } from './TextField';
 *
 * const MyComponent = () => {
 *   const [text, setText] = React.useState("");
 *
 *   return (
 *     <TextField
 *       label="Email"
 *       value={text}
 *       onChangeText={text => setText(text)}
 *     />
 *   );
 * };
 *
 * export default MyComponent;
 * ```
 *
 * @extends TextInput props https://reactnative.dev/docs/textinput#props
 */
const TextField = forwardRef(({
  mode = 'flat',
  dense = false,
  disabled = false,
  error: errorProp = false,
  multiline = false,
  editable = true,
  contentStyle,
  render = DefaultRenderer,
  theme: themeOverrides,
  ...rest
}, ref) => {
  const theme = useInternalTheme(themeOverrides);
  const isControlled = rest.value !== undefined;
  const validInputValue = isControlled ? rest.value : rest.defaultValue;
  const {
    current: labeled
  } = React.useRef(new Animated.Value(validInputValue ? 0 : 1));
  const {
    current: error
  } = React.useRef(new Animated.Value(errorProp ? 1 : 0));
  const [focused, setFocused] = React.useState(false);
  const [displayPlaceholder, setDisplayPlaceholder] = React.useState(false);
  const [uncontrolledValue, setUncontrolledValue] = React.useState(validInputValue);
  // Use value from props instead of local state when input is controlled
  const value = isControlled ? rest.value : uncontrolledValue;
  const [labelTextLayout, setLabelTextLayout] = React.useState({
    width: 33
  });
  const [inputContainerLayout, setInputContainerLayout] = React.useState({
    width: 65
  });
  const [labelLayout, setLabelLayout] = React.useState({
    measured: false,
    width: 0,
    height: 0
  });
  const [leftLayout, setLeftLayout] = React.useState({
    width: null,
    height: null
  });
  const [rightLayout, setRightLayout] = React.useState({
    width: null,
    height: null
  });
  const timer = React.useRef(undefined);
  const root = React.useRef(null);
  const {
    scale
  } = theme.animation;
  React.useImperativeHandle(ref, () => ({
    focus: () => root.current?.focus(),
    clear: () => root.current?.clear(),
    setNativeProps: args => root.current?.setNativeProps(args),
    isFocused: () => root.current?.isFocused() || false,
    blur: () => root.current?.blur(),
    forceFocus: () => root.current?.focus(),
    setSelection: (start, end) => root.current?.setSelection(start, end)
  }));
  React.useEffect(() => {
    // When the input has an error, we wiggle the label and apply error styles
    if (errorProp) {
      // show error
      Animated.timing(error, {
        toValue: 1,
        duration: FOCUS_ANIMATION_DURATION * scale,
        // To prevent this - https://github.com/callstack/react-native-paper/issues/941
        useNativeDriver: true
      }).start();
    } else {
      // hide error
      {
        Animated.timing(error, {
          toValue: 0,
          duration: BLUR_ANIMATION_DURATION * scale,
          // To prevent this - https://github.com/callstack/react-native-paper/issues/941
          useNativeDriver: true
        }).start();
      }
    }
  }, [errorProp, scale, error]);
  React.useEffect(() => {
    // Show placeholder text only if the input is focused, or there's no label
    // We don't show placeholder if there's a label because the label acts as placeholder
    // When focused, the label moves up, so we can show a placeholder
    if (focused || !rest.label) {
      // If the user wants to use the contextMenu, when changing the placeholder, the contextMenu is closed
      // This is a workaround to mitigate this behavior in scenarios where the placeholder is not specified.
      if (rest.placeholder) {
        // Display placeholder in a delay to offset the label animation
        // If we show it immediately, they'll overlap and look ugly
        timer.current = setTimeout(() => setDisplayPlaceholder(true), 50);
      }
    } else {
      // hidePlaceholder
      setDisplayPlaceholder(false);
    }
    return () => {
      if (timer.current) {
        clearTimeout(timer.current);
      }
    };
  }, [focused, rest.label, rest.placeholder]);
  React.useEffect(() => {
    labeled.stopAnimation();
    // The label should be minimized if the text input is focused, or has text
    // In minimized mode, the label moves up and becomes small
    // workaround for animated regression for react native > 0.61
    // https://github.com/callstack/react-native-paper/pull/1440
    if (value || focused) {
      // minimize label
      Animated.timing(labeled, {
        toValue: 0,
        duration: BLUR_ANIMATION_DURATION * scale,
        // To prevent this - https://github.com/callstack/react-native-paper/issues/941
        useNativeDriver: true
      }).start();
    } else {
      // restore label
      Animated.timing(labeled, {
        toValue: 1,
        duration: FOCUS_ANIMATION_DURATION * scale,
        // To prevent this - https://github.com/callstack/react-native-paper/issues/941
        useNativeDriver: true
      }).start();
    }
  }, [focused, value, labeled, scale]);
  const onLeftAffixLayoutChange = React.useCallback(event => {
    const height = roundLayoutSize(event.nativeEvent.layout.height);
    const width = roundLayoutSize(event.nativeEvent.layout.width);
    if (width !== leftLayout.width || height !== leftLayout.height) {
      setLeftLayout({
        width,
        height
      });
    }
  }, [leftLayout.height, leftLayout.width]);
  const onRightAffixLayoutChange = React.useCallback(event => {
    const width = roundLayoutSize(event.nativeEvent.layout.width);
    const height = roundLayoutSize(event.nativeEvent.layout.height);
    if (width !== rightLayout.width || height !== rightLayout.height) {
      setRightLayout({
        width,
        height
      });
    }
  }, [rightLayout.height, rightLayout.width]);
  const handleFocus = args => {
    if (disabled || !editable) {
      return;
    }
    setFocused(true);
    rest.onFocus?.(args);
  };
  const handleBlur = args => {
    if (!editable) {
      return;
    }
    setFocused(false);
    rest.onBlur?.(args);
  };
  const handleChangeText = value => {
    if (!editable || disabled) {
      return;
    }
    if (!isControlled) {
      // Keep track of value in local state when input is not controlled
      setUncontrolledValue(value);
    }
    rest.onChangeText?.(value);
  };
  const handleLayoutAnimatedText = React.useCallback(e => {
    const width = roundLayoutSize(e.nativeEvent.layout.width);
    const height = roundLayoutSize(e.nativeEvent.layout.height);
    if (width !== labelLayout.width || height !== labelLayout.height) {
      setLabelLayout({
        width,
        height,
        measured: true
      });
    }
  }, [labelLayout.height, labelLayout.width]);
  const handleLabelTextLayout = React.useCallback(({
    nativeEvent
  }) => {
    setLabelTextLayout({
      width: nativeEvent.lines.reduce((acc, line) => acc + Math.ceil(line.width), 0)
    });
  }, []);
  const handleInputContainerLayout = React.useCallback(({
    nativeEvent: {
      layout
    }
  }) => {
    setInputContainerLayout({
      width: layout.width
    });
  }, []);
  const forceFocus = React.useCallback(() => root.current?.focus(), []);
  const {
    maxFontSizeMultiplier = 1.5
  } = rest;
  const scaledLabel = !!(value || focused);
  if (mode === 'outlined') {
    return /*#__PURE__*/_jsx(TextFieldOutlined, {
      dense: dense,
      disabled: disabled,
      error: errorProp,
      multiline: multiline,
      editable: editable,
      render: render,
      ...rest,
      theme: theme,
      value: value,
      parentState: {
        labeled,
        error,
        focused,
        displayPlaceholder,
        value,
        labelTextLayout,
        labelLayout,
        leftLayout,
        rightLayout,
        inputContainerLayout
      },
      innerRef: ref => {
        root.current = ref;
      },
      onFocus: handleFocus,
      forceFocus: forceFocus,
      onBlur: handleBlur,
      onChangeText: handleChangeText,
      onLayoutAnimatedText: handleLayoutAnimatedText,
      onInputLayout: handleInputContainerLayout,
      onLabelTextLayout: handleLabelTextLayout,
      onLeftAffixLayoutChange: onLeftAffixLayoutChange,
      onRightAffixLayoutChange: onRightAffixLayoutChange,
      maxFontSizeMultiplier: maxFontSizeMultiplier,
      contentStyle: contentStyle,
      scaledLabel: scaledLabel
    });
  }
  return /*#__PURE__*/_jsx(TextFieldFlat, {
    dense: dense,
    disabled: disabled,
    error: errorProp,
    multiline: multiline,
    editable: editable,
    render: render,
    ...rest,
    theme: theme,
    value: value,
    parentState: {
      labeled,
      error,
      focused,
      displayPlaceholder,
      value,
      labelTextLayout,
      labelLayout,
      leftLayout,
      rightLayout,
      inputContainerLayout
    },
    innerRef: ref => {
      root.current = ref;
    },
    onFocus: handleFocus,
    forceFocus: forceFocus,
    onBlur: handleBlur,
    onInputLayout: handleInputContainerLayout,
    onChangeText: handleChangeText,
    onLayoutAnimatedText: handleLayoutAnimatedText,
    onLabelTextLayout: handleLabelTextLayout,
    onLeftAffixLayoutChange: onLeftAffixLayoutChange,
    onRightAffixLayoutChange: onRightAffixLayoutChange,
    maxFontSizeMultiplier: maxFontSizeMultiplier,
    contentStyle: contentStyle,
    scaledLabel: scaledLabel
  });
});
// @component ./TextField/Adornment/TextFieldIcon.tsx
TextField.Icon = TextFieldIcon;

// @component ./TextField/Adornment/TextFieldAffix.tsx
// @ts-ignore Types of property 'theme' are incompatible.
TextField.Affix = TextFieldAffix;
export default TextField;
//# sourceMappingURL=TextField.js.map