Skip to content

Commit 8b2721a

Browse files
authored
feat(components): add direct manipulation methods to modal components
1 parent 231ba89 commit 8b2721a

File tree

9 files changed

+161
-33
lines changed

9 files changed

+161
-33
lines changed

src/components/ui/autocomplete/autocomplete.component.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ interface State {
5555
*
5656
* @extends React.Component
5757
*
58+
* @method {() => void} show - Sets data list visible.
59+
*
60+
* @method {() => void} hide - Sets data list invisible.
61+
*
5862
* @method {() => void} focus - Focuses Autocomplete and sets data list visible.
5963
*
6064
* @method {() => void} blur - Removes focus from Autocomplete and sets data list invisible.
@@ -97,13 +101,22 @@ export class Autocomplete<O extends Option = Option> extends React.Component<Aut
97101
optionsVisible: false,
98102
};
99103

104+
private popoverRef: React.RefObject<Popover> = React.createRef();
100105
private inputRef: React.RefObject<InputComponent> = React.createRef();
101106

102107
private get data(): O[] {
103108
const hasData: boolean = this.props.data && this.props.data.length > 0;
104109
return hasData && this.props.data || this.props.placeholderData;
105110
}
106111

112+
public show = (): void => {
113+
this.popoverRef.current.show();
114+
};
115+
116+
public hide = (): void => {
117+
this.popoverRef.current.hide();
118+
};
119+
107120
public focus = (): void => {
108121
this.inputRef.current.focus();
109122
};
@@ -191,6 +204,7 @@ export class Autocomplete<O extends Option = Option> extends React.Component<Aut
191204

192205
return (
193206
<Popover
207+
ref={this.popoverRef}
194208
style={styles.popover}
195209
visible={this.state.optionsVisible}
196210
fullWidth={true}

src/components/ui/datepicker/baseDatepicker.component.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ export abstract class BaseDatepickerComponent<P, D = Date> extends React.Compone
7171
visible: false,
7272
};
7373

74+
private popoverRef: React.RefObject<Popover> = React.createRef();
75+
76+
public show = (): void => {
77+
this.popoverRef.current.show();
78+
};
79+
80+
public hide = (): void => {
81+
this.popoverRef.current.hide();
82+
};
83+
7484
public focus = (): void => {
7585
this.setState({ visible: true }, this.dispatchActive);
7686
};
@@ -309,6 +319,7 @@ export abstract class BaseDatepickerComponent<P, D = Date> extends React.Compone
309319
<View style={style}>
310320
{labelElement}
311321
<Popover
322+
ref={this.popoverRef}
312323
style={[popover, styles.popover]}
313324
placement={placement}
314325
visible={this.state.visible}

src/components/ui/datepicker/datepicker.component.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export type DatepickerElement<D = Date> = React.ReactElement<DatepickerProps<D>>
2121
*
2222
* @extends React.Component
2323
*
24+
* @method {() => void} show - Sets picker visible.
25+
*
26+
* @method {() => void} hide - Sets picker invisible.
27+
*
2428
* @method {() => void} focus - Focuses Datepicker and sets it visible.
2529
*
2630
* @method {() => void} blur - Removes focus from Datepicker and sets it invisible. This is the opposite of `focus()`.

src/components/ui/datepicker/rangeDatepicker.component.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export type RangeDatepickerElement<D = Date> = React.ReactElement<RangeDatepicke
2121
*
2222
* @extends React.Component
2323
*
24+
* @method {() => void} show - Sets picker visible.
25+
*
26+
* @method {() => void} hide - Sets picker invisible.
27+
*
2428
* @method {() => void} focus - Focuses Datepicker and sets it visible.
2529
*
2630
* @method {() => void} blur - Removes focus from Datepicker and sets it invisible. This is the opposite of `focus()`.

src/components/ui/modal/modal.component.tsx

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,25 @@ import {
1616
ModalPresentingConfig,
1717
ModalService,
1818
} from '@kitten/theme';
19-
import { MeasureElement } from '../measure/measure.component';
19+
import {
20+
MeasureElement,
21+
MeasuringElement,
22+
} from '../measure/measure.component';
2023
import {
2124
Frame,
2225
Point,
2326
} from '../measure/type';
2427

2528
export interface ModalProps extends ViewProps, ModalPresentingConfig {
26-
visible?: boolean;
29+
visible: boolean;
2730
children: React.ReactNode;
2831
}
2932

3033
export type ModalElement = React.ReactElement<ModalProps>;
3134

3235
interface State {
3336
contentFrame: Frame;
37+
forceMeasure: boolean;
3438
}
3539

3640
const POINT_OUTSCREEN: Point = new Point(-999, -999);
@@ -40,6 +44,10 @@ const POINT_OUTSCREEN: Point = new Point(-999, -999);
4044
*
4145
* @extends React.Component
4246
*
47+
* @method {() => void} show - Sets modal visible.
48+
*
49+
* @method {() => void} hide - Sets modal invisible.
50+
*
4351
* @property {boolean} visible - Determines whether component is visible. By default is false.
4452
*
4553
* @property {ReactElement | ReactElement[]} children - Determines component's children.
@@ -55,69 +63,95 @@ const POINT_OUTSCREEN: Point = new Point(-999, -999);
5563
*
5664
* @overview-example ModalWithBackdrop
5765
*/
58-
export class Modal extends React.Component<ModalProps, State> {
59-
60-
private modalId: string;
66+
export class Modal extends React.PureComponent<ModalProps, State> {
6167

6268
public state: State = {
6369
contentFrame: Frame.zero(),
70+
forceMeasure: false,
6471
};
6572

73+
private modalId: string;
74+
private contentPosition: Point = POINT_OUTSCREEN;
75+
6676
private get contentFlexPosition(): FlexStyle {
6777
const derivedStyle: ViewStyle = StyleSheet.flatten(this.props.style || {});
68-
const centerInWindow: Point = this.state.contentFrame.centerOf(Frame.window()).origin;
78+
const { x: centerX, y: centerY } = this.contentPosition;
6979
// @ts-ignore
70-
return { left: derivedStyle.left || centerInWindow.x, top: derivedStyle.top || centerInWindow.y };
80+
return { left: derivedStyle.left || centerX, top: derivedStyle.top || centerY };
7181
}
7282

7383
private get backdropConfig(): ModalPresentingConfig {
7484
const { onBackdropPress, backdropStyle } = this.props;
7585
return { onBackdropPress, backdropStyle };
7686
}
7787

78-
public componentDidUpdate(): void {
79-
if (!this.modalId && this.props.visible) {
80-
this.modalId = ModalService.show(this.renderModalElement(this.contentFlexPosition), this.backdropConfig);
88+
public show = (): void => {
89+
this.modalId = ModalService.show(this.renderMeasuringContentElement(), this.backdropConfig);
90+
};
91+
92+
public hide = (): void => {
93+
this.modalId = ModalService.hide(this.modalId);
94+
};
95+
96+
public componentDidUpdate(prevProps: ModalProps): void {
97+
if (!this.modalId && this.props.visible && !this.state.forceMeasure) {
98+
this.setState({ forceMeasure: true });
8199
return;
82100
}
83101

84102
if (this.modalId && !this.props.visible) {
85-
this.modalId = ModalService.hide(this.modalId);
103+
// this.contentPosition = POINT_OUTSCREEN;
104+
this.hide();
86105
}
87106
}
88107

89-
private onContentMeasure = (frame: Frame): void => {
90-
this.state.contentFrame = frame;
108+
public componentWillUnmount(): void {
109+
this.hide();
110+
}
91111

92-
if (!this.modalId && this.props.visible) {
93-
this.modalId = ModalService.show(this.renderModalElement(this.contentFlexPosition), this.backdropConfig);
94-
}
112+
private onContentMeasure = (contentFrame: Frame): void => {
113+
this.state.contentFrame = contentFrame;
114+
115+
const displayFrame: Frame = this.state.contentFrame.centerOf(Frame.window());
116+
this.contentPosition = displayFrame.origin;
117+
118+
ModalService.update(this.modalId, this.renderContentElement());
95119
};
96120

97-
private renderModalElement = (style: ViewStyle): React.ReactElement<ViewProps> => {
121+
private renderContentElement = (): React.ReactElement<ViewProps> => {
98122
return (
99123
<View
100124
{...this.props}
101-
style={[this.props.style, styles.contentView, style]}
125+
style={[this.props.style, styles.modalView, this.contentFlexPosition]}
102126
/>
103127
);
104128
};
105129

106-
public render(): React.ReactElement {
130+
private renderMeasuringContentElement = (): MeasuringElement => {
107131
return (
108132
<MeasureElement onMeasure={this.onContentMeasure}>
109-
{this.renderModalElement(styles.outscreen)}
133+
{this.renderContentElement()}
110134
</MeasureElement>
111135
);
136+
};
137+
138+
public render(): React.ReactNode {
139+
if (!this.modalId && this.props.visible) {
140+
this.show();
141+
return null;
142+
}
143+
144+
if (this.modalId && this.props.visible) {
145+
ModalService.update(this.modalId, this.renderContentElement());
146+
return null;
147+
}
148+
149+
return null;
112150
}
113151
}
114152

115153
const styles = StyleSheet.create({
116-
contentView: {
154+
modalView: {
117155
position: 'absolute',
118156
},
119-
outscreen: {
120-
left: POINT_OUTSCREEN.x,
121-
top: POINT_OUTSCREEN.y,
122-
},
123157
});

src/components/ui/overflowMenu/overflowMenu.component.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export type OverflowMenuElement = React.ReactElement<OverflowMenuProps>;
4141
*
4242
* @extends React.Component
4343
*
44+
* @method {() => void} show - Sets menu visible.
45+
*
46+
* @method {() => void} hide - Sets menu invisible.
47+
*
4448
* @property {boolean} visible - Determines whether popover is visible or not.
4549
*
4650
* @property {OverflowMenuItemType[]} data - Determines menu items.
@@ -82,6 +86,16 @@ class OverflowMenuComponent extends React.Component<OverflowMenuProps> {
8286

8387
static styledComponentName: string = 'OverflowMenu';
8488

89+
private popoverRef: React.RefObject<Popover> = React.createRef();
90+
91+
public show = (): void => {
92+
this.popoverRef.current.show();
93+
};
94+
95+
public hide = (): void => {
96+
this.popoverRef.current.hide();
97+
};
98+
8599
private getComponentStyle = (source: StyleType): StyleType => {
86100
const { indicatorBackgroundColor, ...containerParameters } = source;
87101

@@ -117,6 +131,7 @@ class OverflowMenuComponent extends React.Component<OverflowMenuProps> {
117131
return (
118132
<Popover
119133
{...restProps}
134+
ref={this.popoverRef}
120135
style={[container, style]}
121136
content={contentElement}>
122137
{children}

src/components/ui/popover/popover.component.tsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const POINT_OUTSCREEN: Point = new Point(-999, -999);
5353
*
5454
* @extends React.Component
5555
*
56+
* @method {() => void} show - Sets `content` element visible.
57+
*
58+
* @method {() => void} hide - Sets `content` element invisible.
59+
*
5660
* @property {boolean} visible - Determines whether popover is visible or not.
5761
*
5862
* @property {ReactElement} content - Determines the content of the popover.
@@ -112,6 +116,14 @@ export class Popover extends React.Component<PopoverProps, State> {
112116
return { onBackdropPress, backdropStyle };
113117
}
114118

119+
public show = (): void => {
120+
this.modalId = ModalService.show(this.renderMeasuringPopoverElement(), this.backdropConfig);
121+
};
122+
123+
public hide = (): void => {
124+
this.modalId = ModalService.hide(this.modalId);
125+
};
126+
115127
public componentDidUpdate(prevProps: PopoverProps): void {
116128
if (!this.modalId && this.props.visible && !this.state.forceMeasure) {
117129
this.setState({ forceMeasure: true });
@@ -120,15 +132,19 @@ export class Popover extends React.Component<PopoverProps, State> {
120132

121133
if (this.modalId && !this.props.visible) {
122134
this.contentPosition = POINT_OUTSCREEN;
123-
this.modalId = ModalService.hide(this.modalId);
135+
this.hide();
124136
}
125137
}
126138

139+
public componentWillUnmount(): void {
140+
this.hide();
141+
}
142+
127143
private onChildMeasure = (childFrame: Frame): void => {
128144
this.state.childFrame = childFrame;
129145

130146
if (!this.modalId && this.props.visible) {
131-
this.modalId = ModalService.show(this.renderMeasuringPopoverElement(), this.backdropConfig);
147+
this.show();
132148
return;
133149
}
134150

@@ -166,12 +182,10 @@ export class Popover extends React.Component<PopoverProps, State> {
166182
};
167183

168184
private renderPopoverElement = (): PopoverViewElement => {
169-
const { contentContainerStyle, ...props } = this.props;
170-
171185
return (
172186
<PopoverView
173-
{...props}
174-
contentContainerStyle={[contentContainerStyle, styles.popoverView, this.contentFlexPosition]}
187+
{...this.props}
188+
contentContainerStyle={[this.props.contentContainerStyle, styles.popoverView, this.contentFlexPosition]}
175189
placement={this.actualPlacement.reverse()}>
176190
{this.renderContentElement()}
177191
</PopoverView>

0 commit comments

Comments
 (0)