mirror of
https://github.com/Tencent/Hippy.git
synced 2026-10-27 12:17:15 +08:00
feat(hippy-react-web): fix web style issues (#2087)
* feat(hippy-react-web): fix web style issues * feat(hippy-react-web): fix code magic number * feat(hippy-react-web): fix pull header refresh distance * feat(hippy-react-web): fix cr issues Co-authored-by: Zoom Chan <zoom_chan@163.com>
This commit is contained in:
committed by
zoomchan-cxj
co-authored by
Zoom Chan
parent
d648eda41f
commit
e6d04161da
@@ -28,7 +28,7 @@
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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;');
|
||||
|
||||
@@ -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<ListViewProps> = React.forwardRef((props, ref) => {
|
||||
const {
|
||||
getRowStyle = noop, rowShouldSticky, scrollEnabled = true, showScrollIndicator = true,
|
||||
@@ -299,7 +311,7 @@ const ListView: React.FC<ListViewProps> = React.forwardRef((props, ref) => {
|
||||
}
|
||||
}, [pullHeaderRef]);
|
||||
return (
|
||||
<div ref={pullHeaderRef} style={{ visibility: headerVisibility.current, marginTop: `-${pullHeaderHeight.current}px` }}>
|
||||
<div ref={pullHeaderRef} style={{ ...styles.pullHeaderContainer, visibility: headerVisibility.current, marginTop: `-${pullHeaderHeight.current}px` }}>
|
||||
{renderPullHeader()}
|
||||
</div>
|
||||
);
|
||||
@@ -337,7 +349,10 @@ const ListView: React.FC<ListViewProps> = 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<ListViewProps> = React.forwardRef((props, ref) => {
|
||||
>
|
||||
<MListView
|
||||
{...listViewProps}
|
||||
contentContainerStyle={{ position: 'relative' }}
|
||||
ref={listRef}
|
||||
className={(!showScrollIndicator && HIDE_SCROLLBAR_CLASS) || ''}
|
||||
dataSource={getDataSource()}
|
||||
|
||||
@@ -24,6 +24,7 @@ import React, { useState } from 'react';
|
||||
import MPullToRefresh from 'rmc-pull-to-refresh';
|
||||
import { formatWebStyle } from '../adapters/transfer';
|
||||
import { isFunc } from '../utils';
|
||||
import { DEFAULT_CONTAINER_STYLE, DEFAULT_DISTANCE_TO_REFRESH, REFRESH_DISTANCE_SCREEN_Y_OFFSET } from '../constants';
|
||||
|
||||
export interface RefreshWrapperProps {
|
||||
ref?: any;
|
||||
@@ -33,6 +34,17 @@ export interface RefreshWrapperProps {
|
||||
bounceTime?: number;
|
||||
}
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
...DEFAULT_CONTAINER_STYLE,
|
||||
},
|
||||
pullHeaderContainer: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignContent: 'center',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Simply to implement the drag down to refresh feature.
|
||||
* @noInheritDoc
|
||||
@@ -61,7 +73,7 @@ const RefreshWrapper: React.FC<RefreshWrapperProps> = React.forwardRef((props, r
|
||||
}
|
||||
}, [pullHeaderRef]);
|
||||
return (
|
||||
<div ref={pullHeaderRef} style={{ visibility: headerVisibility.current, marginTop: `-${pullHeaderHeight.current}px` }}>
|
||||
<div ref={pullHeaderRef} style={{ ...styles.pullHeaderContainer, visibility: headerVisibility.current, marginTop: `-${pullHeaderHeight.current}px` }}>
|
||||
{getRefresh()}
|
||||
</div>
|
||||
);
|
||||
@@ -109,7 +121,10 @@ const RefreshWrapper: React.FC<RefreshWrapperProps> = 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<RefreshWrapperProps> = React.forwardRef((props, r
|
||||
delete newProps.onRefresh;
|
||||
|
||||
return (
|
||||
<div {...newProps} ref={wrapperRef}>
|
||||
<div {...newProps} style={formatWebStyle({ ...styles.container, ...newProps.style })} ref={wrapperRef}>
|
||||
{ newChildren }
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -264,7 +264,7 @@ const ScrollView: React.FC<ScrollViewProps> = React.forwardRef((props, ref) => {
|
||||
onTouchDown={onTouchStart}
|
||||
onTouchMove={onTouchMove}
|
||||
onTouchEnd={onTouchEnd}
|
||||
className={!showScrollIndicator && HIDE_SCROLLBAR_CLASS}
|
||||
className={ !showScrollIndicator ? HIDE_SCROLLBAR_CLASS : ''}
|
||||
ref={scrollRef}
|
||||
style={[
|
||||
directionStyle,
|
||||
|
||||
@@ -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<TextProps> = 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 <View {...newProps} />;
|
||||
return (
|
||||
<View {...newProps} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 ? (
|
||||
<span data-1={String(hasTextAncestor)} ref={hostRef} {...newProps} />
|
||||
) : (
|
||||
<TextAncestorContext.Provider value={true}>
|
||||
<span ref={hostRef} data-1={String(hasTextAncestor)} {...newProps} />
|
||||
</TextAncestorContext.Provider>
|
||||
);
|
||||
});
|
||||
Text.displayName = 'Text';
|
||||
export {
|
||||
Text,
|
||||
TextProps,
|
||||
};
|
||||
|
||||
export default Text;
|
||||
|
||||
@@ -55,9 +55,15 @@ export const ViewPager: React.FC<ViewPagerProps> = 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 <SwiperSlide key={`ViewPager-${keyParam}`}>{item}</SwiperSlide>;
|
||||
return <SwiperSlide style={swiperSlideStyle} key={`ViewPager-${keyParam}`}>{item}</SwiperSlide>;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user