refactor(react): transition to Stencil React bindings (#23826)

This commit is contained in:
William Martin
2021-08-31 17:29:59 -04:00
committed by GitHub
parent 5ca2ce9197
commit 1680b0ce9f
28 changed files with 530 additions and 314 deletions

16
core/package-lock.json generated
View File

@ -18,6 +18,7 @@
"@jest/core": "^26.6.3",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/react-output-target": "^0.0.12",
"@stencil/sass": "1.3.2",
"@stencil/vue-output-target": "^0.5.1",
"@types/jest": "^26.0.20",
@ -1367,6 +1368,15 @@
"npm": ">=6.0.0"
}
},
"node_modules/@stencil/react-output-target": {
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/@stencil/react-output-target/-/react-output-target-0.0.12.tgz",
"integrity": "sha512-X/lWAI/FW4tg/pjwe5UWy8KbRk2vWcWR+S6tBqNzKO6pKD6qr60dfajN13EO9nnm5hGr48FP1m/M8kqFbjpZrg==",
"dev": true,
"peerDependencies": {
"@stencil/core": ">=1.8.0"
}
},
"node_modules/@stencil/sass": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-1.3.2.tgz",
@ -15010,6 +15020,12 @@
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.6.0.tgz",
"integrity": "sha512-QsxWayZyusnqSZrlCl81R71rA3KqFjVVQSH4E0rGN15F1GdQaFonKlHLyCOLKLig1zzC+DQkLLiUuocexuvdeQ=="
},
"@stencil/react-output-target": {
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/@stencil/react-output-target/-/react-output-target-0.0.12.tgz",
"integrity": "sha512-X/lWAI/FW4tg/pjwe5UWy8KbRk2vWcWR+S6tBqNzKO6pKD6qr60dfajN13EO9nnm5hGr48FP1m/M8kqFbjpZrg==",
"dev": true
},
"@stencil/sass": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-1.3.2.tgz",

View File

@ -40,6 +40,7 @@
"@jest/core": "^26.6.3",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/react-output-target": "^0.0.12",
"@stencil/sass": "1.3.2",
"@stencil/vue-output-target": "^0.5.1",
"@types/jest": "^26.0.20",

View File

@ -1,6 +1,7 @@
import { Config } from '@stencil/core';
import { sass } from '@stencil/sass';
import { vueOutputTarget } from '@stencil/vue-output-target';
import { reactOutputTarget } from '@stencil/react-output-target';
// @ts-ignore
import { apiSpecGenerator } from './scripts/api-spec-generator';
@ -61,6 +62,40 @@ export const config: Config = {
})
],
outputTargets: [
reactOutputTarget({
componentCorePackage: '@ionic/core',
includePolyfills: false,
includeDefineCustomElements: false,
proxiesFile: '../packages/react/src/components/proxies.ts',
excludeComponents: [
// Routing
'ion-router',
'ion-route',
'ion-route-redirect',
'ion-router-link',
'ion-router-outlet',
'ion-back-button',
'ion-tab-button',
'ion-tabs',
'ion-tab-bar',
'ion-button',
'ion-card',
'ion-fab-button',
'ion-item',
'ion-item-option',
// Overlays
'ion-action-sheet',
'ion-alert',
'ion-loading',
'ion-modal',
'ion-picker',
'ion-popover',
'ion-toast',
'ion-icon'
]
}),
vueOutputTarget({
componentCorePackage: '@ionic/core',
includeImportCustomElements: true,

View File

@ -77,7 +77,7 @@
"style-loader": "0.23.1",
"terser-webpack-plugin": "2.3.4",
"ts-pnp": "1.1.5",
"typescript": "3.7.4",
"typescript": "^3.9.5",
"url-loader": "2.3.0",
"wait-on": "^5.3.0",
"webpack": "4.41.5",
@ -19603,9 +19603,10 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"node_modules/typescript": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
"integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
"version": "3.9.10",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -37499,9 +37500,9 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"typescript": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
"integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw=="
"version": "3.9.10",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q=="
},
"undefsafe": {
"version": "2.0.3",

View File

@ -72,7 +72,7 @@
"style-loader": "0.23.1",
"terser-webpack-plugin": "2.3.4",
"ts-pnp": "1.1.5",
"typescript": "3.7.4",
"typescript": "^3.9.5",
"url-loader": "2.3.0",
"wait-on": "^5.3.0",
"webpack": "4.41.5",

View File

@ -4,8 +4,8 @@ import { NavContext } from '../contexts/NavContext';
import { IonicReactProps } from './IonicReactProps';
import { IonIconInner } from './inner-proxies';
import { deprecationWarning } from './react-component-lib/utils/dev';
import { createForwardRef, isPlatform } from './utils';
import { deprecationWarning } from './utils/dev';
interface IonIconProps {
ariaLabel?: string;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { JSX } from '@ionic/core';
import { createReactComponent } from '../createComponent';
import { createReactComponent } from '../react-component-lib';
import { render, fireEvent, cleanup, RenderResult } from '@testing-library/react';
import { IonButton } from '../index';

View File

@ -1,4 +1,4 @@
import * as utils from '../utils';
import * as utils from '../react-component-lib/utils';
import '@testing-library/jest-dom/extend-expect';
describe('isCoveredByReact', () => {

View File

@ -1,7 +1,7 @@
import { OverlayEventDetail } from '@ionic/core';
import React from 'react';
import { attachProps, setRef } from './utils';
import { attachProps, setRef } from './react-component-lib/utils';
interface OverlayBase extends HTMLElement {
present: () => Promise<void>;

View File

@ -4,10 +4,12 @@ import React from 'react';
import {
attachProps,
camelToDashCase,
createForwardRef,
dashToPascalCase,
isCoveredByReact,
mergeRefs,
} from './react-component-lib/utils';
import {
createForwardRef
} from './utils';
type InlineOverlayState = {

View File

@ -2,7 +2,7 @@ import { OverlayEventDetail } from '@ionic/core';
import React from 'react';
import ReactDOM from 'react-dom';
import { attachProps, setRef } from './utils';
import { attachProps, setRef } from './react-component-lib/utils';
interface OverlayElement extends HTMLElement {
present: () => Promise<void>;

View File

@ -8,10 +8,12 @@ import { RouterDirection } from '../models/RouterDirection';
import {
attachProps,
camelToDashCase,
createForwardRef,
dashToPascalCase,
isCoveredByReact,
mergeRefs,
} from './react-component-lib/utils';
import {
createForwardRef
} from './utils';
interface IonicReactInternalProps<ElementType> extends React.HTMLAttributes<ElementType> {
@ -24,9 +26,8 @@ interface IonicReactInternalProps<ElementType> extends React.HTMLAttributes<Elem
routerAnimation?: AnimationBuilder;
}
export const createReactComponent = <PropType, ElementType>(
tagName: string,
routerLinkComponent = false
export const createRoutingComponent = <PropType, ElementType>(
tagName: string
) => {
const displayName = dashToPascalCase(tagName);
const ReactComponent = class extends React.Component<IonicReactInternalProps<PropType>> {
@ -86,7 +87,6 @@ export const createReactComponent = <PropType, ElementType>(
style,
};
if (routerLinkComponent) {
if (this.props.routerLink && !this.props.href) {
newProps.href = this.props.routerLink;
}
@ -101,7 +101,6 @@ export const createReactComponent = <PropType, ElementType>(
} else {
newProps.onClick = this.handleClick;
}
}
return React.createElement(tagName, newProps, children);
}

View File

@ -63,6 +63,7 @@ export {
ToastButton
} from '@ionic/core';
export * from './proxies';
export * from './routing-proxies';
// createControllerComponent
export { IonAlert } from './IonAlert';

View File

@ -1,7 +1,7 @@
import { JSX } from '@ionic/core';
import { JSX as IoniconsJSX } from 'ionicons';
import { /*@__PURE__*/ createReactComponent } from './createComponent';
import { /*@__PURE__*/ createReactComponent } from './react-component-lib';
export const IonTabButtonInner = /*@__PURE__*/ createReactComponent<
JSX.IonTabButton & { onIonTabButtonClick?: (e: CustomEvent) => void },

View File

@ -1,255 +1,77 @@
import { JSX } from '@ionic/core';
/* eslint-disable */
/* tslint:disable */
/* auto-generated react proxies */
import { createReactComponent } from './react-component-lib';
import { createReactComponent } from './createComponent';
import { HrefProps } from './hrefprops';
import type { JSX } from '@ionic/core';
// ionic/core
export const IonAccordion = /*@__PURE__*/createReactComponent<JSX.IonAccordion, HTMLIonAccordionElement>('ion-accordion');
export const IonAccordionGroup = /*@__PURE__*/createReactComponent<JSX.IonAccordionGroup, HTMLIonAccordionGroupElement>('ion-accordion-group');
export const IonApp = /*@__PURE__*/createReactComponent<JSX.IonApp, HTMLIonAppElement>('ion-app');
export const IonTab = /*@__PURE__*/ createReactComponent<JSX.IonTab, HTMLIonTabElement>('ion-tab');
export const IonRouterLink = /*@__PURE__*/ createReactComponent<
HrefProps<JSX.IonRouterLink>,
HTMLIonRouterLinkElement
>('ion-router-link', true);
export const IonAccordion = /*@__PURE__*/ createReactComponent<JSX.IonAccordion, HTMLIonAccordionElement>(
'ion-accordion'
);
export const IonAccordionGroup = /*@__PURE__*/ createReactComponent<JSX.IonAccordionGroup, HTMLIonAccordionGroupElement>(
'ion-accordion-group'
);
export const IonAvatar = /*@__PURE__*/ createReactComponent<JSX.IonAvatar, HTMLIonAvatarElement>(
'ion-avatar'
);
export const IonBackdrop = /*@__PURE__*/ createReactComponent<
JSX.IonBackdrop,
HTMLIonBackdropElement
>('ion-backdrop');
export const IonBadge = /*@__PURE__*/ createReactComponent<JSX.IonBadge, HTMLIonBadgeElement>(
'ion-badge'
);
export const IonBreadcrumb = /*@__PURE__*/ createReactComponent<JSX.IonBreadcrumb, HTMLIonBreadcrumbElement>(
'ion-breadcrumb'
);
export const IonBreadcrumbs = /*@__PURE__*/ createReactComponent<JSX.IonBreadcrumbs, HTMLIonBreadcrumbsElement>(
'ion-breadcrumbs'
);
export const IonButton = /*@__PURE__*/ createReactComponent<
HrefProps<JSX.IonButton>,
HTMLIonButtonElement
>('ion-button', true);
export const IonButtons = /*@__PURE__*/ createReactComponent<JSX.IonButtons, HTMLIonButtonsElement>(
'ion-buttons'
);
export const IonCard = /*@__PURE__*/ createReactComponent<
HrefProps<JSX.IonCard>,
HTMLIonCardElement
>('ion-card', true);
export const IonCardContent = /*@__PURE__*/ createReactComponent<
JSX.IonCardContent,
HTMLIonCardContentElement
>('ion-card-content');
export const IonCardHeader = /*@__PURE__*/ createReactComponent<
JSX.IonCardHeader,
HTMLIonCardHeaderElement
>('ion-card-header');
export const IonCardSubtitle = /*@__PURE__*/ createReactComponent<
JSX.IonCardSubtitle,
HTMLIonCardSubtitleElement
>('ion-card-subtitle');
export const IonCardTitle = /*@__PURE__*/ createReactComponent<
JSX.IonCardTitle,
HTMLIonCardTitleElement
>('ion-card-title');
export const IonCheckbox = /*@__PURE__*/ createReactComponent<
JSX.IonCheckbox,
HTMLIonCheckboxElement
>('ion-checkbox');
export const IonAvatar = /*@__PURE__*/createReactComponent<JSX.IonAvatar, HTMLIonAvatarElement>('ion-avatar');
export const IonBackdrop = /*@__PURE__*/createReactComponent<JSX.IonBackdrop, HTMLIonBackdropElement>('ion-backdrop');
export const IonBadge = /*@__PURE__*/createReactComponent<JSX.IonBadge, HTMLIonBadgeElement>('ion-badge');
export const IonBreadcrumb = /*@__PURE__*/createReactComponent<JSX.IonBreadcrumb, HTMLIonBreadcrumbElement>('ion-breadcrumb');
export const IonBreadcrumbs = /*@__PURE__*/createReactComponent<JSX.IonBreadcrumbs, HTMLIonBreadcrumbsElement>('ion-breadcrumbs');
export const IonButtons = /*@__PURE__*/createReactComponent<JSX.IonButtons, HTMLIonButtonsElement>('ion-buttons');
export const IonCardContent = /*@__PURE__*/createReactComponent<JSX.IonCardContent, HTMLIonCardContentElement>('ion-card-content');
export const IonCardHeader = /*@__PURE__*/createReactComponent<JSX.IonCardHeader, HTMLIonCardHeaderElement>('ion-card-header');
export const IonCardSubtitle = /*@__PURE__*/createReactComponent<JSX.IonCardSubtitle, HTMLIonCardSubtitleElement>('ion-card-subtitle');
export const IonCardTitle = /*@__PURE__*/createReactComponent<JSX.IonCardTitle, HTMLIonCardTitleElement>('ion-card-title');
export const IonCheckbox = /*@__PURE__*/createReactComponent<JSX.IonCheckbox, HTMLIonCheckboxElement>('ion-checkbox');
export const IonChip = /*@__PURE__*/createReactComponent<JSX.IonChip, HTMLIonChipElement>('ion-chip');
export const IonCol = /*@__PURE__*/createReactComponent<JSX.IonCol, HTMLIonColElement>('ion-col');
export const IonContent = /*@__PURE__*/ createReactComponent<JSX.IonContent, HTMLIonContentElement>(
'ion-content'
);
export const IonChip = /*@__PURE__*/ createReactComponent<JSX.IonChip, HTMLIonChipElement>(
'ion-chip'
);
export const IonDatetime = /*@__PURE__*/ createReactComponent<
JSX.IonDatetime,
HTMLIonDatetimeElement
>('ion-datetime');
export const IonContent = /*@__PURE__*/createReactComponent<JSX.IonContent, HTMLIonContentElement>('ion-content');
export const IonDatetime = /*@__PURE__*/createReactComponent<JSX.IonDatetime, HTMLIonDatetimeElement>('ion-datetime');
export const IonFab = /*@__PURE__*/createReactComponent<JSX.IonFab, HTMLIonFabElement>('ion-fab');
export const IonFabButton = /*@__PURE__*/ createReactComponent<
HrefProps<JSX.IonFabButton>,
HTMLIonFabButtonElement
>('ion-fab-button', true);
export const IonFabList = /*@__PURE__*/ createReactComponent<JSX.IonFabList, HTMLIonFabListElement>(
'ion-fab-list'
);
export const IonFooter = /*@__PURE__*/ createReactComponent<JSX.IonFooter, HTMLIonFooterElement>(
'ion-footer'
);
export const IonGrid = /*@__PURE__*/ createReactComponent<JSX.IonGrid, HTMLIonGridElement>(
'ion-grid'
);
export const IonHeader = /*@__PURE__*/ createReactComponent<JSX.IonHeader, HTMLIonHeaderElement>(
'ion-header'
);
export const IonFabList = /*@__PURE__*/createReactComponent<JSX.IonFabList, HTMLIonFabListElement>('ion-fab-list');
export const IonFooter = /*@__PURE__*/createReactComponent<JSX.IonFooter, HTMLIonFooterElement>('ion-footer');
export const IonGrid = /*@__PURE__*/createReactComponent<JSX.IonGrid, HTMLIonGridElement>('ion-grid');
export const IonHeader = /*@__PURE__*/createReactComponent<JSX.IonHeader, HTMLIonHeaderElement>('ion-header');
export const IonImg = /*@__PURE__*/createReactComponent<JSX.IonImg, HTMLIonImgElement>('ion-img');
export const IonInfiniteScroll = /*@__PURE__*/ createReactComponent<
JSX.IonInfiniteScroll,
HTMLIonInfiniteScrollElement
>('ion-infinite-scroll');
export const IonInfiniteScrollContent = /*@__PURE__*/ createReactComponent<
JSX.IonInfiniteScrollContent,
HTMLIonInfiniteScrollContentElement
>('ion-infinite-scroll-content');
export const IonInput = /*@__PURE__*/ createReactComponent<JSX.IonInput, HTMLIonInputElement>(
'ion-input'
);
export const IonItem = /*@__PURE__*/ createReactComponent<
HrefProps<JSX.IonItem>,
HTMLIonItemElement
>('ion-item', true);
export const IonItemDivider = /*@__PURE__*/ createReactComponent<
JSX.IonItemDivider,
HTMLIonItemDividerElement
>('ion-item-divider');
export const IonItemGroup = /*@__PURE__*/ createReactComponent<
JSX.IonItemGroup,
HTMLIonItemGroupElement
>('ion-item-group');
export const IonItemOption = /*@__PURE__*/ createReactComponent<
HrefProps<JSX.IonItemOption>,
HTMLIonItemOptionElement
>('ion-item-option', true);
export const IonItemOptions = /*@__PURE__*/ createReactComponent<
JSX.IonItemOptions,
HTMLIonItemOptionsElement
>('ion-item-options');
export const IonItemSliding = /*@__PURE__*/ createReactComponent<
JSX.IonItemSliding,
HTMLIonItemSlidingElement
>('ion-item-sliding');
export const IonLabel = /*@__PURE__*/ createReactComponent<JSX.IonLabel, HTMLIonLabelElement>(
'ion-label'
);
export const IonList = /*@__PURE__*/ createReactComponent<JSX.IonList, HTMLIonListElement>(
'ion-list'
);
export const IonListHeader = /*@__PURE__*/ createReactComponent<
JSX.IonListHeader,
HTMLIonListHeaderElement
>('ion-list-header');
export const IonMenu = /*@__PURE__*/ createReactComponent<JSX.IonMenu, HTMLIonMenuElement>(
'ion-menu'
);
export const IonMenuButton = /*@__PURE__*/ createReactComponent<
JSX.IonMenuButton,
HTMLIonMenuButtonElement
>('ion-menu-button');
export const IonMenuToggle = /*@__PURE__*/ createReactComponent<
JSX.IonMenuToggle,
HTMLIonMenuToggleElement
>('ion-menu-toggle');
export const IonNote = /*@__PURE__*/ createReactComponent<JSX.IonNote, HTMLIonNoteElement>(
'ion-note'
);
export const IonPickerColumn = /*@__PURE__*/ createReactComponent<
JSX.IonPickerColumn,
HTMLIonPickerColumnElement
>('ion-picker-column');
export const IonInfiniteScroll = /*@__PURE__*/createReactComponent<JSX.IonInfiniteScroll, HTMLIonInfiniteScrollElement>('ion-infinite-scroll');
export const IonInfiniteScrollContent = /*@__PURE__*/createReactComponent<JSX.IonInfiniteScrollContent, HTMLIonInfiniteScrollContentElement>('ion-infinite-scroll-content');
export const IonInput = /*@__PURE__*/createReactComponent<JSX.IonInput, HTMLIonInputElement>('ion-input');
export const IonItemDivider = /*@__PURE__*/createReactComponent<JSX.IonItemDivider, HTMLIonItemDividerElement>('ion-item-divider');
export const IonItemGroup = /*@__PURE__*/createReactComponent<JSX.IonItemGroup, HTMLIonItemGroupElement>('ion-item-group');
export const IonItemOptions = /*@__PURE__*/createReactComponent<JSX.IonItemOptions, HTMLIonItemOptionsElement>('ion-item-options');
export const IonItemSliding = /*@__PURE__*/createReactComponent<JSX.IonItemSliding, HTMLIonItemSlidingElement>('ion-item-sliding');
export const IonLabel = /*@__PURE__*/createReactComponent<JSX.IonLabel, HTMLIonLabelElement>('ion-label');
export const IonList = /*@__PURE__*/createReactComponent<JSX.IonList, HTMLIonListElement>('ion-list');
export const IonListHeader = /*@__PURE__*/createReactComponent<JSX.IonListHeader, HTMLIonListHeaderElement>('ion-list-header');
export const IonMenu = /*@__PURE__*/createReactComponent<JSX.IonMenu, HTMLIonMenuElement>('ion-menu');
export const IonMenuButton = /*@__PURE__*/createReactComponent<JSX.IonMenuButton, HTMLIonMenuButtonElement>('ion-menu-button');
export const IonMenuToggle = /*@__PURE__*/createReactComponent<JSX.IonMenuToggle, HTMLIonMenuToggleElement>('ion-menu-toggle');
export const IonNav = /*@__PURE__*/createReactComponent<JSX.IonNav, HTMLIonNavElement>('ion-nav');
export const IonProgressBar = /*@__PURE__*/ createReactComponent<
JSX.IonProgressBar,
HTMLIonProgressBarElement
>('ion-progress-bar');
export const IonRadio = /*@__PURE__*/ createReactComponent<JSX.IonRadio, HTMLIonRadioElement>(
'ion-radio'
);
export const IonRadioGroup = /*@__PURE__*/ createReactComponent<
JSX.IonRadioGroup,
HTMLIonRadioGroupElement
>('ion-radio-group');
export const IonRange = /*@__PURE__*/ createReactComponent<JSX.IonRange, HTMLIonRangeElement>(
'ion-range'
);
export const IonRefresher = /*@__PURE__*/ createReactComponent<
JSX.IonRefresher,
HTMLIonRefresherElement
>('ion-refresher');
export const IonRefresherContent = /*@__PURE__*/ createReactComponent<
JSX.IonRefresherContent,
HTMLIonRefresherContentElement
>('ion-refresher-content');
export const IonReorder = /*@__PURE__*/ createReactComponent<JSX.IonReorder, HTMLIonReorderElement>(
'ion-reorder'
);
export const IonReorderGroup = /*@__PURE__*/ createReactComponent<
JSX.IonReorderGroup,
HTMLIonReorderGroupElement
>('ion-reorder-group');
export const IonRippleEffect = /*@__PURE__*/ createReactComponent<
JSX.IonRippleEffect,
HTMLIonRippleEffectElement
>('ion-ripple-effect');
export const IonNavLink = /*@__PURE__*/createReactComponent<JSX.IonNavLink, HTMLIonNavLinkElement>('ion-nav-link');
export const IonNote = /*@__PURE__*/createReactComponent<JSX.IonNote, HTMLIonNoteElement>('ion-note');
export const IonProgressBar = /*@__PURE__*/createReactComponent<JSX.IonProgressBar, HTMLIonProgressBarElement>('ion-progress-bar');
export const IonRadio = /*@__PURE__*/createReactComponent<JSX.IonRadio, HTMLIonRadioElement>('ion-radio');
export const IonRadioGroup = /*@__PURE__*/createReactComponent<JSX.IonRadioGroup, HTMLIonRadioGroupElement>('ion-radio-group');
export const IonRange = /*@__PURE__*/createReactComponent<JSX.IonRange, HTMLIonRangeElement>('ion-range');
export const IonRefresher = /*@__PURE__*/createReactComponent<JSX.IonRefresher, HTMLIonRefresherElement>('ion-refresher');
export const IonRefresherContent = /*@__PURE__*/createReactComponent<JSX.IonRefresherContent, HTMLIonRefresherContentElement>('ion-refresher-content');
export const IonReorder = /*@__PURE__*/createReactComponent<JSX.IonReorder, HTMLIonReorderElement>('ion-reorder');
export const IonReorderGroup = /*@__PURE__*/createReactComponent<JSX.IonReorderGroup, HTMLIonReorderGroupElement>('ion-reorder-group');
export const IonRippleEffect = /*@__PURE__*/createReactComponent<JSX.IonRippleEffect, HTMLIonRippleEffectElement>('ion-ripple-effect');
export const IonRow = /*@__PURE__*/createReactComponent<JSX.IonRow, HTMLIonRowElement>('ion-row');
export const IonSearchbar = /*@__PURE__*/ createReactComponent<
JSX.IonSearchbar,
HTMLIonSearchbarElement
>('ion-searchbar');
export const IonSegment = /*@__PURE__*/ createReactComponent<JSX.IonSegment, HTMLIonSegmentElement>(
'ion-segment'
);
export const IonSegmentButton = /*@__PURE__*/ createReactComponent<
JSX.IonSegmentButton,
HTMLIonSegmentButtonElement
>('ion-segment-button');
export const IonSelect = /*@__PURE__*/ createReactComponent<JSX.IonSelect, HTMLIonSelectElement>(
'ion-select'
);
export const IonSelectOption = /*@__PURE__*/ createReactComponent<
JSX.IonSelectOption,
HTMLIonSelectOptionElement
>('ion-select-option');
export const IonSelectPopover = /*@__PURE__*/ createReactComponent<
JSX.IonSelectPopover,
HTMLIonSelectPopoverElement
>('ion-select-popover');
export const IonSkeletonText = /*@__PURE__*/ createReactComponent<
JSX.IonSkeletonText,
HTMLIonSkeletonTextElement
>('ion-skeleton-text');
export const IonSlide = /*@__PURE__*/ createReactComponent<JSX.IonSlide, HTMLIonSlideElement>(
'ion-slide'
);
export const IonSlides = /*@__PURE__*/ createReactComponent<JSX.IonSlides, HTMLIonSlidesElement>(
'ion-slides'
);
export const IonSpinner = /*@__PURE__*/ createReactComponent<JSX.IonSpinner, HTMLIonSpinnerElement>(
'ion-spinner'
);
export const IonSplitPane = /*@__PURE__*/ createReactComponent<
JSX.IonSplitPane,
HTMLIonSplitPaneElement
>('ion-split-pane');
export const IonText = /*@__PURE__*/ createReactComponent<JSX.IonText, HTMLIonTextElement>(
'ion-text'
);
export const IonTextarea = /*@__PURE__*/ createReactComponent<
JSX.IonTextarea,
HTMLIonTextareaElement
>('ion-textarea');
export const IonThumbnail = /*@__PURE__*/ createReactComponent<
JSX.IonThumbnail,
HTMLIonThumbnailElement
>('ion-thumbnail');
export const IonTitle = /*@__PURE__*/ createReactComponent<JSX.IonTitle, HTMLIonTitleElement>(
'ion-title'
);
export const IonToggle = /*@__PURE__*/ createReactComponent<JSX.IonToggle, HTMLIonToggleElement>(
'ion-toggle'
);
export const IonToolbar = /*@__PURE__*/ createReactComponent<JSX.IonToolbar, HTMLIonToolbarElement>(
'ion-toolbar'
);
export const IonVirtualScroll = /*@__PURE__*/ createReactComponent<
JSX.IonVirtualScroll,
HTMLIonVirtualScrollElement
>('ion-virtual-scroll');
export const IonSearchbar = /*@__PURE__*/createReactComponent<JSX.IonSearchbar, HTMLIonSearchbarElement>('ion-searchbar');
export const IonSegment = /*@__PURE__*/createReactComponent<JSX.IonSegment, HTMLIonSegmentElement>('ion-segment');
export const IonSegmentButton = /*@__PURE__*/createReactComponent<JSX.IonSegmentButton, HTMLIonSegmentButtonElement>('ion-segment-button');
export const IonSelect = /*@__PURE__*/createReactComponent<JSX.IonSelect, HTMLIonSelectElement>('ion-select');
export const IonSelectOption = /*@__PURE__*/createReactComponent<JSX.IonSelectOption, HTMLIonSelectOptionElement>('ion-select-option');
export const IonSkeletonText = /*@__PURE__*/createReactComponent<JSX.IonSkeletonText, HTMLIonSkeletonTextElement>('ion-skeleton-text');
export const IonSlide = /*@__PURE__*/createReactComponent<JSX.IonSlide, HTMLIonSlideElement>('ion-slide');
export const IonSlides = /*@__PURE__*/createReactComponent<JSX.IonSlides, HTMLIonSlidesElement>('ion-slides');
export const IonSpinner = /*@__PURE__*/createReactComponent<JSX.IonSpinner, HTMLIonSpinnerElement>('ion-spinner');
export const IonSplitPane = /*@__PURE__*/createReactComponent<JSX.IonSplitPane, HTMLIonSplitPaneElement>('ion-split-pane');
export const IonTab = /*@__PURE__*/createReactComponent<JSX.IonTab, HTMLIonTabElement>('ion-tab');
export const IonText = /*@__PURE__*/createReactComponent<JSX.IonText, HTMLIonTextElement>('ion-text');
export const IonTextarea = /*@__PURE__*/createReactComponent<JSX.IonTextarea, HTMLIonTextareaElement>('ion-textarea');
export const IonThumbnail = /*@__PURE__*/createReactComponent<JSX.IonThumbnail, HTMLIonThumbnailElement>('ion-thumbnail');
export const IonTitle = /*@__PURE__*/createReactComponent<JSX.IonTitle, HTMLIonTitleElement>('ion-title');
export const IonToggle = /*@__PURE__*/createReactComponent<JSX.IonToggle, HTMLIonToggleElement>('ion-toggle');
export const IonToolbar = /*@__PURE__*/createReactComponent<JSX.IonToolbar, HTMLIonToolbarElement>('ion-toolbar');
export const IonVirtualScroll = /*@__PURE__*/createReactComponent<JSX.IonVirtualScroll, HTMLIonVirtualScrollElement>('ion-virtual-scroll');

View File

@ -0,0 +1,93 @@
import React from 'react';
import {
attachProps,
createForwardRef,
dashToPascalCase,
isCoveredByReact,
mergeRefs,
} from './utils';
export interface HTMLStencilElement extends HTMLElement {
componentOnReady(): Promise<this>;
}
interface StencilReactInternalProps<ElementType> extends React.HTMLAttributes<ElementType> {
forwardedRef: React.RefObject<ElementType>;
ref?: React.Ref<any>;
}
export const createReactComponent = <
PropType,
ElementType extends HTMLStencilElement,
ContextStateType = {},
ExpandedPropsTypes = {}
>(
tagName: string,
ReactComponentContext?: React.Context<ContextStateType>,
manipulatePropsFunction?: (
originalProps: StencilReactInternalProps<ElementType>,
propsToPass: any,
) => ExpandedPropsTypes,
) => {
const displayName = dashToPascalCase(tagName);
const ReactComponent = class extends React.Component<StencilReactInternalProps<ElementType>> {
componentEl!: ElementType;
setComponentElRef = (element: ElementType) => {
this.componentEl = element;
};
constructor(props: StencilReactInternalProps<ElementType>) {
super(props);
}
componentDidMount() {
this.componentDidUpdate(this.props);
}
componentDidUpdate(prevProps: StencilReactInternalProps<ElementType>) {
attachProps(this.componentEl, this.props, prevProps);
}
render() {
const { children, forwardedRef, style, className, ref, ...cProps } = this.props;
let propsToPass = Object.keys(cProps).reduce((acc, name) => {
if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
const eventName = name.substring(2).toLowerCase();
if (typeof document !== 'undefined' && isCoveredByReact(eventName)) {
(acc as any)[name] = (cProps as any)[name];
}
} else {
(acc as any)[name] = (cProps as any)[name];
}
return acc;
}, {});
if (manipulatePropsFunction) {
propsToPass = manipulatePropsFunction(this.props, propsToPass);
}
const newProps: Omit<StencilReactInternalProps<ElementType>, 'forwardedRef'> = {
...propsToPass,
ref: mergeRefs(forwardedRef, this.setComponentElRef),
style,
};
return React.createElement(tagName, newProps, children);
}
static get displayName() {
return displayName;
}
};
// If context was passed to createReactComponent then conditionally add it to the Component Class
if (ReactComponentContext) {
ReactComponent.contextType = ReactComponentContext;
}
return createForwardRef<PropType, ElementType>(ReactComponent, displayName);
};

View File

@ -0,0 +1,152 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { OverlayEventDetail } from './interfaces';
import { StencilReactForwardedRef, attachProps, setRef } from './utils';
interface OverlayElement extends HTMLElement {
present: () => Promise<void>;
dismiss: (data?: any, role?: string | undefined) => Promise<boolean>;
}
export interface ReactOverlayProps {
children?: React.ReactNode;
isOpen: boolean;
onDidDismiss?: (event: CustomEvent<OverlayEventDetail>) => void;
onDidPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
onWillDismiss?: (event: CustomEvent<OverlayEventDetail>) => void;
onWillPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
}
export const createOverlayComponent = <
OverlayComponent extends object,
OverlayType extends OverlayElement
>(
displayName: string,
controller: { create: (options: any) => Promise<OverlayType> }
) => {
const didDismissEventName = `on${displayName}DidDismiss`;
const didPresentEventName = `on${displayName}DidPresent`;
const willDismissEventName = `on${displayName}WillDismiss`;
const willPresentEventName = `on${displayName}WillPresent`;
type Props = OverlayComponent &
ReactOverlayProps & {
forwardedRef?: StencilReactForwardedRef<OverlayType>;
};
let isDismissing = false;
class Overlay extends React.Component<Props> {
overlay?: OverlayType;
el!: HTMLDivElement;
constructor(props: Props) {
super(props);
if (typeof document !== 'undefined') {
this.el = document.createElement('div');
}
this.handleDismiss = this.handleDismiss.bind(this);
}
static get displayName() {
return displayName;
}
componentDidMount() {
if (this.props.isOpen) {
this.present();
}
}
componentWillUnmount() {
if (this.overlay) {
this.overlay.dismiss();
}
}
handleDismiss(event: CustomEvent<OverlayEventDetail<any>>) {
if (this.props.onDidDismiss) {
this.props.onDidDismiss(event);
}
setRef(this.props.forwardedRef, null)
}
shouldComponentUpdate(nextProps: Props) {
// Check if the overlay component is about to dismiss
if (this.overlay && nextProps.isOpen !== this.props.isOpen && nextProps.isOpen === false) {
isDismissing = true;
}
return true;
}
async componentDidUpdate(prevProps: Props) {
if (this.overlay) {
attachProps(this.overlay, this.props, prevProps);
}
if (prevProps.isOpen !== this.props.isOpen && this.props.isOpen === true) {
this.present(prevProps);
}
if (this.overlay && prevProps.isOpen !== this.props.isOpen && this.props.isOpen === false) {
await this.overlay.dismiss();
isDismissing = false;
/**
* Now that the overlay is dismissed
* we need to render again so that any
* inner components will be unmounted
*/
this.forceUpdate();
}
}
async present(prevProps?: Props) {
const {
children,
isOpen,
onDidDismiss,
onDidPresent,
onWillDismiss,
onWillPresent,
...cProps
} = this.props;
const elementProps = {
...cProps,
ref: this.props.forwardedRef,
[didDismissEventName]: this.handleDismiss,
[didPresentEventName]: (e: CustomEvent) =>
this.props.onDidPresent && this.props.onDidPresent(e),
[willDismissEventName]: (e: CustomEvent) =>
this.props.onWillDismiss && this.props.onWillDismiss(e),
[willPresentEventName]: (e: CustomEvent) =>
this.props.onWillPresent && this.props.onWillPresent(e),
};
this.overlay = await controller.create({
...elementProps,
component: this.el,
componentProps: {},
});
setRef(this.props.forwardedRef, this.overlay);
attachProps(this.overlay, elementProps, prevProps);
await this.overlay.present();
}
render() {
/**
* Continue to render the component even when
* overlay is dismissing otherwise component
* will be hidden before animation is done.
*/
return ReactDOM.createPortal(this.props.isOpen || isDismissing ? this.props.children : null, this.el);
}
}
return React.forwardRef<OverlayType, Props>((props, ref) => {
return <Overlay {...props} forwardedRef={ref} />;
});
};

View File

@ -0,0 +1,2 @@
export { createReactComponent } from './createComponent';
export { createOverlayComponent } from './createOverlayComponent';

View File

@ -0,0 +1,34 @@
// General types important to applications using stencil built components
export interface EventEmitter<T = any> {
emit: (data?: T) => CustomEvent<T>;
}
export interface StyleReactProps {
class?: string;
className?: string;
style?: { [key: string]: any };
}
export interface OverlayEventDetail<T = any> {
data?: T;
role?: string;
}
export interface OverlayInterface {
el: HTMLElement;
animated: boolean;
keyboardClose: boolean;
overlayIndex: number;
presented: boolean;
enterAnimation?: any;
leaveAnimation?: any;
didPresent: EventEmitter<void>;
willPresent: EventEmitter<void>;
willDismiss: EventEmitter<OverlayEventDetail>;
didDismiss: EventEmitter<OverlayEventDetail>;
present(): Promise<void>;
dismiss(data?: any, role?: string): Promise<boolean>;
}

View File

@ -28,11 +28,10 @@ export const attachProps = (node: HTMLElement, newProps: any, oldProps: any = {}
syncEvent(node, eventNameLc, newProps[name]);
}
} else {
(node as any)[name] = newProps[name];
const propType = typeof newProps[name];
if (propType === 'string') {
node.setAttribute(camelToDashCase(name), newProps[name]);
} else {
(node as any)[name] = newProps[name];
}
}
});

View File

@ -0,0 +1,47 @@
import React from 'react';
import type { StyleReactProps } from '../interfaces';
export type StencilReactExternalProps<PropType, ElementType> = PropType &
Omit<React.HTMLAttributes<ElementType>, 'style'> &
StyleReactProps;
// This will be replaced with React.ForwardedRef when react-output-target is upgraded to React v17
export type StencilReactForwardedRef<T> = ((instance: T | null) => void) | React.MutableRefObject<T | null> | null;
export const setRef = (ref: StencilReactForwardedRef<any> | React.Ref<any> | undefined, value: any) => {
if (typeof ref === 'function') {
ref(value)
} else if (ref != null) {
// Cast as a MutableRef so we can assign current
(ref as React.MutableRefObject<any>).current = value
}
};
export const mergeRefs = (
...refs: (StencilReactForwardedRef<any> | React.Ref<any> | undefined)[]
): React.RefCallback<any> => {
return (value: any) => {
refs.forEach(ref => {
setRef(ref, value)
})
}
};
export const createForwardRef = <PropType, ElementType>(
ReactComponent: any,
displayName: string,
) => {
const forwardRef = (
props: StencilReactExternalProps<PropType, ElementType>,
ref: StencilReactForwardedRef<ElementType>,
) => {
return <ReactComponent {...props} forwardedRef={ref} />;
};
forwardRef.displayName = displayName;
return React.forwardRef(forwardRef);
};
export * from './attachProps';
export * from './case';

View File

@ -0,0 +1,34 @@
import type { JSX } from '@ionic/core';
import { createRoutingComponent } from './createRoutingComponent';
import { HrefProps } from './hrefprops';
export const IonRouterLink = /*@__PURE__*/ createRoutingComponent<
HrefProps<JSX.IonRouterLink>,
HTMLIonRouterLinkElement
>('ion-router-link');
export const IonButton = /*@__PURE__*/ createRoutingComponent<
HrefProps<JSX.IonButton>,
HTMLIonButtonElement
>('ion-button');
export const IonCard = /*@__PURE__*/ createRoutingComponent<
HrefProps<JSX.IonCard>,
HTMLIonCardElement
>('ion-card');
export const IonFabButton = /*@__PURE__*/ createRoutingComponent<
HrefProps<JSX.IonFabButton>,
HTMLIonFabButtonElement
>('ion-fab-button');
export const IonItem = /*@__PURE__*/ createRoutingComponent<
HrefProps<JSX.IonItem>,
HTMLIonItemElement
>('ion-item');
export const IonItemOption = /*@__PURE__*/ createRoutingComponent<
HrefProps<JSX.IonItemOption>,
HTMLIonItemOptionElement
>('ion-item-option');

View File

@ -27,28 +27,6 @@ export const createForwardRef = <PropType, ElementType>(
return React.forwardRef(forwardRef);
};
export const setRef = (ref: React.ForwardedRef<any> | React.Ref<any> | undefined, value: any) => {
if (typeof ref === 'function') {
ref(value)
} else if (ref != null) {
// Cast as a MutableRef so we can assign current
(ref as React.MutableRefObject<any>).current = value
}
};
export const mergeRefs = (
...refs: (React.ForwardedRef<any> | React.Ref<any> | undefined)[]
): React.RefCallback<any> => {
return (value: any) => {
refs.forEach(ref => {
setRef(ref, value)
})
}
};
export * from './attachProps';
export * from './case';
export const isPlatform = (platform: Platforms) => {
return isPlatformCore(window, platform);
};

View File

@ -1,7 +1,7 @@
import { OverlayEventDetail } from '@ionic/core';
import { useMemo, useRef } from 'react';
import { attachProps } from '../components/utils';
import { attachProps } from '../components/react-component-lib/utils';
import { HookOverlayOptions } from './HookOverlayOptions';

View File

@ -2,7 +2,7 @@ import { OverlayEventDetail } from '@ionic/core';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import { attachProps } from '../components/utils';
import { attachProps } from '../components/react-component-lib/utils';
import { HookOverlayOptions } from './HookOverlayOptions';

View File

@ -1,6 +1,6 @@
import React from 'react';
import { mergeRefs } from '../components/utils';
import { mergeRefs } from '../components/react-component-lib/utils';
import { IonLifeCycleContext } from '../contexts/IonLifeCycleContext';
import { RouteInfo } from '../models';