diff --git a/modules/apps/data-engine/data-engine-taglib/build.gradle b/modules/apps/data-engine/data-engine-taglib/build.gradle index 0c1112c1bd6916..52ad23a50e4144 100644 --- a/modules/apps/data-engine/data-engine-taglib/build.gradle +++ b/modules/apps/data-engine/data-engine-taglib/build.gradle @@ -24,6 +24,7 @@ dependencies { compileOnly project(":apps:frontend-js:frontend-js-loader-modules-extender-api") compileOnly project(":apps:frontend-taglib:frontend-taglib") compileOnly project(":apps:frontend-taglib:frontend-taglib-clay") + compileOnly project(":apps:frontend-taglib:frontend-taglib-react") compileOnly project(":apps:frontend-taglib:frontend-taglib-soy") compileOnly project(":apps:portal-template:portal-template-soy-renderer-api") compileOnly project(":core:petra:petra-function") diff --git a/modules/apps/data-engine/data-engine-taglib/src/main/resources/META-INF/resources/data_layout_builder/js/DataLayoutBuilder.es.js b/modules/apps/data-engine/data-engine-taglib/src/main/resources/META-INF/resources/data_layout_builder/js/DataLayoutBuilder.es.js index f9c0d49dca063e..7d7b3018230c52 100644 --- a/modules/apps/data-engine/data-engine-taglib/src/main/resources/META-INF/resources/data_layout_builder/js/DataLayoutBuilder.es.js +++ b/modules/apps/data-engine/data-engine-taglib/src/main/resources/META-INF/resources/data_layout_builder/js/DataLayoutBuilder.es.js @@ -25,30 +25,24 @@ import Component, {Config} from 'metal-jsx'; */ class DataLayoutBuilder extends Component { attached() { - const {layoutProvider} = this.refs; const {localizable} = this.props; if (localizable) { - const dependencies = [this._getTranslationManager()]; - - Promise.all(dependencies).then(results => { - const translationManager = results[0]; + const translationManagerPromise = this._getTranslationManager(); - if (translationManager) { - translationManager.on('availableLocalesChange', event => { - this.props.availableLanguageIds = event.newVal.map( - ({id}) => id - ); - }); + translationManagerPromise.then(translationManager => { + translationManager.on('availableLocales', ({newValue}) => { + this.props.availableLanguageIds = [...newValue.keys()]; + }); - translationManager.on('editingLocaleChange', event => { - this.props.editingLanguageId = event.newVal; - }); + translationManager.on('editingLocale', ({newValue}) => { + this.props.editingLanguageId = newValue; + }); - translationManager.on('deleteAvailableLocale', event => { - layoutProvider.emit('languageIdDeleted', event); - }); - } + translationManager.on( + 'availableLocales', + this.onAvailableLocalesRemoved.bind(this) + ); }); } } @@ -57,6 +51,17 @@ class DataLayoutBuilder extends Component { this.refs.layoutProvider.dispatch(event, payload); } + disposed() { + if (this._translationManager) { + this._translationManager.detach( + 'availableLocales', + this.onAvailableLocalesRemoved.bind(this) + ); + + this._translationManager = null; + } + } + getDefinitionField({settingsContext}) { const fieldConfig = { customProperties: {} @@ -195,6 +200,25 @@ class DataLayoutBuilder extends Component { }; } + onAvailableLocalesRemoved({newValue, previousValue}) { + const {layoutProvider} = this.refs; + + const removedItems = new Map(); + + previousValue.forEach((value, key) => { + if (!newValue.has(key)) { + removedItems.set(key, value); + } + }); + + if (removedItems.size > 0) { + layoutProvider.emit( + 'languageIdDeleted', + removedItems.values().next().value + ); + } + } + render() { const { context, @@ -246,17 +270,7 @@ class DataLayoutBuilder extends Component { } _getTranslationManager() { - let promise; - - const translationManager = Liferay.component('translationManager'); - - if (translationManager) { - promise = Promise.resolve(translationManager); - } else { - promise = Liferay.componentReady('translationManager'); - } - - return promise; + return Liferay.componentReady('translationManager'); } _handlePagesChanged({newVal}) { diff --git a/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/build.gradle b/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/build.gradle index 0f4cf5569cd325..3583d8a4a1274a 100644 --- a/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/build.gradle +++ b/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/build.gradle @@ -25,6 +25,7 @@ dependencies { compileOnly project(":apps:frontend-js:frontend-js-loader-modules-extender-api") compileOnly project(":apps:frontend-taglib:frontend-taglib") compileOnly project(":apps:frontend-taglib:frontend-taglib-clay") + compileOnly project(":apps:frontend-taglib:frontend-taglib-react") compileOnly project(":apps:frontend-taglib:frontend-taglib-soy") compileOnly project(":apps:portal:portal-instance-lifecycle-api") compileOnly project(":apps:portal:portal-upgrade-api") diff --git a/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/src/main/resources/META-INF/resources/admin/js/main.es.js b/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/src/main/resources/META-INF/resources/admin/js/main.es.js index 10688d4483868f..1a8b8d99d79a7f 100644 --- a/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/src/main/resources/META-INF/resources/admin/js/main.es.js +++ b/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/src/main/resources/META-INF/resources/admin/js/main.es.js @@ -84,41 +84,42 @@ class Form extends Component { return editor; }), - this._createEditor('descriptionEditor') + this._createEditor('descriptionEditor'), + this._getTranslationManager() ]; - dependencies.push(this._getTranslationManager()); - if (this.isFormBuilderView()) { dependencies.push(this._getSettingsDDMForm()); } Promise.all(dependencies).then(results => { - const translationManager = results[2]; + this._translationManager = results[2]; - if (translationManager) { + const translationManager = this._translationManager; + + if (this._translationManager) { this.props.defaultLanguageId = translationManager.get( 'defaultLocale' ); + this.props.editingLanguageId = translationManager.get( 'editingLocale' ); - translationManager.on('editingLocaleChange', event => { - this.props.editingLanguageId = event.newVal; + this._translationManager.on('editingLocale', ({newValue}) => { + this.props.editingLanguageId = newValue; - if ( - translationManager.get('defaultLocale') === event.newVal - ) { + if (translationManager.get('defaultLocale') === newValue) { this.showAddButton(); } else { this.hideAddButton(); } }); - translationManager.on('deleteAvailableLocale', event => { - store.emit('languageIdDeleted', event); - }); + this._translationManager.on( + 'availableLocales', + this.onAvailableLocalesRemoved.bind(this) + ); } this._stateSyncronizer = new StateSyncronizer( @@ -262,6 +263,11 @@ class Form extends Component { Notifications.closeAlert(); this._eventHandler.removeAllListeners(); + + this._translationManager.detach( + 'availableLocales', + this.onAvailableLocalesRemoved.bind(this) + ); } hideAddButton() { @@ -297,6 +303,24 @@ class Form extends Component { return ruleBuilderVisible && this.isFormBuilderView(); } + onAvailableLocalesRemoved({previousValue, newValue}) { + const {store} = this.refs; + + const removedItems = new Map(); + + previousValue.forEach((value, key) => { + if (!newValue.has(key)) { + removedItems.set(key, value); + } + }); + + if (removedItems.size > 0) { + store.emit('languageIdDeleted', { + locale: removedItems.keys().next().value + }); + } + } + openSidebar() { this.refs.sidebar.open(); } @@ -668,17 +692,7 @@ class Form extends Component { } _getTranslationManager() { - let promise; - - const translationManager = Liferay.component('translationManager'); - - if (translationManager) { - promise = Promise.resolve(translationManager); - } else { - promise = Liferay.componentReady('translationManager'); - } - - return promise; + return Liferay.componentReady('translationManager'); } _handleBackButtonClicked(event) { diff --git a/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/src/main/resources/META-INF/resources/admin/js/util/StateSyncronizer.es.js b/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/src/main/resources/META-INF/resources/admin/js/util/StateSyncronizer.es.js index 62b81625295af9..4f623ea35a2b59 100644 --- a/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/src/main/resources/META-INF/resources/admin/js/util/StateSyncronizer.es.js +++ b/modules/apps/dynamic-data-mapping/dynamic-data-mapping-form-web/src/main/resources/META-INF/resources/admin/js/util/StateSyncronizer.es.js @@ -33,14 +33,13 @@ class StateSyncronizer extends Component { ); if (translationManager) { - this._eventHandler.add( - translationManager.on('deleteAvailableLocale', ({locale}) => { - this.deleteLanguageId(locale); - }), - translationManager.on('editingLocaleChange', event => { - this.syncEditors(event.newVal); - }) + translationManager.on( + 'availableLocales', + this.onRemoveAvailableLocales.bind(this) ); + translationManager.on('editingLocale', ({newValue}) => { + this.syncEditors(newValue); + }); } } @@ -54,9 +53,15 @@ class StateSyncronizer extends Component { } disposeInternal() { + const {translationManager} = this.props; super.disposeInternal(); this._eventHandler.removeAllListeners(); + + translationManager.detach( + 'availableLocales', + this.onRemoveAvailableLocales.bind(this) + ); } getAvailableLanguageIds() { @@ -64,10 +69,11 @@ class StateSyncronizer extends Component { let availableLanguageIds = [{id: this.getDefaultLanguageId()}]; if (translationManager) { - availableLanguageIds = translationManager.get('availableLocales'); + const availableLocalesMap = translationManager.get('availableLocales'); + availableLanguageIds = [...availableLocalesMap.keys()]; } - return availableLanguageIds.map(({id}) => id); + return availableLanguageIds; } getDefaultLanguageId() { @@ -115,6 +121,20 @@ class StateSyncronizer extends Component { return FormSupport.emptyPages(store.state.pages); } + onRemoveAvailableLocales({newValue, previousValue}) { + const removedItems = new Map(); + + previousValue.forEach((value, key) => { + if (!newValue.has(key)) { + removedItems.set(key, value); + } + }); + + if (removedItems.length > 0) { + this.deleteLanguageId(removedItems.keys().next().value); + } + } + syncEditors(editingLanguageId = this.getDefaultLanguageId()) { const { descriptionEditor, diff --git a/modules/apps/frontend-taglib/frontend-taglib/build.gradle b/modules/apps/frontend-taglib/frontend-taglib/build.gradle index c6da0864e212e3..452310d630c41d 100644 --- a/modules/apps/frontend-taglib/frontend-taglib/build.gradle +++ b/modules/apps/frontend-taglib/frontend-taglib/build.gradle @@ -10,6 +10,7 @@ dependencies { compileOnly project(":apps:frontend-taglib:frontend-taglib-clay") compileOnly project(":apps:frontend-taglib:frontend-taglib-react") compileOnly project(":apps:frontend-taglib:frontend-taglib-soy") + compileOnly project(":apps:portal-template:portal-template-react-renderer-api") compileOnly project(":apps:portal-template:portal-template-soy-api") compileOnly project(":apps:users-admin:users-admin-api") compileOnly project(":core:osgi-service-tracker-collections") diff --git a/modules/apps/frontend-taglib/frontend-taglib/package.json b/modules/apps/frontend-taglib/frontend-taglib/package.json index a83b4494a37c85..011583eec4ab2c 100644 --- a/modules/apps/frontend-taglib/frontend-taglib/package.json +++ b/modules/apps/frontend-taglib/frontend-taglib/package.json @@ -7,13 +7,17 @@ "@clayui/form": "3.2.0", "@clayui/icon": "3.0.1", "@clayui/list": "3.0.3", + "@clayui/modal": "3.1.0", "clay-dropdown": "2.18.5", "clay-modal": "2.18.5", "frontend-js-web": "*", "metal": "2.16.8", "metal-component": "2.16.8", "metal-soy": "2.16.8", - "metal-state": "2.16.8" + "metal-state": "2.16.8", + "prop-types": "15.7.2", + "react": "16.9.0", + "react-dom": "16.9.0" }, "name": "frontend-taglib", "scripts": { diff --git a/modules/apps/frontend-taglib/frontend-taglib/src/main/java/com/liferay/frontend/taglib/servlet/taglib/soy/TranslationManagerTag.java b/modules/apps/frontend-taglib/frontend-taglib/src/main/java/com/liferay/frontend/taglib/servlet/taglib/TranslationManagerTag.java similarity index 69% rename from modules/apps/frontend-taglib/frontend-taglib/src/main/java/com/liferay/frontend/taglib/servlet/taglib/soy/TranslationManagerTag.java rename to modules/apps/frontend-taglib/frontend-taglib/src/main/java/com/liferay/frontend/taglib/servlet/taglib/TranslationManagerTag.java index 95516fa7c017a9..9199090447bbfe 100644 --- a/modules/apps/frontend-taglib/frontend-taglib/src/main/java/com/liferay/frontend/taglib/servlet/taglib/soy/TranslationManagerTag.java +++ b/modules/apps/frontend-taglib/frontend-taglib/src/main/java/com/liferay/frontend/taglib/servlet/taglib/TranslationManagerTag.java @@ -12,9 +12,9 @@ * details. */ -package com.liferay.frontend.taglib.servlet.taglib.soy; +package com.liferay.frontend.taglib.servlet.taglib; -import com.liferay.frontend.taglib.soy.servlet.taglib.ComponentRendererTag; +import com.liferay.frontend.taglib.react.servlet.taglib.ComponentTag; import com.liferay.portal.kernel.json.JSONArray; import com.liferay.portal.kernel.json.JSONFactoryUtil; import com.liferay.portal.kernel.json.JSONObject; @@ -26,53 +26,20 @@ import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.WebKeys; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; import java.util.Set; /** * @author Carlos Lancha + * @author Diego Nascimento */ -public class TranslationManagerTag extends ComponentRendererTag { +public class TranslationManagerTag extends ComponentTag { @Override public int doStartTag() { - JSONArray availableLocalesJSONArray = JSONFactoryUtil.createJSONArray(); - JSONArray localesJSONArray = JSONFactoryUtil.createJSONArray(); - - ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute( - WebKeys.THEME_DISPLAY); - - Set locales = LanguageUtil.getAvailableLocales( - themeDisplay.getSiteGroupId()); - - for (Locale locale : locales) { - String languageId = LocaleUtil.toLanguageId(locale); - - String w3cLanguageId = LocaleUtil.toW3cLanguageId(locale); - - JSONObject localeJSONObject = JSONUtil.put( - "code", w3cLanguageId - ).put( - "icon", StringUtil.toLowerCase(w3cLanguageId) - ).put( - "id", languageId - ).put( - "label", locale.getDisplayName(themeDisplay.getLocale()) - ); - - if (ArrayUtil.contains(_availableLocales, locale)) { - availableLocalesJSONArray.put(localeJSONObject); - } - - localesJSONArray.put(localeJSONObject); - } - - putValue("availableLocales", availableLocalesJSONArray); - putValue("locales", localesJSONArray); - - putValue("pathThemeImages", themeDisplay.getPathThemeImages()); - - setTemplateNamespace("liferay.frontend.TranslationManager.render"); + setData(new HashMap()); return super.doStartTag(); } @@ -84,39 +51,89 @@ public String getModule() { public void setAvailableLocales(Locale[] availableLocales) { _availableLocales = availableLocales; - putValue("availableLocales", availableLocales); } public void setChangeableDefaultLanguage( boolean changeableDefaultLanguage) { - putValue("changeableDefaultLanguage", changeableDefaultLanguage); + _changeableDefaultLanguage = changeableDefaultLanguage; } public void setCssClass(String cssClass) { - putValue("elementClasses", cssClass); + _cssClass = cssClass; } public void setDefaultLanguageId(String defaultLanguageId) { - putValue("defaultLocale", defaultLanguageId); + _defaultLanguageId = defaultLanguageId; } public void setEditingLanguageId(String editingLanguageId) { - putValue("editingLanguageId", editingLanguageId); + _editingLanguageId = editingLanguageId; } public void setId(String id) { - putValue("id", id); + _id = id; } public void setInitialize(boolean initialize) { - putValue("initialize", initialize); + _initialize = initialize; } public void setReadOnly(boolean readOnly) { - putValue("readOnly", readOnly); + _readOnly = readOnly; + } + + @Override + protected void prepareData(Map data) { + JSONArray availableLocalesJSONArray = JSONFactoryUtil.createJSONArray(); + JSONArray localesJSONArray = JSONFactoryUtil.createJSONArray(); + + ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute( + WebKeys.THEME_DISPLAY); + + Set locales = LanguageUtil.getAvailableLocales( + themeDisplay.getSiteGroupId()); + + for (Locale locale : locales) { + String languageId = LocaleUtil.toLanguageId(locale); + + String w3cLanguageId = LocaleUtil.toW3cLanguageId(locale); + + JSONObject localeJSONObject = JSONUtil.put( + "code", w3cLanguageId + ).put( + "icon", StringUtil.toLowerCase(w3cLanguageId) + ).put( + "id", languageId + ).put( + "label", locale.getDisplayName(themeDisplay.getLocale()) + ); + + if (ArrayUtil.contains(_availableLocales, locale)) { + availableLocalesJSONArray.put(localeJSONObject); + } + + localesJSONArray.put(localeJSONObject); + } + + data.put("availableLocales", availableLocalesJSONArray); + data.put("changeableDefaultLanguage", _changeableDefaultLanguage); + data.put("cssClass", _cssClass); + data.put("defaultLanguageId", _defaultLanguageId); + data.put("editingLanguageId", _editingLanguageId); + data.put("id", _id); + data.put("initialize", _initialize); + data.put("locales", localesJSONArray); + data.put("readOnly", _readOnly); } private Locale[] _availableLocales; + private boolean _changeableDefaultLanguage; + private String _cssClass; + private String _defaultLanguageId; + private String _editingLanguageId; + private String _id; + private boolean _initialize; + private boolean _readOnly; } \ No newline at end of file diff --git a/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/liferay-frontend.tld b/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/liferay-frontend.tld index 2154ddf86a1562..db4b2e3ef3824b 100644 --- a/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/liferay-frontend.tld +++ b/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/liferay-frontend.tld @@ -1454,7 +1454,7 @@ Creates a UI component for managing translations of associated content. translation-manager - com.liferay.frontend.taglib.servlet.taglib.soy.TranslationManagerTag + com.liferay.frontend.taglib.servlet.taglib.TranslationManagerTag empty Available locales of translation languages from which to choose. diff --git a/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/resources/translation_manager/TranslationManager.es.js b/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/resources/translation_manager/TranslationManager.es.js index 067955638ec720..0de4a44d22df89 100644 --- a/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/resources/translation_manager/TranslationManager.es.js +++ b/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/resources/translation_manager/TranslationManager.es.js @@ -12,211 +12,327 @@ * details. */ -import 'clay-dropdown'; - -import 'clay-modal'; -import {CompatibilityEventProxy} from 'frontend-js-web'; -import {core} from 'metal'; -import Component from 'metal-component'; -import Soy from 'metal-soy'; +import ClayButton, {ClayButtonWithIcon} from '@clayui/button'; +import ClayDropDown, {Align} from '@clayui/drop-down'; +import ClayIcon from '@clayui/icon'; +import ClayModal, {useModal} from '@clayui/modal'; +import PropTypes from 'prop-types'; +import React, {useState, useEffect, useRef} from 'react'; + +const LocalesContainer = ({children, ...otherProps}) => { + return ( +
+
    {children}
+
+ ); +}; -import templates from './TranslationManager.soy'; +// In JSX is not possible just return myMap.forEach(value, key) => someJSX :/ +const mapIterator = (map, callback) => { + const result = []; -/* eslint-disable react/no-string-refs */ + // How can I avoid this for..of here? + // eslint-disable-next-line + for (const [key, value] of map) { + result.push(callback(value, key)); + } -/** - * TranslationManager - * - * This class adds functionallity to manage existing language options, and - * create new ones. - * @review - */ -class TranslationManager extends Component { - /** - * @inheritDoc - */ - constructor(opt_config, opt_element) { - super(opt_config, opt_element); + return result; +}; - this.resetEditingLocale_(); +const AvailableLocales = ({ + availableLocales, + changeableDefaultLanguage, + defaultLocale, + editingLocale, + onLocaleClicked, + onLocaleRemoved +}) => { + // Remove this line! It's just used to cast `availableLocales` to a Map structure. + const availableLocalesMap = new Map( + availableLocales.map(locale => [locale.id, locale]) + ); + + return mapIterator(availableLocalesMap, (locale, localeId) => ( + onLocaleClicked && onLocaleClicked(locale)} + > + {(changeableDefaultLanguage && defaultLocale === localeId) || + (localeId !== defaultLocale && ( + + onLocaleRemoved && onLocaleRemoved(locale) + } + symbol="times" + /> + ))} + + )); +}; - this.startCompatibility_(); - } +const Locale = ({children, editingLocale, locale, onLocaleClicked}) => ( +
  • + + onLocaleClicked && onLocaleClicked(locale)}> + + + + {locale.label} + + + {children} + +
  • +); + +const DropDownWithState = ({children}) => { + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + + return ( + setIsDropdownOpen(isActive)} + trigger={ + + } + > + {children} + + ); +}; - /** - * Add a language to the available locales list and set it as the - * current editing language. - * @param {MouseEvent} event - * @review - */ - addLocale(event) { - const locale = event.data.item; - - if (this.availableLocales.indexOf(locale) === -1) { - this.availableLocales.push(locale); +const LocalesDropdown = ({locales, onItemClick}) => ( + + + {locales.map(locale => ( + onItemClick && onItemClick(locale)} + symbolRight={locale.icon} + > + {locale.label} + + ))} + + +); + +const useRegistry = ({componentId, states}) => { + const currentState = useRef({...states}); + const eventsRef = useRef([]); + const previousState = useRef({...states}); + + const detach = (stateName, callback) => { + if (eventsRef.current) { + const refIndex = eventsRef.current.findIndex( + event => + stateName === event.stateName && callback === event.callback + ); + if (refIndex !== -1) { + delete eventsRef.current[refIndex]; + } } + }; - this.availableLocales = this.availableLocales; - - this.editingLocale = locale.id; - } + const get = stateName => { + const stateValue = currentState.current[stateName]; - /** - * Registers another EventTarget as a bubble target. - * @param {!Object} target YUI component where events will be emited to - * @review - */ - addTarget(target) { - this.compatibilityEventProxy_.addTarget(target); + if (stateValue) { + return stateValue; + } + }; + + const on = (stateName, callback) => { + eventsRef.current.push({callback, stateName}); + }; + + if (!Liferay.component(componentId)) { + Liferay.component( + componentId, + { + detach, + get, + on + }, + { + destroyOnNavigate: true + } + ); } - /** - * Change the default language. - * @param {MouseEvent} event - * @review - */ - changeDefaultLocale(event) { - const localeId = event.delegateTarget.getAttribute('data-locale-id'); + useEffect(() => { + currentState.current = {...states}; + }, [states]); - this.defaultLocale = localeId; + useEffect(() => { + const stateChanged = []; - this.editingLocale = localeId; - } + Object.entries(states).forEach(([key, value]) => { + if (value !== previousState.current[key]) { + stateChanged.push(key); + } + }); - /** - * Change current editing language. - * @param {MouseEvent} event - * @review - */ - changeLocale(event) { - const localeId = event.delegateTarget.getAttribute('data-locale-id'); + eventsRef.current.forEach(({callback, stateName}) => { + if (stateChanged.includes(stateName)) { + callback({ + newValue: states[stateName], + previousValue: previousState.current[stateName] + }); + } + }); - this.editingLocale = localeId; - } + previousState.current = {...states}; + }, [states]); +}; - /** - * Returns a property. - * @param {String} attr Name of the attribute wanted to get - * @review - */ - get(attr) { - return this[attr]; - } +const TranslationManager = ({ + availableLocales: initialAvailableLocales, + changeableDefaultLanguage, + componentId, + cssClass, + defaultLanguageId: initialDefaultLanguageId, + id, + locales, + readOnly +}) => { + const compId = componentId ? componentId : id; + + const [availableLocales, setAvailableLocales] = useState( + initialAvailableLocales + ); + const [defaultLocale, setDefaultLocale] = useState( + initialDefaultLanguageId + ); + const [editingLocale, setEditingLocale] = useState( + initialDefaultLanguageId + ); + + const [visibleModal, setVisibleModal] = useState(false); + + useRegistry({ + componentId: compId, + states: { + availableLocales, + defaultLocale, + editingLocale + } + }); - /** - * Remove a language from the available locales list and reset the current - * editing language to default if removed one was selected. - * @param {MouseEvent} event - * @review - */ - removeAvailableLocale({delegateTarget}) { - const {availableLocales} = this; - const {localeId} = delegateTarget.dataset; - - window.event.stopPropagation(); - - this.refs.deleteModal.events = { - clickButton: ({target}) => { - if (target.classList.contains('btn-primary')) { - this.refs.deleteModal.emit('hide'); - - this.availableLocales = availableLocales.filter( - ({id}) => id !== localeId - ); - - if (localeId === this.editingLocale) { - this.resetEditingLocale_(); - } - - this.emit('deleteAvailableLocale', { - locale: localeId - }); - } - } - }; + const {observer, onClose} = useModal({ + onClose: () => setVisibleModal(false) + }); - this.refs.deleteModal.show(); - } + const localeToBeRemoved = React.useRef(null); - /** - * Set the current editing locale to the default locale. - * @private - * @review - */ - resetEditingLocale_() { - this.editingLocale = this.defaultLocale; - } + const removeLocale = locale => { + if (defaultLocale === locale.id) { + setDefaultLocale(editingLocale); + } - /** - * Configuration to emit yui-based events to maintain - * backwards compatibility. - * @private - * @review - */ - startCompatibility_() { - this.destroy = this.dispose; - - this.compatibilityEventProxy_ = new CompatibilityEventProxy({ - host: this, - namespace: 'translationmanager' - }); - } -} + if (editingLocale === locale.id) { + setEditingLocale(defaultLocale); + } -/** - * State definition. - * @ignore - * @review - * @static - * @type {!Object} - */ -TranslationManager.STATE = { - /** - * List of available languages keys. - * @review - * @type {Array.} - */ - availableLocales: { - validator: core.isArray - }, - - /** - * Indicates if the default language is editable or not. - * @review - * @type {Boolean} - */ - changeableDefaultLanguage: { - validator: core.isBoolean - }, - - /** - * Default language key. - * @review - * @type {String} - */ - defaultLocale: { - validator: core.isString - }, - - /** - * Current editing language key. - * @review - * @type {String} - */ - editingLocale: { - validator: core.isString - }, - - /** - * Map of all languages - * @review - * @type {Object} - */ - locales: { - validator: core.isObject - } + setAvailableLocales( + availableLocales.filter( + ({id}) => id !== locale.id || id === defaultLocale + ) + ); + }; + + return ( + <> + {visibleModal && ( + + +

    + {Liferay.Language.get( + 'are-you-sure-you-want-to-deactivate-this-language' + )} +

    +
    + + { + localeToBeRemoved.current = null; + onClose(); + }} + > + {Liferay.Language.get('dismiss')} + + { + removeLocale(localeToBeRemoved.current); + onClose(); + }} + > + {Liferay.Language.get('delete')} + + + } + /> +
    + )} + + + { + if (changeableDefaultLanguage) { + setDefaultLocale(locale.id); + } + setEditingLocale(locale.id); + }} + onLocaleRemoved={locale => { + localeToBeRemoved.current = locale; + setVisibleModal(true); + }} + /> + +
  • + { + setEditingLocale(locale.id); + if (availableLocales.indexOf(locale) === -1) { + setAvailableLocales([ + ...availableLocales, + locale + ]); + } + }} + /> +
  • +
    + + ); }; -Soy.register(TranslationManager, templates); +TranslationManager.propTypes = { + availableLocales: PropTypes.instanceOf(Map), + changeableDefaultLanguage: PropTypes.bool, + defaultLanguageId: PropTypes.string, + defaultLocale: PropTypes.string, + editingLocale: PropTypes.string, + locales: PropTypes.array +}; export default TranslationManager; diff --git a/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/resources/translation_manager/TranslationManager.soy b/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/resources/translation_manager/TranslationManager.soy deleted file mode 100644 index c13ce71defc1fe..00000000000000 --- a/modules/apps/frontend-taglib/frontend-taglib/src/main/resources/META-INF/resources/translation_manager/TranslationManager.soy +++ /dev/null @@ -1,199 +0,0 @@ -{namespace liferay.frontend.TranslationManager} - -/** - * Translation Manager Component - **/ -{template .render} - {@param availableLocales: list} - {@param changeableDefaultLanguage: bool} - {@param defaultLocale: string} - {@param locales: list} - {@param? addLocale: any} - {@param? changeDefaultLocale: any} - {@param? changeLocale: any} - {@param? editingLocale: string} - {@param? elementClasses: string} - {@param? id: string} - {@param? removeAvailableLocale: any} - -
    - - - {call .removeAvailableLocaleModal data="all"} - {param removeAvailableLocale: $removeAvailableLocale /} - {/call} -
    -{/template} - -/** - * Renders the default locale element - */ -{template .defaultLocale} - {@param changeableDefaultLanguage: any} - {@param defaultLocale: string} - {@param locales: list} - {@param? changeDefaultLocale: any} - {@param? changeLocale: any} - - {foreach $locale in $locales} - {if $locale.id == $defaultLocale} - {let $optionIcon kind="html"} - {if $changeableDefaultLanguage} - {call .localesDropdown data="all"} - {param onClickDropdownElement: $changeDefaultLocale /} - {param icon: 'change' /} - {/call} - {/if} - {/let} - - {call .locale data="all"} - {param locale: $locale /} - {param optionIcon kind="html"} - {$optionIcon} - {/param} - {param changeLocale: $changeLocale /} - {/call} - {/if} - {/foreach} -{/template} - -/** - * Renders the required list of available locale elements - */ -{template .availableLocales} - {@param availableLocales: list} - {@param defaultLocale: string} - {@param pathThemeImages: string} - {@param removeAvailableLocale: any} - - {foreach $availableLocale in $availableLocales} - {if $availableLocale.id != $defaultLocale} - {call .locale data="all"} - {param locale: $availableLocale /} - {param optionIcon kind="html"} - - - - - - - - {msg desc=""}delete-{$availableLocale.label ?: $availableLocale.id}{/msg} - - - - - - {/param} - {/call} - {/if} - {/foreach} -{/template} - -/** - * Renders a modal to be opened when removing a available language. - */ -{template .removeAvailableLocaleModal} - {@param pathThemeImages: string} - - {let $deleteLabel kind="text"} - {msg desc=""}delete{/msg} - {/let} - - {let $dismissLabel kind="text"} - {msg desc=""}dismiss{/msg} - {/let} - - {call ClayModal.render data="all"} - {param body kind="html"} -

    - {msg desc=""}are-you-sure-you-want-to-deactivate-this-language{/msg} -

    - {/param} - {param spritemap: $pathThemeImages + '/lexicon/icons.svg' /} - {param size: 'sm' /} - {param footerButtons: [ - [ - 'alignment': 'right', - 'label': $dismissLabel, - 'type': 'close', - 'style': 'primary' - ], - [ - 'alignment': 'right', - 'label': $deleteLabel, - 'type': 'button', - 'style': 'primary' - ] - ] /} - {param elementClasses: 'hide' /} - {param ref: 'deleteModal' /} - {param visible: false /} - {/call} -{/template} - -/** - * Renders the required locale element - */ -{template .locale} - {@param locale: ?} - {@param optionIcon: html} - {@param pathThemeImages: string} - {@param? changeLocale: any} - {@param? editingLocale: string} - -
  • - - - - - - - {$locale.icon} - - - {$locale.label} - - - {$optionIcon} - -
  • -{/template} - -/** - * Renders a dropdown with require locale elements - */ -{template .localesDropdown} - {@param locales: list} - {@param icon: string} - {@param pathThemeImages: string} - {@param? onClickDropdownElement: any} - - {call ClayDropdown.render} - {param items: $locales /} - {param spritemap: $pathThemeImages + '/lexicon/icons.svg' /} - {param events: [ - 'itemClicked': $onClickDropdownElement - ] /} - {param icon: $icon /} - {param itemsIconAlignment: 'right' /} - {param triggerClasses: 'icon-monospaced' /} - {param preferredAlign: 'BottomCenter' /} - {/call} -{/template} \ No newline at end of file