@ionic/react 8.3.2 → 8.3.3-dev.11728518847.1ef16a63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1260,6 +1260,8 @@ const IonNav = createForwardRef(IonNavInternal, 'IonNav');
1260
1260
  const IonTabsContext = React.createContext({
1261
1261
  activeTab: undefined,
1262
1262
  selectTab: () => false,
1263
+ hasRouterOutlet: false,
1264
+ tabBarProps: { ref: React.createRef() },
1263
1265
  });
1264
1266
 
1265
1267
  const HTMLElementSSR = (typeof HTMLElement !== 'undefined' ? HTMLElement : class {
@@ -1347,6 +1349,133 @@ class IonRouterOutletContainer extends React.Component {
1347
1349
  }
1348
1350
  const IonRouterOutlet = createForwardRef(IonRouterOutletContainer, 'IonRouterOutlet');
1349
1351
 
1352
+ class IonTabsElement extends HTMLElementSSR {
1353
+ constructor() {
1354
+ super();
1355
+ }
1356
+ }
1357
+ // TODO(FW-2959): types
1358
+ if (typeof window !== 'undefined' && window.customElements) {
1359
+ const element = window.customElements.get('ion-tabs');
1360
+ if (!element) {
1361
+ window.customElements.define('ion-tabs', IonTabsElement);
1362
+ }
1363
+ }
1364
+ const IonTabs = /*@__PURE__*/ (() => class extends React.Component {
1365
+ constructor(props) {
1366
+ super(props);
1367
+ this.tabBarRef = React.createRef();
1368
+ this.ionTabContextState = {
1369
+ activeTab: undefined,
1370
+ selectTab: () => false,
1371
+ hasRouterOutlet: false,
1372
+ /**
1373
+ * Tab bar can be used as a standalone component,
1374
+ * so the props can not be passed directly to the
1375
+ * tab bar component. Instead, props will be
1376
+ * passed through the context.
1377
+ */
1378
+ tabBarProps: { ref: this.tabBarRef },
1379
+ };
1380
+ }
1381
+ componentDidMount() {
1382
+ if (this.tabBarRef.current) {
1383
+ // Grab initial value
1384
+ this.ionTabContextState.activeTab = this.tabBarRef.current.state.activeTab;
1385
+ // Override method
1386
+ this.tabBarRef.current.setActiveTabOnContext = (tab) => {
1387
+ this.ionTabContextState.activeTab = tab;
1388
+ };
1389
+ this.ionTabContextState.selectTab = this.tabBarRef.current.selectTab;
1390
+ }
1391
+ }
1392
+ renderTabsInner(children, outlet) {
1393
+ return (React.createElement(IonTabsInner, Object.assign({}, this.props), React.Children.map(children, (child) => {
1394
+ if (React.isValidElement(child)) {
1395
+ const isRouterOutlet = child.type === IonRouterOutlet ||
1396
+ child.type.isRouterOutlet ||
1397
+ (child.type === Fragment && child.props.children[0].type === IonRouterOutlet);
1398
+ if (isRouterOutlet) {
1399
+ /**
1400
+ * The modified outlet needs to be returned to include
1401
+ * the ref.
1402
+ */
1403
+ return outlet;
1404
+ }
1405
+ }
1406
+ return child;
1407
+ })));
1408
+ }
1409
+ render() {
1410
+ let outlet;
1411
+ // Check if IonTabs has any IonTab children
1412
+ let hasTab = false;
1413
+ const _a = this.props, { className, onIonTabsDidChange, onIonTabsWillChange } = _a, props = __rest(_a, ["className", "onIonTabsDidChange", "onIonTabsWillChange"]);
1414
+ const children = typeof this.props.children === 'function'
1415
+ ? this.props.children(this.ionTabContextState)
1416
+ : this.props.children;
1417
+ React.Children.forEach(children, (child) => {
1418
+ // eslint-disable-next-line no-prototype-builtins
1419
+ if (child == null || typeof child !== 'object' || !child.hasOwnProperty('type')) {
1420
+ return;
1421
+ }
1422
+ if (child.type === IonRouterOutlet || child.type.isRouterOutlet) {
1423
+ outlet = React.cloneElement(child);
1424
+ }
1425
+ else if (child.type === Fragment && child.props.children[0].type === IonRouterOutlet) {
1426
+ outlet = React.cloneElement(child.props.children[0]);
1427
+ }
1428
+ else if (child.type === IonTab) {
1429
+ /**
1430
+ * This indicates that IonTabs will be using a basic tab-based navigation
1431
+ * without the history stack or URL updates associated with the router.
1432
+ */
1433
+ hasTab = true;
1434
+ }
1435
+ this.ionTabContextState.hasRouterOutlet = !!outlet;
1436
+ let childProps = Object.assign({}, this.ionTabContextState.tabBarProps);
1437
+ /**
1438
+ * Only pass these props
1439
+ * down from IonTabs to IonTabBar
1440
+ * if they are defined, otherwise
1441
+ * if you have a handler set on
1442
+ * IonTabBar it will be overridden.
1443
+ */
1444
+ if (onIonTabsDidChange !== undefined) {
1445
+ childProps = Object.assign(Object.assign({}, childProps), { onIonTabsDidChange });
1446
+ }
1447
+ if (onIonTabsWillChange !== undefined) {
1448
+ childProps = Object.assign(Object.assign({}, childProps), { onIonTabsWillChange });
1449
+ }
1450
+ this.ionTabContextState.tabBarProps = childProps;
1451
+ });
1452
+ if (!outlet && !hasTab) {
1453
+ throw new Error('IonTabs must contain an IonRouterOutlet or an IonTab');
1454
+ }
1455
+ if (outlet && hasTab) {
1456
+ throw new Error('IonTabs cannot contain an IonRouterOutlet and an IonTab at the same time');
1457
+ }
1458
+ if (hasTab) {
1459
+ return React.createElement(IonTabsInner, Object.assign({}, this.props));
1460
+ }
1461
+ /**
1462
+ * TODO(ROU-11051)
1463
+ *
1464
+ * There is no error handling for the case where there
1465
+ * is no associated Route for the given IonTabButton.
1466
+ *
1467
+ * More investigation is needed to determine how to
1468
+ * handle this to prevent any overwriting of the
1469
+ * IonTabButton's onClick handler and how the routing
1470
+ * is handled.
1471
+ */
1472
+ return (React.createElement(IonTabsContext.Provider, { value: this.ionTabContextState }, this.context.hasIonicRouter() ? (React.createElement(PageManager, Object.assign({ className: className ? `${className}` : '', routeInfo: this.context.routeInfo }, props), this.renderTabsInner(children, outlet))) : (this.renderTabsInner(children, outlet))));
1473
+ }
1474
+ static get contextType() {
1475
+ return NavContext;
1476
+ }
1477
+ })();
1478
+
1350
1479
  const IonTabButton = /*@__PURE__*/ (() => class extends React.Component {
1351
1480
  constructor(props) {
1352
1481
  super(props);
@@ -1496,15 +1625,16 @@ class IonTabBarUnwrapped extends React.PureComponent {
1496
1625
  };
1497
1626
  }
1498
1627
  onTabButtonClick(e, onClickFn) {
1499
- var _a, _b;
1628
+ var _a;
1500
1629
  const tappedTab = this.state.tabs[e.detail.tab];
1501
1630
  const originalHref = tappedTab.originalHref;
1631
+ const hasRouterOutlet = (_a = this.props.tabsContext) === null || _a === void 0 ? void 0 : _a.hasRouterOutlet;
1502
1632
  /**
1503
1633
  * If the router outlet is not defined, then the tabs is being used
1504
1634
  * as a basic tab navigation without the router. In this case, we
1505
1635
  * don't want to update the href else the URL will change.
1506
1636
  */
1507
- const currentHref = ((_a = this.props.routerOutletRef) === null || _a === void 0 ? void 0 : _a.current) ? e.detail.href : '';
1637
+ const currentHref = hasRouterOutlet ? e.detail.href : '';
1508
1638
  const { activeTab: prevActiveTab } = this.state;
1509
1639
  if (onClickFn) {
1510
1640
  /**
@@ -1527,7 +1657,7 @@ class IonTabBarUnwrapped extends React.PureComponent {
1527
1657
  if (this.props.onIonTabsDidChange) {
1528
1658
  this.props.onIonTabsDidChange(new CustomEvent('ionTabDidChange', { detail: { tab: e.detail.tab } }));
1529
1659
  }
1530
- if ((_b = this.props.routerOutletRef) === null || _b === void 0 ? void 0 : _b.current) {
1660
+ if (hasRouterOutlet) {
1531
1661
  this.setActiveTabOnContext(e.detail.tab);
1532
1662
  this.context.changeTab(e.detail.tab, currentHref, e.detail.routeOptions);
1533
1663
  }
@@ -1563,170 +1693,19 @@ class IonTabBarUnwrapped extends React.PureComponent {
1563
1693
  const IonTabBarContainer = React.memo((_a) => {
1564
1694
  var { forwardedRef } = _a, props = __rest(_a, ["forwardedRef"]);
1565
1695
  const context = useContext(NavContext);
1566
- return (React.createElement(IonTabBarUnwrapped, Object.assign({ ref: forwardedRef }, props, { routeInfo: props.routeInfo || context.routeInfo || { pathname: window.location.pathname }, onSetCurrentTab: context.setCurrentTab }), props.children));
1567
- });
1568
- const IonTabBar = createForwardRef(IonTabBarContainer, 'IonTabBar');
1569
-
1570
- class IonTabsElement extends HTMLElementSSR {
1571
- constructor() {
1572
- super();
1573
- }
1574
- }
1575
- // TODO(FW-2959): types
1576
- if (typeof window !== 'undefined' && window.customElements) {
1577
- const element = window.customElements.get('ion-tabs');
1578
- if (!element) {
1579
- window.customElements.define('ion-tabs', IonTabsElement);
1580
- }
1581
- }
1582
- const hostStyles = {
1583
- display: 'flex',
1584
- position: 'absolute',
1585
- top: '0',
1586
- left: '0',
1587
- right: '0',
1588
- bottom: '0',
1589
- flexDirection: 'column',
1590
- width: '100%',
1591
- height: '100%',
1592
- contain: 'layout size style',
1593
- };
1594
- const tabsInner = {
1595
- position: 'relative',
1596
- flex: 1,
1597
- contain: 'layout size style',
1598
- };
1599
- const IonTabs = /*@__PURE__*/ (() => class extends React.Component {
1600
- constructor(props) {
1601
- super(props);
1602
- this.routerOutletRef = React.createRef();
1603
- this.tabBarRef = React.createRef();
1604
- this.ionTabContextState = {
1605
- activeTab: undefined,
1606
- selectTab: () => false,
1607
- };
1608
- }
1609
- componentDidMount() {
1610
- if (this.tabBarRef.current) {
1611
- // Grab initial value
1612
- this.ionTabContextState.activeTab = this.tabBarRef.current.state.activeTab;
1613
- // Override method
1614
- this.tabBarRef.current.setActiveTabOnContext = (tab) => {
1615
- this.ionTabContextState.activeTab = tab;
1616
- };
1617
- this.ionTabContextState.selectTab = this.tabBarRef.current.selectTab;
1618
- }
1619
- }
1620
- render() {
1621
- let outlet;
1622
- let tabBar;
1623
- // Check if IonTabs has any IonTab children
1624
- let hasTab = false;
1625
- const _a = this.props, { className, onIonTabsDidChange, onIonTabsWillChange } = _a, props = __rest(_a, ["className", "onIonTabsDidChange", "onIonTabsWillChange"]);
1626
- const children = typeof this.props.children === 'function'
1627
- ? this.props.children(this.ionTabContextState)
1628
- : this.props.children;
1629
- const outletProps = {
1630
- ref: this.routerOutletRef,
1631
- };
1632
- React.Children.forEach(children, (child) => {
1633
- // eslint-disable-next-line no-prototype-builtins
1634
- if (child == null || typeof child !== 'object' || !child.hasOwnProperty('type')) {
1635
- return;
1636
- }
1637
- if (child.type === IonRouterOutlet || child.type.isRouterOutlet) {
1638
- outlet = React.cloneElement(child, outletProps);
1639
- }
1640
- else if (child.type === Fragment && child.props.children[0].type === IonRouterOutlet) {
1641
- outlet = React.cloneElement(child.props.children[0], outletProps);
1642
- }
1643
- else if (child.type === IonTab) {
1644
- /**
1645
- * This indicates that IonTabs will be using a basic tab-based navigation
1646
- * without the history stack or URL updates associated with the router.
1647
- */
1648
- hasTab = true;
1649
- }
1650
- let childProps = {
1651
- ref: this.tabBarRef,
1652
- routerOutletRef: this.routerOutletRef,
1653
- };
1654
- /**
1655
- * Only pass these props
1656
- * down from IonTabs to IonTabBar
1657
- * if they are defined, otherwise
1658
- * if you have a handler set on
1659
- * IonTabBar it will be overridden.
1660
- */
1661
- if (onIonTabsDidChange !== undefined) {
1662
- childProps = Object.assign(Object.assign({}, childProps), { onIonTabsDidChange });
1663
- }
1664
- if (onIonTabsWillChange !== undefined) {
1665
- childProps = Object.assign(Object.assign({}, childProps), { onIonTabsWillChange });
1666
- }
1667
- if (child.type === IonTabBar || child.type.isTabBar) {
1668
- tabBar = React.cloneElement(child, childProps);
1669
- }
1670
- else if (child.type === Fragment &&
1671
- (child.props.children[1].type === IonTabBar || child.props.children[1].type.isTabBar)) {
1672
- tabBar = React.cloneElement(child.props.children[1], childProps);
1673
- }
1674
- });
1675
- if (!outlet && !hasTab) {
1676
- throw new Error('IonTabs must contain an IonRouterOutlet or an IonTab');
1677
- }
1678
- if (outlet && hasTab) {
1679
- throw new Error('IonTabs cannot contain an IonRouterOutlet and an IonTab at the same time');
1680
- }
1681
- if (hasTab) {
1682
- return React.createElement(IonTabsInner, Object.assign({}, this.props));
1683
- }
1696
+ const tabsContext = useContext(IonTabsContext);
1697
+ const tabBarRef = forwardedRef || tabsContext.tabBarProps.ref;
1698
+ const updatedTabBarProps = Object.assign(Object.assign({}, tabsContext.tabBarProps), { ref: tabBarRef });
1699
+ return (React.createElement(IonTabBarUnwrapped, Object.assign({ ref: tabBarRef }, props, { routeInfo: props.routeInfo || context.routeInfo || { pathname: window.location.pathname }, onSetCurrentTab: context.setCurrentTab,
1684
1700
  /**
1685
- * TODO(ROU-11051)
1686
- *
1687
- * There is no error handling for the case where there
1688
- * is no associated Route for the given IonTabButton.
1689
- *
1690
- * More investigation is needed to determine how to
1691
- * handle this to prevent any overwriting of the
1692
- * IonTabButton's onClick handler and how the routing
1693
- * is handled.
1701
+ * Tab bar can be used as a standalone component,
1702
+ * so it cannot be modified directly through
1703
+ * IonTabs. Instead, props will be passed through
1704
+ * the context.
1694
1705
  */
1695
- return (React.createElement(IonTabsContext.Provider, { value: this.ionTabContextState }, this.context.hasIonicRouter() ? (React.createElement(PageManager, Object.assign({ className: className ? `${className}` : '', routeInfo: this.context.routeInfo }, props),
1696
- React.createElement(IonTabsInner, Object.assign({}, this.props), React.Children.map(children, (child) => {
1697
- if (React.isValidElement(child)) {
1698
- const isTabBar = child.type === IonTabBar ||
1699
- child.type.isTabBar ||
1700
- (child.type === Fragment &&
1701
- (child.props.children[1].type === IonTabBar || child.props.children[1].type.isTabBar));
1702
- const isRouterOutlet = child.type === IonRouterOutlet ||
1703
- child.type.isRouterOutlet ||
1704
- (child.type === Fragment && child.props.children[0].type === IonRouterOutlet);
1705
- if (isTabBar) {
1706
- /**
1707
- * The modified tabBar needs to be returned to include
1708
- * the context and the overridden methods.
1709
- */
1710
- return tabBar;
1711
- }
1712
- if (isRouterOutlet) {
1713
- /**
1714
- * The modified outlet needs to be returned to include
1715
- * the ref.
1716
- */
1717
- return outlet;
1718
- }
1719
- }
1720
- return child;
1721
- })))) : (React.createElement("div", Object.assign({ className: className ? `${className}` : 'ion-tabs' }, props, { style: hostStyles }),
1722
- (tabBar === null || tabBar === void 0 ? void 0 : tabBar.props.slot) === 'top' ? tabBar : null,
1723
- React.createElement("div", { style: tabsInner, className: "tabs-inner" }, outlet),
1724
- (tabBar === null || tabBar === void 0 ? void 0 : tabBar.props.slot) === 'bottom' ? tabBar : null))));
1725
- }
1726
- static get contextType() {
1727
- return NavContext;
1728
- }
1729
- })();
1706
+ tabsContext: Object.assign(Object.assign({}, tabsContext), { tabBarProps: updatedTabBarProps }) }), props.children));
1707
+ });
1708
+ const IonTabBar = createForwardRef(IonTabBarContainer, 'IonTabBar');
1730
1709
 
1731
1710
  const IonBackButton = /*@__PURE__*/ (() => class extends React.Component {
1732
1711
  constructor() {