UNPKG

apphouse

Version:

Component library for React that uses observable state management and theme-able components.

413 lines (326 loc) 9.72 kB
<img src="https://apphouse-storybook.web.app/assets/ApphouseLogo-bceaa309.svg" width="80px"> # Apphouse \*\* THIS LIBRARY IS UNDER DEVELOPMENT - API MIGHT CHANGE SIGNIFICANTLY \*\* - A highly efficient method for creating a React + Typescript application. - Comprehensive reusable components that can look completely different based on a configurable theme - Models that support observability for faster and more efficient rendering # Why Apphouse? - Apphouse simplifies the pain of writing themes, forms, popups, routes, feedback, etc., providing a comprehensive solution for all these essential components that you always need. Although it can handle a lot, using Apphouse is incredibly easy and user-friendly. ## Usage ### Via CLI With our cli app, you can effortlessly start an entire project in just two simple commands. Note: You must use node v18.0.0 `nvm use v18.0.0` ```sh npm install -g apphouse-cli ``` ```sh apphouse-cli your-project-name ``` ### Manually ```sh npm install apphouse ``` # Sample App ## Initialize Apphouse ```tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import { initApphouse } from 'apphouse'; const store = initApphouse(routes); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <ApphouseApp store={store} /> ); ``` ## Setting Up Routes To define specific routes for your app, simple create a route object and use it when initializing apphouse ```tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import { initApphouse, ApphouseApp } from 'apphouse'; const routes: Route[] = [ { path: '/', title: 'Home', component: () => { return <App />; } } ]; const store = initApphouse(routes); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <ApphouseApp store={store} /> ); ``` ## Setting Up Restricted Routes To define a restricted route, simply wrap it with RequiresFirebaseAuthentication component. We currently only support firebase simple email & password authentication. ```tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import { initApphouse } from 'apphouse'; const routes: Route[] = [ { path: '/', title: 'Home', component: () => { return <App />; } }, { path: '/account', title: 'Account', component: ( <RequiresFirebaseAuthentication showSignInButton showSignOutButton> <MySecretContentComponent /> </RequiresFirebaseAuthentication> ) } ]; const firebaseConfig = { apiKey: 'yourApiKey', authDomain: 'yourAuthDomain', projectId: 'yourProjectId', storageBucket: 'yourStorageBucket', messagingSenderId: 'yourMessagingSenderId', appId: 'yourAppId', measurementId: 'yourmeasurementId' }; const appStore = new AppStoreWithUser(firebaseConfig); const store = initApphouse( Routes, appStore as IStoreWithBase<AppStoreWithUser> ); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <ApphouseApp store={store} /> ); ``` ## Setting Up Routes With Params We also accept parametrized routing ```tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import { initApphouse } from 'apphouse'; const routes: Route[] = [ { path: '/account/:id', title: 'My Account', component: ({ id }) => <MyComponentForPage1 id={id} /> } ]; const store = initApphouse(routes); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <ApphouseApp store={store} /> ); ``` # Extending Theme You can change your app theme simply by extending apphouse's default theme or making your own from scratch ## Extending Theme ```tsx const BRAND_COLOR = 'rgb(236, 0, 138)'; const customTheme: ApphouseTheme = extendTheme(DarkTheme, { colors: { brand: BRAND_COLOR }, tokens: { radius: { default: '30px' } }, styles: { input: { default: { borderRadius: '30px' } } } }); const store = initApphouse(routes); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <ApphouseApp store={store} theme={customTheme} /> ); ``` Check out our demo app to get a sneak peek at how you can easily customize your theme. Simply visit [this link](https://apphouse-demo.web.app/demo) to explore and experiment with various customization options. # Personalized Not Found Page Every time a user tries to access a route that doesn't exist, they will be redirected to this page. By default Apphouse offers a standard 404 Not Found page. If you want to overwrite this page with your own 404 page add your 404 substitute when defining the route ```tsx const routes: Route[] = [ { path: '/404', title: 'Not Found', component: () => <MyCustom404Page /> } ]; ``` # Handling Alerts Anywhere inside of your components you can fire the feedback by calling the alert method ``` const { alert } = useApphouse(); alert({ type: FeedbackType.success, message: 'Success message', duration: 3000 }); ``` # Handling Forms Apphouse provides a simple and efficient way to handle very complex forms. To create a form, simply create a new instance of Form class and pass in the form configuration object. ``` const form = new Form({ id: "signUp", // you will use this id to retrieve this form later title: 'Sign Up!', fields: { // initial value for this field can be set here name: "", // initial value for this field can be set here email: "" }, validations: { email: (value: string) => { // do email validation return true if valid, return false if invalid } }, options: {}, // no options required: ['name', 'email'], // both name and email fields are required ``` ## Updating Values To update form field value simply: ``` form.setValue('name', 'updatedName'); ``` ## Accessing form values ``` const name = form.getValue('name'); ``` ## Get all form data To get current form data ``` const formData = form.data; ``` For more detailed information on how to use the form view the [form documentation](/dist/models/Form/README.md) ## Using the ApphouseForm Component If you just want to create a simple form, you can use the ApphouseForm component. Below is an example that showcases the usage of ApphouseForm component along with the Popup component to create a prompt that asks the user to enter a file name. ```tsx import { ApphouseForm } from '../components/Form/ApphouseForm'; import { Popup } from '../components/popup/Popup'; /** * A prompt that will ask the user to enter a file name */ export const PromptFilename = ApphouseComponent((props: { onConfirm }) => { const { onConfirm } = props; const onSubmission = (formData) => { onConfirm(formData.filename); }; return ( <Popup id="PromptFilenamePopup" title="Save theme as" closeOnClickOutside hideFooterActions showCloseButton width="320px" gutters={4} > <ApphouseForm id="PromptFilenameForm" fields={[ { id: 'filename', label: 'File name', type: 'text', required: true, value: undefined, styleOverwrites: { container: { width: '100%' }, input: { width: '100%' } } } ]} submitButtonLabel="Save" onSubmission={onSubmission} /> </Popup> ); }); ``` # Registering Shortcuts Apphouse can handle shortcuts anywhere in the app. You can register a global shortcut by: ```tsx app.shortcuts.register('escape', callback); ``` Similarly you can unregister a global shortcut by ```tsx app.shortcuts.unregister('escape', callback); ``` If you want to register multiple shortcuts you can do so by using the `registerAppShortcuts` method ```tsx app.shortcuts.registerAppShortcuts([ { combo: 'cmd+s', action: () => { console.log('save'); } } ]); ``` ### Shortcut in Text fields By default all keyboard events will not fire if you are inside of a textarea, input, or select to prevent undesirable things from happening. # Partial Views A partial view refers to any view within the application that does not take up the entire page or is considered "transient" within a flow. Examples of partial views include modals, tabs, popups, pickers, and so on. There are several scenarios in which you would want to set a partial view: 1. When you want to provide the option for users to quickly close the view by using the 'escape' shortcut. 2. When you want to enable users to access the view via a specific URL. 3. When you want to allow the opening and closing of the view through a trigger located in a completely different place from where the view will be displayed. ```tsx // set up route in routes and set openPartial: true { path: '/demo/:themeEditor', title: 'Theme Editor Drawer Open', openPartial: true, component: () => { return <Demo />; } } ``` ```tsx export const DrawerDemo = observer(() => { const { app } = useApphouse(); const { view } = app; return ( <React.Fragment> <div> <Button onClick={() => { // this will open the drawer below and also update the url // to be demo/themeEditor view.setView('themeEditor', true); }} > Edit theme </Button> </div> <Drawer id="theme-editor" open={view.getViewStatus('themeEditor')} onClose={() => view.setView('themeEditor', false)} > drawer content </Drawer> </React.Fragment> ); }); ```