diff --git a/examples/hippy-react-demo/scripts/template.html b/examples/hippy-react-demo/scripts/template.html index aac602042..d4ca7cb9c 100644 --- a/examples/hippy-react-demo/scripts/template.html +++ b/examples/hippy-react-demo/scripts/template.html @@ -28,7 +28,7 @@ } #root { - min-height: 100vh; + height: 100vh; display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; diff --git a/packages/hippy-react-web/README.md b/packages/hippy-react-web/README.md index ab23c0b26..9f8e8880c 100644 --- a/packages/hippy-react-web/README.md +++ b/packages/hippy-react-web/README.md @@ -31,17 +31,10 @@ module.exports = { ## Project progress -| Working | Not Working | -| ---------- | -------------- | -| View | RefreshWrapper | -| Text | Navigator | -| Animation | Modal | -| Image | NetInfo | -| ListView | | -| ScrollView | | -| TextInput | | -| ViewPager | | -| WebView | | -| VideoPlayer| | -| Slider | | -| TabHost | | +| Not Working | +| -------------- | +| Navigator | +| NetInfo | +| RippleViewAndorid | +| WaterFallView | +| VideoPlayer | diff --git a/packages/hippy-react-web/src/adapters/hide-scrollbar.ts b/packages/hippy-react-web/src/adapters/hide-scrollbar.ts index fa8dfb901..8bac21c7b 100644 --- a/packages/hippy-react-web/src/adapters/hide-scrollbar.ts +++ b/packages/hippy-react-web/src/adapters/hide-scrollbar.ts @@ -23,9 +23,9 @@ import { canUseDOM } from '../utils'; export const HIDE_SCROLLBAR_CLASS = '__hippy-react-hide-scrollbar'; const hideScrollbarKey = '__hippyReactHideScrollbarActive'; -export const shouldHideScrollBar = (isHideScrollBar: boolean) => { +export const shouldHideScrollBar = (isShowScrollBar: boolean) => { if (canUseDOM) { - if (isHideScrollBar && !window[hideScrollbarKey]) { + if (!isShowScrollBar && !window[hideScrollbarKey]) { window[hideScrollbarKey] = true; document.styleSheets[0].addRule(`.${HIDE_SCROLLBAR_CLASS}::-webkit-scrollbar`, 'display: none'); document.styleSheets[0].addRule(`.${HIDE_SCROLLBAR_CLASS}`, '-ms-overflow-style: none; scrollbar-width: none;'); diff --git a/packages/hippy-react-web/src/components/list-view.tsx b/packages/hippy-react-web/src/components/list-view.tsx index 8e4d183a4..c0d4b6dc4 100644 --- a/packages/hippy-react-web/src/components/list-view.tsx +++ b/packages/hippy-react-web/src/components/list-view.tsx @@ -27,6 +27,7 @@ import { formatWebStyle } from '../adapters/transfer'; import { canUseDOM, isFunc, noop } from '../utils'; import { HIDE_SCROLLBAR_CLASS, shouldHideScrollBar } from '../adapters/hide-scrollbar'; import { LayoutEvent } from '../types'; +import { DEFAULT_DISTANCE_TO_REFRESH, REFRESH_DISTANCE_SCREEN_Y_OFFSET } from '../constants'; import View from './view'; interface ListViewItemProps { @@ -80,7 +81,17 @@ const styles = StyleSheet.create({ listDefault: { flex: 1, }, + container: { + display: 'flex', + flexDirection: 'column', + }, + pullHeaderContainer: { + display: 'flex', + justifyContent: 'center', + alignContent: 'center', + }, }); + let didWarn = !canUseDOM; const setIntersectionObserve = (observeCallback: (entries: any[]) => void) => { let observe: null | IntersectionObserver = null; @@ -120,7 +131,7 @@ function ListViewItem(props: ListViewItemProps) { } }, [listItemRef]); - const liElementProps = { ...props, style: { ...formatWebStyle(style), ...itemStyle } }; + const liElementProps = { ...props, style: { ...styles.container, ...formatWebStyle(style), ...itemStyle } }; delete liElementProps.observer; delete liElementProps.height; delete liElementProps.getRowKey; @@ -132,6 +143,7 @@ function ListViewItem(props: ListViewItemProps) { ); } + const ListView: React.FC = React.forwardRef((props, ref) => { const { getRowStyle = noop, rowShouldSticky, scrollEnabled = true, showScrollIndicator = true, @@ -299,7 +311,7 @@ const ListView: React.FC = React.forwardRef((props, ref) => { } }, [pullHeaderRef]); return ( -
+
{renderPullHeader()}
); @@ -337,7 +349,10 @@ const ListView: React.FC = React.forwardRef((props, ref) => { refreshing={refreshing} onRefresh={refresh} indicator={pullIndicator} - distanceToRefresh={pullHeaderHeight.current || 100} + distanceToRefresh={ + pullHeaderHeight.current + ? pullHeaderHeight.current - REFRESH_DISTANCE_SCREEN_Y_OFFSET : DEFAULT_DISTANCE_TO_REFRESH + } />; } @@ -347,6 +362,7 @@ const ListView: React.FC = React.forwardRef((props, ref) => { > = React.forwardRef((props, r } }, [pullHeaderRef]); return ( -
+
{getRefresh()}
); @@ -109,7 +121,10 @@ const RefreshWrapper: React.FC = React.forwardRef((props, r refreshing={refreshing} onRefresh={handleOnRefresh} indicator={pullIndicator} - distanceToRefresh={pullHeaderHeight.current || 100} + distanceToRefresh={ + pullHeaderHeight.current + ? pullHeaderHeight.current - REFRESH_DISTANCE_SCREEN_Y_OFFSET : DEFAULT_DISTANCE_TO_REFRESH + } />, }); @@ -119,7 +134,7 @@ const RefreshWrapper: React.FC = React.forwardRef((props, r delete newProps.onRefresh; return ( -
+
{ newChildren }
); diff --git a/packages/hippy-react-web/src/components/scroll-view.tsx b/packages/hippy-react-web/src/components/scroll-view.tsx index f980c98e2..3d62c614e 100644 --- a/packages/hippy-react-web/src/components/scroll-view.tsx +++ b/packages/hippy-react-web/src/components/scroll-view.tsx @@ -264,7 +264,7 @@ const ScrollView: React.FC = React.forwardRef((props, ref) => { onTouchDown={onTouchStart} onTouchMove={onTouchMove} onTouchEnd={onTouchEnd} - className={!showScrollIndicator && HIDE_SCROLLBAR_CLASS} + className={ !showScrollIndicator ? HIDE_SCROLLBAR_CLASS : ''} ref={scrollRef} style={[ directionStyle, diff --git a/packages/hippy-react-web/src/components/text.tsx b/packages/hippy-react-web/src/components/text.tsx index 03ffd4907..df6ead101 100644 --- a/packages/hippy-react-web/src/components/text.tsx +++ b/packages/hippy-react-web/src/components/text.tsx @@ -19,42 +19,69 @@ */ // @ts-nocheck -import React from 'react'; +import React, { createContext, useRef } from 'react'; import { formatWebStyle } from '../adapters/transfer'; -import View from './view'; +import { LayoutEvent } from '../types'; +import useResponderEvents from '../modules/use-responder-events'; +import useElementLayout from '../modules/use-element-layout'; +import { TouchEvent } from '../modules/use-responder-events/types'; +import { DEFAULT_CONTAINER_STYLE } from '../constants'; + +const baseTextStyle = { + ...DEFAULT_CONTAINER_STYLE, + backgroundColor: 'transparent', + border: '0 solid black', + boxSizing: 'border-box', + color: 'black', + font: '14px System', + listStyle: 'none', + margin: 0, + padding: 0, + textAlign: 'inherit', + textDecoration: 'none', + whiteSpace: 'pre-wrap', + wordWrap: 'break-word', +}; const styles = { - initial: { - borderWidth: 0, - boxSizing: 'border-box', - color: 'inherit', - fontFamily: 'System', - fontSize: 14, - fontStyle: 'inherit', - fontVariant: ['inherit'], - fontWeight: 'inherit', - lineHeight: 'inherit', - margin: 0, - padding: 0, - textDecorationLine: 'none', - whiteSpace: 'pre-wrap', - wordWrap: 'break-word', + text: { + ...baseTextStyle, }, - isInAParentTextFontStyle: { - // inherit parent font styles + textHasParent: { + ...baseTextStyle, fontFamily: 'inherit', fontSize: 'inherit', whiteSpace: 'inherit', }, - singleLineStyle: { + singleText: { maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - display: 'block', + wordWrap: 'normal', + }, + multiText: { + display: '-webkit-box', + maxWidth: '100%', + overflow: 'hidden', + textOverflow: 'ellipsis', + WebkitBoxOrient: 'vertical', }, }; +const TextAncestorContext = createContext(false); + +interface TextProps { + style?: HippyTypes.Style | HippyTypes.Style[]; + numberOfLines?: number; + opacity?: number; + ellipsizeMode?: 'clip' | 'ellipsis'; + onLayout: (e: LayoutEvent) => void; + onTouchDown?: (e: TouchEvent) => void; + onTouchMove?: (e: TouchEvent) => void; + onTouchEnd?: (e: TouchEvent) => void; + onTouchCancel?: (e: TouchEvent) => void; +} /** * A React component for displaying text. @@ -62,54 +89,63 @@ const styles = { * `Text` doesn't support nesting. * @noInheritDoc */ -export class Text extends React.Component { - private static childContextTypes = { - isInAParentText: () => {}, - }; +const Text: React.FC = React.forwardRef((props: TextProps, ref) => { + const hasTextAncestor = React.useContext(TextAncestorContext); + const { style = {}, numberOfLines = 1, ellipsizeMode = 'ellipsis' } = props; - public constructor(props) { - super(props); - this.state = {}; + const hostRef: any = useRef(null); + React.useImperativeHandle(ref, () => hostRef.current, [hostRef.current]); + + const { onTouchDown, onTouchEnd, onTouchCancel, onTouchMove } = props; + useResponderEvents(hostRef, { onTouchDown, onTouchEnd, onTouchCancel, onTouchMove }); + useElementLayout(hostRef, props.onLayout); + + const newProps = { ...props }; + let newStyle: HippyTypes.Style = {}; + if (hasTextAncestor) { + newStyle = { ...newStyle, ...styles.textHasParent }; + } else { + newStyle = { ...newStyle, ...styles.text }; } - - public getChildContext() { - return { isInAParentText: true }; - } - - public render() { - let { style } = this.props; - const { isInAParentText } = this.context; - const { numberOfLines, ellipsizeMode } = this.props; - if (style) { - style = formatWebStyle(style); - } - - const textOverflow = ellipsizeMode === 'clip' ? 'clip' : 'ellipsis'; - const baseStyle = { - textOverflow, - overflow: 'hidden', - display: numberOfLines > 1 ? '-webkit-box' : 'inline', - WebkitBoxOrient: 'vertical', - WebkitLineClamp: numberOfLines ? numberOfLines.toString() : '0', + if (numberOfLines === 1) { + newStyle = { ...newStyle, ...styles.singleText }; + } else { + newStyle = { + ...newStyle, + ...styles.multiText, + WebkitLineClamp: numberOfLines > 1 ? numberOfLines : 1, }; - const newStyle = Object.assign({}, style, baseStyle); - const newProps = Object.assign({}, this.props, { - style: formatWebStyle([styles.initial, - isInAParentText === true && styles.isInAParentText, - newStyle, - numberOfLines === 1 && Object.assign({}, styles.singleLineStyle, { textOverflow }), - isInAParentText === true && { display: 'inline' }, - ]), - }); - delete newProps.numberOfLines; - delete newProps.ellipsizeMode; - delete newProps.enableScale; - - if (isInAParentText) return ; - return ( - - ); } -} + + if (props.opacity) { + newStyle.opacity = props.opacity; + } + const composedStyle = Array.isArray(style) ? [newStyle, ...style] : { ...newStyle, ...style }; + const formatedStyle = formatWebStyle(composedStyle); + newProps.style = formatWebStyle({ ...formatedStyle, textOverflow: ellipsizeMode }); + + // delete span unsupported props + delete newProps.ellipsizeMode; + delete newProps.numberOfLines; + delete newProps.onLayout; + delete newProps.onTouchDown; + delete newProps.onTouchMove; + delete newProps.onTouchEnd; + delete newProps.onTouchCancel; + delete newProps.enableScale; + + return hasTextAncestor ? ( + + ) : ( + + + + ); +}); +Text.displayName = 'Text'; +export { + Text, + TextProps, +}; export default Text; diff --git a/packages/hippy-react-web/src/components/view-pager.tsx b/packages/hippy-react-web/src/components/view-pager.tsx index 0cca4d6b7..51a096e24 100644 --- a/packages/hippy-react-web/src/components/view-pager.tsx +++ b/packages/hippy-react-web/src/components/view-pager.tsx @@ -55,9 +55,15 @@ export const ViewPager: React.FC = React.forwardRef((props, ref) const renderViewPagerItem = () => { if (!children || (children as React.ReactNodeArray).length === 0) return null; + const swiperSlideStyle: React.CSSProperties = { + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }; return children.map((item: any, index: number) => { const keyParam = index; - return {item}; + return {item}; }); }; diff --git a/packages/hippy-react-web/src/components/view.tsx b/packages/hippy-react-web/src/components/view.tsx index c9e4c327d..ae8b8be52 100644 --- a/packages/hippy-react-web/src/components/view.tsx +++ b/packages/hippy-react-web/src/components/view.tsx @@ -24,17 +24,14 @@ import useElementLayout from '../modules/use-element-layout'; import { TouchEvent } from '../modules/use-responder-events/types'; import { LayoutEvent } from '../types'; import { warn } from '../utils'; +import { DEFAULT_CONTAINER_STYLE } from '../constants'; const styles = { root: { - alignItems: 'stretch', + ...DEFAULT_CONTAINER_STYLE, borderWidth: 0, borderStyle: 'solid', boxSizing: 'border-box', - display: 'flex', - flexBasis: 'auto', - flexDirection: 'column', - flexShrink: 0, margin: 0, padding: 0, position: 'relative', diff --git a/packages/hippy-react-web/src/constants.ts b/packages/hippy-react-web/src/constants.ts index d6d5c2ce0..bfcc6ae69 100644 --- a/packages/hippy-react-web/src/constants.ts +++ b/packages/hippy-react-web/src/constants.ts @@ -29,3 +29,14 @@ export const UNSUPPORTED_PROPS_MAP = { webview: ['userAgent', 'method', 'onLoad', 'onLoadStart'], view: ['nativeBackgroundAndroid'], }; + +export const DEFAULT_CONTAINER_STYLE = { + display: 'flex', + flexDirection: 'column', + alignItems: 'stretch', + flexBasis: 'auto', + flexShrink: 0, +}; + +export const DEFAULT_DISTANCE_TO_REFRESH = 50; +export const REFRESH_DISTANCE_SCREEN_Y_OFFSET = 1;