-
Notifications
You must be signed in to change notification settings - Fork 29
Creating a Tab
Module tabs enable a module to provide an interface to users. Tabs are written in JSX and are designed for the React based frontend.
The Frontend expects each tab to provide a default export of an object conforming to the following interface:
interface Tab {
toSpawn: ((context: DebuggerContext) => boolean) | undefined;
body: ((context: DebuggerContext) => JSX.Element;)
label: string;
iconName: string;
}
Each tab should be contained within a folder with index.tsx
as its entry point, otherwise the build system will not be able to transpile the tab. For example:
.
└── src/
└── tabs/
└── Curve/
├── index.tsx // Entry point
├── components.tsx
└── ...
Here is an example of a tab object:
// Curve/index.tsx
export default {
toSpawn(context: DebuggerContext) {
return context.context?.moduleHelpers.curve.state.drawnCurves.length > 0;
},
body(context: DebuggerContext) {
const { context: { modules: { contexts: { curve: { drawnCurves } } } } } = context;
/*
* Implementation hidden...
*/
return <MultiItemDisplay elements={canvases} />;
},
label: 'Curves Tab',
iconName: 'media',
};
Here are explanations for each member of the tab interface:
If not provided, when it's corresponding bundle is loaded, the tab will also be loaded.
Otherwise, the tab will be spawned depending on the return value of the function, true to spawn the tab, false otherwise. This is where you can use module contexts to determine if the tab should be spawned.
// Will spawn the Curve tab if there are any drawn curves
const toSpawn = (context) => context.context.moduleHelpers.curve.state.drawnCurves.length > 0
If toSpawn
returns true, this function will be called to generate the content to be displayed. You can use JSX syntax for this.
const body = (context) => <div>This is the repeat tab</div>;
Similarly, the debugger context is available here, which allows you to access module contexts.
A string containing the text for the tooltip to display when the user hovers over the tab's icon.
The name of the BlueprintJS icon to use for the tab icon. You can refer to the list of icon names here
A module's tabs are always loaded after its bundle, and only if its bundle was imported by the Source program. This means that it is safe to assume that a module's context object has been initialized (if there is code in the bundle that does so)
Sometimes, it makes sense to import functions from a bundle to use with a tab.
// Repeat/index.tsx
import { foo } from '../../bundles/repeat';
export default {
body: () => {
foo(); // From bundle!
// implementation...
},
}
Both the bundle and tab build with no issue, but you may find that when attempting to spawn the tab, you receive the following error:
This is likely because you are importing something from the bundle that uses the context object. For example:
// repeat/index.ts
import { context } from 'js-slang/moduleHelpers';
export const bar = () => console.log(context);
export { foo } from './functions';
// repeat/functions.ts
export const foo () => 'do nothing';
If you import from repeat/index.ts
either by specifying import { foo } from '../../bundles/repeat';
or import { foo } from '../../bundles/repeat/index'
, because repeat/index.ts
imports the context object, the build process will mark the context as external and assume that the frontend will provide it directly to the tab (which is not the case, the context is passed via toSpawn
or body
). Since the frontend does not provide the context directly, the import will fail, thus resulting in the error.
To get around this, you can import the function directly from a file whose dependencies do not include the context import:
// import straight from functions.ts, which does not rely on the context
import { foo } from '../../bundles/repeat/functions';
export default {
body: () => {
foo(); // From bundle!
// implementation...
},
}
This way, when the tab is transpiled, the context import is never triggered.
Functions that need the context should not be imported by tabs. If you need that functionality you probably should be using the module context directly.
- Home
- Overview
- System Implementation
-
Development Guide
- Getting Started
- Repository Structure
-
Creating a New Module
- Creating a Bundle
- Creating a Tab
- Writing Documentation
- Developer Documentation (TODO)
- Build System
- Source Modules
- FAQs
Try out Source Academy here.
Check out the Source Modules generated API documentation here.