diff --git a/README-koKR.md b/README-koKR.md index 023bb7e35..748947ceb 100644 --- a/README-koKR.md +++ b/README-koKR.md @@ -50,14 +50,14 @@ yarn add @dtinsight/molecule ```javascript import React from 'react'; import ReactDOM from 'react-dom'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; -const App = () => ( - - - -); +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); ReactDOM.render(, document.getElementById('root')); ``` diff --git a/README-zhCN.md b/README-zhCN.md index be9750d1e..c75175e42 100644 --- a/README-zhCN.md +++ b/README-zhCN.md @@ -49,14 +49,14 @@ yarn add @dtinsight/molecule ```javascript import React from 'react'; import ReactDOM from 'react-dom'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; -const App = () => ( - - - -); +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); ReactDOM.render(, document.getElementById('root')); ``` diff --git a/README.md b/README.md index e5b33be73..729630119 100644 --- a/README.md +++ b/README.md @@ -49,14 +49,14 @@ yarn add @dtinsight/molecule ```javascript import React from 'react'; import ReactDOM from 'react-dom'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; -const App = () => ( - - - -); +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); ReactDOM.render(, document.getElementById('root')); ``` diff --git a/build/web/app.js b/build/web/app.js index f9adc247b..40d67cff7 100644 --- a/build/web/app.js +++ b/build/web/app.js @@ -1,14 +1,17 @@ import React, { StrictMode } from 'react'; import ReactDOM from 'react-dom'; -import { Workbench, MoleculeProvider } from '../../esm'; +import { create, Workbench } from '../../esm'; import '../../esm/style/mo.css'; -const App = () => ( +const moInstance = create({ + extensions: [], +}); + +export const App = () => moInstance.render(); + +ReactDOM.render( - - - - + + , + document.getElementById('root') ); - -ReactDOM.render(, document.getElementById('root')); diff --git a/src/controller/settings.tsx b/src/controller/settings.tsx index a2d373c2c..52db6cf9d 100644 --- a/src/controller/settings.tsx +++ b/src/controller/settings.tsx @@ -69,6 +69,7 @@ export class SettingsController id: SETTING_ID!, value: next, render(value) { + /* istanbul ignore next */ return ; }, }; diff --git a/src/extensions/__tests__/folderTree.test.tsx b/src/extensions/__tests__/folderTree.test.tsx index e989f48a1..08df008d4 100644 --- a/src/extensions/__tests__/folderTree.test.tsx +++ b/src/extensions/__tests__/folderTree.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import '@testing-library/jest-dom'; -import molecule, { MoleculeProvider, Workbench } from 'mo'; +import molecule, { create, Workbench } from 'mo'; import { cleanup, fireEvent, render } from '@testing-library/react'; import type { ITreeNodeItemProps } from 'mo/components'; import type { IEditorTab } from 'mo/model/workbench/editor'; @@ -35,11 +35,10 @@ describe('folderTree extension', () => { afterEach(cleanup); test('Execute the listener function of onUpdateFileName', () => { - const { getByRole } = render( - - - - ); + const container = create({ + extensions: [], + }).render(); + const { getByRole } = render(container); molecule.folderTree.setState({ folderTree: { data: mockTreeData } }); expect(molecule.folderTree.getState().folderTree?.data).toEqual( diff --git a/src/i18n/__tests__/localeService.test.ts b/src/i18n/__tests__/localeService.test.ts index 048de2f45..5ac84d49c 100644 --- a/src/i18n/__tests__/localeService.test.ts +++ b/src/i18n/__tests__/localeService.test.ts @@ -1,15 +1,27 @@ import 'reflect-metadata'; import { container } from 'tsyringe'; +import { expectLoggerErrorToBeCalled } from '@test/utils'; +import { ILocale } from '../localization'; import { LocaleService } from '..'; -import { BuiltInLocales, BuiltInDefault, ILocale } from '../localization'; describe('The Locale Service', () => { - const TestLocale = { + const TestLocale: ILocale = { id: 'test', source: new Map(), name: 'test', }; + // LocaleService is support to add an object source but ILocale is banned + const TestEnLocale: ILocale = { + id: 'en', + name: 'English', + source: { + // @ts-ignore + 'molecule.welcome': 'Welcome to Molecule', + 'test.id': 'hello ${i}', + }, + }; + afterEach(() => { localStorage.clear(); }); @@ -19,82 +31,72 @@ describe('The Locale Service', () => { expect(localeService).not.toBeUndefined(); }); - test('Reset the LocaleService', () => { + test('Initialize the locales with testLocale', () => { const localeService = new LocaleService(); - expect(localeService.getCurrentLocale()!.id).toBe(BuiltInDefault.id); - localeService.reset(); - expect(localeService.getCurrentLocale()!.id).toBe(BuiltInDefault.id); - }); + localeService.initialize([TestLocale], TestLocale.id); - test('Get default Locale', () => { - const localeService = new LocaleService(); - const defaultLocale = localeService.getDefaultLocale(); - expect(defaultLocale).toEqual(BuiltInDefault); - }); + expect(localeService.getCurrentLocale()?.id).toEqual(TestLocale.id); + expect(localeService.getLocales().length).toBe(1); - test('Get default Locales', () => { - const localeService = new LocaleService(); - const defaultLocale = localeService.getDefaultLocales(); - expect(defaultLocale).toEqual(BuiltInLocales); + localeService.reset(); + expectLoggerErrorToBeCalled(() => { + // @ts-ignore + localeService.initialize([TestEnLocale, TestLocale]); + }); }); - test('The size of Built-in Locales should be 3', () => { + test('Reset the LocaleService', () => { const localeService = new LocaleService(); - const locales = localeService.getLocales(); - expect(locales.length).toBe(3); - }); + expect(localeService.getCurrentLocale()).toBeUndefined(); - test('Initialize the locales', () => { - const localeService = new LocaleService(); - localeService.initialize([TestLocale]); - expect(localeService.getCurrentLocale()!.id).toEqual( - localeService.getDefaultLocale().id - ); - expect(localeService.getLocales().length).toBe(4); - localeService.initialize([], 'test'); - expect(localeService.getCurrentLocale()!.id).toEqual(BuiltInDefault.id); - // Clear the cached locale value - localStorage.clear(); - localeService.initialize([], 'test'); - expect(localeService.getCurrentLocale()!.id).toEqual('test'); - localeService.initialize([]); - // Get from the localStorage cache - expect(localeService.getCurrentLocale()!.id).toEqual('test'); + localeService.initialize([TestLocale], TestLocale.id); + expect(localeService.getCurrentLocale()).toEqual(TestLocale); + + localeService.reset(); + expect(localeService.getCurrentLocale()).toBeUndefined(); }); test('Get/Set current locale', () => { const localeService = new LocaleService(); - (localeService as any)._current = undefined; - expect(localeService.getCurrentLocale()).toBe(BuiltInDefault); - localeService.addLocales([TestLocale]); - localeService.setCurrentLocale(TestLocale.id); - expect(localeService.getCurrentLocale()!.id).toEqual(TestLocale.id); + expect(localeService.getCurrentLocale()).toBeUndefined(); + + localeService.initialize([TestLocale, TestEnLocale], TestLocale.id); + + expect(localeService.getCurrentLocale()?.id).toEqual(TestLocale.id); + localeService.setCurrentLocale(TestEnLocale.id); + expect(localeService.getCurrentLocale()?.id).toEqual(TestEnLocale.id); + // set an unknow locale will fail expect(localeService.setCurrentLocale('unknown')).toEqual(false); }); test('Add locales', () => { const localeService = new LocaleService(); - expect(localeService.getLocales().length).toBe(3); + expect(localeService.getLocales().length).toBe(0); + localeService.addLocales([TestLocale]); - expect(localeService.getLocales().length).toBe(4); + expect(localeService.getLocales().length).toBe(1); + localeService.addLocales([]); - expect(localeService.getLocales().length).toBe(4); + expect(localeService.getLocales().length).toBe(1); + // Add an existed locale localeService.addLocales([TestLocale]); - expect(localeService.getLocales().length).toBe(4); + expect(localeService.getLocales().length).toBe(1); }); test('Add an locale inherit the en', () => { const localeService = new LocaleService(); + localeService.initialize([TestEnLocale], TestEnLocale.id); + expect(TestLocale.source.size).toBe(0); - (TestLocale as ILocale).inherit = 'en'; + TestLocale.inherit = 'en'; localeService.addLocales([TestLocale]); expect(localeService.getLocale(TestLocale.id)?.source.size).not.toBe(0); // Inherit an not exist locale localeService.removeLocale(TestLocale.id); - (TestLocale as ILocale).inherit = 'unknown'; + TestLocale.inherit = 'unknown'; localeService.addLocales([TestLocale]); expect(localeService.getLocale(TestLocale.id)?.source.size).toBe(0); }); @@ -110,38 +112,47 @@ describe('The Locale Service', () => { test('Remove a locale', () => { const localeService = new LocaleService(); - localeService.addLocales([TestLocale]); + localeService.initialize([TestLocale, TestEnLocale], TestLocale.id); expect(localeService.getLocale(TestLocale.id)?.id).toEqual( TestLocale.id ); - localeService.removeLocale(TestLocale.id); + + const removedLocale = localeService.removeLocale(TestLocale.id); expect(localeService.getLocale(TestLocale.id)).toBeUndefined(); + expect(removedLocale).toEqual(TestLocale); + localeService.addLocales([TestLocale]); localeService.setCurrentLocale(TestLocale.id); //Remove the current locale - expect(localeService.getCurrentLocale()!.id).toEqual(TestLocale.id); + expect(localeService.getCurrentLocale()?.id).toEqual(TestLocale.id); localeService.removeLocale(TestLocale.id); - expect(localeService.getCurrentLocale()!.id).toEqual( - localeService.getDefaultLocale().id - ); + expect(localeService.getCurrentLocale()!.id).toEqual(TestEnLocale.id); // Remove an undefined expect(localeService.removeLocale(TestLocale.id)); + + expect(localeService.getLocales().length).toBe(1); + // The last one couldn't be removed + expect(localeService.removeLocale(TestEnLocale.id)).toBeFalsy(); }); test('Listen to the current locale change event', () => { - const target = 'zh-CN'; const localeService = new LocaleService(); const fn = jest.fn(); localeService.onChange(fn); - localeService.setCurrentLocale(target); + + localeService.initialize([TestLocale, TestEnLocale], TestLocale.id); + localeService.setCurrentLocale(TestEnLocale.id); + expect(fn).toBeCalledTimes(1); - expect(localeService.getCurrentLocale()!.id).toEqual(target); + expect(localeService.getCurrentLocale()!.id).toEqual(TestEnLocale.id); }); test('Localize the source key', () => { const localeService = new LocaleService(); + + localeService.initialize([TestLocale, TestEnLocale], TestEnLocale.id); let res = localeService.localize('test'); expect(res).toEqual(''); @@ -154,19 +165,10 @@ describe('The Locale Service', () => { res = localeService.localize('molecule.welcome', 'default'); expect(res).toEqual('Welcome to Molecule'); - const map = new Map(); - map.set('test.id', 'hello ${i}'); - const mockData = { - id: 'mock', - name: 'mock', - source: map, - }; - localeService.addLocales([mockData]); - localeService.setCurrentLocale(mockData.id); res = localeService.localize('test.id', '', 'world'); expect(res).toEqual('hello world'); - (localeService as any)._current = null; + localeService.setCurrentLocale(TestLocale.id); res = localeService.localize('molecule.welcome', 'default'); expect(res).toEqual('default'); }); diff --git a/src/i18n/localeService.ts b/src/i18n/localeService.ts index eaf529203..7c66ef47a 100644 --- a/src/i18n/localeService.ts +++ b/src/i18n/localeService.ts @@ -1,23 +1,17 @@ import { APP_PREFIX } from 'mo/common/const'; -import { - ILocale, - LocalizationEvent, - BuiltInLocales, - BuiltInDefault, -} from 'mo/i18n/localization'; +import logger from 'mo/common/logger'; +import { ILocale, LocalizationEvent } from 'mo/i18n/localization'; import { Component } from 'mo/react'; import { singleton } from 'tsyringe'; export interface ILocaleService { /** - * Initialize the locales data, and the default current locale language, - * this method first uses the cached `locale` in localStorage, then use the - * localeId argument, if both the values are null, finally apply the built-in BuiltInZhCN + * Initialize the locales data, and the current locale language, * @param locales * @param localeId */ - initialize(locales: ILocale[], localeId?: string): void; + initialize(locales: ILocale[], localeId: string): void; /** * Set the current locale language by id * @param id @@ -36,14 +30,6 @@ export interface ILocaleService { * @param id */ getLocale(id: string): ILocale | undefined; - /** - * Get the default locale - */ - getDefaultLocale(): ILocale; - /** - * Get the default locales; - */ - getDefaultLocales(): ILocale[]; /** * Add multiple local languages * @param locales @@ -91,15 +77,11 @@ export class LocaleService extends Component implements ILocaleService { state = {}; private static LOCALIZE_REPLACED_WORD = '${i}'; - private _locales: Map = new Map(); + private _locales = new Map(); private _current: ILocale | undefined; constructor() { super(); - /** - * TODO: It will then be removed accordingly - */ - this.initialize(BuiltInLocales); } public reset(): void { @@ -108,37 +90,21 @@ export class LocaleService extends Component implements ILocaleService { this._locales.clear(); } - public getDefaultLocale(): ILocale { - return Object.assign({}, BuiltInDefault); - } - - public getDefaultLocales(): ILocale[] { - return BuiltInLocales.concat(); - } - public getLocales(): ILocale[] { return Array.from(this._locales.values()); } - public initialize(locales: ILocale[], localeId?: string) { + public initialize(locales: ILocale[], localeId: string) { this.addLocales(locales); - let finalLocale = BuiltInDefault!.id; - if (localeId) { - finalLocale = localeId; - } - const cachedLocaleId = localStorage.getItem(STORE_KEY); - if (cachedLocaleId) { - finalLocale = cachedLocaleId; + if (this._locales.get(localeId)) { + this._current = this._locales.get(localeId); + } else { + logger.error(`Cannot initialize the locale with ${localeId}`); } - - this.setCurrentLocale(finalLocale); } public getCurrentLocale(): ILocale | undefined { - return ( - (this._current && Object.assign({}, this._current)) || - BuiltInDefault - ); + return this._current && Object.assign({}, this._current); } public getLocale(id: string | null): ILocale | undefined { @@ -149,8 +115,14 @@ export class LocaleService extends Component implements ILocaleService { public removeLocale(id: string): ILocale | undefined { const locale = this._locales.get(id); if (locale !== undefined) { + if (this._locales.size === 1) { + logger.error( + "You can't remove this Locale because there must have one locale at least" + ); + return undefined; + } if (this._current && this._current.id === locale.id) { - this._current = this.getDefaultLocale(); + this._current = this._locales.values().next().value; } this._locales.delete(id); return locale; diff --git a/src/index.ts b/src/index.ts index 00d2859a1..fe603c50e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,7 @@ import * as molecule from './molecule.api'; -export { MoleculeProvider } from 'mo/provider'; -export type { IMoleculeProps } from 'mo/provider'; - export { Workbench } from 'mo/workbench/workbench'; -export { molecule }; +export { create } from 'mo/provider'; +export { molecule }; export default molecule; diff --git a/src/model/settings.ts b/src/model/settings.ts index 88b4701e6..1d211d12e 100644 --- a/src/model/settings.ts +++ b/src/model/settings.ts @@ -20,9 +20,9 @@ export interface ISettings { export class SettingsModel implements ISettings { colorTheme: string; editor: IEditorOptions; - locale: string; + locale?: string; - constructor(colorTheme: string, editor: IEditorOptions, locale: string) { + constructor(colorTheme: string, editor: IEditorOptions, locale?: string) { this.colorTheme = colorTheme; this.editor = editor; this.locale = locale; diff --git a/src/molecule.api.ts b/src/molecule.api.ts index 6d43aeaf6..420d7cf02 100644 --- a/src/molecule.api.ts +++ b/src/molecule.api.ts @@ -5,7 +5,6 @@ export * as event from 'mo/common/event'; export * as react from 'mo/react'; export * as component from 'mo/components'; export * as monaco from 'mo/monaco/api'; - export * from 'mo/i18n'; export * from 'mo/workbench'; export * from 'mo/services'; @@ -49,6 +48,7 @@ import { } from 'mo/services'; import { ILocaleService, LocaleService } from 'mo/i18n'; +import { IMonacoService, MonacoService } from './monaco/monacoService'; /** * The locale service @@ -103,3 +103,5 @@ export const builtin = container.resolve(BuiltinService); * The Extension service */ export const extension = container.resolve(ExtensionService); + +export const monacoService = container.resolve(MonacoService); diff --git a/src/provider/__tests__/__snapshots__/molecule.test.tsx.snap b/src/provider/__tests__/__snapshots__/molecule.test.tsx.snap index ba13c3332..4615822b3 100644 --- a/src/provider/__tests__/__snapshots__/molecule.test.tsx.snap +++ b/src/provider/__tests__/__snapshots__/molecule.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Test MoleculeProvider Match The MoleculeProvider snapshot 1`] = ` +exports[`The molecule Provider Match the Snapshot 1`] = `
{ - let original; - beforeEach(() => { - original = HTMLElement.prototype.getBoundingClientRect; - // @ts-ignore - HTMLElement.prototype.getBoundingClientRect = () => ({ - width: 500, - height: 0, - }); - - // Reset the extensions loaded state - molecule.extension.setLoaded(false); - }); +describe('The create function', () => { afterEach(() => { - HTMLElement.prototype.getBoundingClientRect = original; + clearInstance(); }); - test('Match The MoleculeProvider snapshot', () => { - const { asFragment } = render( - - - - ); - expect(asFragment()).toMatchSnapshot(); + test('Should create an instance', () => { + const instance = create({}); + expect(instance).toBeInstanceOf(InstanceService); }); - test('MoleculeProvider should render built-in Workbench extensions', () => { - render( - - - - ); - expect( - select('div[data-id="sidebar.explore.title"]') - ).toBeInTheDocument(); - expect( - select('div[data-id="sidebar.search.title"]') - ).toBeInTheDocument(); - expect(select('.mo-welcome')).toBeInTheDocument(); - expect( - select('div[id="statusbar.problems.title"]') - ).toBeInTheDocument(); - }); + test('Should to be a standalone', () => { + const instance = create({}); + const nextInstance = create({}); - test('MoleculeProvider load the extensions', async () => { - render( - - - - ); - await expect( - select('div[data-id="ActivityBarTestPane"]') - ).toBeInTheDocument(); + expect(instance).toBe(nextInstance); }); - test('MoleculeProvider load the locale language extensions', () => { - localStorage.removeItem('mo.localeId'); + test('Should call methods normally', () => { + create({}); + molecule.editor.isOpened(1); + }); +}); - render( - +describe('The molecule Provider', () => { + test('Match the Snapshot', () => { + const { asFragment } = render( + - + ); + expect(asFragment()).toMatchSnapshot(); }); }); diff --git a/src/provider/create.ts b/src/provider/create.ts new file mode 100644 index 000000000..beaaf8925 --- /dev/null +++ b/src/provider/create.ts @@ -0,0 +1,48 @@ +import { IExtension } from 'mo/model'; +import InstanceService from 'mo/services/instanceService'; + +export interface IConfigProps { + /** + * Molecule Extension instances, after the MoleculeProvider + * did mount, then handle it. + */ + extensions?: IExtension[]; + /** + * Specify a default locale Id, the Molecule built-in `zh-CN`, `en` two languages, and + * default locale Id is `en`. + */ + defaultLocale?: string; +} + +namespace stanalone { + let instance: InstanceService | null = null; + + /** + * Create an instance + */ + export function create(config: IConfigProps) { + if (instance) { + return instance; + } + instance = new InstanceService(config); + return instance; + } + + /** + * Do NOT call it in production, ONLY used for test cases + */ + export function clearInstance() { + instance = null; + } +} + +export default function create(config: IConfigProps) { + return stanalone.create(config); +} + +/** + * Do NOT call it in production, ONLY used for test cases + */ +export function clearInstance() { + stanalone.clearInstance(); +} diff --git a/src/provider/index.tsx b/src/provider/index.tsx index 84b9d21ac..1a07838e1 100644 --- a/src/provider/index.tsx +++ b/src/provider/index.tsx @@ -1 +1,3 @@ export * from 'mo/provider/molecule'; + +export { default as create } from './create'; diff --git a/src/provider/molecule.tsx b/src/provider/molecule.tsx index a2f0c80c6..31dc678a2 100644 --- a/src/provider/molecule.tsx +++ b/src/provider/molecule.tsx @@ -1,121 +1,19 @@ -import 'reflect-metadata'; -import { container } from 'tsyringe'; -import React, { createContext, Component } from 'react'; - -import { defaultExtensions } from 'mo/extensions'; -import { BuiltInId } from 'mo/extensions/locales-defaults'; -import { IExtension } from 'mo/model/extension'; -import { - ExtensionService, - IExtensionService, -} from 'mo/services/extensionService'; -import { LocaleService, ILocaleService } from 'mo/i18n'; -import { STORE_KEY, DEFAULT_LOCALE_ID } from 'mo/i18n/localeService'; -import { IMonacoService, MonacoService } from 'mo/monaco/monacoService'; -import { ILayoutService, LayoutService } from 'mo/services'; -import * as controllers from 'mo/controller'; -import type { Controller } from 'mo/react'; - -export interface IMoleculeProps { - /** - * Molecule Extension instances, after the MoleculeProvider - * did mount, then handle it. - */ - extensions?: IExtension[]; - /** - * Specify a default locale Id, the Molecule built-in `zh-CN`, `en` two languages, and - * default locale Id is `en`. - */ - defaultLocale?: string; -} -export interface IMoleculeState {} - -export const MoleculeCtx = createContext({}); - -export class MoleculeProvider extends Component { - private readonly extensionService!: IExtensionService; - private readonly monacoService!: IMonacoService; - private readonly layoutService!: ILayoutService; - private readonly localeService!: ILocaleService; - - constructor(props: IMoleculeProps) { - super(props); - - this.monacoService = container.resolve(MonacoService); - this.extensionService = container.resolve(ExtensionService); - this.layoutService = container.resolve(LayoutService); - this.localeService = container.resolve(LocaleService); - } - - componentDidMount() { - // The monacoService needs to be initialized each time the MoleculeProvider is loaded to - // ensure that Keybinding events can be bound to the latest dom. - this.monacoService.initWorkspace(this.layoutService.container!); - - if (!this.extensionService.isLoaded()) { - this.initialize(); - } - } - - initialize() { - const { extensions = [] } = this.props; - - const [languages, restExts] = - this.extensionService.splitLanguagesExts(extensions); - - // Molecule should load the language extensions first to - // ensure that the custom language extensions is registered in localeService - this.initLocaleExts(languages); - - // The Workbench depends on the monaco and locale extension - this.initWorkbenchUI(); - - // Init the built-in extensions - this.extensionService.load(defaultExtensions); - - // Finally, handle the rest of extensions - this.extensionService.load(restExts); - - // Mark the extensionService as loaded - this.extensionService.setLoaded(); - } - - initLocaleExts(languages: IExtension[]) { - const { defaultLocale = BuiltInId } = this.props; - - this.extensionService.load(languages); - - // And Molecule should set the correct locale before loading normal extensions in case of - // the localize method returns incorrect international text caused by incorrect current locale in the normal extensions - const currentLocale = localStorage.getItem(STORE_KEY) || defaultLocale; - const preDefaultLocale = localStorage.getItem(DEFAULT_LOCALE_ID); - let finalLocale = currentLocale; - - if (defaultLocale !== preDefaultLocale) { - finalLocale = defaultLocale; - } - - this.localeService.setCurrentLocale(finalLocale); - localStorage.setItem(DEFAULT_LOCALE_ID, defaultLocale); - } - - /** - * Register all controllers and execute the initView method automatically to inject the default value - * into the corresponding service - */ - initWorkbenchUI() { - Object.keys(controllers).forEach((key) => { - const module = controllers[key]; - const controller = container.resolve(module); - controller.initView?.(); +import React, { useLayoutEffect } from 'react'; +import create, { IConfigProps } from './create'; + +export default function Provider({ + defaultLocale, + extensions, + children, +}: IConfigProps & { children: React.ReactElement }) { + useLayoutEffect(() => { + const instance = create({ + defaultLocale, + extensions, }); - } - public render() { - return ( - - {this.props.children} - - ); - } + instance.render(children); + }, []); + + return children; } diff --git a/src/services/__tests__/__snapshots__/instanceService.test.tsx.snap b/src/services/__tests__/__snapshots__/instanceService.test.tsx.snap new file mode 100644 index 000000000..35b9d2ecd --- /dev/null +++ b/src/services/__tests__/__snapshots__/instanceService.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`The InstanceService Should support render workbench 1`] = ` + +
+ 123 +
+
+`; diff --git a/src/services/__tests__/instanceService.test.tsx b/src/services/__tests__/instanceService.test.tsx new file mode 100644 index 000000000..58e443ca0 --- /dev/null +++ b/src/services/__tests__/instanceService.test.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { defaultExtensions } from 'mo/extensions'; +import InstanceService from '../instanceService'; +import { render } from '@testing-library/react'; + +describe('The InstanceService', () => { + test('Constuctor with default config', () => { + const instance = new InstanceService({}); + const config = instance.getConfig(); + expect(config.defaultLocale).toBe('en'); + expect(config.extensions).toEqual(defaultExtensions); + }); + + test('Should init with params', () => { + const instance = new InstanceService({ + defaultLocale: 'test', + extensions: [ + { + id: 1, + name: 'test', + activate: () => {}, + dispose: () => {}, + }, + ], + }); + const config = instance.getConfig(); + expect(config.defaultLocale).toBe('test'); + expect(config.extensions).toHaveLength(defaultExtensions.length + 1); + }); + + test('Should support render workbench', () => { + const instance = new InstanceService({}); + const { asFragment } = render(instance.render(
123
)); + expect(asFragment()).toMatchSnapshot(); + }); + + test('Should support liftCycle hooks', () => { + const instance = new InstanceService({}); + const mockFn = jest.fn(); + instance.onBeforeInit(mockFn); + instance.onBeforeLoad(mockFn); + instance.render(
123
); + + expect(mockFn).toBeCalledTimes(2); + }); +}); diff --git a/src/services/__tests__/settingsService.test.ts b/src/services/__tests__/settingsService.test.ts index a61cf99fe..f69054f10 100644 --- a/src/services/__tests__/settingsService.test.ts +++ b/src/services/__tests__/settingsService.test.ts @@ -1,4 +1,3 @@ -import { BuiltInDefault } from 'mo/i18n/localization'; import { ISettings, SettingsEvent } from 'mo/model/settings'; import 'reflect-metadata'; import { SettingsService } from '../settingsService'; @@ -25,7 +24,7 @@ describe('Test the SettingsService', () => { const config = settingsService.getSettings(); expect(config.colorTheme).toEqual(BuiltInColorTheme.id); expect(config.editor).toEqual({}); - expect(config.locale).toEqual(BuiltInDefault.id); + expect(config.locale).toBeUndefined(); }); test('Append a new setting item', () => { @@ -60,7 +59,7 @@ describe('Test the SettingsService', () => { settingsService.update(expectedSettings); const config = settingsService.getSettings() as TestSetting; expect(config.colorTheme).toEqual(BuiltInColorTheme.id); - expect(config.locale).toEqual(BuiltInDefault!.id); + expect(config.locale).toBeUndefined(); expect(config.editor?.tabSize).toBe(expectedSettings.editor.tabSize); expect(config.project).toEqual(expectedSettings.project); }); diff --git a/src/services/instanceService.tsx b/src/services/instanceService.tsx new file mode 100644 index 000000000..fee90f1a5 --- /dev/null +++ b/src/services/instanceService.tsx @@ -0,0 +1,92 @@ +import { ReactElement } from 'react'; +import { ILocale } from 'mo/i18n'; +import { container } from 'tsyringe'; +import * as controllers from 'mo/controller'; +import type { Controller } from 'mo/react'; +import { defaultExtensions } from 'mo/extensions'; +import { GlobalEvent } from 'mo/common/event'; +import { IConfigProps } from 'mo/provider/create'; +import { IExtension } from 'mo/model'; +import { STORE_KEY } from 'mo/i18n/localeService'; +import molecule from 'mo'; + +interface IInstanceServiceProps { + getConfig: () => IConfigProps; + render: (dom: ReactElement) => ReactElement; + onBeforeInit: (callback: () => void) => void; + onBeforeLoad: (callback: () => void) => void; +} + +enum InstanceHookKind { + beforeInit = 'before.init', + beforeLoad = 'before.load', +} + +export default class InstanceService + extends GlobalEvent + implements IInstanceServiceProps +{ + private _config = { + extensions: defaultExtensions.concat(), + defaultLocale: 'en', + }; + + constructor(config: IConfigProps) { + super(); + if (config.defaultLocale) { + this._config.defaultLocale = config.defaultLocale; + } + + if (Array.isArray(config.extensions)) { + this._config.extensions.push(...config.extensions); + } + } + + private initialLocaleService = (languagesExts: IExtension[]) => { + const locales = languagesExts.reduce((pre, cur) => { + const languages = cur.contributes?.languages || []; + return pre.concat(languages); + }, [] as ILocale[]); + + molecule.i18n.initialize( + locales, + localStorage.getItem(STORE_KEY) || this._config.defaultLocale + ); + }; + + public getConfig: () => IConfigProps = () => { + return Object.assign({}, this._config); + }; + + public render = (workbench: ReactElement) => { + this.emit(InstanceHookKind.beforeInit); + + // get all locales including builtin and custom locales + const [languages, others] = molecule.extension.splitLanguagesExts( + this._config.extensions + ); + this.initialLocaleService(languages); + + // resolve all controllers, and call `initView` to inject initial values into services + Object.keys(controllers).forEach((key) => { + const module = controllers[key]; + const controller = container.resolve(module); + controller.initView?.(); + }); + + this.emit(InstanceHookKind.beforeLoad); + molecule.extension.load(others); + + molecule.monacoService.initWorkspace(molecule.layout.container!); + + return workbench; + }; + + public onBeforeInit = (callback: () => void) => { + this.subscribe(InstanceHookKind.beforeInit, callback); + }; + + public onBeforeLoad = (callback: () => void) => { + this.subscribe(InstanceHookKind.beforeLoad, callback); + }; +} diff --git a/src/services/settingsService.ts b/src/services/settingsService.ts index b4dab821f..513489682 100644 --- a/src/services/settingsService.ts +++ b/src/services/settingsService.ts @@ -107,7 +107,7 @@ export class SettingsService extends GlobalEvent implements ISettingsService { const editorOptions = this.editorService.getState().editorOptions; const theme = this.colorThemeService.getColorTheme(); const locale = this.localeService.getCurrentLocale(); - return new SettingsModel(theme.id, editorOptions!, locale!.id); + return new SettingsModel(theme.id, editorOptions!, locale?.id); } public getDefaultSettingsTab(): BuiltInSettingsTabType { diff --git a/src/workbench/notification/__tests__/localeNotification.test.tsx b/src/workbench/notification/__tests__/localeNotification.test.tsx index f0c361240..5ef4ddaa8 100644 --- a/src/workbench/notification/__tests__/localeNotification.test.tsx +++ b/src/workbench/notification/__tests__/localeNotification.test.tsx @@ -1,10 +1,18 @@ +import 'reflect-metadata'; import React from 'react'; -import { fireEvent, render } from '@testing-library/react'; import { create } from 'react-test-renderer'; +import { container } from 'tsyringe'; +import { LocaleService } from 'mo/i18n'; +import { ExtendsLocales } from 'mo/extensions/locales-defaults'; +import { fireEvent, render } from '@testing-library/react'; import LocaleNotification from '../notificationPane/localeNotification'; import '@testing-library/jest-dom'; describe('The LocaleNotification Component', () => { + // initial locales + const localeService = container.resolve(LocaleService); + localeService.initialize(ExtendsLocales.contributes!.languages!, 'en'); + test('Match Snapshot', () => { const component = create(); expect(component.toJSON()).toMatchSnapshot(); diff --git a/src/workbench/notification/__tests__/notificationPane.test.tsx b/src/workbench/notification/__tests__/notificationPane.test.tsx index 8c346d8e7..b00985092 100644 --- a/src/workbench/notification/__tests__/notificationPane.test.tsx +++ b/src/workbench/notification/__tests__/notificationPane.test.tsx @@ -1,3 +1,4 @@ +import 'reflect-metadata'; import React from 'react'; import { fireEvent, render } from '@testing-library/react'; import renderer from 'react-test-renderer'; @@ -9,8 +10,15 @@ import { } from '../notificationPane'; import { select } from 'mo/common/dom'; import { expectFnCalled } from '@test/utils'; +import { container } from 'tsyringe'; +import { ExtendsLocales } from 'mo/extensions/locales-defaults'; +import { LocaleService } from 'mo/i18n'; describe('Test NotificationPane Component', () => { + // initial locales + const localeService = container.resolve(LocaleService); + localeService.initialize(ExtendsLocales.contributes!.languages!, 'en'); + test('Match The NotificationPane snapshot', () => { const component = renderer.create( diff --git a/src/workbench/panel/__tests__/panel.test.tsx b/src/workbench/panel/__tests__/panel.test.tsx index cd5207829..d12692987 100644 --- a/src/workbench/panel/__tests__/panel.test.tsx +++ b/src/workbench/panel/__tests__/panel.test.tsx @@ -1,3 +1,4 @@ +import 'reflect-metadata'; import React from 'react'; import { render } from '@testing-library/react'; import renderer from 'react-test-renderer'; @@ -10,6 +11,9 @@ import { select } from 'mo/common/dom'; import { modules } from 'mo/services/builtinService/const'; import { cloneDeep } from 'lodash'; import Output from '../output'; +import { container } from 'tsyringe'; +import { ExtendsLocales } from 'mo/extensions/locales-defaults'; +import { LocaleService } from 'mo/i18n'; function panelMockModel(): PanelModel { const output = modules.builtInOutputPanel(); @@ -32,6 +36,10 @@ function panelMockModel(): PanelModel { } describe('Test Panel Component', () => { + // initial locales + const localeService = container.resolve(LocaleService); + localeService.initialize(ExtendsLocales.contributes!.languages!, 'en'); + test('Match the PanelView snapshot', () => { const component = renderer.create(); expect(component.toJSON()).toMatchSnapshot(); diff --git a/src/workbench/sidebar/__tests__/editorTree.test.tsx b/src/workbench/sidebar/__tests__/editorTree.test.tsx index 764a32baf..59e58a067 100644 --- a/src/workbench/sidebar/__tests__/editorTree.test.tsx +++ b/src/workbench/sidebar/__tests__/editorTree.test.tsx @@ -1,3 +1,4 @@ +import 'reflect-metadata'; import React from 'react'; import renderer from 'react-test-renderer'; import { cleanup, fireEvent, render } from '@testing-library/react'; @@ -10,6 +11,9 @@ import { editorTreeGroupClassName, } from '../explore/base'; import { constants } from 'mo/services/builtinService/const'; +import { container } from 'tsyringe'; +import { LocaleService } from 'mo/i18n'; +import { ExtendsLocales } from 'mo/extensions/locales-defaults'; const PaneEditorTree = (props: Omit) => { return ; @@ -73,6 +77,10 @@ jest.mock('react', () => { }); describe('The EditorTree Component', () => { + // initial locales + const localeService = container.resolve(LocaleService); + localeService.initialize(ExtendsLocales.contributes!.languages!, 'en'); + afterEach(cleanup); test('Match Snapshot', () => { diff --git a/stories/workbench/0-Workbench.stories.tsx b/stories/workbench/0-Workbench.stories.tsx index d1bf41059..ecb9519e2 100644 --- a/stories/workbench/0-Workbench.stories.tsx +++ b/stories/workbench/0-Workbench.stories.tsx @@ -1,19 +1,28 @@ import React from 'react'; -import { MoleculeProvider, Workbench } from 'mo'; +import molecule, { create, Workbench } from 'mo'; import 'mo/style/mo.scss'; import { customExtensions } from '../extensions'; import '../demo.scss'; -export const IDEDemo = () => ( - - - -); +const moInstance = create({ + extensions: customExtensions, + defaultLocale: 'japanese', +}); + +moInstance.onBeforeInit(() => { + molecule.builtin.inactiveModule('activityBarData'); +}); + +export const IDEDemo = () => moInstance.render(); IDEDemo.story = { name: 'Workbench', }; +if (module.hot) { + module.hot.accept(); +} + export default { title: 'Workbench', component: IDEDemo, diff --git a/website/docs/guides/builtin.md b/website/docs/guides/builtin.md new file mode 100644 index 000000000..a2588b79d --- /dev/null +++ b/website/docs/guides/builtin.md @@ -0,0 +1,66 @@ +--- +title: Builtin +sidebar_label: Builtin +--- + +For convenience, molecule has some built-in data to initialize the layout UI. But sometimes, you don't need some default data. For example now, we want to initialize an application without the default activity bar. + +In `src/App.js`, we get this code: + +```js title="src/App.js" +import React from 'react'; +import { create, Workbench } from '@dtinsight/molecule'; +import '@dtinsight/molecule/esm/style/mo.css'; + +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); + +export default App; +``` + +And we have a service named `builtinService` to manage the built-in data. So if we want to **disable** the default activity bar, we could call `molecule.builtin.inactiveModule` method to set module inactive. + +But here is a question where should I call this method? + +It's not working that you call `inactiveModule` method in extensions, because when molecule loads extensions, the layout UI already initialized with built-in data. It's obviously not working if you inactive a module after actived. + +## hooks + +For solving the timing problems, there are some hooks you can call from moInstance. + +### onBeforeInit + +The `onBeforeInit` is called before initializing. At this time, the layout UI isn't initialized yet. And the extensions are not loaded too. + +You can disable some built-in data at this hook. + +### onBeforeLoad + +The `onBeforeLoad` is called before loading extensions. At this time, the layout UI is initialized. But the extensions are not loaded. + +You can't disable some built-in data at this hook. But You can inactive some extensions at this hook. + +## Example + +```js title="src/App.js" +import React from 'react'; +import { create, Workbench } from '@dtinsight/molecule'; +import '@dtinsight/molecule/esm/style/mo.css'; + +const moInstance = create({ + extensions: [], +}); + +moInstance.onBeforeInit(() => { + molecule.builtin.inactiveModule('activityBarData'); +}); + +const App = () => moInstance.render(); + +export default App; +``` + +The module you can refer to [constants](https://github.com/DTStack/molecule/blob/main/src/services/builtinService/const.ts#L96) diff --git a/website/docs/guides/extend-color-theme.md b/website/docs/guides/extend-color-theme.md index c0bd335e9..c30e68d16 100644 --- a/website/docs/guides/extend-color-theme.md +++ b/website/docs/guides/extend-color-theme.md @@ -93,13 +93,9 @@ Finally, we add the extension package in `App.js` ```js title="src/App.js" import { OneDarkPro } from './extensions/OneDark-Pro'; -function App() { - return ( - - - - ); -} +const moInstance = create({ + extensions: [OneDarkPro], +}); ``` We can use the shortcut key `Command/Ctrl + K` to quickly access the **Color Theme Panel**. @@ -215,19 +211,15 @@ export { MyTheme }; ### Apply color theme extension -Similarly, custom theme extensions are also need to be introduced in the [MoleculeProvider](../api/classes/MoleculeProvider) component in `App.js`: +Similarly, custom theme extensions are also need to be introduced in the params of [create](../api#create) component in `App.js`: ```js title="src/App.js" import { OneDarkPro } from './extensions/OneDark-Pro'; import { MyTheme } from './extensions/MyTheme'; -function App() { - return ( - - - - ); -} +const moInstance = create({ + extensions: [OneDarkPro, MyTheme], +}); ``` Open **the color theme quick access panel**, we should be able to see the theme of `My Theme`. After selecting this theme, the **background color** of the [StatusBar](extend-workbench#statusbar) at the bottom changes to red. diff --git a/website/docs/guides/extension.md b/website/docs/guides/extension.md index 8e3d7fba7..23feeb1ba 100644 --- a/website/docs/guides/extension.md +++ b/website/docs/guides/extension.md @@ -66,33 +66,30 @@ In some cases, you may want to **disable** some built-in extensions in Molecule. ```ts import React from 'react'; -import molecule from '@dtinsight/molecule'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import molecule, { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; // All Extension instances import extensions from './extensions'; -molecule.extension.inactive((extension: IExtension) => { - // Inactive the Extension which id is ExampleExt - if (extension.id === 'ExampleExt') { - return true; - } +const moInstance = create({ + extensions, }); -function App() { - return ( - - - - ); -} +moInstance.onBeforeLoad(() => { + molecule.extension.inactive((ext) => { + // Inactive the Extension which id is ExampleExt + return extension.id === 'ExampleExt'; + }); +}); + +const App = () => moInstance.render(); export default App; ``` :::caution -It should be noted that the [inactive][inactive-url] method needs to be declared before [MoleculeProvider](../api/classes/MoleculeProvider). +It should be noted that the [inactive][inactive-url] method needs to be declared after [create](../api#create). ::: [inactive-url]: ../api/interfaces/molecule.IExtensionService#inactive diff --git a/website/docs/introduction.md b/website/docs/introduction.md index b15b8ecf6..014161d3f 100644 --- a/website/docs/introduction.md +++ b/website/docs/introduction.md @@ -52,14 +52,14 @@ yarn add @dtinsight/molecule ```javascript import React from 'react'; import ReactDOM from 'react-dom'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; -const App = () => ( - - - -); +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); ReactDOM.render(, document.getElementById('root')); ``` diff --git a/website/docs/quick-start.md b/website/docs/quick-start.md index 47dae66ee..5094f98a7 100644 --- a/website/docs/quick-start.md +++ b/website/docs/quick-start.md @@ -47,16 +47,14 @@ Open the `src/App.js` file and replace the contents of the file with the followi ```js title="src/App.js" import React from 'react'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; -function App() { - return ( - - - - ); -} +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); export default App; ``` diff --git a/website/docs/the-first-extension.md b/website/docs/the-first-extension.md index b3196ea48..edc566511 100644 --- a/website/docs/the-first-extension.md +++ b/website/docs/the-first-extension.md @@ -99,7 +99,7 @@ Pay more attention: In reality, the **data type** returned by `API.getFolderTree ### Use extension -After defining class `FirstExtension`, it is used with [MoleculeProvider][provider-url]. Here we export all **extension objects** that need to be loaded in `extensions/index.ts` by default: +After defining class `FirstExtension`, it is used with [create][create-url]. Here we export all **extension objects** that need to be loaded in `extensions/index.ts` by default: ```ts title="/src/extensions/index.ts" import { IExtension } from '@dtinsight/molecule/esm/model'; @@ -115,12 +115,12 @@ Import the `FirstExtension` object and instantiate it, and finally use `extensio ```tsx title="/src/app.tsx" import extensions from './extensions'; - - -; +const moInstance = create({ + extensions, +}); ``` -Finally, introduce `extensions` and pass in the `extensions` property of [MoleculeProvider][provider-url]. +Finally, introduce `extensions` and pass in the `extensions` property of [create][create-url]. :::info The above example is just a very simple application scenario. If you want to achieve a richer extension to [Workbench](/guides/extend-workbench.md), you can refer to [Workbench Extension Guide](./guides/extend-workbench.md) ). @@ -137,4 +137,4 @@ Please [view][demo-url] the complete source code of **First Extension** [demo-url]: https://github.com/DTStack/molecule-examples/tree/main/packages/molecule-demo/src/extensions/theFirstExtension [foldertree-url]: ./guides/extend-builtin-ui#foldertree -[provider-url]: ./api/classes/MoleculeProvider +[create-url]: ./api#create diff --git a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/builtin.md b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/builtin.md new file mode 100644 index 000000000..7153c1b82 --- /dev/null +++ b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/builtin.md @@ -0,0 +1,66 @@ +--- +title: 内置数据(Builtin) +sidebar_label: 内置数据 +--- + +为了方便用户,Molecule 会用一些内置数据来初始化 UI 界面。但是有时候,用户并不需要这些默认的数据。举个例子,如果我们这时候想要初始化一个没有默认 activity bar 数据的 Molecule 应用。 + +在 `src/App.js` 文件中, 我们有如下代码: + +```js title="src/App.js" +import React from 'react'; +import { create, Workbench } from '@dtinsight/molecule'; +import '@dtinsight/molecule/esm/style/mo.css'; + +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); + +export default App; +``` + +然后 Molecule 中有一个 `builtinService` 服务,用来管理内置数据。如果我们想要**禁用**默认的 activity bar,我们可以通过调用 `molecule.builtin.inactiveModule` 方法来禁用默认模块。 + +但是我们应该在什么地方调用这个方法呢? + +如果我们在扩展(Extensions)里调用 `inactiveModule` 方法是不生效的。因为当 molecule 加载扩展的时候,界面 UI 早已经初始化成功了,而默认数据也早已经加载到页面上。所以很明显在初始化成功之后去禁用默认模块是不生效的。 + +## hooks + +为了解决调用的时机问题,我们有一些 hooks 以供调用。 + +### onBeforeInit + +`onBeforeInit` 是在初始化之前被调用。在这个阶段,界面 UI 还未初始化,扩展(Extensions)也还没加载。 + +用户可以在这个 hook 去禁用默认数据。 + +### onBeforeLoad + +`onBeforeLoad` 是在加载扩展之前,初始化界面之后。在这个阶段,界面 UI 已经初始化,但是扩展还没加载。 + +在这个界面,用户禁用默认数据会失效。但是可以禁用扩展(Extensions)。 + +## 例子 + +```js title="src/App.js" +import React from 'react'; +import { create, Workbench } from '@dtinsight/molecule'; +import '@dtinsight/molecule/esm/style/mo.css'; + +const moInstance = create({ + extensions: [], +}); + +moInstance.onBeforeInit(() => { + molecule.builtin.inactiveModule('activityBarData'); +}); + +const App = () => moInstance.render(); + +export default App; +``` + +模块默认值可以参考 [内置数据变量](https://github.com/DTStack/molecule/blob/main/src/services/builtinService/const.ts#L96) diff --git a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/extend-color-theme.md b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/extend-color-theme.md index ccd09a3e7..b845dbfcb 100644 --- a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/extend-color-theme.md +++ b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/extend-color-theme.md @@ -93,13 +93,11 @@ export { OneDarkPro }; ```js title="src/App.js" import { OneDarkPro } from './extensions/OneDark-Pro'; -function App() { - return ( - - - - ); -} +const moInstance = create({ + extensions: [OneDarkPro], +}); + +const App = () => moInstance.render(); ``` 我们可以通过快捷键 `Command/Ctrl + K` 快速访问**「颜色主题面板」**。 @@ -216,19 +214,17 @@ export { MyTheme }; ### 应用颜色主题扩展 -同样, 自定义的主题扩展程序也是在 `App.js` 中的 [MoleculeProvider](../api/classes/MoleculeProvider) 组件中引入: +同样, 自定义的主题扩展程序也是在 `App.js` 中的 [create](../api#create) 方法中传入: ```js title="src/App.js" import { OneDarkPro } from './extensions/OneDark-Pro'; import { MyTheme } from './extensions/MyTheme'; -function App() { - return ( - - - - ); -} +const moInstance = create({ + extensions: [OneDarkPro, MyTheme], +}); + +const App = () => moInstance.render(); ``` 打开在**颜色主题快速访问面板**,我们应该就能看到 `My Theme` 的主题了。选择该主题后,底部 [StatusBar](extend-workbench#状态栏statusbar) 的**背景颜色**即变成了红色。 diff --git a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/extension.md b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/extension.md index 5ce470385..c4a600259 100644 --- a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/extension.md +++ b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/guides/extension.md @@ -67,32 +67,30 @@ molecule.extension.getExtension(extensionId); ```ts import React from 'react'; import molecule from '@dtinsight/molecule'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; // All Extension instances import extensions from './extensions'; -molecule.extension.inactive((extension: IExtension) => { - // Inactive the Extension which id is ExampleExt - if (extension.id === 'ExampleExt') { - return true; - } +const moInstance = create({ + extensions, }); -function App() { - return ( - - - - ); -} +moInstance.onBeforeLoad(() => { + molecule.extension.inactive((ext) => { + // Inactive the Extension which id is ExampleExt + return extension.id === 'ExampleExt'; + }); +}); + +const App = () => moInstance.render(); export default App; ``` :::caution -需要注意到是,[inactive][inactive-url] 方法,需要在 [MoleculeProvider](../api/classes/MoleculeProvider) 之前声明 +需要注意到是,[inactive][inactive-url] 方法,需要在 [create](../api#create) 导出的生命周期 onBeforeLoad 中使用。 ::: [inactive-url]: ../api/interfaces/molecule.IExtensionService#inactive diff --git a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/introduction.md b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/introduction.md index 9239eb3a5..48b604793 100644 --- a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/introduction.md +++ b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/introduction.md @@ -52,14 +52,14 @@ yarn add @dtinsight/molecule ```javascript import React from 'react'; import ReactDOM from 'react-dom'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; -const App = () => ( - - - -); +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); ReactDOM.render(, document.getElementById('root')); ``` diff --git a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-start.md b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-start.md index d86b63b63..312233eef 100644 --- a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-start.md +++ b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-start.md @@ -47,16 +47,14 @@ npm install @dtinsight/molecule ```js title="src/App.js" import React from 'react'; -import { MoleculeProvider, Workbench } from '@dtinsight/molecule'; +import { create, Workbench } from '@dtinsight/molecule'; import '@dtinsight/molecule/esm/style/mo.css'; -function App() { - return ( - - - - ); -} +const moInstance = create({ + extensions: [], +}); + +const App = () => moInstance.render(); export default App; ``` diff --git a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/the-first-extension.md b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/the-first-extension.md index ed1bfb9dd..1fccea467 100644 --- a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/the-first-extension.md +++ b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/the-first-extension.md @@ -98,7 +98,7 @@ export function handleSelectFolderTree() { ### 使用扩展 -定义好的 `FirstExtension` 对象,最后需要配合 [MoleculeProvider][provider-url] 来使用。这里我们默认在 `extensions/index.ts` 中导出所有需要加载的**扩展对象**: +定义好的 `FirstExtension` 对象,最后需要配合 [create][create-url] 来使用。这里我们默认在 `extensions/index.ts` 中导出所有需要加载的**扩展对象**: ```ts title="/src/extensions/index.ts" import { IExtension } from '@dtinsight/molecule/esm/model'; @@ -109,17 +109,17 @@ const extensions: IExtension[] = [new FirstExtension()]; export default extensions; ``` -导入 `FirstExtension` 对象并将其实例化,最后使用 `extensions` 导出所有**扩展对象实例**。 +导入 `FirstExtension` 对象并将其实例化,最后使用 `create` 传入所有**扩展对象实例**。 ```tsx title="/src/app.tsx" import extensions from './extensions'; - - -; +const moInstance = create({ + extensions, +}); ``` -最后,引入 `extensions` 并传入 [MoleculeProvider][provider-url] 的 `extensions` 属性。 +最后,引入 `extensions` 并传入 [create][create-url] 的 `extensions` 参数。 :::info 上例只是一个很简单的应用场景,想要实现对 [Workbench](/guides/extend-workbench.md) 更丰富的扩展,可以参考 [工作台扩展指南](./guides/extend-workbench.md)。 @@ -135,4 +135,4 @@ import extensions from './extensions'; [demo-url]: https://github.com/DTStack/molecule-examples/tree/main/packages/molecule-demo/src/extensions/theFirstExtension [foldertree-url]: ./guides/extend-builtin-ui#文件树foldertree -[provider-url]: ./api/classes/MoleculeProvider +[create-url]: ./api#create diff --git a/website/sidebars.js b/website/sidebars.js index 16155caf6..97bcb5846 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -22,6 +22,7 @@ module.exports = { label: 'Guides', collapsed: false, items: [ + 'guides/builtin', 'guides/extension', { type: 'category',