react#useImperativeHandle JavaScript Examples

The following examples show how to use react#useImperativeHandle. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: SkillsDisplay.js    From skills-client with Apache License 2.0 6 votes vote down vote up
SkillDisplay = forwardRef(({ theme = null, version = null, userId = null, handleRouteChanged = null, options = {} }, ref) => {
  useEffect(() => {
    clientDisplay = new SkillsDisplayJS({
      options,
      theme,
      version,
      userId,
      handleRouteChanged,
    });

    clientDisplay.attachTo(document.getElementById('clientSkillsDisplayContainer'));

    return () => {
      destroy(clientDisplay);
    };
  }, [theme, version, userId, handleRouteChanged, options]);

  useImperativeHandle(ref, () => ({
    navigate(path) {
      if (clientDisplay) {
        clientDisplay.navigate(path);
      }
    },
  }), []);

  return (<div id='clientSkillsDisplayContainer'/>);
})
Example #2
Source File: Avatar.jsx    From chat-ui-kit-react with MIT License 6 votes vote down vote up
function AvatarInner(
  { name, src, size, status, className, active, children, ...rest },
  ref
) {
  const cName = `${prefix}-avatar`;
  const sizeClass = typeof size !== "undefined" ? ` ${cName}--${size}` : "";

  const avatarRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => avatarRef.current.focus(),
  }));

  return (
    <div
      ref={avatarRef}
      {...rest}
      className={classNames(
        `${cName}${sizeClass}`,
        { [`${cName}--active`]: active },
        className
      )}
    >
      {children ? (
        children
      ) : (
        <>
          <img src={src} alt={name} />
          {typeof status === "string" && (
            <Status status={status} size={size} />
          )}{" "}
        </>
      )}
    </div>
  );
}
Example #3
Source File: BlockFooter.jsx    From konsta with MIT License 6 votes vote down vote up
BlockFooter = forwardRef((props, ref) => {
  const {
    component = 'div',
    className,

    ios,
    material,

    // Children
    children,

    // Rest
    ...rest
  } = props;

  const elRef = useRef(null);

  useImperativeHandle(ref, () => ({
    el: elRef.current,
  }));

  const Component = component;

  const attrs = {
    ...rest,
  };

  const themeClasses = useThemeClasses({ ios, material });

  const c = themeClasses(BlockFooterClasses(), className);

  return (
    <Component ref={elRef} className={c.base} {...attrs}>
      {children}
    </Component>
  );
})
Example #4
Source File: Accordion.js    From lundium with MIT License 6 votes vote down vote up
Accordion = forwardRef(({ openItemIndex, className, children }, ref) => {
	const [indexOpen, setIndexOpen] = useState(openItemIndex);

	useEffect(() => {
		if (!isNaN(openItemIndex)) {
			setIndexOpen(openItemIndex);
		}
	}, [openItemIndex]);

	useImperativeHandle(ref, () => ({ setActiveIndex: setIndexOpen }));

	return (
		<Box className={cx('accordion', className)}>
			{Children.map(children, (child, index) =>
				cond([
					[
						o(equals(AccordionItem), prop('type')),
						clone({ index, indexOpen, setIndexOpen }),
					],
					[T, identity],
				])(child),
			)}
		</Box>
	);
})
Example #5
Source File: SearchInput.js    From lx-music-mobile with Apache License 2.0 6 votes vote down vote up
SearchInput = (props, ref) => {
  const textInputRef = useRef()

  useImperativeHandle(ref, () => ({
    blur() {
      if (!textInputRef.current) return
      textInputRef.current.blur()
    },
  }))

  return (
    <View style={{ ...styles.container, ...props.styles }}>
      <Input {...props} ref={textInputRef} />
    </View>
  )
}
Example #6
Source File: modal.js    From stakeit.space with GNU Affero General Public License v3.0 6 votes vote down vote up
Modal = forwardRef( (props, ref) => {
   const [display, setDisplay] = useState(false);

   useImperativeHandle(ref, () => {
      return {
         openMod: () => open(),
         closeMod: () => close()
      }
   })

   const open = () => {
      setDisplay(true);
   };

   const close = () => {
      setDisplay(false);
   };

   if (display) {
      return ReactDOM.createPortal(
         <ModalWrapper>
            <ModalBackdrop onClick={close} />

            <ModalWindow>{props.children}</ModalWindow>
         </ModalWrapper>,

         document.getElementById("modal_root")
      );
   } else {
      return null;
   }
})
Example #7
Source File: StripeInput.jsx    From rysolv with GNU Affero General Public License v3.0 6 votes vote down vote up
StripeInput = ({ component: Component, inputRef, ...restProps }) => {
  const elementRef = useRef();
  useImperativeHandle(inputRef, () => ({
    focus: () => elementRef.current.focus,
  }));
  return (
    <Component
      onReady={element => (elementRef.current = element)}
      {...restProps}
    />
  );
}
Example #8
Source File: index.js    From react-native-echarts-pro with MIT License 6 votes vote down vote up
function APP(props, ref) {
  const chartRef = useRef();
  useImperativeHandle(ref, () => ({
    setNewOption(option, optionSetting) {
      chartRef.current.setNewOption(option, optionSetting);
    },
    /**
     * 触发ECharts 中支持的图表行为
     * Chart actions supported by ECharts are triggered through dispatchAction.
     * @param {object|array} action
     */
    dispatchAction(action) {
      chartRef.current.dispatchAction(action);
    },
    /**
     * Get echarts instance support function.
     * @param {string} functionName
     * @param {object} params
     */
    async getInstance(functionName, params) {
      return await chartRef.current.getInstance(functionName, params);
    },
  }));
  return (
    <Container width={props.width}>
      <Echarts {...props} ref={chartRef} />
    </Container>
  );
}
Example #9
Source File: StripeInput.js    From turqV2 with GNU General Public License v3.0 6 votes vote down vote up
StripeInput = (
     { component: Component, inputRef, ...props }
) => {     const elementRef = useRef();
     useImperativeHandle(inputRef, () => ({
          focus: () => elementRef.current.focus
     }));     return (
          <Component
               onReady={element => (elementRef.current = element)}     
               {...props}
          />
     )
}
Example #10
Source File: MarkdownEditor.component.jsx    From Stackoverflow-Clone-Frontend with MIT License 5 votes vote down vote up
MarkdownEditor = forwardRef((props, ref) => {
  const [value, setValue] = useState(RichTextEditor.createEmptyValue());

  useImperativeHandle(ref, () => ({
    cleanEditorState() {
      setValue(RichTextEditor.createEmptyValue());
    },
  }));

  const onChange = (newValue) => {
    setValue(newValue);
    if (props.onChange) {
      // Send the changes up to the parent component as an HTML string.
      // This is here to demonstrate using `.toString()` but in a real app it
      // would be better to avoid generating a string on each change.
      props.onChange(newValue.toString('html'));
    }
  };

  // The toolbarConfig object allows you to specify custom buttons, reorder buttons and to add custom css classes.
  // Supported inline styles: https://github.com/facebook/draft-js/blob/master/docs/Advanced-Topics-Inline-Styles.md
  // Supported block types: https://github.com/facebook/draft-js/blob/master/docs/Advanced-Topics-Custom-Block-Render.md#draft-default-block-render-map
  const toolbarConfig = {
    // Optionally specify the groups to display (displayed in the order listed).
    display: [
      'INLINE_STYLE_BUTTONS',
      'BLOCK_TYPE_BUTTONS',
      'LINK_BUTTONS',
      // 'BLOCK_TYPE_DROPDOWN',
      // 'HISTORY_BUTTONS',
    ],
    INLINE_STYLE_BUTTONS: [
      {label: 'Bold', style: 'BOLD', className: 'button-format'},
      {label: 'Italic', style: 'ITALIC', className: 'button-format'},
      {label: 'Underline', style: 'UNDERLINE', className: 'button-format'},
      // {label: 'Monospace', style: 'CODE', className: 'button-format'},
    ],
    // BLOCK_TYPE_DROPDOWN: [
    //   {label: 'Normal', style: 'unstyled'},
    //   {label: 'Heading Large', style: 'header-one'},
    //   {label: 'Heading Medium', style: 'header-two'},
    //   {label: 'Heading Small', style: 'header-three'},
    // ],
    BLOCK_TYPE_BUTTONS: [
      {label: 'UL', style: 'unordered-list-item', className: 'button-format'},
      {label: 'OL', style: 'ordered-list-item', className: 'button-format'},
      {label: 'Blockquote', style: 'blockquote', className: 'button-format'},
      {
        label: 'Code Block',
        style: 'code-block',
        className: 'button-format code-block',
      },
    ],
  };
  return (
    <RichTextEditor
      className='rich-text-editor-root'
      toolbarClassName='rich-text-editor-toolbar'
      editorClassName='rich-text-editor-editor'
      toolbarConfig={toolbarConfig}
      value={value}
      onChange={onChange}
    />
  );
})
Example #11
Source File: DateSelect.js    From actual with MIT License 5 votes vote down vote up
DatePicker = React.forwardRef(
  ({ value, dateFormat, onUpdate, onSelect }, ref) => {
    let picker = useRef(null);
    let mountPoint = useRef(null);

    useImperativeHandle(
      ref,
      () => ({
        handleInputKeyDown(e) {
          let newDate = null;
          switch (e.keyCode) {
            case 37:
              e.preventDefault();
              newDate = d.subDays(picker.current.getDate(), 1);
              break;
            case 38:
              e.preventDefault();
              newDate = d.subDays(picker.current.getDate(), 7);
              break;
            case 39:
              e.preventDefault();
              newDate = d.addDays(picker.current.getDate(), 1);
              break;
            case 40:
              e.preventDefault();
              newDate = d.addDays(picker.current.getDate(), 7);
              break;
            default:
          }
          if (newDate) {
            picker.current.setDate(newDate, true);
            onUpdate && onUpdate(newDate);
          }
        }
      }),
      []
    );

    useLayoutEffect(() => {
      picker.current = new Pikaday({
        theme: 'actual-date-picker',
        keyboardInput: false,
        defaultDate: value
          ? d.parse(value, dateFormat, new Date())
          : new Date(),
        setDefaultDate: true,
        toString(date) {
          return d.format(date, dateFormat);
        },
        parse(dateString) {
          return d.parse(dateString, dateFormat, new Date());
        },
        onSelect
      });

      mountPoint.current.appendChild(picker.current.el);

      return () => {
        picker.current.destroy();
      };
    }, []);

    useEffect(() => {
      if (picker.current.getDate() !== value) {
        picker.current.setDate(d.parse(value, dateFormat, new Date()), true);
      }
    }, [value, dateFormat]);

    return (
      <View style={[pickerStyles, { flex: 1 }]} innerRef={mountPoint}></View>
    );
  }
)
Example #12
Source File: Search.jsx    From chat-ui-kit-react with MIT License 5 votes vote down vote up
function SearchInner(
  { placeholder, value, onChange, onClearClick, className, disabled, ...rest },
  ref
) {
  const cName = `${prefix}-search`;

  const isControlled = useMemo(() => typeof value !== "undefined", []);

  const [searchValue, setSearchValue] = useControlledOrNot("", value);

  const [clearActive, setClearActive] = useState(
    isControlled ? searchValue.length > 0 : false
  );

  if (isControlled !== (typeof value !== "undefined")) {
    throw "Search: Changing from controlled to uncontrolled component and vice versa is not allowed";
  }

  const inputRef = useRef(undefined);

  // Public API
  const focus = () => {
    if (typeof inputRef.current !== "undefined") {
      inputRef.current.focus();
    }
  };

  // Return object with public Api
  useImperativeHandle(ref, () => ({
    focus,
  }));

  const handleChange = (e) => {
    const value = e.target.value;
    setClearActive(value.length > 0);
    if (isControlled === false) {
      setSearchValue(value);
    }
    onChange(value);
  };

  const handleClearClick = () => {
    if (isControlled === false) {
      setSearchValue("");
    }

    setClearActive(false);

    onClearClick();
  };

  return (
    <div
      {...rest}
      className={classNames(
        cName,
        { [`${cName}--disabled`]: disabled },
        className
      )}
    >
      <FontAwesomeIcon icon={faSearch} className={`${cName}__search-icon`} />
      <input
        ref={inputRef}
        type="text"
        className={`${cName}__input`}
        placeholder={placeholder}
        onChange={handleChange}
        disabled={disabled}
        value={searchValue}
      />
      <FontAwesomeIcon
        icon={faTimes}
        className={classNames(`${cName}__clear-icon`, {
          [`${cName}__clear-icon--active`]: clearActive,
        })}
        onClick={handleClearClick}
      />
    </div>
  );
}
Example #13
Source File: DropZone.js    From sailplane-web with GNU General Public License v3.0 5 votes vote down vote up
export function DropZone({children, sharedFs, currentDirectory}, ref) {
  const styles = {
    container: {
      cursor: 'pointer',
      textAlign: 'center',
      color: primary5,
      fontSize: 16,
      fontWeight: 200,
      fontFamily: 'Open Sans',
      outline: 0,
      userSelect: 'none',
      height: '100%',
    },
  };

  const dispatch = useDispatch();

  const onDrop = useCallback(
    async (acceptedFiles) => {
      dispatch(setStatus({message: 'Uploading'}));
      const listSource = fileListSource(acceptedFiles, {preserveMtime: true});
      const totalSize = acceptedFiles.reduce((prev, cur) => cur.size + prev, 0);

      try {
        await sharedFs.current.upload(currentDirectory, listSource, {
          progress: (length) => {
            dispatch(
              setStatus({
                message: `[${getPercent(length, totalSize)}%] Uploading files`,
              }),
            );
          },
        });
        dispatch(setStatus({}));
      } catch (e) {
        // will add sharedFs.canWrite method later for richer ux
        dispatch(
          setStatus({
            message: `Missing write permissions.`,
            isError: true,
          }),
        );
        delay(4000).then(() => dispatch(setStatus({})));
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [currentDirectory],
  );

  const {getRootProps, getInputProps, isDragActive, open} = useDropzone({
    onDrop,
    noClick: true,
  });

  useImperativeHandle(ref, () => ({
    openUpload: () => {
      open();
    },
  }));

  return (
    <div {...getRootProps()} style={styles.container} id={'dropZone'}>
      <input {...getInputProps()} id={'fileUpload'} />
      {isDragActive ? <FileDragBlock isActive={true} /> : <div>{children}</div>}
    </div>
  );
}
Example #14
Source File: Actions.jsx    From konsta with MIT License 5 votes vote down vote up
Actions = forwardRef((props, ref) => {
  const {
    component = 'div',
    className,

    ios,
    material,

    opened,
    backdrop = true,
    onBackdropClick,

    // Children
    children,

    // Rest
    ...rest
  } = props;

  const elRef = useRef(null);

  useImperativeHandle(ref, () => ({
    el: elRef.current,
  }));

  const state = opened ? 'opened' : 'closed';

  const Component = component;

  const attrs = {
    ...rest,
  };

  const themeClasses = useThemeClasses({ ios, material });

  const c = themeClasses(ActionsClasses(props, className), className);

  return (
    <>
      {backdrop && (
        <div className={c.backdrop[state]} onClick={onBackdropClick} />
      )}
      <Component ref={elRef} className={c.base[state]} {...attrs}>
        {children}
      </Component>
    </>
  );
})
Example #15
Source File: Input.js    From lx-music-mobile with Apache License 2.0 5 votes vote down vote up
Input = ({ onChangeText, onClearText, clearBtn, style, ...props }, ref) => {
  const inputRef = useRef()
  const theme = useGetter('common', 'theme')
  // const scaleClearBtn = useRef(new Animated.Value(0)).current

  useImperativeHandle(ref, () => ({
    blur() {
      if (!inputRef.current) return
      inputRef.current.blur()
    },
    focus() {
      if (!inputRef.current) return
      inputRef.current.focus()
    },
    clear() {
      inputRef.current.clear()
    },
    isFocused() {
      return inputRef.current.isFocused()
    },
  }))

  // const showClearBtn = useCallback(() => {
  //   Animated.timing(scaleClearBtn, {
  //     toValue: 1,
  //     duration: 200,
  //     useNativeDriver: true,
  //   }).start()
  // }, [scaleClearBtn])
  // const hideClearBtn = useCallback(() => {
  //   Animated.timing(scaleClearBtn, {
  //     toValue: 0,
  //     duration: 200,
  //     useNativeDriver: true,
  //   }).start()
  // }, [scaleClearBtn])

  const clearText = useCallback(() => {
    inputRef.current.clear()
    // hideClearBtn()
    onChangeText && onChangeText('')
    onClearText && onClearText()
  }, [onChangeText, onClearText])

  const changeText = useCallback(text => {
    // if (text.length) {
    //   showClearBtn()
    // } else {
    //   hideClearBtn()
    // }
    onChangeText && onChangeText(text)
  }, [onChangeText])

  return (
    <View style={styles.content}>
      <TextInput
        autoCapitalize="none"
        onChangeText={changeText}
        autoCompleteType="off"
        style={{ ...styles.input, color: theme.normal, ...style }}
        placeholderTextColor={theme.normal50}
        selectionColor={theme.secondary10}
        ref={inputRef} {...props} />
      {/* <View style={styles.clearBtnContent}>
      <Animated.View style={{ ...styles.clearBtnContent, transform: [{ scale: scaleClearBtn }] }}> */}
        {clearBtn
          ? <View style={styles.clearBtnContent}>
              <TouchableOpacity style={styles.clearBtn} onPress={clearText}>
                <Icon name="remove" style={{ fontSize: 14, color: theme.normal50 }} />
              </TouchableOpacity>
            </View>
          : null
        }
      {/* </Animated.View>
      </View> */}
    </View>
  )
}
Example #16
Source File: index.js    From rainbow-modules with MIT License 5 votes vote down vote up
DatePickerCarouselInput = React.forwardRef((props, ref) => {
    const {
        id,
        className,
        style,
        readOnly,
        required,
        disabled,
        name,
        label,
        value,
        locale,
        onLabelClick,
        onChange,
        formatStyle,
        selectionType,
        minDate,
        maxDate,
    } = props;
    const currentLocale = useLocale(locale);
    const inputRef = useRef();
    const labelRef = useRef();
    const formattedDate = useFormatDate({
        value,
        formatStyle,
        locale: currentLocale,
        selectionType,
    });

    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus();
        },
        click: () => {
            labelRef.current.click();
        },
        blur: () => {
            inputRef.current.blur();
        },
    }));

    return (
        <StyledContainer id={id} className={className} style={style} readOnly={readOnly}>
            <StyledLabelContainer>
                <StyledLabel
                    ref={labelRef}
                    onClick={onLabelClick}
                    data-cy="carousel-input__label-button"
                >
                    {label}
                    <ChevronDown />
                </StyledLabel>
                <input
                    type="hidden"
                    value={formattedDate}
                    required={required}
                    disabled={disabled}
                    name={name}
                />
            </StyledLabelContainer>
            <CarouselCalendar
                ref={inputRef}
                value={value}
                locale={currentLocale}
                onChange={onChange}
                minDate={minDate}
                maxDate={maxDate}
            />
        </StyledContainer>
    );
})
Example #17
Source File: Menu.js    From react-menu with MIT License 5 votes vote down vote up
Menu = /*#__PURE__*/forwardRef(function Menu(_ref, externalRef) {
  var ariaLabel = _ref['aria-label'],
      menuButton = _ref.menuButton,
      instanceRef = _ref.instanceRef,
      onMenuChange = _ref.onMenuChange,
      restProps = _objectWithoutPropertiesLoose(_ref, _excluded);

  var _useMenuStateAndFocus = useMenuStateAndFocus(restProps),
      stateProps = _useMenuStateAndFocus[0],
      toggleMenu = _useMenuStateAndFocus[1],
      openMenu = _useMenuStateAndFocus[2];

  var isOpen = isMenuOpen(stateProps.state);
  var skipOpen = useRef(false);
  var buttonRef = useRef(null);
  var handleClose = useCallback(function (e) {
    toggleMenu(false);
    if (e.key) buttonRef.current.focus();
  }, [toggleMenu]);

  var handleClick = function handleClick(e) {
    if (skipOpen.current) return;
    openMenu(e.detail === 0 ? FocusPositions.FIRST : undefined);
  };

  var handleKeyDown = function handleKeyDown(e) {
    var handled = false;

    switch (e.key) {
      case Keys.UP:
        openMenu(FocusPositions.LAST);
        handled = true;
        break;

      case Keys.DOWN:
        openMenu(FocusPositions.FIRST);
        handled = true;
        break;
    }

    if (handled) e.preventDefault();
  };

  var button = safeCall(menuButton, {
    open: isOpen
  });
  if (!button || !button.type) throw new Error('Menu requires a menuButton prop.');

  var buttonProps = _extends({
    ref: useCombinedRef(button.ref, buttonRef)
  }, attachHandlerProps({
    onClick: handleClick,
    onKeyDown: handleKeyDown
  }, button.props));

  if (getName(button.type) === 'MenuButton') {
    buttonProps.isOpen = isOpen;
  }

  var renderButton = /*#__PURE__*/cloneElement(button, buttonProps);
  useMenuChange(onMenuChange, isOpen);
  useImperativeHandle(instanceRef, function () {
    return {
      openMenu: openMenu,
      closeMenu: function closeMenu() {
        return toggleMenu(false);
      }
    };
  });
  return /*#__PURE__*/jsxs(Fragment, {
    children: [renderButton, /*#__PURE__*/jsx(ControlledMenu, _extends({}, restProps, stateProps, {
      "aria-label": ariaLabel || (typeof button.props.children === 'string' ? button.props.children : 'Menu'),
      anchorRef: buttonRef,
      ref: externalRef,
      onClose: handleClose,
      skipOpen: skipOpen
    }))]
  });
})
Example #18
Source File: SelectDropdown.js    From react-native-select-dropdown with MIT License 4 votes vote down vote up
SelectDropdown = (
  {
    data /* array */,
    onSelect /* function  */,
    defaultButtonText /* String */,
    buttonTextAfterSelection /* function */,
    rowTextForSelection /* function */,
    defaultValue /* any */,
    defaultValueByIndex /* integer */,
    disabled /* boolean */,
    disableAutoScroll /* boolean */,
    onFocus /* function  */,
    onBlur /* function  */,
    /////////////////////////////
    buttonStyle /* style object for button */,
    buttonTextStyle /* style object for button text */,
    renderCustomizedButtonChild /* function returns React component for customized button */,
    /////////////////////////////
    renderDropdownIcon,
    dropdownIconPosition,
    statusBarTranslucent,
    dropdownStyle,
    dropdownOverlayColor /* string */,
    /////////////////////////////
    rowStyle /* style object for row */,
    rowTextStyle /* style object for row text */,
    selectedRowStyle /* style object for selected row */,
    selectedRowTextStyle /* style object for selected row text */,
    renderCustomizedRowChild /* function returns React component for customized row */,
    /////////////////////////////
    search /* boolean */,
    searchInputStyle /* style object for search input */,
    searchInputTxtColor /* text color for search input */,
    searchPlaceHolder /* placeholder text for search input */,
    searchPlaceHolderColor /* text color for search input placeholder */,
    renderSearchInputLeftIcon /* function returns React component for search input icon */,
    renderSearchInputRightIcon /* function returns React component for search input icon */,
  },
  ref,
) => {
  ///////////////////////////////////////////////////////
  useImperativeHandle(ref, () => ({
    reset: () => {
      reset();
    },
    openDropdown: () => {
      openDropdown();
    },
    closeDropdown: () => {
      closeDropdown();
    },
  }));
  ///////////////////////////////////////////////////////
  const DropdownButton = useRef(); // button ref to get positions
  const [isVisible, setIsVisible] = useState(false); // dropdown visible ?
  const [buttonLayout, setButtonLayout] = useState(null);
  const [dropdownPX, setDropdownPX] = useState(0); // position x
  const [dropdownPY, setDropdownPY] = useState(0); // position y
  const [dropdownHEIGHT, setDropdownHEIGHT] = useState(() => {
    return calculateDropdownHeight(dropdownStyle, rowStyle, data?.length || 0, search);
  }); // dropdown height
  const [dropdownWIDTH, setDropdownWIDTH] = useState(0); // dropdown width
  ///////////////////////////////////////////////////////
  const [selectedItem, setSelectedItem] = useState(null); // selected item from dropdown
  const [selectedIndex, setSelectedIndex] = useState(-1); // index of selected item from dropdown
  const dropDownFlatlistRef = useRef(null); // ref to the drop down flatlist
  ///////////////////////////////////////////////////////
  const [searchTxt, setSearchTxt] = useState('');
  const keyboardHeight = useKeyboardHeight();
  const remainigHeightAvoidKeyboard = height - keyboardHeight;
  const safeDropdownViewUnderKeyboard = rowStyle && rowStyle.height ? rowStyle.height * 3 : 50 * 3;
  /* ******************* useEffect ******************* */
  // data array changes
  useEffect(() => {
    if (!data || data.length == 0) {
      reset();
    }
  }, [data]);
  // default value by index added or changed
  useEffect(() => {
    // defaultValueByIndex may be equals zero
    if (isExist(defaultValueByIndex)) {
      if (data && isExist(data[defaultValueByIndex])) {
        setDefault(defaultValueByIndex);
      }
    }
  }, [defaultValueByIndex]);
  // default value added or changed
  useEffect(() => {
    // defaultValue may be equals zero
    if (isExist(defaultValue)) {
      if (data && findIndexInArr(defaultValue, data) >= 0) {
        setDefault(findIndexInArr(defaultValue, data));
      }
    }
  }, [defaultValue]);
  // for height changes
  useEffect(() => {
    setDropdownHEIGHT(calculateDropdownHeight(dropdownStyle, rowStyle, data?.length || 0, search));
  }, [dropdownStyle, rowStyle, data]);
  ///////////////////////////////////////////////////////
  /* ******************** Methods ******************** */
  const openDropdown = () => {
    DropdownButton.current.measure((fx, fy, w, h, px, py) => {
      // console.log('position y => ', py, '\nheight', h, '\nposition x => ', px)
      setButtonLayout({w, h, px, py});
      if (height - 18 < py + h + dropdownHEIGHT) {
        setDropdownPX(px);
        setDropdownPY(py - 2 - dropdownHEIGHT);
      } else {
        setDropdownPX(px);
        setDropdownPY(py + h + 2);
      }
      setDropdownWIDTH(dropdownStyle?.width || w);
      setIsVisible(true);
      onFocus && onFocus();
    });
  };
  const closeDropdown = () => {
    setIsVisible(false);
    setSearchTxt('');
    onBlur && onBlur();
  };
  const reset = () => {
    setSelectedItem(null);
    setSelectedIndex(-1);
    setSearchTxt('');
  };
  const setDefault = index => {
    setSelectedItem(data[index]);
    setSelectedIndex(index);
  };
  const getItemLayout = (data, index) => ({
    index,
    length: data?.length || 0,
    offset: rowStyle && rowStyle.height ? rowStyle.height * index : 50 * index,
  });
  const onLayout = () => {
    if (disableAutoScroll) {
      return;
    }
    if (selectedIndex >= 3 && dropDownFlatlistRef) {
      dropDownFlatlistRef.current.scrollToOffset({
        offset: rowStyle && rowStyle.height ? rowStyle.height * selectedIndex : 50 * selectedIndex,
        animated: true,
      });
    }
  };
  const onSelectItem = (item, index) => {
    closeDropdown();
    onSelect(item, index);
    setSelectedItem(item);
    setSelectedIndex(index);
  };
  ///////////////////////////////////////////////////////
  /* ******************** Render Methods ******************** */
  const renderSearchView = () => {
    return (
      search && (
        <View style={{...styles.searchViewStyle, ...{width: buttonLayout.w}}}>
          <Input
            value={searchTxt}
            valueColor={searchInputTxtColor}
            placeholder={searchPlaceHolder}
            placeholderTextColor={searchPlaceHolderColor}
            onChangeText={setSearchTxt}
            inputStyle={searchInputStyle}
            renderLeft={renderSearchInputLeftIcon}
            renderRight={renderSearchInputRightIcon}
          />
        </View>
      )
    );
  };
  const renderFlatlistItem = ({item, index}) => {
    return item ? (
      <TouchableOpacity
        activeOpacity={0.8}
        style={{...styles.dropdownRow, ...rowStyle, ...(index == selectedIndex && selectedRowStyle)}}
        onPress={() => onSelectItem(item, index)}>
        {renderCustomizedRowChild ? (
          <View style={styles.dropdownCustomizedRowParent}>{renderCustomizedRowChild(item, index)}</View>
        ) : (
          <Text
            numberOfLines={1}
            allowFontScaling={false}
            style={{...styles.dropdownRowText, ...rowTextStyle, ...(index == selectedIndex && selectedRowTextStyle)}}>
            {rowTextForSelection ? rowTextForSelection(item, index) : item.toString()}
          </Text>
        )}
      </TouchableOpacity>
    ) : (
      <></>
    );
  };
  const renderDropdown = () => {
    return (
      isVisible && (
        <Modal
          supportedOrientations={['portrait', 'landscape']}
          animationType="none"
          transparent={true}
          statusBarTranslucent={statusBarTranslucent || false}
          visible={isVisible}>
          <TouchableOpacity
            activeOpacity={1}
            onPress={() => closeDropdown()}
            style={{
              ...styles.dropdownOverlay,
              ...(dropdownOverlayColor && {
                backgroundColor: dropdownOverlayColor,
              }),
            }}
          />
          <View
            style={{
              ...styles.dropdownOverlayView,
              ...styles.shadow,
              ...dropdownStyle,
              ...{
                position: 'absolute',
                top:
                  remainigHeightAvoidKeyboard < dropdownPY + safeDropdownViewUnderKeyboard
                    ? remainigHeightAvoidKeyboard - safeDropdownViewUnderKeyboard
                    : dropdownPY,
                height: dropdownHEIGHT,
                width: dropdownWIDTH,
                borderTopWidth: 0,
                overflow: 'hidden',
              },
              ...(I18nManager.isRTL
                ? {right: dropdownStyle?.right || dropdownPX}
                : {left: dropdownStyle?.left || dropdownPX}),
            }}>
            {!data || data.length == 0 ? (
              <View style={styles.dropdownActivityIndicatorView}>
                <ActivityIndicator size="small" color={'#999999'} />
              </View>
            ) : (
              <FlatList
                data={searchTxt ? deepSearchInArr(searchTxt, data) : data}
                keyExtractor={(item, index) => index.toString()}
                ref={ref => (dropDownFlatlistRef.current = ref)}
                renderItem={renderFlatlistItem}
                getItemLayout={getItemLayout}
                onLayout={onLayout}
                ListHeaderComponent={renderSearchView()}
                stickyHeaderIndices={search && [0]}
                keyboardShouldPersistTaps="always"
              />
            )}
          </View>
        </Modal>
      )
    );
  };
  ///////////////////////////////////////////////////////
  return (
    <TouchableOpacity
      ref={DropdownButton}
      disabled={disabled}
      activeOpacity={0.8}
      style={{
        ...styles.dropdownButton,
        ...(dropdownIconPosition == 'left' ? {flexDirection: 'row'} : {flexDirection: 'row-reverse'}),
        ...buttonStyle,
      }}
      onPress={openDropdown}>
      {renderDropdown()}
      {renderDropdownIcon && renderDropdownIcon(isVisible)}
      {renderCustomizedButtonChild ? (
        <View style={styles.dropdownCustomizedButtonParent}>
          {renderCustomizedButtonChild(selectedItem, selectedIndex)}
        </View>
      ) : (
        <Text numberOfLines={1} allowFontScaling={false} style={{...styles.dropdownButtonText, ...buttonTextStyle}}>
          {isExist(selectedItem)
            ? buttonTextAfterSelection
              ? buttonTextAfterSelection(selectedItem, selectedIndex)
              : selectedItem.toString()
            : defaultButtonText || 'Select an option.'}
        </Text>
      )}
    </TouchableOpacity>
  );
}
Example #19
Source File: Player.js    From spotify-clone-client with MIT License 4 votes vote down vote up
Player = React.forwardRef(({ token }, ref) => {
	const setMessage = useContext(MessageContext);
	const [playbackState, setPlaybackState] = useState({
		play: false,
		shuffle: false,
		repeat: false,
		progress: 0,
		total_time: 0,
	});

	const [scrubPb, setScrubPb] = useState(null);
	const [playback, setPlayback] = useState(0);
	const [volume, setVolume] = useState(1);
	const [connectTip, setConnectTip] = useState(false);
	const [playInfo, setPlayInfo] = useState({
		album: {},
		artists: [],
		name: "",
		id: "",
	});

	const timerRef = useRef(null);
	let player = useRef(null);

	useEffect(() => {
		loadScript();
		apiUpdate();

		window.onSpotifyWebPlaybackSDKReady = () => playerInit();

		return () => {
			source.cancel();
			clearTimeout(timerRef.current);
			player.disconnect();
		};
		// eslint-disable-next-line
	}, []);

	const loadScript = () => {
		const script = document.createElement("script");

		script.id = "spotify-player";
		script.type = "text/javascript";
		script.async = "async";
		script.defer = "defer";
		script.src = "https://sdk.scdn.co/spotify-player.js";

		document.body.appendChild(script);
	};

	const playerInit = () => {
		console.log("player init");
		player = new window.Spotify.Player({
			name: "Spotify Clone Player",
			getOAuthToken: (cb) => {
				cb(token);
			},
		});

		// Error handling
		player.addListener("initialization_error", ({ message }) => {
			setMessage(message);
		});
		player.addListener("authentication_error", ({ message }) => {
			setMessage(message);
		});
		player.addListener("account_error", ({ message }) => {
			setMessage(message);
		});
		player.addListener("playback_error", ({ message }) => {
			setMessage(message);
		});

		// Playback status updates
		player.addListener("player_state_changed", (state) => {
			console.log(state);
			try {
				const {
					duration,
					position,
					paused,
					shuffle,
					repeat_mode,
					track_window,
				} = state;
				const { current_track } = track_window;

				setPlayInfo(current_track);
				setPlayback(position / duration);
				setPlaybackState((state) => ({
					...state,
					play: !paused,
					shuffle: shuffle,
					repeat: repeat_mode !== 0,
					progress: position,
					total_time: duration,
				}));
			} catch (error) {
				console.log(error);
			}
		});

		// Ready
		player.addListener("ready", ({ device_id }) => {
			console.log("Ready with Device ID", device_id);
			const tipAccess = localStorage.getItem("tipAccess");
			if (!tipAccess) {
				localStorage.setItem("tipAccess", "true");
				setConnectTip(true);
			}
		});

		// Not Ready
		player.addListener("not_ready", ({ device_id }) => {
			console.log("Device ID has gone offline", device_id);
		});

		// Connect to the player!
		player.connect();
	};

	//Reference for parent component to use updateState
	useImperativeHandle(ref, () => ({
		updateState: () => {
			setPlaybackState((state) => ({ ...state, play: true }));
			updateState();
		},
	}));

	//Use for other components to update the player state only if not connected to the web player
	const updateState = () => {
		if (!player.current) {
			apiUpdate();
		}
	};

	const apiUpdate = () => {
		console.log("hello");
		if (timerRef.current) {
			clearTimeout(timerRef.current);
		}
		const requestInfo = reqWithToken(
			"https://api.spotify.com/v1/me/player",
			token,
			source
		);

		requestInfo()
			.then((response) => {
				if (response.status === 200) {
					const {
						repeat_state,
						shuffle_state,
						is_playing,
						progress_ms,
						item,
						device,
					} = response.data;
					setPlayback(progress_ms / item.duration_ms);

					timerRef.current = setTimeout(
						() => updateState(),
						item.duration_ms - progress_ms + 10
					);

					setVolume(device.volume_percent / 100);
					setPlaybackState((state) => ({
						...state,
						play: is_playing,
						shuffle: shuffle_state,
						repeat: repeat_state !== "off",
						progress: progress_ms,
						total_time: item.duration_ms,
					}));
					setPlayInfo(item);
				} else if (response.status === 204) {
					setMessage(
						"Player is not working, select a device to start listening"
					);
					setConnectTip(true);
				} else {
					setMessage(
						`ERROR: server response with ${response}. Player feature is unavailable!`
					);
				}
			})
			.catch((error) => console.log(error));
	};

	const updatePlayback = () => {
		const interval = 500 / playbackState.total_time;
		setPlayback((playback) => playback + interval);
		setPlaybackState((state) => ({ ...state, progress: state.progress + 500 }));
	};

	const source = axios.CancelToken.source();

	const togglePlay = () => {
		const url = playbackState.play
			? "https://api.spotify.com/v1/me/player/pause"
			: "https://api.spotify.com/v1/me/player/play";

		const request = putWithToken(url, token, source);
		request()
			.then((response) => {
				if (response.status !== 204) {
					setMessage(
						`ERROR: Something went wrong! Server response: ${response}`
					);
				} else {
					setPlaybackState((state) => ({ ...state, play: !state.play }));
					updateState();
				}
			})
			.catch((error) => setMessage(`ERROR: ${error}`));
	};

	const toggleShuffle = () => {
		const request = putWithToken(
			`https://api.spotify.com/v1/me/player/shuffle?state=${!playbackState.shuffle}`,
			token,
			source
		);
		request()
			.then((response) => {
				if (response.status === 204) {
					setMessage(`Shuffle ${playbackState.shuffle ? "Off" : "On"}`);
					setPlaybackState((state) => ({ ...state, shuffle: !state.shuffle }));
					updateState();
				} else {
					setMessage(
						`ERROR: Something went wrong! Server response: ${response.status}`
					);
				}
			})
			.catch((error) => setMessage(`ERROR: ${error}`));
	};

	const toggleRepeat = () => {
		const url = playbackState.repeat
			? "https://api.spotify.com/v1/me/player/repeat?state=off"
			: "https://api.spotify.com/v1/me/player/repeat?state=track";

		const request = putWithToken(url, token, source);
		request()
			.then((response) => {
				if (response.status === 204) {
					setMessage(`Repeat Track ${playbackState.repeat ? "Off" : "On"}`);
					setPlaybackState((state) => ({ ...state, repeat: !state.repeat }));
					updateState();
				} else {
					setMessage(
						`ERROR: Something went wrong! Server response: ${response.status}`
					);
				}
			})
			.catch((error) => setMessage(`ERROR: ${error}`));
	};

	const skipNext = () => {
		const request = putWithToken(
			"https://api.spotify.com/v1/me/player/next",
			token,
			source,
			{},
			"POST"
		);
		request()
			.then((response) => {
				if (response.status !== 204) {
					setMessage(
						`ERROR: Something went wrong! Server response: ${response.status}`
					);
					return;
				}
				updateState();
			})
			.catch((error) => setMessage(`ERROR: ${error}`));
	};

	const skipPrev = () => {
		const request = putWithToken(
			"https://api.spotify.com/v1/me/player/previous",
			token,
			source,
			{},
			"POST"
		);
		request()
			.then((response) => {
				if (response.status !== 204) {
					setMessage(
						`ERROR: Something went wrong! Server response: ${response.status}`
					);
					return;
				}
				updateState();
			})
			.catch((error) => setMessage(`ERROR: ${error}`));
	};

	const seekPlayback = (ratio) => {
		const time = Math.round(ratio * playbackState.total_time);
		const request = putWithToken(
			`https://api.spotify.com/v1/me/player/seek?position_ms=${time}`,
			token,
			source,
			{}
		);
		request()
			.then((response) => {
				if (response.status === 204) {
					setPlayback(ratio);
					setPlaybackState((state) => ({ ...state, progress: time }));
					updateState();
				} else {
					setMessage(
						`ERROR: Something went wrong! Server response: ${response.status}`
					);
				}
			})
			.catch((error) => setMessage(`ERROR: ${error}`));

		setScrubPb(null);
	};

	const scrubPlayback = (ratio) => {
		const time = ratio * playbackState.total_time;
		setScrubPb(time);
	};

	const seekVolume = (ratio) => {
		const integer = Math.round(ratio * 100);
		const request = putWithToken(
			`https://api.spotify.com/v1/me/player/volume?volume_percent=${integer}`,
			token,
			source,
			null
		);
		request()
			.then((response) => {
				if (response.status === 204) {
					setVolume(ratio);
				} else {
					setMessage(
						`ERROR: Something went wrong! Server response: ${response.status}`
					);
				}
			})
			.catch((error) => setMessage(`ERROR: ${error}`));
	};

	return (
		<>
			{/* {playbackState.play ? null:<Heartbeat heartbeatFunction={updateState} heartbeatInterval={10000}/>} */}
			{playbackState.play ? (
				<Heartbeat heartbeatFunction={updatePlayback} heartbeatInterval={500} />
			) : null}
			<div className="player">
				<div className="player-left">
					<NowPlaying playInfo={playInfo} />
				</div>

				<div className="player-center">
					<div className="player-control-buttons">
						<ControlButton
							title="Toggle Shuffle"
							icon="Shuffle"
							active={playbackState.shuffle}
							onClick={toggleShuffle}
						/>
						<ControlButton
							title="Previous"
							icon="TrackBack"
							size="x-smaller"
							onClick={skipPrev}
						/>
						<ControlButton
							title={playbackState.play ? "Pause" : "Play"}
							icon={playbackState.play ? "Pause" : "Play"}
							size={playbackState.play ? "smaller" : "larger"}
							extraClass="circle-border"
							onClick={togglePlay}
						/>
						<ControlButton
							title="Next"
							icon="TrackNext"
							size="x-smaller"
							onClick={skipNext}
						/>
						<ControlButton
							title="Toggle Repeat"
							icon="Repeat"
							active={playbackState.repeat}
							onClick={toggleRepeat}
						/>
					</div>

					<div className="player-playback" draggable="false">
						<div className="playback-time" draggable="false">
							{scrubPb
								? msTimeFormat(scrubPb)
								: msTimeFormat(playbackState.progress)}
						</div>
						<ProgressBar
							extraClass="playback"
							value={playback}
							engageClass="engage"
							setValue={(ratio) => seekPlayback(ratio)}
							scrubFunction={scrubPlayback}
						/>
						<div className="playback-time" draggable="false">
							{msTimeFormat(playbackState.total_time)}
						</div>
					</div>
				</div>

				<div className="player-right">
					<div className="extra-controls">
						<span className="connect-devices-wrapper">
							{connectTip && (
								<ConnectDevices
									token={token}
									closeTip={() => setConnectTip(false)}
								/>
							)}
							<ControlButton
								title="Devices"
								icon="Speaker"
								size="x-larger"
								onClick={() => setConnectTip(!connectTip)}
								active={playbackState.play}
							/>
						</span>

						<div className="volume-control">
							<ControlButton
								title="Volume"
								icon="Volume"
								size="x-larger"
								extraClass="volume"
							/>
							<div style={{ width: "100%" }}>
								<ProgressBar
									extraClass="volume"
									value={volume}
									engageClass="engage"
									setValue={(ratio) => seekVolume(ratio)}
								/>
							</div>
						</div>
					</div>
				</div>
			</div>
		</>
	);
})
Example #20
Source File: payees.js    From actual with MIT License 4 votes vote down vote up
ManagePayees = React.forwardRef(
  (
    {
      modalProps,
      payees,
      ruleCounts,
      categoryGroups,
      tableNavigatorOpts,
      initialSelectedIds,
      ruleActions,
      onBatchChange,
      onViewRules,
      onCreateRule,
      ...props
    },
    ref
  ) => {
    let [highlightedRows, setHighlightedRows] = useState(null);
    let [filter, setFilter] = useState('');
    let table = useRef(null);
    let scrollTo = useRef(null);
    let resetAnimation = useRef(false);

    let filteredPayees = useMemo(
      () =>
        filter === ''
          ? payees
          : payees.filter(p =>
              p.name.toLowerCase().includes(filter.toLowerCase())
            ),
      [payees, filter]
    );

    let selected = useSelected('payees', filteredPayees, initialSelectedIds);

    function applyFilter(f) {
      if (filter !== f) {
        table.current && table.current.setRowAnimation(false);
        setFilter(f);
        resetAnimation.current = true;
      }
    }

    function _scrollTo(id) {
      applyFilter('');
      scrollTo.current = id;
    }

    useEffect(() => {
      if (resetAnimation.current) {
        // Very annoying, for some reason it's as if the table doesn't
        // actually update its contents until the next tick or
        // something? The table keeps being animated without this
        setTimeout(() => {
          table.current && table.current.setRowAnimation(true);
        }, 0);
        resetAnimation.current = false;
      }
    });

    useImperativeHandle(ref, () => ({
      selectRows: (ids, scroll) => {
        tableNavigator.onEdit(null);
        selected.dispatch({ type: 'select-all', ids });
        setHighlightedRows(null);

        if (scroll && ids.length > 0) {
          _scrollTo(ids[0]);
        }
      },

      highlightRow: id => {
        tableNavigator.onEdit(null);
        setHighlightedRows(new Set([id]));
        _scrollTo(id);
      }
    }));

    // `highlightedRows` should only ever be true once, and we
    // immediately discard it. This triggers an animation.
    useEffect(() => {
      if (highlightedRows) {
        setHighlightedRows(null);
      }
    }, [highlightedRows]);

    useLayoutEffect(() => {
      if (scrollTo.current) {
        table.current.scrollTo(scrollTo.current);
        scrollTo.current = null;
      }
    });

    let onUpdate = useStableCallback((id, name, value) => {
      let payee = payees.find(p => p.id === id);
      if (payee[name] !== value) {
        onBatchChange({ updated: [{ id, [name]: value }] });
      }
    });

    let getSelectableIds = useCallback(() => {
      return filteredPayees.filter(p => p.transfer_acct == null).map(p => p.id);
    }, [filteredPayees]);

    function onDelete() {
      onBatchChange({ deleted: [...selected.items].map(id => ({ id })) });
      selected.dispatch({ type: 'select-none' });
    }

    async function onMerge() {
      let ids = [...selected.items];
      await props.onMerge(ids);

      tableNavigator.onEdit(ids[0], 'name');
      selected.dispatch({ type: 'select-none' });
      _scrollTo(ids[0]);
    }

    let buttonsDisabled = selected.items.size === 0;

    let tableNavigator = useTableNavigator(
      filteredPayees,
      item =>
        ['select', 'name', 'rule-count'].filter(name => {
          switch (name) {
            case 'select':
              return item.transfer_acct == null;
            default:
              return true;
          }
        }),
      tableNavigatorOpts
    );

    let payeesById = getPayeesById(payees);

    return (
      <Modal
        title="Payees"
        padding={0}
        {...modalProps}
        style={[modalProps.style, { flex: 'inherit', maxWidth: '90%' }]}
      >
        <View
          style={{
            maxWidth: '100%',
            width: 900,
            height: 550
          }}
        >
          <View
            style={{
              flexDirection: 'row',
              alignItems: 'center',
              padding: '0 10px'
            }}
          >
            <Component initialState={{ menuOpen: false }}>
              {({ state, setState }) => (
                <View>
                  <Button
                    bare
                    style={{ marginRight: 10 }}
                    disabled={buttonsDisabled}
                    onClick={() => setState({ menuOpen: true })}
                  >
                    {buttonsDisabled
                      ? 'No payees selected'
                      : selected.items.size +
                        ' ' +
                        plural(selected.items.size, 'payee', 'payees')}
                    <ExpandArrow
                      width={8}
                      height={8}
                      style={{ marginLeft: 5 }}
                    />
                  </Button>
                  {state.menuOpen && (
                    <PayeeMenu
                      payeesById={payeesById}
                      selectedPayees={selected.items}
                      onClose={() => setState({ menuOpen: false })}
                      onDelete={onDelete}
                      onMerge={onMerge}
                    />
                  )}
                </View>
              )}
            </Component>
            <View style={{ flex: 1 }} />
            <Input
              placeholder="Filter payees..."
              value={filter}
              onChange={e => {
                applyFilter(e.target.value);
                tableNavigator.onEdit(null);
              }}
              style={{
                width: 350,
                borderColor: 'transparent',
                backgroundColor: colors.n11,
                ':focus': {
                  backgroundColor: 'white',
                  '::placeholder': { color: colors.n8 }
                }
              }}
            />
          </View>

          <SelectedProvider instance={selected} fetchAllIds={getSelectableIds}>
            <View
              style={{
                flex: 1,
                border: '1px solid ' + colors.border,
                borderRadius: 4,
                overflow: 'hidden',
                margin: 5
              }}
            >
              <PayeeTableHeader />
              {filteredPayees.length === 0 ? (
                <EmptyMessage text="No payees" style={{ marginTop: 15 }} />
              ) : (
                <PayeeTable
                  ref={table}
                  payees={filteredPayees}
                  ruleCounts={ruleCounts}
                  categoryGroups={categoryGroups}
                  highlightedRows={highlightedRows}
                  navigator={tableNavigator}
                  onUpdate={onUpdate}
                  onViewRules={onViewRules}
                  onCreateRule={onCreateRule}
                />
              )}
            </View>
          </SelectedProvider>
        </View>
      </Modal>
    );
  }
)
Example #21
Source File: DEFViewer.js    From EDAViewer with MIT License 4 votes vote down vote up
DEFViewer = (props) => {
	const {
		design,
		layerVisibility,
		onResetDirtyLayers,
		settings,
		zoom,
		width,
		height,
		metalLayerColors,
		setSelectedComponentName,
		exportDialogOpen,
		exportDialogType,
		setExportDialogOpen,
		forwardedRef,
		showCellDialog,
		showViaDialog,
		showWireDialog,
		showPortDialog,
	} = props;
	const classes = useStyles();

	const [isLoading, setIsLoading] = React.useState(true);

	const {
		Geometries: geometries,
		InstancePins: pins,
		RoutingVias: routingVias,
		ViaDefinitions: viaDefs,
		Die: die,
	} = design;

	const geometryMap = {};
	geometries.forEach((geom) => {
		geometryMap[geom.ID] = geom;
	});

	const pinMap = {};
	pins.forEach((pin) => {
		pinMap[pin.ID] = pin;
	});
	const viaMap = {};
	routingVias.forEach((via) => {
		viaMap[via.ID] = via;
	});
	viaDefs.forEach((via) => {
		viaMap[via.ID] = via;
	});

	const scaleFactorX =
		(width - settings.shapes.chip.strokeWidth * 2) / (die.XMax - die.XMin);
	const scaleFactorY =
		(height - settings.shapes.chip.strokeWidth * 2) / (die.YMax - die.YMin);

	const originOffsetX =
		-die.XMin * scaleFactorX + settings.shapes.chip.strokeWidth;
	const originOffsetY =
		die.YMin * scaleFactorY - settings.shapes.chip.strokeWidth;

	const internalConfig = {
		width,
		height,
		scaleFactorX,
		scaleFactorY,
		originOffsetX,
		originOffsetY,
		metalLayerColors,
	};
	const config = Object.assign({}, internalConfig, settings);

	const appRef = useRef({
		app: null,
		viewport: null,
		chip: null,
		cells: null,
		wires: null,
		ports: null,
		gcells: null,
		rows: null,
		tracks: null,
		width,
		height,
	});
	const ref = React.createRef();

	const onCellClicked = (cell) => {
		setSelectedComponentName(
			<span>
				Cell: <b>{cell.Name}</b>
			</span>
		);
	};
	const onCellDoubleClicked = (cell) => showCellDialog(cell);
	const onWireClicked = (wire) => {
		setSelectedComponentName(
			<span>
				Wire: <b>{wire.Name}</b>
			</span>
		);
	};
	const onWireDoubleClicked = (wire) => showWireDialog(wire);
	const onViaClicked = (via) => {
		setSelectedComponentName(
			<span>
				Via: <b>{via.Name}</b>
			</span>
		);
	};
	const onViaDoubleClicked = (via) => showViaDialog(via);
	const onPortClicked = (port) => {
		setSelectedComponentName(
			<span>
				Port: <b>{port.Name}</b>
			</span>
		);
	};
	const onPortDoubleClicked = (port) => showPortDialog(port);
	const onExportCancel = () => {
		setExportDialogOpen(false);
	};
	const onExport = () => {
		setExportDialogOpen(false);
		if (appRef.current.app) {
			const extract = appRef.current.app.renderer.plugins.extract;
			const tempLink = document.createElement("a");
			document.body.append(tempLink);
			tempLink.download = design.Name + "." + exportDialogType;
			tempLink.href = extract.base64(
				appRef.current.viewport,
				"image/" + exportDialogType
			);
			tempLink.click();
			tempLink.remove();
		}
	};
	const resetViewport = (clearState = true) => {
		if (!appRef.current.viewport) {
			return;
		}

		["tracks", "rows", "gcells", "ports", "wires", "cells", "chip"].forEach(
			(layer) => {
				if (appRef.current[layer]) {
					appRef.current.viewport.removeChild(appRef.current[layer]);
					appRef.current[layer].destroy(true);
					appRef.current[layer] = null;
				}
			}
		);
		appRef.current.app.stage.removeChild(appRef.current.viewport);
		appRef.current.viewport.destroy(true);
		appRef.current.viewport = null;
		appRef.current.app.stage.destroy(true);
		appRef.current.app.destroy(false, true);
		appRef.current.app = null;
		if (clearState) {
			setSelectedComponentName(null);
			onResetDirtyLayers();
		}
	};

	useEffect(
		createDesignApp(
			design,
			{
				config,
				layerVisibility,
				pins: pinMap,
				vias: viaMap,
				geometries: geometryMap,
				handlers: {
					resetViewport,
					onResetDirtyLayers,
					onCellClicked,
					onCellDoubleClicked,
					onWireClicked,
					onWireDoubleClicked,
					onViaClicked,
					onViaDoubleClicked,
					onPortClicked,
					onPortDoubleClicked,
					setIsLoading,
				},
			},
			appRef,
			ref
		),
		[
			layerVisibility,
			zoom,
			settings,
			internalConfig.width,
			internalConfig.height,
		]
	);
	useImperativeHandle(forwardedRef, () => ({
		zoomIn: () => {
			if (appRef.current.viewport) {
				appRef.current.viewport.setZoom(
					appRef.current.viewport.scaled * config.scaleStepFactor,
					true
				);
			}
		},
		zoomOut: () => {
			if (appRef.current.viewport) {
				appRef.current.viewport.setZoom(
					appRef.current.viewport.scaled / config.scaleStepFactor,
					true
				);
			}
		},
		fit: () => {
			if (appRef.current.viewport) {
				appRef.current.viewport.setZoom(1);
				appRef.current.viewport.position.set(0, 0);
			}
		},
		reset: () => {
			resetViewport();
		},
	}));
	return (
		<React.Fragment>
			<ExportDesignDialog
				open={exportDialogOpen}
				type={exportDialogType}
				onExport={onExport}
				onCancel={onExportCancel}
			/>
			{isLoading && (
				<div
					className={classes.viewerProgressBar}
					style={{ width, height }}
				>
					<CircularProgress className={classes.progress} />
				</div>
			)}
			<canvas
				id="defviewer"
				ref={ref}
				width={width}
				height={height}
				className={classes.defViewerCanvas}
				style={{
					...(config.viewportBorder
						? { border: "1px dashed #0000006e" }
						: {}),
				}}
			></canvas>
		</React.Fragment>
	);
}
Example #22
Source File: PhoneNumberInput.js    From rakning-c19-app with MIT License 4 votes vote down vote up
PhoneNumberInput = forwardRef(({ onSendPin, onPressFlag }, ref) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { createAlert } = useAlert();
  const phoneInputRef = useRef();
  const numberInputRef = useRef();

  const [tosAccepted, setTosAccepted] = useState(false);
  const dimensions = useWindowDimensions();
  const fontScale = isNaN(dimensions.fontScale) ? 1 : dimensions.fontScale;
  const inputHeight = scale(fontScale <= 1 ? 50 : 50 * Math.min(fontScale, 2));
  const { t, i18n } = useTranslation();

  useImperativeHandle(ref, () => ({
    onSelectCountry,
    phoneNumberInputFocus,
    cca2,
  }));

  const onChangePhoneNumber = phoneNumber => {
    dispatch({ type: 'updatePhoneNumber', phoneNumber });
  };

  const onChangeCallingCode = callingCode => {
    dispatch({ type: 'updateCallingCode', callingCode });
  };

  function onSelectCountry({ cca2, callingCode }) {
    phoneInputRef.current.selectCountry(cca2.toLowerCase());
    dispatch({ type: 'updateLocation', cca2, callingCode });
  }

  const phoneNumberInputFocus = () => {
    numberInputRef?.current?.focus();
  };

  const cca2 = () => state.cca2;

  const acceptTOS = () => {
    setTosAccepted(!tosAccepted);
  };

  const openPP = () => {
    WebBrowser.openBrowserAsync(privacyUrls[i18n.language] || privacyUrls.en);
  };

  const getPinNumber = async () => {
    const { phoneNumber } = state;
    const countryCode = phoneInputRef.current;

    if (!isValid(countryCode.getCountryCode(), phoneNumber)) {
      createAlert({
        message: t('phoneValidationMessage'),
        type: 'error',
      });
      return;
    }

    try {
      const pinToken = await getPin(countryCode.getCountryCode(), phoneNumber);
      onSendPin(countryCode.getCountryCode(), phoneNumber, pinToken);
    } catch (error) {
      createAlert({
        message: t('genericErrorMessage'),
        type: 'error',
      });
    }
  };

  const linkHitSlop = {
    top: linkTouchPadding,
    right: linkTouchPadding,
    bottom: linkTouchPadding,
    left: linkTouchPadding,
  };

  const Link = ({ children, onPress }) => {
    return (
      <TouchableWithoutFeedback onPress={onPress} hitSlop={linkHitSlop}>
        <TOSLink>{children[0]}</TOSLink>
      </TouchableWithoutFeedback>
    );
  };

  return (
    <>
      <Vertical unit={1} />
      <View
        style={{
          ...styles.phoneInputContainer,
          ...(fontScale <= 1 && {
            flexDirection: 'row',
          }),
        }}
      >
        <PhoneInput
          ref={phoneInputRef}
          onPressFlag={onPressFlag}
          initialCountry="is"
          style={{
            ...styles.phoneViewStyle,
            minHeight: inputHeight,
            width: fontScale <= 1 ? '40%' : '100%',
          }}
          textStyle={{
            ...styles.codeTextStyle,
            minHeight: inputHeight,
            textAlign: isRTL() ? 'right' : 'left',
          }}
          flagStyle={styles.flag}
          offset={6}
          onChangePhoneNumber={onChangeCallingCode}
        />
        <TextInput
          ref={numberInputRef}
          placeholder={t('phoneNr')}
          keyboardType="phone-pad"
          returnKeyType="done"
          autoCapitalize="none"
          placeholderTextColor={Colors.placeholder}
          autoCorrect={false}
          style={{
            ...styles.phoneViewStyle,
            ...styles.phoneTextStyle,
            minHeight: inputHeight,
            textAlign: isRTL() ? 'right' : 'left',
            width: fontScale <= 1 ? '60%' : '100%',
          }}
          onChangeText={onChangePhoneNumber}
        />
      </View>
      <Vertical unit={1} />
      <Checkbox checked={tosAccepted} onPress={acceptTOS}>
        <Trans i18nKey="tosAcceptance">
          I agree to the <Link onPress={openPP}>Privacy Policy</Link>.
        </Trans>
      </Checkbox>
      <Vertical fill />
      <CtaButton
        disabled={!tosAccepted || state.phoneNumber.length <= 0}
        onPress={getPinNumber}
        image={covidIcon}
        imageDimensions={{ height: scale(28), width: scale(24) }}
      >
        {t('next')}
      </CtaButton>
    </>
  );
})
Example #23
Source File: MessageInput.jsx    From chat-ui-kit-react with MIT License 4 votes vote down vote up
function MessageInputInner(
  {
    value,
    onSend,
    onChange,
    autoFocus,
    placeholder,
    fancyScroll,
    className,
    activateAfterChange,
    disabled,
    sendDisabled,
    sendOnReturnDisabled,
    attachDisabled,
    sendButton,
    attachButton,
    onAttachClick,
    ...rest
  },
  ref
) {
  const scrollRef = useRef();
  const msgRef = useRef();
  const [stateValue, setStateValue] = useControllableState(value, "");
  const [stateSendDisabled, setStateSendDisabled] = useControllableState(
    sendDisabled,
    true
  );

  // Public API
  const focus = () => {
    if (typeof msgRef.current !== "undefined") {
      msgRef.current.focus();
    }
  };

  // Return object with public Api
  useImperativeHandle(ref, () => ({
    focus,
  }));

  // Set focus
  useEffect(() => {
    if (autoFocus === true) {
      focus();
    }
  }, []);

  // Update scroll
  useEffect(() => {
    if (typeof scrollRef.current.updateScroll === "function") {
      scrollRef.current.updateScroll();
    }
  });

  const getContent = () => {
    // Direct reference to contenteditable div
    const contentEditableRef = msgRef.current.msgRef.current;
    return [
      contentEditableRef.textContent,
      contentEditableRef.innerText,
      contentEditableRef.cloneNode(true).childNodes,
    ];
  };

  const send = () => {
    if (stateValue.length > 0) {
      // Clear input only when it's uncontrolled mode
      if (value === undefined) {
        setStateValue("");
      }

      // Disable send button only when it's uncontrolled mode
      if (typeof sendDisabled === "undefined") {
        setStateSendDisabled(true);
      }

      const content = getContent();

      onSend(stateValue, content[0], content[1], content[2]);
    }
  };

  const handleKeyPress = (evt) => {
    if (
      evt.key === "Enter" &&
      evt.shiftKey === false &&
      sendOnReturnDisabled === false
    ) {
      evt.preventDefault();
      send();
    }
  };

  const handleChange = (innerHTML, textContent, innerText) => {
    setStateValue(innerHTML);
    if (typeof sendDisabled === "undefined") {
      setStateSendDisabled(textContent.length === 0);
    }

    if (typeof scrollRef.current.updateScroll === "function") {
      scrollRef.current.updateScroll();
    }

    const content = getContent();

    onChange(innerHTML, textContent, innerText, content[2]);
  };

  const cName = `${prefix}-message-input`,
    ph = typeof placeholder === "string" ? placeholder : "";

  return (
    <div
      {...rest}
      className={classNames(
        cName,
        { [`${cName}--disabled`]: disabled },
        className
      )}
    >
      {attachButton === true && (
        <div className={`${cName}__tools`}>
          <AttachmentButton
            onClick={onAttachClick}
            disabled={disabled === true || attachDisabled === true}
          />
        </div>
      )}

      <div className={`${cName}__content-editor-wrapper`}>
        <EditorContainer
          fancyScroll={fancyScroll}
          ref={scrollRef}
          className={`${cName}__content-editor-container`}
        >
          <ContentEditable
            ref={msgRef}
            className={`${cName}__content-editor`}
            disabled={disabled}
            placeholder={ph}
            onKeyPress={handleKeyPress}
            onChange={handleChange}
            activateAfterChange={activateAfterChange}
            value={stateValue}
          />
        </EditorContainer>
      </div>
      {sendButton === true && (
        <div className={`${cName}__tools`}>
          <SendButton
            onClick={send}
            disabled={disabled === true || stateSendDisabled === true}
          />
        </div>
      )}
    </div>
  );
}
Example #24
Source File: ChartContainer.js    From react-orgchart with MIT License 4 votes vote down vote up
ChartContainer = forwardRef(
  (
    {
      datasource,
      pan,
      zoom,
      zoomoutLimit,
      zoominLimit,
      containerClass,
      chartClass,
      NodeTemplate,
      draggable,
      collapsible,
      multipleSelect,
      onClickNode,
      onClickChart
    },
    ref
  ) => {
    const container = useRef();
    const chart = useRef();
    const downloadButton = useRef();

    const [startX, setStartX] = useState(0);
    const [startY, setStartY] = useState(0);
    const [transform, setTransform] = useState("");
    const [panning, setPanning] = useState(false);
    const [cursor, setCursor] = useState("default");
    const [exporting, setExporting] = useState(false);
    const [dataURL, setDataURL] = useState("");
    const [download, setDownload] = useState("");

    const attachRel = (data, flags) => {
      data.relationship =
        flags + (data.children && data.children.length > 0 ? 1 : 0);
      if (data.children) {
        data.children.forEach(function(item) {
          attachRel(item, "1" + (data.children.length > 1 ? 1 : 0));
        });
      }
      return data;
    };

    const [ds, setDS] = useState(datasource);
    useEffect(() => {
      setDS(datasource);
    }, [datasource]);

    const dsDigger = new JSONDigger(datasource, "id", "children");

    const clickChartHandler = event => {
      if (!event.target.closest(".oc-node")) {
        if (onClickChart) {
          onClickChart();
        }
        selectNodeService.clearSelectedNodeInfo();
      }
    };

    const panEndHandler = () => {
      setPanning(false);
      setCursor("default");
    };

    const panHandler = e => {
      let newX = 0;
      let newY = 0;
      if (!e.targetTouches) {
        // pand on desktop
        newX = e.pageX - startX;
        newY = e.pageY - startY;
      } else if (e.targetTouches.length === 1) {
        // pan on mobile device
        newX = e.targetTouches[0].pageX - startX;
        newY = e.targetTouches[0].pageY - startY;
      } else if (e.targetTouches.length > 1) {
        return;
      }
      if (transform === "") {
        if (transform.indexOf("3d") === -1) {
          setTransform("matrix(1,0,0,1," + newX + "," + newY + ")");
        } else {
          setTransform(
            "matrix3d(1,0,0,0,0,1,0,0,0,0,1,0," + newX + ", " + newY + ",0,1)"
          );
        }
      } else {
        let matrix = transform.split(",");
        if (transform.indexOf("3d") === -1) {
          matrix[4] = newX;
          matrix[5] = newY + ")";
        } else {
          matrix[12] = newX;
          matrix[13] = newY;
        }
        setTransform(matrix.join(","));
      }
    };

    const panStartHandler = e => {
      if (e.target.closest(".oc-node")) {
        setPanning(false);
        return;
      } else {
        setPanning(true);
        setCursor("move");
      }
      let lastX = 0;
      let lastY = 0;
      if (transform !== "") {
        let matrix = transform.split(",");
        if (transform.indexOf("3d") === -1) {
          lastX = parseInt(matrix[4]);
          lastY = parseInt(matrix[5]);
        } else {
          lastX = parseInt(matrix[12]);
          lastY = parseInt(matrix[13]);
        }
      }
      if (!e.targetTouches) {
        // pand on desktop
        setStartX(e.pageX - lastX);
        setStartY(e.pageY - lastY);
      } else if (e.targetTouches.length === 1) {
        // pan on mobile device
        setStartX(e.targetTouches[0].pageX - lastX);
        setStartY(e.targetTouches[0].pageY - lastY);
      } else if (e.targetTouches.length > 1) {
        return;
      }
    };

    const updateChartScale = newScale => {
      let matrix = [];
      let targetScale = 1;
      if (transform === "") {
        setTransform("matrix(" + newScale + ", 0, 0, " + newScale + ", 0, 0)");
      } else {
        matrix = transform.split(",");
        if (transform.indexOf("3d") === -1) {
          targetScale = Math.abs(window.parseFloat(matrix[3]) * newScale);
          if (targetScale > zoomoutLimit && targetScale < zoominLimit) {
            matrix[0] = "matrix(" + targetScale;
            matrix[3] = targetScale;
            setTransform(matrix.join(","));
          }
        } else {
          targetScale = Math.abs(window.parseFloat(matrix[5]) * newScale);
          if (targetScale > zoomoutLimit && targetScale < zoominLimit) {
            matrix[0] = "matrix3d(" + targetScale;
            matrix[5] = targetScale;
            setTransform(matrix.join(","));
          }
        }
      }
    };

    const zoomHandler = e => {
      let newScale = 1 + (e.deltaY > 0 ? -0.2 : 0.2);
      updateChartScale(newScale);
    };

    const exportPDF = (canvas, exportFilename) => {
      const canvasWidth = Math.floor(canvas.width);
      const canvasHeight = Math.floor(canvas.height);
      const doc =
        canvasWidth > canvasHeight
          ? new jsPDF({
              orientation: "landscape",
              unit: "px",
              format: [canvasWidth, canvasHeight]
            })
          : new jsPDF({
              orientation: "portrait",
              unit: "px",
              format: [canvasHeight, canvasWidth]
            });
      doc.addImage(canvas.toDataURL("image/jpeg", 1.0), "JPEG", 0, 0);
      doc.save(exportFilename + ".pdf");
    };

    const exportPNG = (canvas, exportFilename) => {
      const isWebkit = "WebkitAppearance" in document.documentElement.style;
      const isFf = !!window.sidebar;
      const isEdge =
        navigator.appName === "Microsoft Internet Explorer" ||
        (navigator.appName === "Netscape" &&
          navigator.appVersion.indexOf("Edge") > -1);

      if ((!isWebkit && !isFf) || isEdge) {
        window.navigator.msSaveBlob(canvas.msToBlob(), exportFilename + ".png");
      } else {
        setDataURL(canvas.toDataURL());
        setDownload(exportFilename + ".png");
        downloadButton.current.click();
      }
    };

    const changeHierarchy = async (draggedItemData, dropTargetId) => {
      await dsDigger.removeNode(draggedItemData.id);
      await dsDigger.addChildren(dropTargetId, draggedItemData);
      setDS({ ...dsDigger.ds });
    };

    useImperativeHandle(ref, () => ({
      exportTo: (exportFilename, exportFileextension) => {
        exportFilename = exportFilename || "OrgChart";
        exportFileextension = exportFileextension || "png";
        setExporting(true);
        const originalScrollLeft = container.current.scrollLeft;
        container.current.scrollLeft = 0;
        const originalScrollTop = container.current.scrollTop;
        container.current.scrollTop = 0;
        html2canvas(chart.current, {
          width: chart.current.clientWidth,
          height: chart.current.clientHeight,
          onclone: function(clonedDoc) {
            clonedDoc.querySelector(".orgchart").style.background = "none";
            clonedDoc.querySelector(".orgchart").style.transform = "";
          }
        }).then(
          canvas => {
            if (exportFileextension.toLowerCase() === "pdf") {
              exportPDF(canvas, exportFilename);
            } else {
              exportPNG(canvas, exportFilename);
            }
            setExporting(false);
            container.current.scrollLeft = originalScrollLeft;
            container.current.scrollTop = originalScrollTop;
          },
          () => {
            setExporting(false);
            container.current.scrollLeft = originalScrollLeft;
            container.current.scrollTop = originalScrollTop;
          }
        );
      },
      expandAllNodes: () => {
        chart.current
          .querySelectorAll(
            ".oc-node.hidden, .oc-hierarchy.hidden, .isSiblingsCollapsed, .isAncestorsCollapsed"
          )
          .forEach(el => {
            el.classList.remove(
              "hidden",
              "isSiblingsCollapsed",
              "isAncestorsCollapsed"
            );
          });
      }
    }));

    return (
      <div
        ref={container}
        className={"orgchart-container " + containerClass}
        onWheel={zoom ? zoomHandler : undefined}
        onMouseUp={pan && panning ? panEndHandler : undefined}
      >
        <div
          ref={chart}
          className={"orgchart " + chartClass}
          style={{ transform: transform, cursor: cursor }}
          onClick={clickChartHandler}
          onMouseDown={pan ? panStartHandler : undefined}
          onMouseMove={pan && panning ? panHandler : undefined}
        >
          <ul>
            <ChartNode
              datasource={attachRel(ds, "00")}
              NodeTemplate={NodeTemplate}
              draggable={draggable}
              collapsible={collapsible}
              multipleSelect={multipleSelect}
              changeHierarchy={changeHierarchy}
              onClickNode={onClickNode}
            />
          </ul>
        </div>
        <a
          className="oc-download-btn hidden"
          ref={downloadButton}
          href={dataURL}
          download={download}
        >
          &nbsp;
        </a>
        <div className={`oc-mask ${exporting ? "" : "hidden"}`}>
          <i className="oci oci-spinner spinner"></i>
        </div>
      </div>
    );
  }
)
Example #25
Source File: TopologyGraph.js    From ThreatMapper with Apache License 2.0 4 votes vote down vote up
TopologyGraph = forwardRef(
  ({ data, onNodeExpanded, onNodeCollapsed, onNodeClicked, onHover }, ref) => {
    const [container, setContainer] = useState(null);
    const [width, height] = useDOMSize(container);
    const { graph } = useGraph(container, data || {});
    const visible = useVisibilityState();
    const layoutManagerRef = useRef(null);
    const updateManagerRef = useRef(null);

    const trackedItem = useRef(null);
    const setTrackedItem = (item) => {
      trackedItem.current = item;
    };

    const graphRef = useRef(graph);
    useImperativeHandle(ref, () => ({
      updateRootNodes: (delta) => {
        updateManagerRef.current.updateRootNodes(delta);
      },

      updateNode: (node_id, delta) => {
        updateManagerRef.current.updateNode(node_id, delta);
      },

      updateEdges: (delta) => {
        updateManagerRef.current.updateEdges(delta);
      },

      findById: (node_id) => {
        const graph = graphRef.current;
        return graph.findById(node_id);
      },

      getParents: (node_id) => {
        const graph = graphRef.current;
        const item = graph.findById(node_id);
        return getParents(graph, item);
      },

      expandNode: (node_id) => {
        const graph = graphRef.current;
        const item = graph.findById(node_id);
        if (!itemIsExpanded(item)) {
          expandNode(graph, item);
          if (onNodeExpanded) {
            onNodeExpanded(item);
          }
        }
      },

      collapseNode: (node_id) => {
        const graph = graphRef.current;
        const item = graph.findById(node_id);
        if (itemIsExpanded(item)) {
          collapseNode(graph, item, onNodeCollapsed);
        }
      },
    }));

    useEffect(() => {
      // update the graph size when the container element is resized
      if (graph !== null) {
        // WTF something keeps resizing the container!?
        if (height < 1000) {
          graph.changeSize(width, height);
        }
        return;
      }
    }, [width, height]);

    useEffect(() => {
      if (layoutManagerRef.current) {
        if (visible) {
          layoutManagerRef.current.resume();
        } else {
          layoutManagerRef.current.pause();
        }
      }
    }, [visible]);

    useEffect(() => {
      if (graph === null) {
        return;
      }

      // this is used by the exported imperative API
      graphRef.current = graph;

      layoutManagerRef.current = new LayoutManager(graph, {
        tick: debounce(() => {
          if (trackedItem.current) {
            nodeToFront(trackedItem.current);
            graph.focusItem(trackedItem.current, true);
          }
        }, 500),

        onLayoutStart: () => {
          updateManagerRef.current.pause();
        },

        onLayoutEnd: () => {
          updateManagerRef.current.resume();
        },
      });

      updateManagerRef.current = new GraphUpdateManager(
        graph,
        layoutManagerRef.current
      );

      graph.on("node:mouseenter", (e) => {
        if (onHover) {
          onHover(e.item, true);
        }
      });
      graph.on("node:mouseleave", (e) => {
        if (onHover) {
          onHover(e.item, false);
        }
      });

      graph.on("node:drag", (e) => {
        e.preventDefault();
      });

      graph.on("combo:drag", (e) => {
        e.preventDefault();
      });

      graph.on("combo:dragend", (e) => {
        try {
          const combo = e.item;

          fixCombo(graph, combo);
        } catch (e) {
          console.error("exception handling dragend", e);
        }
      });

      graph.on("combo:click", (e) => {
        graph.focusItem(e.item, true);
      });

      graph.on("node:click", (e) => {
        try {
          const item = e.item;

          if (onNodeClicked) {
            const model = item.get("model");
            onNodeClicked(model);
          }

          if (itemExpands(item)) {
            if (itemIsExpanded(item)) {
              collapseNode(graph, item, onNodeCollapsed);
            } else {
              expandNode(graph, item);
              if (onNodeExpanded) {
                onNodeExpanded(item);
              }
            }
          }
        } catch (e) {
          console.error("exception handling click", e);
        }
      });

      graph.on("dragstart", () => {
        setTrackedItem(null);
      });

      graph.on("df-track-item", (e) => {
        setTrackedItem(e.item);
      });

      graph.on("beforeremoveitem", (e) => {
        const item = e.item;
        if (trackedItem.current?.get("model")?.id === item.id) {
          setTrackedItem(null);
        }
      });
    }, [graph, onNodeExpanded]);

    return <div className="TopologyGraph" ref={setContainer}></div>;
  }
)
Example #26
Source File: Video.js    From react-native-tv-demo with MIT License 4 votes vote down vote up
Video = forwardRef((props, ref) => {
  const {
    source,
    poster,
    paused,
    autoplay,
    controls,
    muted,
    repeat,
    inline,
    volume,
    rate,
    onLoad,
    onLoadStart,
    onReadyForDisplay,
    onPlaybackRateChange,
    onProgress,
    onSeek,
    onEnd,
    onError,
    onExitFullscreen,
  } = props;

  // State
  const [format, setFormat] = useState(FORMAT_HTML5);
  const [shakaPlayer, setShakaPlayer] = useState(null);

  // Video ref
  const video = useRef(null);

  useEffect(() => {
    //console.log('Video.useEffect([])');
    // Bind listeners
    bindListeners();
    return () => {
      // Destroy shaka palyer
      if (shakaPlayer) {
        shakaPlayer.destroy();
      }
      // Unbind listeners
      unbindListeners();
    };
  }, []);

  useEffect(() => {
    if (source) {
      // Get file extension from source
      const extension = source.split(/[#?]/)[0].split('.').pop().trim();
      // Get format
      let format = FORMAT_HTML5;
      if (extension === 'm3u8') {
        format = FORMAT_HLS;
      } else if (extension === 'mpd') {
        format = FORMAT_DASH;
        setShakaPlayer(new shaka.Player(video.current));
      }
      setFormat(format);
    }
  }, [source]);

  useEffect(() => {
    //console.log('Video.useEffect([shakaPlayer, source])');
    if (shakaPlayer && source) {
      shakaPlayer.load(source);
    }
  }, [shakaPlayer, source]);

  useEffect(() => {
    //console.log("Video.useEffect([paused])", paused);
    // Toggle play / pause from parent
    if (paused === false) {
      play();
    } else if (paused === true) {
      pause();
    }
  }, [paused]);

  useEffect(() => {
    console.log('rate: ', rate);
    if (rate) {
      if (rate === 0) {
        pause();
      } else {
        play();
        // TODO: handle rate < 1
      }
    }
  }, [rate]);

  useEffect(() => {
    if (volume >= 0 && volume <= 1) {
      video.current.volume = volume;
    }
  }, [volume]);

  // Private methods

  function play() {
    //console.log('Video.play()');
    if (video.current.paused) {
      if (video.current.currentTime > 0 || !autoplay) {
        // Handle old exception
        let playPromise = null;
        try {
          playPromise = video.current.play();
        } catch (error) {
          onError(error);
        }
        // Handle promise
        if (playPromise !== undefined) {
          playPromise
            .then(() => {
              // playback started
            })
            .catch((error) => {
              onError(error);
            });
        }
      }
    }
  }

  function pause() {
    //console.log('Video.pause()');
    if (!video.current.paused) {
      video.current.pause();
    }
  }

  // react-native-video API (public methods)

  useImperativeHandle(ref, () => ({
    /**
     * react-native-video seek() method requires seconds
     *
     * @param seconds
     */
    seek: (seconds) => {
      //console.log('Video.seek(' + seconds + ')');
      if (seconds) {
        if (seconds >= 0 && seconds < video.current.duration) {
          video.current.currentTime = seconds;
        }
      }
    },

    presentFullscrenPlayer: () => {
      //console.log('Video.presentFullscreenPlayer()');
      if (video) {
        if (video.current.requestFullscreen) {
          video.current.requestFullscreen();
        }
        // Deprecated
        else if (video.current.enterFullscreen) {
          video.current.enterFullscreen();
        } else if (video.current.webkitEnterFullscreen) {
          video.current.webkitEnterFullscreen();
        }
      }
    },

    dismissFullscreenPlayer: () => {
      //console.log('Video.dismissFullscreenPlayer()');
      if (document.exitFullscreen) {
        document.exitFullscreen();
      }
    },
  }));

  // react-native-video callback proxy

  /**
   * loadedmetadata => onLoad
   */
  function onVideoLoadedMetadata() {
    //console.log('Video.onVideoLoadedMetadata()');
    if (onLoad && video) {
      onLoad({
        currentPosition: 0,
        duration: video.current.duration,
        // TODO: naturalSize, audioTracks, textTracks
      });
    }
  }

  /**
   * loadstart => onLoadStart
   */
  function onVideoLoadStart() {
    if (source) {
      //console.log('Video.onVideoLoadStart()');
      if (onLoadStart && video) {
        onLoadStart({
          isNetwork: true,
          type: '',
          uri: source.uri,
        });
      }
    }
  }

  /**
   * waiting => onLoadStart
   */
  function onVideoWaiting() {
    //console.log('Video.onVideoWaiting()');
    if (onLoadStart && video) {
      onLoadStart({
        isNetwork: true,
        type: '',
        uri: source.uri,
      });
    }
  }

  /**
   * canplaythrough => onReadyForDisplay
   */
  function onVideoCanPlayThrough() {
    //console.log('Video.onVideoCanPlayThrough()');
    if (video) {
      if (onReadyForDisplay) {
        onReadyForDisplay();
      }
    }
  }

  /**
   * play => onPlaybackRateChange
   */
  function onVideoPlay() {
    //console.log('Video.onVideoPlay()');
    if (onPlaybackRateChange) {
      onPlaybackRateChange({playbackRate: 1});
    }
  }

  /**
   * pause => onPlaybackRateChange
   */
  function onVideoPause() {
    //console.log('Video.onVideoPause()');
    if (onPlaybackRateChange) {
      onPlaybackRateChange({playbackRate: 0});
    }
  }

  /**
   * ratechange => onPlaybackRateChange
   */
  function onVideoRateChange() {
    //console.log('Video.onVideoRateChange()');
    if (onPlaybackRateChange && video) {
      onPlaybackRateChange({
        playbackRate: video.current.playbackRate,
      });
    }
  }

  /**
   * timeupdate => onProgress
   */
  function onVideoTimeUpdate() {
    //console.log('Video.onVideoTimeUpdate()');
    if (onProgress && video) {
      onProgress({
        seekableDuration: video.current.duration,
        playbableDuration: video.current.duration,
        currentTime: video.current.currentTime,
      });
    }
  }

  /**
   * seeked => onSeek
   */
  function onVideoSeeked() {
    //console.log('Video.onVideoSeeked()');
    if (onSeek && video) {
      onSeek({
        currentTime: video.current.currentTime,
        seekTime: video.current.currentTime,
      });
    }
  }

  /**
   * ended => onEnd
   */
  function onVideoEnded() {
    //console.log('Video.onVideoEnded()');
    if (onEnd) {
      onEnd();
    }
  }

  /**
   * error => onError
   */
  function onVideoError() {
    if (source) {
      //console.log('Video.onVideoError()');
      let error = {};
      // TODO: return same errors as react-native-video
      if (onError) {
        onError(error);
      }
    }
  }

  /**
   * Get exit fullscreen event for webkit
   */
  function onVideoEndFullscreen() {
    //console.log('Video.onVideoEndFullscreen()');
    if (onExitFullscreen) {
      onExitFullscreen();
    }
  }

  /**
   * get exit fullscreen event for firefox
   */
  function onVideoFullscreenChange(e) {
    //console.log('Video.onVideoEndFullscreen()');
    if (document.fullscreenElement) {
      // enter fullscreen
    } else if (onExitFullscreen) {
      onExitFullscreen();
    }
  }

  // Listeners

  function bindListeners() {
    //console.log('Video.bindListeners()');
    if (video && video.current) {
      // Unsupported native listeners
      video.current.addEventListener(
        'webkitendfullscreen',
        onVideoEndFullscreen,
      );
    }
    // Listeners on document
    document.addEventListener('fullscreenchange', onVideoFullscreenChange);
  }

  function unbindListeners() {
    //console.log('Video.unbindListeners()');
    if (video && video.current) {
      // Unsupported native listeners
      video.current.removeEventListener(
        'webkitendfullscreen',
        onVideoEndFullscreen,
      );
      // Listeners on document
      document.removeEventListener('fullscreenchange', onVideoFullscreenChange);
    }
  }

  // Optional params
  let controlsProp = controls ? {controls: 'controls'} : {};
  let autoPlayProp = autoplay ? {autoplay: 'autoplay'} : {};
  let mutedProp = muted ? {muted: 'muted'} : {};
  let repeatProp = repeat ? {loop: 'loop'} : {};
  let playsInlineProp = inline ? {playsInline: 'playsInline'} : {};

  // Build <video> element
  return (
    <video
      className="video"
      ref={video}
      src={source.uri || source}
      poster={poster}
      {...controlsProp}
      {...autoPlayProp}
      {...mutedProp}
      {...repeatProp}
      {...playsInlineProp}
      onLoadedMetadata={onVideoLoadedMetadata}
      onLoadStart={onVideoLoadStart}
      onWaiting={onVideoWaiting}
      onCanPlayThrough={onVideoCanPlayThrough}
      onPlay={onVideoPlay}
      onPause={onVideoPause}
      onRateChange={onVideoRateChange}
      onSeeked={onVideoSeeked}
      onTimeUpdate={onVideoTimeUpdate}
      onEnded={onVideoEnded}
      onError={onVideoError}
      style={{width: '100%', height: '100%'}}
    />
  );
})
Example #27
Source File: navBar.js    From AgileTC with Apache License 2.0 4 votes vote down vote up
NavBar = (props, ref) => {
  const { minder } = props;
  const [zoomValue, setZoomValue] = useState(100);
  // const [showMini, setShowMini] = useState(true);

  if (minder !== null) {
    minder.setDefaultOptions({ zoom });
  }
  const iconStyle = {
    width: 24,
    height: 24,
  };
  const handleClick = (key) => {
    minder.execCommand(key);
  };
  const handleCameraClick = () => {
    minder.execCommand('camera', minder.getRoot(), 600);
  };
  if (minder) {
    minder.on('zoom', () => {
      setZoomValue(minder.queryCommandValue('zoom'));
    });
  }

  // useEffect(() => {
  //   if (props.minder) {
  //     // 画布,渲染缩略图
  //     const paper = new kity.Paper(document.getElementsByClassName('nav-previewer')[0]);
  //     //  // 用两个路径来挥之节点和连线的缩略图
  //     const nodeThumb = paper.put(new kity.Path());
  //     const connectionThumb = paper.put(new kity.Path());

  //     //  // 表示可视区域的矩形
  //     const visibleRect = paper.put(new kity.Rect(100, 100).stroke('red', '1%'));
  //     let contentView = new kity.Box();
  //     let visibleView = new kity.Box();

  //     let pathHandler = getPathHandler(props.minder.getTheme());

  //     // 主题切换事件
  //     props.minder.on('themechange', (e) => {
  //       pathHandler = getPathHandler(e.theme);
  //     });

  //     function getPathHandler(theme) {
  //       switch (theme) {
  //         case 'tianpan':
  //         case 'tianpan-compact':
  //           return function (nodePathData, x, y, width, height) {
  //             const r = width >> 1;
  //             nodePathData.push('M', x, y + r, 'a', r, r, 0, 1, 1, 0, 0.01, 'z');
  //           };
  //         default: {
  //           return function (nodePathData, x, y, width, height) {
  //             nodePathData.push('M', x, y, 'h', width, 'v', height, 'h', -width, 'z');
  //           };
  //         }
  //       }
  //     }

  //     function bind() {
  //       props.minder.on('layout layoutallfinish', updateContentView);
  //       props.minder.on('viewchange', updateVisibleView);
  //     }

  //     bind();

  //     function updateContentView() {
  //       const view = props.minder.getRenderContainer().getBoundaryBox();
  //       contentView = view;
  //       const padding = 30;

  //       paper.setViewBox(
  //         view.x - padding - 0.5,
  //         view.y - padding - 0.5,
  //         view.width + padding * 2 + 1,
  //         view.height + padding * 2 + 1
  //       );

  //       const nodePathData = [];
  //       const connectionThumbData = [];

  //       props.minder.getRoot().traverse(function (node) {
  //         const box = node.getLayoutBox();
  //         pathHandler(nodePathData, box.x, box.y, box.width, box.height);
  //         if (node.getConnection() && node.parent && node.parent.isExpanded()) {
  //           connectionThumbData.push(node.getConnection().getPathData());
  //         }
  //       });

  //       paper.setStyle('background', props.minder.getStyle('background'));

  //       if (nodePathData.length) {
  //         nodeThumb.fill(props.minder.getStyle('root-background')).setPathData(nodePathData);
  //       } else {
  //         nodeThumb.setPathData(null);
  //       }

  //       if (connectionThumbData.length) {
  //         connectionThumb
  //           .stroke(props.minder.getStyle('connect-color'), '0.5%')
  //           .setPathData(connectionThumbData);
  //       } else {
  //         connectionThumb.setPathData(null);
  //       }

  //       updateVisibleView();
  //     }

  //     updateContentView();

  //     function updateVisibleView() {
  //       visibleView = props.minder.getViewDragger().getView();
  //       visibleRect.setBox(visibleView.intersect(contentView));
  //     }
  //     updateVisibleView();
  //   }
  // }, [props]);

  useImperativeHandle(ref, () => ({
    // 暴露给父组件的方法
    handleClick,
  }));
  return (
    <div className="nav-bar">
      <a onClick={() => handleClick('ZoomIn')} title="放大">
        <CustomIcon type="zoomIn" style={iconStyle} />
      </a>
      <span className="zoom-text">{zoomValue}%</span>
      <a onClick={() => handleClick('ZoomOut')} title="缩小">
        <CustomIcon type="zoomOut" style={iconStyle} />
      </a>
      <a style={{ marginLeft: 8 }} onClick={handleCameraClick} title="定位根节点">
        <CustomIcon type="target" style={iconStyle} />
      </a>
      {/* <a
        style={{ marginLeft: 8 }}
        onClick={() => {
          setShowMini(!showMini);
        }}
        title="展示/隐藏缩略图"
      >
        <Icon type="eye" style={{ fontSize: 24, color: 'rgba(0, 0, 0, 0.85)' }} />
      </a>
      <div className="nav-previewer" style={{ visibility: showMini ? 'visible' : 'hidden' }}></div> */}
    </div>
  );
}
Example #28
Source File: Recaptcha.js    From react-native-recaptcha-that-works with MIT License 4 votes vote down vote up
Recaptcha = forwardRef(({
    headerComponent,
    footerComponent,
    loadingComponent,
    webViewProps,
    modalProps,
    onVerify,
    onExpire,
    onError,
    onClose,
    onLoad,
    theme,
    size,
    siteKey,
    baseUrl,
    lang,
    style,
    enterprise,
    recaptchaDomain,
    gstaticDomain
}, $ref,
) => {
    const $isClosed = useRef(true);
    const $webView = useRef();
    const [visible, setVisible] = useState(false);
    const [loading, setLoading] = useState(true);

    const isInvisibleSize = size === 'invisible';

    const html = useMemo(() => {
        return getTemplate({
            siteKey,
            size,
            theme,
            lang,
        }, enterprise, recaptchaDomain, gstaticDomain);
    }, [siteKey, size, theme, lang, enterprise]);

    const handleLoad = useCallback((...args) => {
        onLoad && onLoad(...args);

        if (isInvisibleSize) {
            $webView.current.injectJavaScript(`
                window.rnRecaptcha.execute();
            `);
        }

        setLoading(false);
    }, [onLoad, isInvisibleSize]);

    const handleClose = useCallback((...args) => {
        if ($isClosed.current) {
            return;
        }
        $isClosed.current = true;
        setVisible(false);
        onClose && onClose(...args);
    }, [onClose]);

    const handleMessage = useCallback((content) => {
        try {
            const payload = JSON.parse(content.nativeEvent.data);
            if (payload.close && isInvisibleSize) {
                handleClose();
            }
            if (payload.load) {
                handleLoad(...payload.load);
            }
            if (payload.expire) {
                onExpire && onExpire(...payload.expire);
            }
            if (payload.error) {
                handleClose();
                onError && onError(...payload.error);
            }
            if (payload.verify) {
                handleClose();
                onVerify && onVerify(...payload.verify);
            }
        } catch (err) {
            console.warn(err);
        }
    }, [onVerify, onExpire, onError, handleClose, handleLoad, isInvisibleSize]);

    const source = useMemo(() => ({
        html,
        baseUrl,
    }), [html, baseUrl]);

    useImperativeHandle($ref, () => ({
        open: () => {
            setVisible(true);
            setLoading(true);
            $isClosed.current = false;
        },
        close: handleClose,
    }), [handleClose]);

    const handleNavigationStateChange = useCallback(() => {
        // prevent navigation on Android
        if (!loading) {
            $webView.current.stopLoading();
        }
    }, [loading]);

    const handleShouldStartLoadWithRequest = useCallback(event => {
        // prevent navigation on iOS
        return event.navigationType === 'other';
    }, [loading]);

    const webViewStyles = useMemo(() => [
        styles.webView,
        style,
    ], [style]);

    const renderLoading = () => {
        if (!loading && source) {
            return null;
        }
        return (
            <View style={styles.loadingContainer}>
                {loadingComponent || <ActivityIndicator size="large" />}
            </View>
        );
    };

    return (
        <Modal
            transparent
            {...modalProps}
            visible={visible}
            onRequestClose={handleClose}
        >
            {headerComponent}
            <WebView
                bounces={false}
                allowsBackForwardNavigationGestures={false}
                {...webViewProps}
                source={source}
                style={webViewStyles}
                originWhitelist={originWhitelist}
                onMessage={handleMessage}
                onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest}
                onNavigationStateChange={handleNavigationStateChange}
                ref={$webView}
            />
            {footerComponent}
            {renderLoading()}
        </Modal>
    );
})
Example #29
Source File: index.js    From eosio-components with MIT License 4 votes vote down vote up
Backdrop = forwardRef(
  (
    {
      frontLayer,
      backLayer,
      layerHeightUp,
      classes: extraClasses,
      className,
      headerText,
      backgroundColor,
      isStaticPage,
      layerHeightDown
    },
    ref
  ) => {
    const theme = useTheme()
    const classes = useStyles()
    const rootClasses = useRootStyles({ color: backgroundColor })
    const isMobile = useMediaQuery(theme.breakpoints.down('xs'))
    const isLandscape = useMediaQuery('(orientation: landscape)')
    const [frontLayerHeight, setFrontLayerHeight] = useState(layerHeightUp)
    const [transaction, setTransaction] = useState(false)
    const [isArrowUp, setIsArrowUp] = useState(false)
    const [showHeader, setShowHeader] = useState(false)

    const handleOnClick = () => {
      const height = window.innerHeight
      const contentHeight = isArrowUp
        ? height - layerHeightDown
        : height - (height - layerHeightUp)

      setIsArrowUp(!isArrowUp)
      setNewHeight(contentHeight)
    }

    useImperativeHandle(ref, () => ({
      toggleOnClickMobile: () => {
        if (isMobile) {
          handleOnClick()
        }
      }
    }))

    const setNewHeight = useCallback(async (value) => {
      setTransaction(true)
      setFrontLayerHeight(value)
      setTimeout(() => {
        setTransaction(false)
      }, TRANSITION_DURATION)
    }, [])

    useEffect(() => {
      const height = window.innerHeight

      setShowHeader(
        (height < MIN_HEIGHT_TO_COLLAPSE && isLandscape) ||
          (isMobile && !isStaticPage)
      )

      if (isStaticPage) {
        setNewHeight(height - (height - layerHeightUp))

        return
      }

      if (isMobile) {
        setNewHeight(height - layerHeightDown)

        return
      }

      if (!isMobile) {
        setNewHeight(
          isLandscape && height < MIN_HEIGHT_TO_COLLAPSE
            ? height - layerHeightDown
            : height - (height - layerHeightUp)
        )
      }
    }, [
      isMobile,
      layerHeightDown,
      layerHeightUp,
      setNewHeight,
      isStaticPage,
      isLandscape
    ])

    return (
      <div className={clsx(className, rootClasses.root, extraClasses.root)}>
        <div
          className={clsx(
            classes.backLayer,
            transaction ? classes.backlayerTransition : null,
            extraClasses.backLayer
          )}
          style={{
            height: frontLayerHeight
          }}
        >
          {backLayer}
        </div>
        <Paper className={clsx(classes.frontLayer, extraClasses.frontLayer)}>
          <div className={clsx(classes.headerBox, extraClasses.headerBox)}>
            {headerText}
            {showHeader && (
              <IconButton
                aria-label=""
                classes={{ root: classes.iconDrop }}
                onClick={handleOnClick}
              >
                {isArrowUp ? <DropDown /> : <DropUp />}
              </IconButton>
            )}
          </div>
          <div className={classes.contentWrapper}>
            <div className={classes.frontLayerContent}>{frontLayer}</div>
          </div>
        </Paper>
      </div>
    )
  }
)