# ReExt - ExtJS Components in React

<img src="https://reextdemo.vercel.app/ReExt/ReExt.png" width="250px" height="100px"/>

### Unleash the power of Sencha Ext JS components in React

ReExt enables the integration of  <a target="_blank" href="https://www.sencha.com/products/extjs/">Sencha Ext JS components</a> into React applications.

It bridges between React and the robust Ext JS framework, allowing you to leverage an extensive library of UI components and tools within your React environment.

### Table of Contents
1. [ReExt Examples](#reext-examples)
2. [Using the ReExt Designer VS Code Extension (preview version)](#using-the-reext-designer-vs-code-extension)
3. [Quick Start - not using VS Code Extension](#quick-start-not-using-vs-code-extension)
4. [Step by Step Guide not using VS Code Extension](#step-by-step-guide-not-using-vs-code-extension)
5. [Installation](#installation)
6. [Installing the Sencha ExtJS SDK](#installing-the-sencha-extjs-sdk)
7. [Aquiring a Licensed Version of Ext JS](#aquiring-a-licensed-version-of-ext-js)
8. [ReExt Trial Key](#reext-trial-key)
9. [Documentation](#documentation)
10. [Using ExtJS Overrides](#using-extjs-overrides)
11. [Using Custom Ext JS Components](#using-custom-ext-js-components)
12. [Version History](#version-history)



## ReExt Examples

<a target="_blank" href="https://reextdemo.vercel.app?version=7.9.0&toolkit=classic&theme=classic">Examples using classic toolkit and classic theme</a>


## Using the ReExt Designer VS Code Extension 

(preview version)

The best way to get started with ReExt is to use the ReExt Designer Extension (preview version) in <a target="_blank" href="https://code.visualstudio.com/download">VS Code</a>

<a download href="https://reextdemo.vercel.app/ReExt/reextdesigner-latest.vsix">Download the ReExt Designer VS Code Extension (preview version)</a>

To install the ReExt Designer Extension (preview version) in VS Code, click on the Extensions Activity Bar item, then click the '...' menu in the upper right corner of the Extensions View, and select 'Install from VSIX...'

<img src="https://reextdemo.vercel.app/ReExt/InstallFromVSIX.png" width="700px"/>

## Quick Start not using VS Code Extension
### (takes less than 1 minute to run)

Note: Quick Start assumes npm is installed. To install npm, install <a target="_blank" href="https://nodejs.org/en">Node.js</a>

The commands can be run all at one time. 

### MacOS

Copy and paste the commands below into a MacOS terminal window.

#### Vite

```bash
npm create vite@latest reextvite -- --template react-swc
cd reextvite
npm install @sencha/reext@latest
cp node_modules/@sencha/reext/dist/example/vite.config.js vite.config.js
cp node_modules/@sencha/reext/dist/example/App.jsx src/App.jsx
cp node_modules/@sencha/reext/dist/example/main.jsx src/main.jsx
npx vite --open
```

#### Next.js with JavaScript

```bash
npx create-next-app@latest reextnext --js --eslint --no-tailwind --no-src-dir --app --import-alias "@/*"
cd reextnext
npm install @sencha/reext@latest
cp node_modules/@sencha/reext/dist/example/next.config.mjs next.config.mjs
cp node_modules/@sencha/reext/dist/example/layout.js app/layout.js
cp node_modules/@sencha/reext/dist/example/page.js app/page.js
npm run dev
```

#### Next.js with TypeScript

```bash
npx create-next-app@latest reextnext --ts --eslint --no-tailwind --no-src-dir --app --import-alias "@/*"
cd reextnext
npm install @sencha/reext@latest
cp node_modules/@sencha/reext/dist/example/next.config.mjs next.config.mjs
cp node_modules/@sencha/reext/dist/example/layout.tsx app/layout.tsx
cp node_modules/@sencha/reext/dist/example/page.tsx app/page.tsx
npm run dev
```

#### Create React App

```bash
npx create-react-app reextcra
cd reextcra
npm install @sencha/reext@latest
cp node_modules/@sencha/reext/dist/example/vite.config.js vite.config.js
cp node_modules/@sencha/reext/dist/example/App.jsx src/App.js
cp node_modules/@sencha/reext/dist/example/main.jsx src/index.js
npm start
```



#### Create React App

```bash
npx create-react-app reextcra
cd reextcra
npm install @sencha/reext@latest
copy node_modules\@sencha\reext\dist\example\vite.config.js vite.config.js
copy node_modules\@sencha\reext\dist\example\App.jsx src\App.js
copy node_modules\@sencha\reext\dist\example\main.jsx src\index.js
npm start
```

# Step by Step Guide not using VS Code Extension

## Create a React application

### create react app 
<a target="_blank" href="https://create-react-app.dev/docs/getting-started/">https://create-react-app.dev/docs/getting-started/</a>

```bash
npx create-react-app reextcra
```

### vite
<a target="_blank" href="https://vitejs.dev/guide/">https://vitejs.dev/guide/</a>

```bash
npm create vite@latest reextvite -- --template react-swc
```

## Installation

Use the package manager [npm](https://npmjs.com) to install ReExt.

```bash
npm install @sencha/reext
```

## Installing the Sencha ExtJS SDK

### IMPORTANT-EXTJS FRAMEWORK REQUIREMENT:

ReExt is dependent on Sencha ExtJS, and to run the framework locally you must have the framework installed in the public folder of your React project.

The Quick Start application is configured to run the Sencha ExtJS version 7.9.0 Trial from a remote server.

This is for demo purposes only and is not configured for Licensed development.

A licensed Commercial version of the framework can be acquired from the Sencha Support Portal directly or this can be done using the ReExt Designer VS Code Extension (preview version).

ReExt can be configured to use any commercially available version of the Sencha Ext JS framework (7x and above).

For more information, download the ReExt Designer VS Code Extension (preview version), or contact us at sales@sencha.com.

ReExt has been tested with the latest version of the Sencha Ext JS framework, which at the time of this publishing is Sencha ExtJS version 7.9.0.

To purchase a commercial version of Sencha ExtJS, go to <a target="_blank" href="https://store.sencha.com/">https://store.sencha.com/</a>.


### Aquiring a Licensed Version of Ext JS

To acquire a licensed version of Ext JS: 

1. login to the Sencha Support Portal at <a target="_blank" href="https://support.sencha.com/">https://support.sencha.com/</a>

2. Click the downloads menu on the left side of the page - the Sencha Downloads page will display.

3. Click the 'Downloads' button - the 7.9.0.zip file will be downloaded (which is the latest version of Ext JS).

4. In the file system where the 7.9.0.zip file has been downloaded, unzip the file.  

5. A folder named ext-7.9.0 will be created

6. Copy thje ext-7.9.0 folder to the public folder of your React application

7. In the <ReExtProvider/> component in your React application, change to the following ("location": "local")

```js
import { ReExtProvider } from '@sencha/reext';

var reactroot = ReactDOM.createRoot(document.getElementById('root'));

var ReExtData = {
  "sdkversion": "7.9.0",
  "toolkit": "classic",
  "theme": "classic",
  "rtl": false,
  "locale": "en",
  "debug": false,
  "urlbase": "./",
  "location": "local"
}
var trialkey = 'trial key from email goes here...'
reactroot.render(
  <ReExtProvider splash={true} ReExtData={ReExtData} reextkey={trialkey}>
    <App />
  </ReExtProvider>
)
```


## ReExt Trial Key

To get a ReExt Trial Key, fill in the form on the dialog that appears when you run a ReExt application.  You will be emailed the ReExt Trial Key.

## React Version

ReExt has been tested with the latest version of React, which at the time of this publishing is React 18.3.1.

## Usage - ReExtProvider:

In main.jsx or index.js you use the ReExtProvider component:

```js
import { ReExtProvider } from '@sencha/reext';

var reactroot = ReactDOM.createRoot(document.getElementById('root'));

var ReExtData = {
  "sdkversion": "7.9.0",
  "toolkit": "classic",
  "theme": "classic",
  "rtl": false,
  "locale": "en",
  "debug": false,
  "urlbase": "./",
  "location": "remote"
}
var trialkey = 'trial key from email goes here...'
reactroot.render(
  <ReExtProvider splash={true} ReExtData={ReExtData} reextkey={trialkey}>
    <App />
  </ReExtProvider>
)
```

## Usage - Example Code:

```js
import React, { useState, useRef } from 'react';
import ReExt from '@sencha/reext';

const App=()=>{
 const [labelcmp, setLabelCmp] = useState(null);
 const labelcmpRef = useRef();
 labelcmpRef.current = labelcmp;
 const [labeltext, setLabelText] = useState('initial text');
 const [row, setRow] = useState(null);

 return (
  <div style={{
   boxSizing:'border-box',height:'100%',
   display:'flex',flexDirection:'column'
  }}>
   <ReExt xtype='logo'/>
   <div style={{display:'flex'}}>
    <ReExt xtype='button'
     config={{text:'click me',width:100,ariaLabel:'demobutton'}}
     onTap={()=>{
      labelcmpRef.current.setHtml('set using method call');
      setLabelText('set using state');
     }}
    />
   </div>
   <ReExt xtype='grid'
    style={{height:300}}
    config={{
     title: 'grid',
     columns: [
      {text:'Name',dataIndex:'name',width:200},
      {text:'Email',dataIndex:'email',flex:1},
      {text:'Phone',dataIndex:'phone',width:200}
     ],
     data: [
      {name:'Lisa',email:'lisa@simpsons.com',phone:'555-111-1224'},
      {name:'Bart',email:'bart@simpsons.com',phone:'555-222-1234'},
      {name:'Homer',email:'homer@simpsons.com',phone:'555-333-1244'},
      {name:'Marge',email:'marge@simpsons.com',phone:'555-444-1254'}
     ]
    }}
    onSelect={(grid, selected)=>{
     var row = selected[0].data
     setRow(row)
     var rowString = JSON.stringify(row)
     labelcmpRef.current.setHtml(rowString)
     setLabelText(rowString)
    }}
   />
   <div style={{flex:1,padding:20,border:'1px solid gray'}}>
    {row !== null &&
     <>
     <ReExt xtype='label' config={{html: `name: ${row.name}`}}/>
     <ReExt xtype='label' config={{html: `email: ${row.email}`}}/>
     <ReExt xtype='label' config={{html: `phone: ${row.phone}`}}/>
     </>
    }
   </div>
   <div style={{flex:1,padding:20,border:'1px solid gray'}}>
    <ReExt xtype='label'
     config={{html:'initial text'}}
     ready={(cmp)=>{
      setLabelCmp(cmp)
     }}
    />
    <ReExt xtype='label'
     config={{html:labeltext}}
    />
   </div>
  </div>
 )
}
export default App
```

## Run the  React application

### Create React App 

```bash
npm start
```

### Vite

```bash
npx vite --open
```

# Documentation

## ReExt and child components

With ReExt panels and containers, child components are supported and are used like this: 

```js
<ReExt xtype='panel' config={{ title: 'parent' }}>
  <ReExt xtype='button' config={{ text: 'child1' }} />
  <ReExt xtype='button' config={{ text: 'child2' }} />
</ReExt>
```
If no 'layout' prop is specified for the parent component, this default is used:

```js
layout: { type: 'vbox', align: 'stretch' }
```

If no 'childFlex' prop is used for the children, this default is used:

```js
childFlex={1}
```

So the defaults would be the same as this:

```js
<ReExt xtype="panel" config={{title:'parent',layout:{type:'vbox',align:'stretch'}}}>
  <ReExt childFlex={1} xtype='button' config={{ text: 'child1' }} />
  <ReExt childFlex={1} xtype='button' config={{ text: 'child2' }} />
</ReExt>
```

You can change the layout and flex to new values, for example:

```js
<ReExt xtype="panel" config={{title:'parent',layout:{type:'hbox',align:'stretch'}}}>
  <ReExt childFlex={1} xtype='button' config={{ text: 'child1' }} />
  <ReExt childFlex={1} xtype='button' config={{ text: 'child2' }} />
</ReExt>
```

childHeight (for vbox) or childWidth (for hbox) can be used instead of childFlex

```js
<ReExt xtype="panel" config={{title:'parent',layout:{type:'hbox',align:'stretch'}}}>
  <ReExt childWidth={50} xtype='button' config={{ text: 'child1' }} />
  <ReExt childFlex={1} xtype='button' config={{ text: 'child2' }} />
</ReExt>
```


## The ReExtProvider component

The ReExtProvider component has the following:

```js
interface ReExtProviderProps {
  ReExtData: ReExtData;
  reextkey?: string;
  splash?: string;
  children?: React.ReactNode;
}
```

ReExtData has the following:

```js
interface ReExtData {
  sdkversion: string;
  toolkit: string;
  theme: string;
  packages?: Packages;
  rtl?: boolean;
  locale?: string;
  debug?: boolean;
  urlbase: string;
  location: string;
  overrides?: boolean;
  customfolder?: string;
  customfiles?: Array<string>;
} 
```

customfolder: a path in the public folder where custom Ext JS component files will be loaded

customfiles: an array of strings of the names of the custom Ext JS component files

Packages has the following:

## The ReExtProvider component

## The ReExt Component

The ReExt component has 5 static props (only xtype is required) and any number of optional event props

Example of a ReExt button

```js
<ReExt xtype='button'
 rootClass='okbutton'
 rootStyle={{color:'green'}}
 references={false}
 childHeight={50}
 childWidth={200}
 childFlex={2}
 childRegion={'north'}
 childSplit={true}
 config={{text:'OK'}}
 ready={(cmp)=>{console.log(cmp)}}
 onTap={(button,e,eOpts)=>{console.log(`${button.text}`)}}
/>
```

In the above example, here are the links to the ExtJS documentation

* <a target="_blank" href="https://docs.sencha.com/extjs/7.9.0/modern/Ext.Button.html">xtype='button'</a>
* <a target="_blank" href="https://docs.sencha.com/extjs/7.9.0/modern/Ext.Button.html#cfg-text">config={{text:'click me'}}</a>
* <a target="_blank" href="https://docs.sencha.com/extjs/7.9.0/modern/Ext.Button.html#event-tap">onTap={(button,e,eOpts)=>{console.log(`${button.text}`)}}/a>

More details on the  the ReExt props

 * xtype (required): the xtype of the component as defined in <a target="_blank" href="https://docs.sencha.com/extjs/7.9.0/">https://docs.sencha.com/extjs/7.9.0/</a>
 * rootClass (optional): the css className string to be added to the root React div of the underlying Ext JS component (each underlying ExtJS component is wrapped with a root React div)
 * rootStyle (optional): the css style to be added to the root React div of the underlying Ext JS component (each underlying ExtJS component is wrapped with a root React div)
 * references (optional): used if using an Ext JS reference
 * childHeight (optional): used for border layout
 * childWidth (optional): used for border layout
 * childFlex (optional): used for border layout
 * childRegion (optional): used for border layout
 * childSplit (optional): used for border layout
 * config (optional): a JavaScript object with the configs of the component as defined in <a target="_blank" href="https://docs.sencha.com/extjs/7.9.0/">https://docs.sencha.com/extjs/7.9.0/</a>
 * ready(cmp) (optional): the event that is fired by the ReExt component when the underlying ExtJS component is created.  The value of this config is a function in your React hosting component with the parameter being the created ExtJS object.
 * storeloaded(store, cmp) (optional): the event that is fired by the ReExt component when the async store associated with the component is loaded.
 * 'on' props (optional): a prop that corresponds to an event fired by the ExtJS component; follow the pattern of 'on' with the first letter of the event capitalized. (ie, the button xtype has a tap event, and the corresponding prop is onTap).  The value of this prop is a function in your React hosting component.

## Documentation for ReExt configs and events

ReExt utilizes the Ext JS framework components by providing a value for the ReExt 'xtype' prop. This prop maps to the xtype in the ExtJS documentation.  You can search for any xtype in the upper right corner of the Ext JS documentation page.

For Example ```<ReExt xtype="button"/>``` for the classic toolkit maps to this page in the Ext JS 7.8 documentation :

<a target="_blank" href="https://docs.sencha.com/extjs/7.9.0/classic/Ext.button.Button.html">https://docs.sencha.com/extjs/7.9.0/classic/Ext.button.Button.html</a>


#### configs

The configs tab shows all values valid for the ReExt 'config' prop. ```<ReExt xtype="button" config={}/>```

#### events 

The events tab shows all events - to use, prepend 'on' and capitalize the first letter of the event name:

For example, this page is for the ReExt 'onClick' event ```<ReExt xtype="button" onClick={()=>{}}/>```

<a target="_blank" href="https://docs.sencha.com/extjs/7.9.0/classic/Ext.button.Button.html#event-click">https://docs.sencha.com/extjs/7.9.0/classic/Ext.button.Button.html#event-click</a>

#### methods

The methods tab shows all methods that can be called with the cmp parameter of the ReExt ready event ```<ReExt xtype="button" ready={(cmp)=>{cmp.setText('new text')}}/>```




<a target="_blank" href="https://docs.sencha.com/extjs/7.9.0/classic/Ext.button.Button.html#method-setText">https://docs.sencha.com/extjs/7.9.0/classic/Ext.button.Button.html#method-setText</a>


## Using ExtJS Overrides

To use Ext JS overrides, create a classicoverrides.js or modernoverrides.js in the root of the sdk folder in public (for example: /public/ext-7.9.0/classicoverrides.js)

Include all overrides classes in this file.

also includes overrides=true in ReExtData object (will only work for location = 'local')

```js
var ReExtData = {
    "sdkversion": "7.9.0",
    "toolkit": "classic",
    "theme": "classic",
    "rtl": false,
    "locale:" "en",
    "debug": false,
    "urlbase": "./",
    "location": "local",
    "overrides": true
}
```

## Using Custom Ext JS Components

Here is an example of using custom Ext JS components in your React / Next.js app. (app/layout.js)

```bash
'use client';
import { ReExtProvider } from '@sencha/reext';

export default function RootLayout({ children }) {
  var ReExtData = { 
  sdkversion: '7.9.0', 
  toolkit: 'classic', 
  theme: 'classic', 
  urlbase: './', 
  location: 'local' 
  };
  return (
    <html lang="en">
      <body>
          <ReExtProvider ReExtData={ReExtData}>
            {children}
          </ReExtProvider>
      </body>
    </html>
  )
}
```

Custom ExtJS components can now be added as an import.

in app/personnel/page.js
```bash
'use client'
import ReExt from '@sencha/reext';
import { useState, useEffect } from 'react';

export default function Page() {
  const [ready, setReady] = useState(false);
  useEffect(() => {
    const doAsync = async () => {
      try {
        await import('./MainList');
        setReady(true)
      } catch (e) {
        console.error('fail', e)
      }
    }
    doAsync()
  }, []);
  if (!ready) {
    return <div>Loading...</div>
  }
  else {
    return (
      <ReExt xtype='mainlist'
        style={{ flex: 2, border: '1px solid gray' }}
        onSelect={(sender, record) => 
        { 
            console.log('row selected', record[0])
        }}
      />
    )
  }
}
```

./MainList.jsx
```bash
import './PersonnelStore'
Ext.define('ReExt.view.main.List', {
    extend: 'Ext.grid.Panel',
    xtype: 'mainlist',
    store: { type: 'personnel' },
    columns: [
        { text: 'Name', dataIndex: 'name' }
    ]
})
```

./PersonnelStore.jsx
```bash
Ext.define('ReExt.store.Personnel', {
    extend: 'Ext.data.Store',
    alias: 'store.personnel',
    data: { items: [
{name:'Jean Luc',email:"jeanluc@enterprise.com",phone:"555-111-1111"},
{name:'Worf',email:"worf@enterprise.com",phone:"555-222-2222"},
{name:'Deanna',email:"deanna@enterprise.com",phone:"555-333-3333"},
{name:'Data',email:"data@enterprise.com",phone:"555-444-4444"}
    ]},
    proxy: {
     type: 'memory',
     reader: {
      type: 'json',
      rootProperty: 'items'
     }
    }
   })
```

## Version History

1.1.1 - support for Ext JS version 7.9

1.0.425 - removed more logging

1.0.420 - removed logging

1.0.415 - fixed issue with plugin dialog

1.0.410 - fixed issue with trial dialog

1.0.400 - fixed issue with ReExt Charts and Next.js

1.0.380 - fixed issue with ReExt application in an iframe

1.0.365 - support for Remix

1.0.355 - fixed a drawing issue with D3 components

1.0.350 - support hot module loading for ReExt children

1.0.345 - support for Next.js 15

1.0.340 - fixed flickering components on prop change

1.0.335 - fixed hot module loading error

1.0.330 - child component nesting bug fixes

1.0.325 - enhancements to child component nesting

1.0.315 - support for react 19

1.0.310 - fixed hot module loading issue

1.0.300 - support for custom file loading via ReExtData

# License

[Trial](https://www.sencha.com/legal/#Sencha_Ext_JS)
