Skip to content

Commit

Permalink
feat(ui): tooltip component. Closes #261 (#263)
Browse files Browse the repository at this point in the history
* feat(ui): popover components

* feat(playground): popover component demos

* test(ui): popover model tests

* fix(playground): bundler and compiler issues

* refactor(ui): popover redundant cloneElement

* refactor(ui): popover content as prop

* refactor(ui): document popover component

* feat(ui): popover - add ability to pass placement as object

* feat(ui): popover - apply mappings
  • Loading branch information
artyorsh authored and 32penkin committed Feb 26, 2019
1 parent 1ea28bd commit 9028953
Show file tree
Hide file tree
Showing 16 changed files with 1,857 additions and 316 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions src/framework/ui/drawable/arrow/arrow.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import {
View,
ViewProps,
} from 'react-native';
import { StyleType } from '@kitten/theme';

export type Props = ViewProps;

export class Arrow extends React.Component<Props> {

private getComponentStyle = (source: StyleType): StyleType => {
return {
borderLeftWidth: source.width,
borderRightWidth: source.width,
borderBottomWidth: source.height,
borderBottomColor: source.backgroundColor,
borderLeftColor: 'transparent',
borderRightColor: 'transparent',
backgroundColor: 'transparent',
};
};

public render(): React.ReactElement<ViewProps> {
const { style, ...props } = this.props;
const componentStyle = this.getComponentStyle(style);

return (
<View
{...props}
style={[style, componentStyle]}
/>
);
}
}
20 changes: 20 additions & 0 deletions src/framework/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ import {
TabBar as TabBarComponent,
Props as TabBarProps,
} from './tab/tabBar.component';
import {
Popover as PopoverComponent,
Props as PopoverProps,
} from './popover/popover.component';
import {
Tooltip as TooltipComponent,
Props as TooltipProps,
} from './tooltip/tooltip.component';
import {
ViewPager,
Props as ViewPagerProps,
Expand Down Expand Up @@ -67,6 +75,10 @@ import {
ButtonAlignment,
ButtonAlignments,
} from './button/type';
import {
Placement as PopoverPlacement,
Placements as PopoverPlacements,
} from './popover/type';

const Button = styled<ButtonComponent, ButtonProps>(ButtonComponent);
const Text = styled<TextComponent, TextProps>(TextComponent);
Expand All @@ -77,6 +89,8 @@ const Toggle = styled<ToggleComponent, ToggleProps>(ToggleComponent);
const CheckBox = styled<CheckBoxComponent, CheckBoxProps>(CheckBoxComponent);
const Tab = styled<TabComponent, TabProps>(TabComponent);
const TabBar = styled<TabBarComponent, TabBarProps>(TabBarComponent);
const Popover = styled<PopoverComponent, PopoverProps>(PopoverComponent);
const Tooltip = styled<TooltipComponent, TooltipProps>(TooltipComponent);
const BottomTabNavigator = styled<BottomTabNavigatorComponent, BottomTabNavigatorProps>(BottomTabNavigatorComponent);
const BottomNavigatorTab = styled<BottomNavigatorTabComponent, BottomNavigatorTabProps>(BottomNavigatorTabComponent);
const TopNavigationBar = styled<TopNavigationBarComponent, TopNavigationBarProps>(TopNavigationBarComponent);
Expand All @@ -98,12 +112,16 @@ export {
CheckBox,
Tab,
TabBar,
Popover,
Tooltip,
ViewPager,
TabView,
ButtonProps,
CheckBoxProps,
TabProps,
TabBarProps,
PopoverProps,
TooltipProps,
ViewPagerProps,
TabViewProps,
BottomTabNavigator,
Expand All @@ -118,4 +136,6 @@ export {
ModalProps,
ButtonAlignment,
ButtonAlignments,
PopoverPlacement,
PopoverPlacements,
};
2 changes: 0 additions & 2 deletions src/framework/ui/modal/modal.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,6 @@ export class Modal extends React.Component<Props> {
const styles = StyleSheet.create({
container: {
position: 'absolute',
width: 0,
height: 0,
},
backdrop: {
width: width,
Expand Down
16 changes: 0 additions & 16 deletions src/framework/ui/modal/modal.spec.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ exports[`@modal component checks * component styled with mappings 1`] = `
<View
style={
Object {
"height": 0,
"position": "absolute",
"width": 0,
}
}
>
Expand Down Expand Up @@ -52,9 +50,7 @@ exports[`@modal component checks * modal closes on passed prop 1`] = `
<View
style={
Object {
"height": 0,
"position": "absolute",
"width": 0,
}
}
>
Expand Down Expand Up @@ -142,9 +138,7 @@ exports[`@modal component checks * modal closes on passed prop 2`] = `
<View
style={
Object {
"height": 0,
"position": "absolute",
"width": 0,
}
}
>
Expand Down Expand Up @@ -246,9 +240,7 @@ exports[`@modal component checks * modal component close on backDrop checks 1`]
<View
style={
Object {
"height": 0,
"position": "absolute",
"width": 0,
}
}
>
Expand Down Expand Up @@ -296,9 +288,7 @@ exports[`@modal component checks * modal component close on backDrop checks 2`]
<View
style={
Object {
"height": 0,
"position": "absolute",
"width": 0,
}
}
>
Expand Down Expand Up @@ -360,9 +350,7 @@ exports[`@modal component checks * modal component renders properly 1`] = `
<View
style={
Object {
"height": 0,
"position": "absolute",
"width": 0,
}
}
>
Expand Down Expand Up @@ -434,9 +422,7 @@ exports[`@modal component checks * with animations 1`] = `
<View
style={
Object {
"height": 0,
"position": "absolute",
"width": 0,
}
}
>
Expand Down Expand Up @@ -536,9 +522,7 @@ exports[`@modal component checks * with animations 2`] = `
<View
style={
Object {
"height": 0,
"position": "absolute",
"width": 0,
}
}
>
Expand Down
140 changes: 140 additions & 0 deletions src/framework/ui/popover/measure.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React from 'react';
import {
findNodeHandle,
UIManager,
View,
ViewProps,
Dimensions,
ScaledSize,
} from 'react-native';
import { Frame } from './type';

export type MeasuringElement = React.ReactElement<MeasuringElementProps>;
export type MeasuringElementProps = { tag: string | number } & any;
export type MeasuringNode = React.ReactElement<MeasureNodeProps>;
export type MeasuringNodeProps = MeasureNodeProps & ViewProps;

export type MeasuredElementProps = MeasuringElementProps & { frame: Frame };
export type MeasuredElement = React.ReactElement<MeasuredElementProps>;

export interface MeasureResult {
[tag: string]: Frame;
}

export interface MeasureElementProps {
onResult: (result: MeasuredElement) => void;
children: MeasuringElement;
}

export interface MeasureNodeProps {
onResult: (result: MeasureResult) => void;
children: MeasuringElement[];
}

/**
* Measures child element size and it's screen position asynchronously.
* Returns measure result in `onResult` callback.
*
* Usage:
*
* const onMeasure = (element: ElementToMeasure): void => {
* const { x, y, width, height } = element.props.frame;
* ...
* };
*
* <MeasureElement onResult={this.onMeasure}>
* <ElementToMeasure tag='@measure/measure-me!'/>
* </MeasureElement>
*/
export const MeasureElement = (props: MeasureElementProps): MeasuringElement => {

const ref: React.RefObject<any> = React.createRef();

const { children, onResult } = props;

const bindToWindow = (frame: Frame, window: ScaledSize): Frame => {
if (frame.origin.x < window.width) {
return frame;
}

const boundFrame: Frame = new Frame(
frame.origin.x - window.width,
frame.origin.y,
frame.size.width,
frame.size.height,
);

return bindToWindow(boundFrame, window);
};

const measureSelf = () => {
const node: number = findNodeHandle(ref.current);

UIManager.measureInWindow(node, (x: number, y: number, w: number, h: number) => {
const window: ScaledSize = Dimensions.get('window');
const frame: Frame = bindToWindow(new Frame(x, y, w, h), window);

const measuredElement: MeasuredElement = React.cloneElement(children, { frame });

onResult(measuredElement);
});
};

return React.cloneElement(children, {
ref,
onLayout: measureSelf,
});
};

/**
* Measures passed child elements size and it's screen position asynchronously.
* Returns measure result in `onResult` callback.
*
* Does the same as `MeasureElement` but calls `onResult` after all children are measured.
*
* Usage:
*
* const onMeasure = (result: MeasureResult): void => {
* const { [0]: firstElementFrame, [1]: secondElementFrame } = result;
* const { x, y, width, height } = firstElementFrame;
* ...
* };
*
* <MeasureNode onResult={this.onMeasure}>
* <ElementToMeasure tag={0}/>
* <ElementToMeasure tag={1}/>
* </MeasureNode>
*/
export const MeasureNode = (props: MeasuringNodeProps): MeasuringNode => {

const result: MeasureResult = {};

const { children, onResult, ...derivedProps } = props;

const onChildMeasure = (element: MeasuredElement) => {
const { tag, frame } = element.props;

result[tag] = frame;

if (Object.keys(result).length === children.length) {
onResult(result);
}
};

const createMeasuringElement = (element: MeasuringElement, index: number): MeasuringElement => {
return (
<MeasureElement
key={index}
onResult={onChildMeasure}>
{element}
</MeasureElement>
);
};

return (
<View
{...derivedProps}>
{children.map(createMeasuringElement)}
</View>
);
};
Loading

0 comments on commit 9028953

Please sign in to comment.