Commit 29c55a96 authored by Pedro Eduardo Trujillo's avatar Pedro Eduardo Trujillo
Browse files

Introduce nuevo concepto de diseños para mapa

Aplica una nueva lógica de agrupación de componentes de diseño,
enfocados a reducir la herencia y potenciar la ampliación de
funcionalidades como mixins acumulables.

Estrena el concepto adaptando el diseño de mapa con contenido lateral,
aplicado con éxito a las vistas de Atlas y de Monitorización marina.
parent 6e51d56c
Loading
Loading
Loading
Loading
+69 −0
Original line number Diff line number Diff line
define([
	'dojo/_base/declare'
], function (
	declare
) {

	return declare(null, {
		// summary:
		//   Base común para los controladores de diseño.
		//   Reúne funciones básicas usadas por todos los controladores de diseño.

		_prepareComponentDefinition: function(/*Array*/ baseDefinitionArray, /*Object*/ extensionEnableFlags,
			/*Object*/ extensionDefinitions) {
			// summary:
			//   Recibe un array de definiciones base del componente y dos objetos, uno de flags de activación y otro
			//   de correspondencias con extensiones de definición, ambos con los mismos nombres de propiedad.
			//   Devuelve la declaración del componente con todas las extensiones que se hayan habilitado.

			for (let enabledProp in extensionEnableFlags) {
				if (extensionEnableFlags[enabledProp]) {
					baseDefinitionArray.push(extensionDefinitions[enabledProp]);
				}
			}

			return declare(baseDefinitionArray);
		},

		_initialize: function() {
			// Método perteneciente al ciclo de vida de un componente.

			this.inherited(arguments);

			// Este método debe devolver un objeto con las instancias de los componentes, indexadas por su propName.
			const componentInstances = this._createDesignControllerComponents?.();

			for (let key in componentInstances) {
				this._setComponentInstance(componentInstances[key], key);
			}
		},

		_setComponentInstance: function(/*Object*/ instance, /*String?*/ instancePropName) {
			// summary:
			//   Recibe una instancia de un componente y, opcionalmente, un nombre de propiedad para referenciarlo.
			//   En caso de no recibir instancePropName, usa el valor devuelto por instance.getOwnChannel para ello.
			//   Almacena la instancia para poder recuperarla más adelante si no existía una con ese nombre.
			//   Devuelve el nombre de la propiedad usado para almacenar la instancia internamente.

			const propName = instancePropName || instance.getOwnChannel();

			this._designComponentInstances ??= {};

			if (this._designComponentInstances[propName]) {
				console.error(`Instance with name ${propName} already exists at design ${this.getChannel()}.`);
				return;
			}

			this._designComponentInstances[propName] = instance;

			return propName;
		},

		getComponentInstance: function(/*String*/ instancePropName) {
			// summary:
			//   Recibe un nombre de propiedad para recuperar una instancia de un componente previamente almacenada.

			return this._designComponentInstances?.[instancePropName];
		}
	});
});
+70 −0
Original line number Diff line number Diff line
define([
	'dojo/_base/declare'
], function (
	declare
) {

	return declare(null, {
		// summary:
		//   Base común para las maquetaciones de diseño.
		//   Reúne funciones básicas usadas por todas las maquetaciones de diseño.

		buildRendering: function() {
			// Método perteneciente al ciclo de vida de un widget Dojo.

			this.inherited(arguments);

			this.layoutClasses?.length && this._setLayoutClass(this.layoutClasses);
		},

		_setLayoutClass: function(/*String*/ classNames) {
			// summary:
			//   Recibe un string con una o más clases CSS para aplicarlas al nodo principal de la maquetación.
			//   Es importante que este método se llame antes de terminar la fase de buildRendering del componente,
			//   en postCreate ya será demasiado tarde para que se apliquen los cambios.

			if (!classNames?.length) {
				return;
			}

			this.class = classNames.includes('.') ? classNames.replace(/\./g, ' ') : classNames;
		},

		postCreate: function() {
			// Método perteneciente al ciclo de vida de un widget Dojo.

			this.inherited(arguments);

			// Este método debe devolver un objeto con los nodos de maquetación, indexados por su propName.
			const layoutNodes = this._createDesignLayoutNodes?.();

			for (let propName in layoutNodes) {
				this._setLayoutNode(layoutNodes[propName], propName);
			}

			this._populateDesignLayoutNodes?.();
		},

		_setLayoutNode: function(/*Object*/ node, /*String*/ nodePropName) {
			// summary:
			//   Recibe un nodo de maquetación y un nombre de propiedad para referenciarlo.
			//   Almacena el nodo para poder recuperarlo más adelante si no existía uno con ese nombre.

			this._designLayoutNodes ??= {};

			if (this._designLayoutNodes[nodePropName]) {
				console.error(`Node with name ${nodePropName} already exists at design ${this.getChannel()}.`);
				return;
			}

			this._designLayoutNodes[nodePropName] = node;
		},

		getLayoutNode: function(/*String*/ nodePropName) {
			// summary:
			//   Recibe un nombre de propiedad para recuperar un nodo de maquetación previamente almacenado.

			return this._designLayoutNodes?.[nodePropName];
		}
	});
});
+94 −0
Original line number Diff line number Diff line
define([
	'dojo/_base/declare'
	, 'dojo/_base/lang'
	, 'src/component/atlas/Atlas'
	, 'src/component/layout/TabsDisplayer'
	, 'src/component/mapQuery/QueryOnMap'
], function (
	declare
	, lang
	, Atlas
	, TabsDisplayer
	, QueryOnMap
) {

	return declare(null, {
		// summary:
		//   Lógica de diseño para añadir un componente Atlas, junto con otros para mostrarlo y para gestionar
		//   pulsaciones sobre el mapa.
		//   Debe asociarse como mixin a un componente al instanciarlo, junto con la parte de controlador y alguna
		//   maquetación de este diseño.

		_setConfigurations: function() {

			this.inherited(arguments);

			const parentChannel = this.getChannel();

			this.tabsDisplayerConfig = this._merge([{
				parentChannel
			}, this.tabsDisplayerConfig || {}]);

			this.atlasConfig = this._merge([{
				parentChannel
			}, this.atlasConfig || {}]);

			this.queryOnMapConfig = this._merge([{
				parentChannel
			}, this.queryOnMapConfig || {}]);
		},

		_createDesignControllerComponents: function() {

			let inheritedComponents = this.inherited(arguments);

			const mapInstance = inheritedComponents.map,
				getMapChannel = lang.hitch(mapInstance, mapInstance.getChannel);

			const tabsDisplayer = this._createDesignTabDisplayerComponent(),
				tabsDisplayerChannel = tabsDisplayer.getChannel(),
				addTabChannel = tabsDisplayer.getChannel('ADD_TAB');

			const atlas = this._createDesignAtlasComponent(getMapChannel, addTabChannel);

			const queryOnMap = this._createDesignQueryOnMapComponent(getMapChannel, tabsDisplayerChannel);

			return lang.mixin(inheritedComponents, {tabsDisplayer, atlas, queryOnMap});
		},

		_createDesignTabDisplayerComponent: function() {

			return new TabsDisplayer(this.tabsDisplayerConfig);
		},

		_createDesignAtlasComponent: function(getMapChannel, addTabChannel) {

			this.atlasConfig = this._merge([{
				getMapChannel, addTabChannel
			}, this.atlasConfig || {}]);

			return new Atlas(this.atlasConfig);
		},

		_createDesignQueryOnMapComponent: function(getMapChannel, tabsDisplayerChannel) {

			this.queryOnMapConfig = this._merge([{
				getMapChannel, tabsDisplayerChannel
			}, this.queryOnMapConfig || {}]);

			return new QueryOnMap(this.queryOnMapConfig);
		},

		_populateDesignLayoutNodes: function() {

			this.inherited(arguments);

			const tabsDisplayerInstance = this.getComponentInstance('tabsDisplayer'),
				additionalContentNode = this.getLayoutNode('additionalContent');

			this._publish(tabsDisplayerInstance.getChannel('SHOW'), {
				node: additionalContentNode
			});
		}
	});
});
+77 −0
Original line number Diff line number Diff line
define([
	'dojo/_base/declare'
	, 'dojo/_base/lang'
	, 'src/component/map/_LeafletDraw'
	, 'src/component/map/_PlaceNamesButton'
	, 'src/component/map/LeafletImpl'
	, 'src/design/_DesignController'
], function (
	declare
	, lang
	, _LeafletDraw
	, _PlaceNamesButton
	, LeafletImpl
	, _DesignController
) {

	const mapComponentExtensionDefinitions = {
		toponyms: _PlaceNamesButton,
		draw: _LeafletDraw
	};

	return declare(_DesignController, {
		// summary:
		//   Lógica de diseño para mostrar un componente mapa con extensiones opcionales.
		//   Debe asociarse como mixin a un componente al instanciarlo.

		constructor: function(args) {

			const defaultConfig = {
				events: {
					ADD_LAYER: 'addLayer',
					REMOVE_LAYER: 'removeLayer'
				},
				enabledMapExtensions: {
					toponyms: true,
					draw: false
				}
			};

			lang.mixin(this, this._merge([this, defaultConfig, args]));
		},

		_setConfigurations: function() {

			this.inherited(arguments);

			this.mapConfig = this._merge([{
				parentChannel: this.getChannel()
			}, this.mapConfig || {}]);

			this._MapComponentDefinition = this._prepareComponentDefinition(
				[LeafletImpl], this.enabledMapExtensions, mapComponentExtensionDefinitions);
		},

		_createDesignControllerComponents: function() {

			const map = new this._MapComponentDefinition(this.mapConfig);

			return {map};
		},

		_definePublications: function() {

			this.inherited(arguments);

			const mapInstance = this.getComponentInstance('map');

			this.publicationsConfig.push({
				event: 'ADD_LAYER',
				channel: mapInstance.getChannel('ADD_LAYER')
			},{
				event: 'REMOVE_LAYER',
				channel: mapInstance.getChannel('REMOVE_LAYER')
			});
		}
	});
});
+48 −0
Original line number Diff line number Diff line
define([
	'dojo/_base/declare'
	, 'dojo/_base/lang'
	, 'put-selector'
	, 'src/design/_DesignLayout'
], function(
	declare
	, lang
	, put
	, _DesignLayout
) {

	return declare(_DesignLayout, {
		// summary:
		//   Estructura de diseño para mostrar un componente adicional junto a un mapa.
		//   Debe asociarse como mixin a un componente al instanciarlo.

		constructor: function(args) {

			const defaultConfig = {
				layoutClasses: 'designLayoutContainer.mapAndContentLayoutMapWithSideContentDesign',
				mapNodeClasses: 'mediumSolidContainer.mapContainer.borderRadius',
				additionalContentNodeClasses: 'mediumSolidContainer.mapSideContainer.borderRadius'
			};

			lang.mixin(this, this._merge([this, defaultConfig, args]));
		},

		_createDesignLayoutNodes: function() {

			const map = put(this.domNode, `div.${this.mapNodeClasses}`),
				additionalContent = put(this.domNode, `div.${this.additionalContentNodeClasses}`);

			return {map, additionalContent};
		},

		_populateDesignLayoutNodes: function() {

			const mapPropName = 'map',
				mapInstance = this.getComponentInstance(mapPropName),
				mapNode = this.getLayoutNode(mapPropName);

			this._publish(mapInstance?.getChannel('SHOW'), {
				node: mapNode
			});
		}
	});
});
Loading