@openedx/frontend-base 1.0.0-alpha.0
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/LICENSE +661 -0
- package/README.md +112 -0
- package/config/babel/babel.config.js +28 -0
- package/config/config-helpers/createConfig.js +13 -0
- package/config/config-helpers/createLintConfig.js +16 -0
- package/config/config-helpers/getBaseConfig.js +12 -0
- package/config/defaultConfigPaths.js +35 -0
- package/config/eslint/base.eslint.config.js +113 -0
- package/config/index.js +12 -0
- package/config/jest/jest.config.js +30 -0
- package/config/tsconfig.json +32 -0
- package/config/types.js +25 -0
- package/config/webpack/common-config/all/getCodeRules.js +52 -0
- package/config/webpack/common-config/all/getFileLoaderRules.js +26 -0
- package/config/webpack/common-config/all/getIgnoreWarnings.js +14 -0
- package/config/webpack/common-config/all/getImageMinimizer.js +25 -0
- package/config/webpack/common-config/all/getStylesheetRule.js +112 -0
- package/config/webpack/common-config/dev/getDevServer.js +38 -0
- package/config/webpack/common-config/index.js +18 -0
- package/config/webpack/common-config/site/getHtmlWebpackPlugin.js +16 -0
- package/config/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.js +91 -0
- package/config/webpack/plugins/html-webpack-new-relic-plugin/index.js +7 -0
- package/config/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +3 -0
- package/config/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +108 -0
- package/config/webpack/plugins/paragon-webpack-plugin/index.js +7 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +64 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +53 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/index.js +9 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +114 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +146 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +126 -0
- package/config/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +57 -0
- package/config/webpack/types.js +2 -0
- package/config/webpack/utils/getLocalAliases.js +65 -0
- package/config/webpack/utils/getPublicPath.js +6 -0
- package/config/webpack/utils/getResolvedSiteConfigPath.js +32 -0
- package/config/webpack/utils/paragonUtils.js +138 -0
- package/config/webpack/webpack.config.build.js +80 -0
- package/config/webpack/webpack.config.dev.js +76 -0
- package/config/webpack/webpack.config.dev.shell.js +110 -0
- package/eslint.config.js +18 -0
- package/frontend-base.d.ts +8 -0
- package/index.ts +7 -0
- package/jest.config.js +7 -0
- package/openedx-frontend-base.tgz +0 -0
- package/package.json +149 -0
- package/runtime/analytics/MockAnalyticsService.js +71 -0
- package/runtime/analytics/SegmentAnalyticsService.js +243 -0
- package/runtime/analytics/index.ts +12 -0
- package/runtime/analytics/interface.js +145 -0
- package/runtime/auth/AxiosCsrfTokenService.js +60 -0
- package/runtime/auth/AxiosJwtAuthService.js +363 -0
- package/runtime/auth/AxiosJwtTokenService.js +134 -0
- package/runtime/auth/LocalForageCache.js +76 -0
- package/runtime/auth/MockAuthService.js +278 -0
- package/runtime/auth/index.ts +19 -0
- package/runtime/auth/interceptors/createCsrfTokenProviderInterceptor.js +36 -0
- package/runtime/auth/interceptors/createJwtTokenProviderInterceptor.js +37 -0
- package/runtime/auth/interceptors/createProcessAxiosRequestErrorInterceptor.js +20 -0
- package/runtime/auth/interceptors/createRetryInterceptor.js +74 -0
- package/runtime/auth/interface.js +309 -0
- package/runtime/auth/utils.js +105 -0
- package/runtime/babel.config.js +3 -0
- package/runtime/config/index.ts +295 -0
- package/runtime/constants.ts +68 -0
- package/runtime/i18n/index.js +118 -0
- package/runtime/i18n/injectIntlWithShim.jsx +48 -0
- package/runtime/i18n/lib.ts +272 -0
- package/runtime/index.ts +134 -0
- package/runtime/initialize.js +352 -0
- package/runtime/jest.config.js +32 -0
- package/runtime/logging/MockLoggingService.js +31 -0
- package/runtime/logging/NewRelicLoggingService.js +184 -0
- package/runtime/logging/index.ts +9 -0
- package/runtime/logging/interface.js +109 -0
- package/runtime/logging/types.ts +4 -0
- package/runtime/react/AuthenticatedPageRoute.jsx +43 -0
- package/runtime/react/CombinedAppProvider.tsx +46 -0
- package/runtime/react/CurrentAppContext.tsx +25 -0
- package/runtime/react/CurrentAppProvider.tsx +46 -0
- package/runtime/react/Divider.tsx +5 -0
- package/runtime/react/ErrorBoundary.jsx +47 -0
- package/runtime/react/ErrorPage.jsx +72 -0
- package/runtime/react/LoginRedirect.jsx +16 -0
- package/runtime/react/PageWrap.jsx +24 -0
- package/runtime/react/SiteContext.tsx +32 -0
- package/runtime/react/SiteProvider.tsx +78 -0
- package/runtime/react/hooks.ts +106 -0
- package/runtime/react/index.ts +19 -0
- package/runtime/routing/index.ts +1 -0
- package/runtime/routing/utils.ts +34 -0
- package/runtime/scripts/GoogleAnalyticsLoader.ts +59 -0
- package/runtime/scripts/index.ts +1 -0
- package/runtime/setupTest.js +49 -0
- package/runtime/slots/Slot.tsx +32 -0
- package/runtime/slots/SlotContext.tsx +8 -0
- package/runtime/slots/hooks.ts +35 -0
- package/runtime/slots/index.ts +7 -0
- package/runtime/slots/layout/DefaultSlotLayout.tsx +9 -0
- package/runtime/slots/layout/hooks.ts +65 -0
- package/runtime/slots/layout/index.ts +5 -0
- package/runtime/slots/layout/types.ts +45 -0
- package/runtime/slots/layout/utils.ts +14 -0
- package/runtime/slots/types.ts +23 -0
- package/runtime/slots/utils.ts +59 -0
- package/runtime/slots/widget/WidgetContext.tsx +9 -0
- package/runtime/slots/widget/WidgetProvider.tsx +30 -0
- package/runtime/slots/widget/hooks.ts +105 -0
- package/runtime/slots/widget/iframe/IFrameContentWrapper.messages.tsx +16 -0
- package/runtime/slots/widget/iframe/IFrameContentWrapper.tsx +84 -0
- package/runtime/slots/widget/iframe/IFrameWidget.tsx +59 -0
- package/runtime/slots/widget/iframe/constants.ts +19 -0
- package/runtime/slots/widget/iframe/hooks.ts +179 -0
- package/runtime/slots/widget/iframe/index.ts +6 -0
- package/runtime/slots/widget/iframe/types.ts +7 -0
- package/runtime/slots/widget/index.ts +6 -0
- package/runtime/slots/widget/types.ts +134 -0
- package/runtime/slots/widget/utils.tsx +201 -0
- package/runtime/subscriptions.ts +60 -0
- package/runtime/testing/index.ts +9 -0
- package/runtime/testing/initializeMockApp.ts +81 -0
- package/runtime/testing/mockMessages.ts +23 -0
- package/runtime/utils.js +178 -0
- package/shell/DefaultLayout.tsx +18 -0
- package/shell/DefaultMain.tsx +7 -0
- package/shell/Logo.tsx +28 -0
- package/shell/Shell.messages.ts +61 -0
- package/shell/Shell.tsx +18 -0
- package/shell/app.scss +149 -0
- package/shell/app.ts +24 -0
- package/shell/babel.config.js +3 -0
- package/shell/dev/devFooter/app.tsx +43 -0
- package/shell/dev/devFooter/index.ts +1 -0
- package/shell/dev/devHeader/BarContext.tsx +13 -0
- package/shell/dev/devHeader/BarLink.tsx +16 -0
- package/shell/dev/devHeader/BarProvider.tsx +25 -0
- package/shell/dev/devHeader/CoursesLink.tsx +16 -0
- package/shell/dev/devHeader/FooContext.tsx +13 -0
- package/shell/dev/devHeader/FooLink.tsx +16 -0
- package/shell/dev/devHeader/FooProvider.tsx +25 -0
- package/shell/dev/devHeader/app.tsx +53 -0
- package/shell/dev/devHeader/index.ts +1 -0
- package/shell/dev/devHeader/providers.tsx +11 -0
- package/shell/dev/devHome/HomePage.tsx +28 -0
- package/shell/dev/devHome/app.ts +18 -0
- package/shell/dev/devHome/i18n/index.ts +27 -0
- package/shell/dev/devHome/index.ts +1 -0
- package/shell/dev/devHome/messages.ts +11 -0
- package/shell/dev/devUser/app.tsx +24 -0
- package/shell/dev/devUser/index.ts +1 -0
- package/shell/dev/index.ts +5 -0
- package/shell/dev/slotShowcase/HorizontalSlotLayout.tsx +11 -0
- package/shell/dev/slotShowcase/LayoutWithOptions.tsx +17 -0
- package/shell/dev/slotShowcase/SlotShowcasePage.tsx +66 -0
- package/shell/dev/slotShowcase/WidgetWithOptions.tsx +11 -0
- package/shell/dev/slotShowcase/app.tsx +373 -0
- package/shell/dev/slotShowcase/index.ts +1 -0
- package/shell/footer/CenterLinks.tsx +11 -0
- package/shell/footer/CopyrightNotice.tsx +36 -0
- package/shell/footer/Footer.tsx +34 -0
- package/shell/footer/LabeledLinkColumn.tsx +19 -0
- package/shell/footer/LanguageMenu.tsx +35 -0
- package/shell/footer/LanguageMenuItem.tsx +23 -0
- package/shell/footer/LeftLinks.tsx +11 -0
- package/shell/footer/LegalNotices.tsx +17 -0
- package/shell/footer/PoweredBy.tsx +17 -0
- package/shell/footer/RevealLinks.tsx +43 -0
- package/shell/footer/RightLinks.tsx +11 -0
- package/shell/footer/app.tsx +73 -0
- package/shell/footer/data/api.ts +48 -0
- package/shell/footer/index.ts +2 -0
- package/shell/header/AuthenticatedMenu.tsx +32 -0
- package/shell/header/Header.tsx +17 -0
- package/shell/header/anonymous-menu/AnonymousMenu.tsx +14 -0
- package/shell/header/anonymous-menu/LoginButton.tsx +14 -0
- package/shell/header/anonymous-menu/RegisterButton.tsx +15 -0
- package/shell/header/app.tsx +142 -0
- package/shell/header/desktop/DesktopLayout.tsx +22 -0
- package/shell/header/desktop/PrimaryNavLinks.tsx +10 -0
- package/shell/header/desktop/SecondaryNavLinks.tsx +10 -0
- package/shell/header/index.ts +2 -0
- package/shell/header/mobile/MobileLayout.tsx +47 -0
- package/shell/header/mobile/MobileNavLinks.tsx +10 -0
- package/shell/i18n/index.ts +25 -0
- package/shell/index.ts +7 -0
- package/shell/jest.config.js +30 -0
- package/shell/menus/LinkMenuItem.tsx +64 -0
- package/shell/menus/NavDropdownMenuSlot.tsx +29 -0
- package/shell/menus/ProfileLinkMenuItem.tsx +33 -0
- package/shell/menus/data/utils.ts +19 -0
- package/shell/public/index.html +10 -0
- package/shell/router/createRouter.ts +17 -0
- package/shell/router/getAppRoutes.ts +21 -0
- package/shell/setupTest.js +48 -0
- package/shell/site.config.dev.tsx +49 -0
- package/shell/site.tsx +41 -0
- package/test-site/app.d.ts +15 -0
- package/test-site/dist/176.436443549ebb858db483.js +2 -0
- package/test-site/dist/176.436443549ebb858db483.js.map +1 -0
- package/test-site/dist/362.536eff787d2380fe246c.js +2 -0
- package/test-site/dist/362.536eff787d2380fe246c.js.map +1 -0
- package/test-site/dist/653.486966b108d224551296.js +2 -0
- package/test-site/dist/653.486966b108d224551296.js.map +1 -0
- package/test-site/dist/74e025d3fe9a7b7f8503054e2563b353.jpg +0 -0
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js +3 -0
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js.LICENSE.txt +106 -0
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js.map +1 -0
- package/test-site/dist/95ec738c0b7faac5b5c9126794446bbd.svg +4 -0
- package/test-site/dist/app.612058b36c74787759ac.css +61 -0
- package/test-site/dist/app.612058b36c74787759ac.css.map +1 -0
- package/test-site/dist/app.612058b36c74787759ac.js +2 -0
- package/test-site/dist/app.612058b36c74787759ac.js.map +1 -0
- package/test-site/dist/cb28cdb1468c915e27e5cec9af64f22f.svg +1 -0
- package/test-site/dist/index.html +1 -0
- package/test-site/dist/report.html +39 -0
- package/test-site/dist/runtime.c7aeaf7b967496cb076f.js +2 -0
- package/test-site/dist/runtime.c7aeaf7b967496cb076f.js.map +1 -0
- package/test-site/eslint.config.js +12 -0
- package/test-site/package-lock.json +19226 -0
- package/test-site/package.json +29 -0
- package/test-site/public/index.html +10 -0
- package/test-site/site.config.build.tsx +27 -0
- package/test-site/site.config.dev.tsx +27 -0
- package/test-site/src/authenticated-page/AuthenticatedPage.tsx +18 -0
- package/test-site/src/authenticated-page/i18n/index.ts +27 -0
- package/test-site/src/authenticated-page/index.tsx +28 -0
- package/test-site/src/example-page/ExamplePage.tsx +79 -0
- package/test-site/src/example-page/Image.tsx +11 -0
- package/test-site/src/example-page/ParagonPreview.jsx +66 -0
- package/test-site/src/example-page/apple.jpg +0 -0
- package/test-site/src/example-page/apple.svg +1 -0
- package/test-site/src/example-page/index.ts +16 -0
- package/test-site/src/i18n/README.md +3 -0
- package/test-site/src/i18n/messages/frontend-app-sample/ar.json +4 -0
- package/test-site/src/i18n/messages/frontend-app-sample/eo.json +1 -0
- package/test-site/src/i18n/messages/frontend-app-sample/es_419.json +4 -0
- package/test-site/src/i18n/messages/frontend-component-emptylangs/ar.json +1 -0
- package/test-site/src/i18n/messages/frontend-component-singlelang/ar.json +3 -0
- package/test-site/src/iframe-widget/IframeWidget.tsx +14 -0
- package/test-site/src/iframe-widget/index.ts +16 -0
- package/test-site/src/index.tsx +3 -0
- package/test-site/src/messages.js +11 -0
- package/test-site/src/site.scss +11 -0
- package/test-site/tsconfig.json +14 -0
- package/tools/babel/babel.config.js +27 -0
- package/tools/babel.config.js +3 -0
- package/tools/cli/README.md +29 -0
- package/tools/cli/commands/pack.ts +9 -0
- package/tools/cli/commands/release.ts +27 -0
- package/tools/cli/commands/serve.ts +43 -0
- package/tools/cli/intl-imports.ts +274 -0
- package/tools/cli/openedx.ts +101 -0
- package/tools/cli/transifex-utils.ts +75 -0
- package/tools/cli/utils/ensureConfigFilenameOption.ts +40 -0
- package/tools/cli/utils/formatter.ts +10 -0
- package/tools/cli/utils/getResolvedConfigPath.ts +23 -0
- package/tools/cli/utils/prettyPrintTitle.ts +15 -0
- package/tools/cli/utils/printUsage.ts +53 -0
- package/tools/config-helpers/createConfig.ts +8 -0
- package/tools/config-helpers/createLintConfig.ts +14 -0
- package/tools/config-helpers/getBaseConfig.ts +11 -0
- package/tools/defaultConfigPaths.ts +30 -0
- package/tools/dist/babel/babel.config.js +28 -0
- package/tools/dist/cli/commands/pack.js +14 -0
- package/tools/dist/cli/commands/release.js +28 -0
- package/tools/dist/cli/commands/serve.js +44 -0
- package/tools/dist/cli/intl-imports.js +233 -0
- package/tools/dist/cli/openedx.js +100 -0
- package/tools/dist/cli/transifex-utils.js +68 -0
- package/tools/dist/cli/utils/ensureConfigFilenameOption.js +42 -0
- package/tools/dist/cli/utils/formatter.js +10 -0
- package/tools/dist/cli/utils/getResolvedConfigPath.js +28 -0
- package/tools/dist/cli/utils/prettyPrintTitle.js +17 -0
- package/tools/dist/cli/utils/printUsage.js +48 -0
- package/tools/dist/config-helpers/createConfig.js +13 -0
- package/tools/dist/config-helpers/createLintConfig.js +16 -0
- package/tools/dist/config-helpers/getBaseConfig.js +12 -0
- package/tools/dist/defaultConfigPaths.js +35 -0
- package/tools/dist/eslint/base.eslint.config.js +113 -0
- package/tools/dist/eslint.config.js +11 -0
- package/tools/dist/index.js +12 -0
- package/tools/dist/jest/jest.config.js +30 -0
- package/tools/dist/jest.config.js +20 -0
- package/tools/dist/types.js +25 -0
- package/tools/dist/typescript/tsconfig.json +32 -0
- package/tools/dist/webpack/common-config/all/getCodeRules.js +52 -0
- package/tools/dist/webpack/common-config/all/getFileLoaderRules.js +26 -0
- package/tools/dist/webpack/common-config/all/getIgnoreWarnings.js +14 -0
- package/tools/dist/webpack/common-config/all/getImageMinimizer.js +25 -0
- package/tools/dist/webpack/common-config/all/getStylesheetRule.js +112 -0
- package/tools/dist/webpack/common-config/dev/getDevServer.js +38 -0
- package/tools/dist/webpack/common-config/index.js +18 -0
- package/tools/dist/webpack/common-config/site/getHtmlWebpackPlugin.js +16 -0
- package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.js +91 -0
- package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/index.js +7 -0
- package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +3 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +108 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/index.js +7 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +64 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +53 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/index.js +9 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +114 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +146 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +126 -0
- package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +57 -0
- package/tools/dist/webpack/types.js +2 -0
- package/tools/dist/webpack/utils/getLocalAliases.js +65 -0
- package/tools/dist/webpack/utils/getPublicPath.js +6 -0
- package/tools/dist/webpack/utils/getResolvedSiteConfigPath.js +32 -0
- package/tools/dist/webpack/utils/paragonUtils.js +138 -0
- package/tools/dist/webpack/webpack.config.build.js +80 -0
- package/tools/dist/webpack/webpack.config.dev.js +76 -0
- package/tools/dist/webpack/webpack.config.dev.shell.js +110 -0
- package/tools/eslint/base.eslint.config.js +124 -0
- package/tools/eslint/modules.d.ts +5 -0
- package/tools/eslint.config.js +15 -0
- package/tools/index.ts +3 -0
- package/tools/jest/jest.config.js +30 -0
- package/tools/jest.config.js +19 -0
- package/tools/tsconfig.json +24 -0
- package/tools/types.ts +21 -0
- package/tools/typescript/tsconfig.json +32 -0
- package/tools/webpack/common-config/README.md +15 -0
- package/tools/webpack/common-config/all/getCodeRules.ts +51 -0
- package/tools/webpack/common-config/all/getFileLoaderRules.ts +23 -0
- package/tools/webpack/common-config/all/getIgnoreWarnings.ts +13 -0
- package/tools/webpack/common-config/all/getImageMinimizer.ts +26 -0
- package/tools/webpack/common-config/all/getStylesheetRule.ts +111 -0
- package/tools/webpack/common-config/dev/getDevServer.ts +35 -0
- package/tools/webpack/common-config/index.ts +6 -0
- package/tools/webpack/common-config/site/getHtmlWebpackPlugin.ts +11 -0
- package/tools/webpack/modules.d.ts +6 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.ts +102 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/LICENSE +21 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/README.md +7 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/index.js +3 -0
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +1 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.ts +134 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/index.ts +3 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.ts +71 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.ts +72 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/index.ts +6 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.ts +131 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.ts +144 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.ts +106 -0
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.ts +54 -0
- package/tools/webpack/types.ts +69 -0
- package/tools/webpack/utils/getLocalAliases.ts +65 -0
- package/tools/webpack/utils/getPublicPath.ts +3 -0
- package/tools/webpack/utils/getResolvedSiteConfigPath.ts +28 -0
- package/tools/webpack/utils/paragonUtils.ts +152 -0
- package/tools/webpack/webpack.config.build.ts +93 -0
- package/tools/webpack/webpack.config.dev.shell.ts +122 -0
- package/tools/webpack/webpack.config.dev.ts +90 -0
- package/tsconfig.json +23 -0
- package/types.ts +99 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FunctionComponent,
|
|
3
|
+
ReactNode,
|
|
4
|
+
useEffect, useMemo, useState,
|
|
5
|
+
} from 'react';
|
|
6
|
+
|
|
7
|
+
import { useIntl } from '../../../i18n';
|
|
8
|
+
import { ErrorBoundary } from '../../../react';
|
|
9
|
+
import { IFRAME_RESIZE } from './constants';
|
|
10
|
+
import {
|
|
11
|
+
dispatchMountedEvent,
|
|
12
|
+
dispatchReadyEvent,
|
|
13
|
+
dispatchUnmountedEvent,
|
|
14
|
+
useHostEvent
|
|
15
|
+
} from './hooks';
|
|
16
|
+
import messages from './IFrameContentWrapper.messages';
|
|
17
|
+
|
|
18
|
+
const ErrorFallbackDefault = () => {
|
|
19
|
+
const { formatMessage } = useIntl();
|
|
20
|
+
return (
|
|
21
|
+
<div>
|
|
22
|
+
<h2>
|
|
23
|
+
{formatMessage(messages.unexpectedError)}
|
|
24
|
+
</h2>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
interface IFrameContentWrapperProps {
|
|
30
|
+
children: ReactNode,
|
|
31
|
+
className?: string,
|
|
32
|
+
style?: Record<string, string>,
|
|
33
|
+
ready?: boolean,
|
|
34
|
+
errorFallbackComponent?: FunctionComponent,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default function IFrameContentWrapper({
|
|
38
|
+
children, className, style = {}, ready = true, errorFallbackComponent,
|
|
39
|
+
}: IFrameContentWrapperProps) {
|
|
40
|
+
const [dimensions, setDimensions] = useState({
|
|
41
|
+
width: 0,
|
|
42
|
+
height: 0,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const finalStyle = useMemo(() => ({
|
|
46
|
+
...dimensions,
|
|
47
|
+
...style,
|
|
48
|
+
}), [dimensions, style]);
|
|
49
|
+
|
|
50
|
+
// Need to confirm: When an error is caught here, the logging will be sent to the child MFE's logging service
|
|
51
|
+
|
|
52
|
+
const ErrorFallback = errorFallbackComponent ?? ErrorFallbackDefault;
|
|
53
|
+
|
|
54
|
+
useHostEvent(IFRAME_RESIZE, ({ payload }) => {
|
|
55
|
+
setDimensions({
|
|
56
|
+
width: payload.width,
|
|
57
|
+
height: payload.height,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
dispatchMountedEvent();
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
dispatchUnmountedEvent();
|
|
66
|
+
};
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
// Ready defaults to true, but can be used to defer rendering the Plugin until certain processes
|
|
71
|
+
// have occurred or conditions have been met
|
|
72
|
+
if (ready) {
|
|
73
|
+
dispatchReadyEvent();
|
|
74
|
+
}
|
|
75
|
+
}, [ready]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className={className} style={finalStyle}>
|
|
79
|
+
<ErrorBoundary fallbackComponent={<ErrorFallback />}>
|
|
80
|
+
{children}
|
|
81
|
+
</ErrorBoundary>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Spinner } from '@openedx/paragon';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
import { dispatchPluginEvent, useElementSize, usePluginEvent } from './hooks';
|
|
6
|
+
import { IFRAME_FEATURE_POLICY, IFRAME_MOUNTED, IFRAME_READY, IFRAME_RESIZE } from './constants';
|
|
7
|
+
|
|
8
|
+
interface IFrameWidgetProps {
|
|
9
|
+
url: string,
|
|
10
|
+
title: string,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function IFrameWidget({ url, title }: IFrameWidgetProps) {
|
|
14
|
+
const [mounted, setMounted] = useState(false);
|
|
15
|
+
const [ready, setReady] = useState(false);
|
|
16
|
+
|
|
17
|
+
const { ref: iframeRef, element: iframeElement, width, height } = useElementSize();
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (mounted) {
|
|
21
|
+
dispatchPluginEvent(iframeElement as HTMLIFrameElement, {
|
|
22
|
+
type: IFRAME_RESIZE,
|
|
23
|
+
payload: {
|
|
24
|
+
width,
|
|
25
|
+
height,
|
|
26
|
+
},
|
|
27
|
+
}, url);
|
|
28
|
+
}
|
|
29
|
+
}, [iframeElement, mounted, width, height, url]);
|
|
30
|
+
|
|
31
|
+
usePluginEvent(iframeElement, IFRAME_MOUNTED, () => {
|
|
32
|
+
setMounted(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
usePluginEvent(iframeElement, IFRAME_READY, () => {
|
|
36
|
+
setReady(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const fallback = (
|
|
40
|
+
<Spinner animation="border" variant="light" screenReaderText="Loading" />
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<iframe
|
|
46
|
+
ref={iframeRef}
|
|
47
|
+
title={title}
|
|
48
|
+
src={url}
|
|
49
|
+
allow={IFRAME_FEATURE_POLICY}
|
|
50
|
+
referrerPolicy="origin" // The sent referrer will be limited to the origin of the referring page: its scheme, host, and port.
|
|
51
|
+
className={classNames(
|
|
52
|
+
'border border-0 w-100',
|
|
53
|
+
{ 'd-none': !ready },
|
|
54
|
+
)}
|
|
55
|
+
/>
|
|
56
|
+
{!ready && fallback}
|
|
57
|
+
</>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// IFrame lifecycle events
|
|
2
|
+
export const IFRAME_MOUNTED = 'IFRAME_MOUNTED';
|
|
3
|
+
export const IFRAME_UNMOUNTED = 'IFRAME_UNMOUNTED';
|
|
4
|
+
export const IFRAME_READY = 'IFRAME_READY';
|
|
5
|
+
export const IFRAME_RESIZE = 'IFRAME_RESIZE';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Feature policy for iframe, allowing access to certain courseware-related media.
|
|
9
|
+
*
|
|
10
|
+
* We must use the wildcard (*) origin for each feature, as courseware content
|
|
11
|
+
* may be embedded in external iframes. Notably, xblock-lti-consumer is a popular
|
|
12
|
+
* block that iframes external course content.
|
|
13
|
+
|
|
14
|
+
* This policy was selected in conference with the edX Security Working Group.
|
|
15
|
+
* Changes to it should be vetted by them (security@edx.org).
|
|
16
|
+
*/
|
|
17
|
+
export const IFRAME_FEATURE_POLICY = (
|
|
18
|
+
'fullscreen; microphone *; camera *; midi *; geolocation *; encrypted-media *'
|
|
19
|
+
);
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks file for functions that handle the communication between a Plugin and its Host
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
LegacyRef,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useLayoutEffect,
|
|
10
|
+
useMemo,
|
|
11
|
+
useRef,
|
|
12
|
+
useState
|
|
13
|
+
} from 'react';
|
|
14
|
+
|
|
15
|
+
import { IFRAME_MOUNTED, IFRAME_READY, IFRAME_UNMOUNTED } from './constants';
|
|
16
|
+
import { MessageEventCallback } from './types';
|
|
17
|
+
|
|
18
|
+
/* Listening for events */
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Dynamically add an event listener to the provided source window.
|
|
22
|
+
* The source window can be the global parent (ie. the "window" object in the browser)
|
|
23
|
+
* or it can be the content window of an individual element (ie. iFrame plugin container)
|
|
24
|
+
*/
|
|
25
|
+
export function useMessageEvent(srcWindow: Window | null, type: string, callback: MessageEventCallback) {
|
|
26
|
+
// useLayoutEffect is called before the browser repaints the screen
|
|
27
|
+
useLayoutEffect(() => {
|
|
28
|
+
// Create a listener callback function
|
|
29
|
+
const listener = (event: MessageEvent<{ type: string, payload: any }>) => {
|
|
30
|
+
// Filter messages to those from our source window.
|
|
31
|
+
// NOTE: the "srcWindow" is determined by the below useHostEvent and usePluginEvent functions
|
|
32
|
+
if (event.source === srcWindow) {
|
|
33
|
+
// Fire callback if the type from the listened event matches the type from the message event
|
|
34
|
+
if (event.data.type === type) {
|
|
35
|
+
callback({ type, payload: event.data.payload });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
// Add the listener to the global object if the srcWindow is not null
|
|
40
|
+
if (srcWindow !== null) {
|
|
41
|
+
global.addEventListener('message', listener);
|
|
42
|
+
}
|
|
43
|
+
// useEffect cleanup
|
|
44
|
+
return () => {
|
|
45
|
+
global.removeEventListener('message', listener);
|
|
46
|
+
};
|
|
47
|
+
}, [srcWindow, type, callback]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Called by the Plugin component to use events that were listened to (ie. PLUGIN_RESIZE)
|
|
52
|
+
*/
|
|
53
|
+
export function useHostEvent(type: string, callback: MessageEventCallback) {
|
|
54
|
+
useMessageEvent(global.parent, type, callback);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Used to listen for events from a wrapped Plugin element (eg. PluginContainerIframe)
|
|
59
|
+
*
|
|
60
|
+
* @param element - Plugin element (eg. <iframe>)
|
|
61
|
+
* @param type - Event type (eg. PLUGIN_RESIZE)
|
|
62
|
+
* @param callback - Function to call when the event is triggered
|
|
63
|
+
*/
|
|
64
|
+
export function usePluginEvent(element: HTMLIFrameElement | undefined, type: string, callback: MessageEventCallback) {
|
|
65
|
+
const contentWindow = element ? element.contentWindow : null;
|
|
66
|
+
useMessageEvent(contentWindow, type, callback);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Dispatching events */
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Base dispatch function called by dispatchHostEvent and dispatchPluginEvent.
|
|
73
|
+
* Uses the `postMessage` method to enable cross-origin communication between Window objects
|
|
74
|
+
*
|
|
75
|
+
* @param targetWindow - Window that the message event is being dispatched to
|
|
76
|
+
* @param message - Data object for the message
|
|
77
|
+
* @param targetOrigin - URL for the window that the message event is being dispatched from
|
|
78
|
+
*/
|
|
79
|
+
export function dispatchMessageEvent(targetWindow: Window | null, message: any, targetOrigin: string) {
|
|
80
|
+
/** Checking targetOrigin falsiness here since '', null or undefined would all be
|
|
81
|
+
* reasons not to try to post a message to the origin.
|
|
82
|
+
*/
|
|
83
|
+
if (targetWindow && targetOrigin) {
|
|
84
|
+
targetWindow.postMessage(message, targetOrigin);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Used to dispatch events for a Plugin
|
|
90
|
+
*
|
|
91
|
+
* @param element - Plugin element (eg. <iframe>)
|
|
92
|
+
* @param message - Data object for the message
|
|
93
|
+
* @param targetOrigin - URL for the window that the message event is being dispatched from
|
|
94
|
+
*/
|
|
95
|
+
export function dispatchPluginEvent(element: HTMLIFrameElement, message: any, targetOrigin: string) {
|
|
96
|
+
dispatchMessageEvent(element.contentWindow, message, targetOrigin);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Used to dispatch events for the Host
|
|
101
|
+
*
|
|
102
|
+
* @param message - Data object for the message
|
|
103
|
+
*/
|
|
104
|
+
export function dispatchHostEvent(message: any) {
|
|
105
|
+
dispatchMessageEvent(global.parent, message, global.document.referrer);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Called inside Plugin when 'ready' prop is true
|
|
109
|
+
export function dispatchReadyEvent() {
|
|
110
|
+
dispatchHostEvent({ type: IFRAME_READY });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Below mounted events are called in a useEffect inside Plugin with [] dependencies — https://react.dev/learn/synchronizing-with-effects
|
|
114
|
+
export function dispatchMountedEvent() {
|
|
115
|
+
dispatchHostEvent({ type: IFRAME_MOUNTED });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function dispatchUnmountedEvent() {
|
|
119
|
+
dispatchHostEvent({ type: IFRAME_UNMOUNTED });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Used to determine the size of an element as it is being resized in the browser.
|
|
124
|
+
* ResizeObserver (https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) is used to maintain a reference to the element's content/border box.
|
|
125
|
+
*
|
|
126
|
+
* @returns Memoized value that contains a reference to the Plugin element (eg. iframe)
|
|
127
|
+
*/
|
|
128
|
+
export function useElementSize() {
|
|
129
|
+
// Holds a reference to the ResizeObserver
|
|
130
|
+
const observerRef = useRef<ResizeObserver>();
|
|
131
|
+
|
|
132
|
+
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
133
|
+
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
|
134
|
+
const [element, setElement] = useState<HTMLIFrameElement | undefined>();
|
|
135
|
+
// Sets a reference to the Plugin element when passed to the Plugin element as a "ref" attribute (eg. <iframe>)
|
|
136
|
+
const measuredRef: LegacyRef<HTMLIFrameElement> = useCallback(_element => {
|
|
137
|
+
setElement(_element);
|
|
138
|
+
}, []);
|
|
139
|
+
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
// Create a new ResizeObserver
|
|
142
|
+
observerRef.current = new ResizeObserver(() => {
|
|
143
|
+
if (element) {
|
|
144
|
+
// Set dimensions and any offset
|
|
145
|
+
setDimensions({
|
|
146
|
+
width: element.clientWidth,
|
|
147
|
+
height: element.clientHeight,
|
|
148
|
+
});
|
|
149
|
+
setOffset({
|
|
150
|
+
x: element.offsetLeft,
|
|
151
|
+
y: element.offsetTop,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
if (element) {
|
|
156
|
+
// Tell the ResizeObserver to start watching the element — this enables the hook to detect resizing
|
|
157
|
+
observerRef.current.observe(element);
|
|
158
|
+
}
|
|
159
|
+
}, [element]);
|
|
160
|
+
|
|
161
|
+
return useMemo<{
|
|
162
|
+
ref: LegacyRef<HTMLIFrameElement>,
|
|
163
|
+
element: HTMLIFrameElement | undefined,
|
|
164
|
+
width: number,
|
|
165
|
+
height: number,
|
|
166
|
+
x: number,
|
|
167
|
+
y: number,
|
|
168
|
+
}>(
|
|
169
|
+
() => ({
|
|
170
|
+
ref: measuredRef,
|
|
171
|
+
element: element,
|
|
172
|
+
width: dimensions.width,
|
|
173
|
+
height: dimensions.height,
|
|
174
|
+
x: offset.x,
|
|
175
|
+
y: offset.y
|
|
176
|
+
}),
|
|
177
|
+
[measuredRef, element, dimensions, offset],
|
|
178
|
+
);
|
|
179
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base UI Widget Operations
|
|
3
|
+
*
|
|
4
|
+
* "Widget" operations are a sub-type of UI operations which allow manipulation of the list of widgets (UI components) loaded into the slot. Widget operations are divided further into two sub-types: absolute and relative.
|
|
5
|
+
*
|
|
6
|
+
* Absolute widget operations allow simple list operations like appending and prepending which don't depend on any other list elements.
|
|
7
|
+
*
|
|
8
|
+
* Relative widget operations depend on a relationship to some other list element, such as "insert before" and "insert after". Some affect those elements, some add something to the list near them.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { ReactNode } from 'react';
|
|
12
|
+
import { BaseSlotOperation } from '../types';
|
|
13
|
+
|
|
14
|
+
export enum WidgetOperationTypes {
|
|
15
|
+
/**
|
|
16
|
+
* Adds a widget to the end of the slot's list of widgets.
|
|
17
|
+
*/
|
|
18
|
+
APPEND = 'widgetAppend',
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Adds a widget to the beginning of the slot's list of widgets.
|
|
22
|
+
*/
|
|
23
|
+
PREPEND = 'widgetPrepend',
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Adds a widget after the specified widget ID. Multiple "insert after" operations on the same widget ID occur in the order they were declared.
|
|
27
|
+
*/
|
|
28
|
+
INSERT_AFTER = 'widgetInsertAfter',
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Adds a widget before the specified widget ID. Multiple "insert after" operations on the same widget ID occur in the order they were declared.
|
|
32
|
+
*/
|
|
33
|
+
INSERT_BEFORE = 'widgetInsertBefore',
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Removes the specified widget ID and adds a widget in its place. Multiple "replace" operations on the same widget ID occur in the order they were declared, and only the first will succeed unless the new widget's ID is the same as the one that was removed.
|
|
37
|
+
*/
|
|
38
|
+
REPLACE = 'widgetReplace',
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Removes the specified widget ID from the slot's list of widgets. Subsequent relative widget operations on that widget ID will not be applied.
|
|
42
|
+
*/
|
|
43
|
+
REMOVE = 'widgetRemove',
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Provides options to the specified widget ID. Multiple "options" operations on the same widget ID will merge with and override any duplicate properties in the options object - last one in wins.
|
|
47
|
+
*/
|
|
48
|
+
OPTIONS = 'widgetOptions',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type AbsoluteWidgetOperationTypes = WidgetOperationTypes.APPEND | WidgetOperationTypes.PREPEND;
|
|
52
|
+
|
|
53
|
+
export type RelativeWidgetOperationTypes = WidgetOperationTypes.INSERT_AFTER | WidgetOperationTypes.INSERT_BEFORE | WidgetOperationTypes.REPLACE | WidgetOperationTypes.OPTIONS;
|
|
54
|
+
|
|
55
|
+
export interface BaseWidgetOperation extends BaseSlotOperation {
|
|
56
|
+
op: WidgetOperationTypes,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Widget renderer props
|
|
60
|
+
|
|
61
|
+
export interface WidgetComponentProps {
|
|
62
|
+
component: React.ComponentType,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface WidgetElementProps {
|
|
66
|
+
element: ReactNode,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface WidgetIFrameProps {
|
|
70
|
+
url: string,
|
|
71
|
+
title: string,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type WidgetRendererProps = (
|
|
75
|
+
WidgetComponentProps | WidgetElementProps | WidgetIFrameProps
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export interface WidgetIdentityProps {
|
|
79
|
+
id: string,
|
|
80
|
+
role?: string,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface WidgetRelationshipProps {
|
|
84
|
+
relatedId: string,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Concrete UI Widget Operations
|
|
88
|
+
|
|
89
|
+
export type WidgetAppendOperation = BaseWidgetOperation & WidgetIdentityProps & WidgetRendererProps & {
|
|
90
|
+
op: WidgetOperationTypes.APPEND,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export type WidgetPrependOperation = BaseWidgetOperation & WidgetIdentityProps & WidgetRendererProps & {
|
|
94
|
+
op: WidgetOperationTypes.PREPEND,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export type WidgetInsertAfterOperation = BaseWidgetOperation & WidgetIdentityProps & WidgetRendererProps & WidgetRelationshipProps & {
|
|
98
|
+
op: WidgetOperationTypes.INSERT_AFTER,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export type WidgetInsertBeforeOperation = BaseWidgetOperation & WidgetIdentityProps & WidgetRendererProps & WidgetRelationshipProps & {
|
|
102
|
+
op: WidgetOperationTypes.INSERT_BEFORE,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export type WidgetRemoveOperation = BaseWidgetOperation & WidgetRelationshipProps & {
|
|
106
|
+
op: WidgetOperationTypes.REMOVE,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type WidgetOptionsOperation = BaseWidgetOperation & WidgetRelationshipProps & {
|
|
110
|
+
op: WidgetOperationTypes.OPTIONS,
|
|
111
|
+
options: Record<string, unknown>,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export type WidgetReplaceOperation = BaseWidgetOperation & WidgetIdentityProps & WidgetRendererProps & WidgetRelationshipProps & { op: WidgetOperationTypes.REPLACE };
|
|
115
|
+
|
|
116
|
+
export type WidgetAbsoluteOperation = WidgetAppendOperation | WidgetPrependOperation;
|
|
117
|
+
|
|
118
|
+
export type WidgetRelativeRendererOperation = WidgetInsertAfterOperation | WidgetInsertBeforeOperation | WidgetReplaceOperation;
|
|
119
|
+
|
|
120
|
+
export type WidgetRelativeOperation = WidgetRelativeRendererOperation | WidgetRemoveOperation | WidgetOptionsOperation;
|
|
121
|
+
|
|
122
|
+
export type WidgetRendererOperation = WidgetAbsoluteOperation | WidgetRelativeRendererOperation;
|
|
123
|
+
|
|
124
|
+
export type WidgetOperation = WidgetAbsoluteOperation | WidgetRelativeOperation;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* An identified widget is a simple data structure to associate an ID with a ReactNode so we can
|
|
128
|
+
* apply widget operations to our list of widgets. It helps us find a ReactNode with a particular
|
|
129
|
+
* ID for relative widget operations.
|
|
130
|
+
*/
|
|
131
|
+
export interface IdentifiedWidget {
|
|
132
|
+
id: string,
|
|
133
|
+
node: ReactNode,
|
|
134
|
+
}
|