@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
208 lines (206 loc) • 7.62 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import * as React from 'react';
import { lazy } from 'react';
import * as ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import CrafterCMSNextBridge from '../components/CrafterCMSNextBridge/CrafterCMSNextBridge';
import { nou } from '../utils/object';
import * as babel from '../env/babel';
import * as rxjs from 'rxjs';
import * as messages from './i18n-legacy';
import { translateElements } from './i18n-legacy';
import * as mui from '@mui/material';
import { createDefaultThemeOptions } from '../styles/theme';
import getStore from '../state/store';
import palette from '../styles/palette';
import {
buildStoredLanguageKey,
dispatchLanguageChange,
getCurrentIntl,
getStoredLanguage,
intl$,
setStoredLanguage
} from '../utils/i18n';
import { getHostToHostBus } from '../utils/subjects';
import { createCustomDocumentEventListener } from '../utils/dom';
import { components as studioUIComponents, services, utils } from './studioUI';
import logoIcon from '../assets/crafter-icon.svg';
const ErrorState = studioUIComponents.ErrorState;
export function updateIntl(nextIntl) {
// @ts-ignore
if (window.CrafterCMSNext) {
// @ts-ignore
window.CrafterCMSNext.i18n.intl = nextIntl;
}
}
export function createCodebaseBridge() {
const Bridge = {
// React
React,
ReactDOM,
ReactDOMClient: { createRoot },
rxjs,
components: {
...studioUIComponents,
CrafterCMSNextBridge,
SearchPage: lazy(() => import('../pages/Search')),
Global: lazy(() => import('../pages/Global')),
Preview: lazy(() => import('../pages/Preview')),
SiteTools: lazy(() => import('../pages/SiteTools')),
Login: lazy(() => import('../pages/Login')),
PagesWidget: lazy(() => import('../components/PathNavigator/PathNavigator')),
QuickCreateMenu: lazy(() => import('../pages/QuickCreateMenu')),
DeleteContentTypeButton: lazy(() => import('../pages/DeleteContentTypeButton')),
PreviewCompatDialog: lazy(() => import('../components/PreviewCompatibilityDialog/PreviewCompatibilityDialog'))
},
system: {
createDefaultThemeOptions,
palette,
store: null,
getHostToHostBus,
getStore
},
mui,
assets: {
logoIcon
},
util: { ...utils, babel },
i18n: {
intl: getCurrentIntl(),
messages,
translateElements,
getStoredLanguage,
setStoredLanguage,
dispatchLanguageChange,
buildStoredLanguageKey
},
services,
render(container, component, props = {}, isLegacy = true) {
if (typeof component === 'string') {
if (!Boolean(Bridge.components[component])) {
console.warn(`The supplied component name ('${component}') is not a know component of CrafterCMSNext.`);
}
} else if (!Object.values(Bridge.components).includes(component)) {
console.warn('The supplied module is not a know component of CrafterCMSNext.');
}
const element = typeof container === 'string' ? document.querySelector(container) : container;
let Component = typeof component === 'string' ? Bridge.components[component] : component;
if (nou(Component)) {
Component = function () {
return React.createElement(ErrorState, {
imageUrl: '/studio/static-assets/images/warning_state.svg',
message: `The supplied component name ('${component}') is not a know component of CrafterCMSNext. Please re-check supplied name ('${component}'), make sure you've build the app and created the component.`
});
};
}
return new Promise((resolve, reject) => {
try {
const root = createRoot(element);
const unmount = (options) => {
root.unmount();
options.removeContainer && element.parentNode.removeChild(element);
};
root.render(
React.createElement(
CrafterCMSNextBridge,
{ mountGlobalDialogManager: !isLegacy, mountSnackbarProvider: !isLegacy, mountCssBaseline: !isLegacy },
React.createElement(Component, { ...props })
)
);
resolve({
unmount: (options) => {
options = Object.assign(
{
delay: false,
removeContainer: false
},
options || {}
);
if (options.delay) {
setTimeout(() => unmount(options), options.delay);
} else {
unmount(options);
}
}
});
} catch (e) {
reject(e);
}
});
},
renderBackgroundUI(options) {
const { mountLegacyConcierge = false } = options ?? {};
const element = document.createElement('div');
const root = createRoot(element);
element.setAttribute('class', 'craftercms-background-ui');
document.body.appendChild(element);
return new Promise((resolve, reject) => {
try {
const unmount = () => {
root.unmount();
document.body.removeChild(element);
};
root.render(
React.createElement(CrafterCMSNextBridge, {
mountCssBaseline: false,
mountLegacyConcierge: mountLegacyConcierge,
suspenseFallback: ''
})
);
resolve({
unmount: (options) => {
options = Object.assign({ delay: false }, options || {});
if (options.delay) {
setTimeout(unmount, options.delay);
} else {
unmount();
}
}
});
} catch (e) {
reject(e);
}
});
},
createLegacyCallbackListener: createCustomDocumentEventListener
};
// @ts-ignore
window.CrafterCMSNext = Bridge;
// The login screen 1. doesn't need redux at all 2. there's no token yet (i.e. not loggeed in)
// and the store creation is dependent on successfully retrieving the JWT.
if (!window.location.pathname.includes('/studio/login')) {
getStore().subscribe((store) => {
Bridge.system.store = store;
});
}
}
intl$.subscribe(updateIntl);