diff --git a/react/README.md b/react/README.md index 9f56065c8f..e9e3b36109 100644 --- a/react/README.md +++ b/react/README.md @@ -15,24 +15,36 @@ Below is a list of components and their current status. Please know that these | Component | Development Status | Tests | | ------------------ |:------------------:|:-------------:| -| `ion-action-sheet` | under development | :black_square_button: | +| `ion-action-sheet` | :white_check_mark: | :black_square_button: | | `ion-alert` | :white_check_mark: | :black_square_button: | +| `ion-anchor` | :black_square_button: | :black_square_button: | +| `ion-animation` | :black_square_button: | :black_square_button: | | `ion-app` | :white_check_mark: | :black_square_button: | | `ion-avatar` | :white_check_mark: | :black_square_button: | +| `ion-back-button` | :black_square_button: | :black_square_button: | +| `ion-backdrop` | :black_square_button: | :black_square_button: | +| `ion-badge` | :black_square_button: | :black_square_button: | | `ion-button` | :white_check_mark: | :black_square_button: | | `ion-buttons` | :white_check_mark: | :black_square_button: | | `ion-card` | :white_check_mark: | :black_square_button: | | `ion-card-content` | :white_check_mark: | :black_square_button: | | `ion-card-header` | :white_check_mark: | :black_square_button: | +| `ion-card-subtitle` | :black_square_button: | :black_square_button: | +| `ion-card-title` | :black_square_button: | :black_square_button: | +| `ion-checkbox` | :black_square_button: | :black_square_button: | +| `ion-chip` | :black_square_button: | :black_square_button: | | `ion-col` | :white_check_mark: | :black_square_button: | | `ion-content` | :white_check_mark: | :black_square_button: | -| `ion-datetime` | :white_check_mark: | :black_square_button: | +| `ion-datetime` | :black_square_button: | :black_square_button: | | `ion-fab` | :white_check_mark: | :black_square_button: | | `ion-fab-button` | :white_check_mark: | :black_square_button: | | `ion-fab-list` | :white_check_mark: | :black_square_button: | +| `ion-footer` | :black_square_button: | :black_square_button: | | `ion-grid` | :white_check_mark: | :black_square_button: | | `ion-header` | :white_check_mark: | :black_square_button: | | `ion-icon` | :white_check_mark: | :black_square_button: | +| `ion-img` | :black_square_button: | :black_square_button: | +| `ion-infinite-scroll` | :black_square_button: | :black_square_button: | | `ion-input` | :white_check_mark: | :black_square_button: | | `ion-item` | :white_check_mark: | :black_square_button: | | `ion-item-divider` | :white_check_mark: | :black_square_button: | @@ -47,26 +59,46 @@ Below is a list of components and their current status. Please know that these | `ion-menu` | :white_check_mark: | :black_square_button: | | `ion-menu-button` | :white_check_mark: | :black_square_button: | | `ion-menu-toggle` | :white_check_mark: | :black_square_button: | -| `ion-modal` | under development | :black_square_button: | -| `ion-nav` | :white_check_mark: | :black_square_button: | -| `ion-popover` | under development | :black_square_button: | +| `ion-modal` | :white_check_mark: | :black_square_button: | +| `ion-nav` | :black_square_button: | :black_square_button: | +| `ion-nav-pop` | :black_square_button: | :black_square_button: | +| `ion-nav-push` | :black_square_button: | :black_square_button: | +| `ion-nav-set-root` | :black_square_button: | :black_square_button: | +| `ion-note` | :black_square_button: | :black_square_button: | +| `ion-picker` | :black_square_button: | :black_square_button: | +| `ion-picker-column` | :black_square_button: | :black_square_button: | +| `ion-popover` | :white_check_mark: | :black_square_button: | +| `ion-progress-bar` | :black_square_button: | :black_square_button: | +| `ion-radio` | :black_square_button: | :black_square_button: | +| `ion-radio-group` | :black_square_button: | :black_square_button: | +| `ion-range` | :black_square_button: | :black_square_button: | | `ion-refresher` | :white_check_mark: | :black_square_button: | | `ion-refresher-content` | :white_check_mark: | :black_square_button: | +| `ion-reorder` | :black_square_button: | :black_square_button: | +| `ion-reorder-group` | :black_square_button: | :black_square_button: | +| `ion-ripple-effect` | :black_square_button: | :black_square_button: | +| `ion-router-outlet` | :black_square_button: | :black_square_button: | | `ion-row` | :white_check_mark: | :black_square_button: | | `ion-searchbar` | :white_check_mark: | :black_square_button: | | `ion-segment` | :white_check_mark: | :black_square_button: | | `ion-segment-button` | :white_check_mark: | :black_square_button: | | `ion-select` | :white_check_mark: | :black_square_button: | | `ion-select-option` | :white_check_mark: | :black_square_button: | +| `ion-select-popover` | :black_square_button: | :black_square_button: | +| `ion-skeleton-text` | :black_square_button: | :black_square_button: | | `ion-slide` | :white_check_mark: | :black_square_button: | | `ion-slides` | :white_check_mark: | :black_square_button: | +| `ion-spinner` | :black_square_button: | :black_square_button: | | `ion-split-pane` | :white_check_mark: | :black_square_button: | | `ion-tab` | :white_check_mark: | :black_square_button: | | `ion-tab-bar` | :white_check_mark: | :black_square_button: | | `ion-tab-button` | :white_check_mark: | :black_square_button: | | `ion-tabs` | :white_check_mark: | :black_square_button: | +| `ion-text` | :black_square_button: | :black_square_button: | | `ion-textarea` | :white_check_mark: | :black_square_button: | +| `ion-thumbnail` | :black_square_button: | :black_square_button: | | `ion-title` | :white_check_mark: | :black_square_button: | | `ion-toast` | :white_check_mark: | :black_square_button: | | `ion-toggle` | :white_check_mark: | :black_square_button: | | `ion-toolbar` | :white_check_mark: | :black_square_button: | +| `ion-virtual-scroll` | :black_square_button: | :black_square_button: | diff --git a/react/src/components/IonActionSheet.tsx b/react/src/components/IonActionSheet.tsx index 4729ffb8d3..76523f17d9 100644 --- a/react/src/components/IonActionSheet.tsx +++ b/react/src/components/IonActionSheet.tsx @@ -1,8 +1,8 @@ import { Components } from '@ionic/core'; -import { createControllerComponent } from './createControllerComponent'; +import { createOverlayComponent } from './createOverlayComponent'; import { Omit } from './types'; export type ActionSheetOptions = Omit; -const IonActionSheet = createControllerComponent('ion-action-sheet', 'ion-action-sheet-controller') +const IonActionSheet = createOverlayComponent('ion-action-sheet', 'ion-action-sheet-controller') export default IonActionSheet; diff --git a/react/src/components/IonModal.tsx b/react/src/components/IonModal.tsx index 322cbc4068..9b970e0133 100644 --- a/react/src/components/IonModal.tsx +++ b/react/src/components/IonModal.tsx @@ -1,8 +1,10 @@ import { Components } from '@ionic/core'; -import { createControllerComponent } from './createControllerComponent'; +import { createOverlayComponent } from './createOverlayComponent'; import { Omit } from './types'; -export type ModalOptions = Omit; +export type ModalOptions = Omit & { + children: React.ReactNode; +}; -const IonModal = createControllerComponent('ion-modal', 'ion-modal-controller') +const IonModal = createOverlayComponent('ion-modal', 'ion-modal-controller') export default IonModal; diff --git a/react/src/components/IonPopover.tsx b/react/src/components/IonPopover.tsx index c5c2aad450..6bdb8ab0b4 100644 --- a/react/src/components/IonPopover.tsx +++ b/react/src/components/IonPopover.tsx @@ -1,8 +1,10 @@ import { Components } from '@ionic/core'; -import { createControllerComponent } from './createControllerComponent'; +import { createOverlayComponent } from './createOverlayComponent'; import { Omit } from './types'; -export type PopoverOptions = Omit; +export type PopoverOptions = Omit & { + children: React.ReactNode; +}; -const IonPopover = createControllerComponent('ion-popover', 'ion-popover-controller') +const IonPopover = createOverlayComponent('ion-popover', 'ion-popover-controller') export default IonPopover; diff --git a/react/src/components/createOverlayComponent.tsx b/react/src/components/createOverlayComponent.tsx new file mode 100644 index 0000000000..b55c701e54 --- /dev/null +++ b/react/src/components/createOverlayComponent.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { attachEventProps } from './utils' +import { ensureElementInBody, dashToPascalCase } from './utils'; + +export function createOverlayComponent(tagName: string, controllerTagName: string) { + const displayName = dashToPascalCase(tagName); + + type IonicReactInternalProps = { + forwardedRef?: React.RefObject; + children: React.ReactNode; + show: boolean; + } + + return class ReactControllerComponent extends React.Component { + element: E; + controllerElement: C; + el: HTMLDivElement; + + constructor(props: T & IonicReactInternalProps) { + super(props); + + this.el = document.createElement('div'); + } + + static get displayName() { + return displayName; + } + + async componentDidMount() { + this.controllerElement = ensureElementInBody(controllerTagName); + await (this.controllerElement as any).componentOnReady(); + } + + async componentDidUpdate(prevProps: T & IonicReactInternalProps) { + if (prevProps.show !== this.props.show && this.props.show === true) { + const { children, show, ...cProps} = this.props as any; + cProps.component = this.el; + cProps.componentProps = {}; + + this.element = await (this.controllerElement as any).create(cProps); + await (this.element as any).present(); + + attachEventProps(this.element, cProps); + } + if (prevProps.show !== this.props.show && this.props.show === false) { + return await (this.element as any).dismiss(); + } + } + + render() { + return ReactDOM.createPortal( + this.props.children, + this.el, + ); + } + } +} +