-
Notifications
You must be signed in to change notification settings - Fork 13
Creación de plugins_es
Si deseas crear tu propio plugin, estás en la sección correcta de la documentación. Puedes basarte en los ya existentes para poder crear el tuyo propio. Es muy fácil tomar componentes de React ya existentes e integrarlos en la aplicación como plugins.
-
Desde el directorio del proyecto ejecuta el siguiente comando:
yarn run create-plugin "Nombre Del Plugin"Esto creará una nueva carpeta dentro del directoriopluginsllamadaNombreDelPlugin, que contiene todos los ficheros que se necesitan. -
Para añadir el plugin a Ediphy, lo único que hay que hacer es incluirlo en la lista
pluginListdel fichero ~/core/config.es6.
El plugin que se crea es un plugin simple que muestra un div con el texto "Hello Ediphy". Si se observa la plantilla, se puede combrobar que la segunda palabra de este texto está ligada al estado del plugin, pudiendo configurarse desde la toolbar.
Este plugin debe usarse como un punto de partida para plugins más complejos. El comando lanzado también crea una carpeta para traducciones (locales), de momento español e inglés, y otra para la plantilla de visualización.
El comando también permite especificar una serie de acciones, como la categoría a la que pertenece el plugin (ejemplo: yarn run create-plugin "Nombre Del Plugin" category text), si es un plugin enriquecido (yarn run create-plugin "Nombre Del Plugin" category text rich) o si no necesita una plantilla de visualización (yarn run create-plugin "Nombre Del Plugin" no-visor).
Antes de ponernos a programar nuestro plugin debemos crear las carpetas y los ficheros necesarios para ello:
-
Creamos una carpeta con el nombre del Plugin en el siguiente directorio:
**PROJECT_DIR/plugins/<NombreDelPlugin>** -
Dentro de esta carpeta podemos crear más carpetas, en función de lo que queramos que haga.
- La carpeta visor sirve para almacenar el comportamiento del plugin cuando se está visualizando, ya sea previsualizando o exportando el curso.
- La carpeta locales almacenará los archivos de traducción.
- La carpeta css almacenará los estilos.
- El archivo package.json definirá las dependencias que tenga de librerías externas, ya sea de paquetes npm como de librerías específicas descargadas dentro del proyecto.
-
Para definir el comportamiento del plugin, crearemos un archivo JavaScript con el mismo nombre que el del plugin , tanto al nivel general como dentro de la carpeta visor (si queremos que el plugin tenga ese comportamiento).
Estructura típica de un plugin
PROJECT_DIR/plugins └── <NombreDelPlugin> ├── <NombreDelPlugin>.js ├── locales | ├── en.js | └── es.js └── visor └── <NombreDelPlugin>.js -
Después de crear los directorios y ficheros para nuestro plugin debemos decir a Ediphy que va a tener un nuevo plugin, Para ello vamos al fichero de ~/core/config.es6. y añadimos una línea con el nombre del plugin en el apartado pluginList (todas las líneas deben estar separadas por comas excepto la última)
#~/dist/core/config.es6 ... sections_have_content: false, pluginList: [ 'BasicImage', 'BasicText', 'RichText', 'BasicVideo', 'Youtube', 'Webpage', 'CajasColor', 'Container', 'ListaNumerada', 'RelacionaAll' ], availableLanguages:[ 'en', 'es' ] ...
En este momento deberíamos de tener nuestro entorno preparado para empezar a crear el plugin.
Tras preparar el entorno la siguiente tarea que debemos realizar es colocar el esqueleto base de nuestro plugin para más tarde ir rellenándolo. A lo largo del manual se irá explicando para qué sirve y qué debe ir dentro de cada función.
El fichero plugins/<NombreDelPlugin>/<NombreDelPlugin>.js es el fichero principal del plugin. En él se define la interfaz que tendrá en Ediphy, cómo el usuario va a poder customizar su contenido, qué características particulares tiene, etc.
export function <NombreDelPlugin>(base){
return{
init: function(){
},
getConfig: function(state){
},
getToolbar: function(){
},
getInitialState: function(){
},
getRenderTemplate: function(state, props){
},
getConfigTemplate: function(state){
},
}
}
En ocasiones, la forma en la que queremos que se muestre el plugin al autor del contenido no coincide con la que queremos mostrar al usuario final. Por ello, se puede definir un fichero plugins/<NombreDelPlugin>/visor/<NombreDelPlugin> en el que se especifique cómo se debe mostrar el contenido creado por el plugin al usuario final.
export function <NombreDelPlugin>(base){
return{
init: function(){
},
getRenderTemplate: function(state, props){
},
}
}
Una vez que tenemos la estructura de ficheros y el esqueleto necesario para desarrollar nuestro plugin vamos a ir rellenando poco a poco cada una de las partes del mismo.
Este método se usa para inicializar parámetros necesarios para el correcto funcionamiento del plugin. Se ejecuta una sola vez mientras se carga la aplicación. Un ejemplo es la descarga de un script necesario para el uso del plugin, por ejemplo la API de Google Maps. En la mayoría de los casos no será necesario emplear este método.
export function BasicImage(base) {
return {
init: function () {
// Código aquí
},
...
};
}
Aquí podremos especificar configuración concreta de este plugin. Es prácticamente el único método obligatorio (el resto son más o menos opcionales). Debe devolver un objeto con la configuración:
getConfig: function () {
return {
name: <NombreDelPlugin>,
displayName: Ediphy.i18n.t('PluginName'),
category: 'image',
icon: 'image'
};
}
-
name: debe ser una cadena de texto con el que hemos utilizado para crear el plugin y que da nombre a la carpeta que lo contiene. -
displayName(opcional): es el nombre que aparecerá en la aplicación de cara al usuario. Si no se especifica, se usará name. Se puede usar un valor de los archivos de traducción usando Ediphy.i18n.t("clave") siendo "clave" la clave asignada en estos archivos. -
category(opcional): define a qué sección de la barra superior debe añadirse, toma uno de los siguientes valores ("text", "image", "multimedia", "objects", "evaluation" ). Si no se asigna ninguno, tendrá por defecto el valor "text", si se asigna uno inválido, el plugin no aparecerá en la barra superior. -
icon(opcional): especifica el icono que aparecerá junto al nombre del plugin, debe seleccionarse de esta lista iconos. En caso de que iconFromUrl (a continuación) seatrue, podrá utilizarse cualquier imagen desde una url en vez de un icono de la lista anterior. -
iconFromUrl(opcional): es un valor booleano que permite utilizar imágenes en vez de iconos en el apartado icon. Por defecto, esfalse. -
isRich(opcional): es un valor booleano que da acceso a las funcionalidades de plugins enriquecidos. Por defecto, es falso. -
flavor(opcional): especifica de qué modo ha sido escrito el plugin, puede ser "plain" o "react". Por defecto es "react" indicando que ha sido escrito usando React, ya que los plugins en JavaScript estándar están en proceso de ser deprecados. -
needsConfigModal(opcional): valor booleano que define si el plugin necesita un diálogo de configuración adicional. Por defecto es falso, pero si se le asigna el valor verdadero, entonces es necesario añadir el métodogetConfigTemplateal plugin para definir cómo es la interfaz de ese diálogo. -
needsTextEdition(opcional): valor booleano usado para especificar si el plugin necesitará hacer uso de las herramientas proporcionadas para la edición de texto. Por defecto es falso. Si se le asigna el valor verdadero, aparecerá en el estado una propiedad "oculta" llamada "__text" que contendrá el texto del plugin. Es la única situación en la que un plugin puede no tener el métodogetRenderTemplate, ya que en caso de faltar, se genera automáticamente devolviendo el valor de __text. -
aspectRatioButtonConfig(opcional): es un objeto que sirve para configurar el botón que bloquea la relación de aspecto al cambiar el tamaño de la caja creada con el plugin. Tiene dos propiedades, una "location" que es un array en el que se va especificando las claves de los distintos antecesores que va a tener en la barra de herramientas (pestaña, acordeón y subacordeón si existiera), y otra "defaultValue" que especifica si tiene que estar activo o no inicialmente (por defecto, no). Esta configuración cambiará a un valor booleano ya que se fijará la localización (["main", "structure"]). Ver plugin imagen como ejemplo. -
needsPointerEvents(opcional): valor booleano que añade un atajo del plugin que permite manipular el plugin con el ratón en modo edición, en lugar de solo en visualización, facilitando así su configuración (ejemplo de uso: centrar y zoom mapa). Por defecto es falso. -
limitToOneInstance(opcional): valor booleano que permite limitar a una instancia del plugin por página. -
createFromLibrary(opcional): esta propiedad indica si se puede crear una instancia del plugin directamente desde la ventana de importación de contenidos. Se trata de un array cuyo primer valor indica el tipo MIME con el que está asociado este plugin y el segundo valor indica cuál es la parte del estado del plugin con la que está asociada ese tipo MIME. Por ejemplo, en el caso de las imágenes el valor que tomaría esta propiedad sería el siguiente:['image/*', 'url']. -
searchIcon(opcional): valor booleano que indica si se añade un atajo del plugin que permita cambiar fácilmente su contenido a través de a ventana de importación de contenido. Si ya se ha especificado el valor del tipo MIME y la parte del estado con la que está asociada el contenido en el apratado anterior, será suficiente con otorgarle a este campo el valortrue. También se puede especificar un array con valores diferentes o escribir el string ```type```` -
isComplex(opcional): valor booleano que indica si este plugin puede contener instancias de otros plugins en su interior.
También en getConfig() se especifican los valores iniciales de ancho y alto. Las slides solo pueden tener plugins con medidas relativas(%) ya que escalan al tamaño de la pantalla.
-
initialWidth: Especifica el ancho inicial del plugin en los documentos. Si no se especifica, el valor por defecto es '20%'. Puede ser un valor en porcentaje, en píxeles o 'auto'. -
initialHeight: Especfica el ancho inicial del plugin en los documentos. Si no se especifica, el valor por defecto es 'auto'. Puede ser un valor en porcentaje, en píxeles o 'auto'. -
initialWidthSlide: Especifica el ancho inicial del plugin en las slides. Si no se especifica, el valor por defecto esinitialWidth. Puede ser un valor en porcentaje o 'auto'. Es necesario especificarlo cuandoìnitialWidthse da en px. -
initialHeightSlide: Especifica el alto inicial del plugin en las slides. Si no se especifica, el valor por defecto esinitialHeight. Puede ser un valor en porcentaje o 'auto'. Es necesario especificarlo cuandoìnitialHeightse da en px.
Aquí se configura los botones que queremos que aparezcan en la barra de herramientas. Básicamente se compone de tres partes, pestañas, acordeones y botones (las pestañas por el momento están deshabilitadas ya que sólo puede haber una, la pestaña principal o "Main").
getToolbar: function (state) {
return {
main: {
__name: "Main",
accordions: {
basic: {
__name: Ediphy.i18n.t('BasicVideo.Video'),
icon: 'link',
buttons: {
url: {
__name: Ediphy.i18n.t('BasicVideo.URL'),
type: 'text',
value: state.url,
autoManaged: false
},
controls: {
__name: Ediphy.i18n.t('BasicVideo.Show_controls'),
type: 'checkbox',
checked: state.controls,
autoManaged: false
},
autoplay: {
__name: Ediphy.i18n.t('BasicVideo.Autoplay'),
type: 'checkbox',
checked: state.autoplay,
autoManaged: false
}
}
},
style: {
__name: Ediphy.i18n.t('BasicVideo.box_style'),
icon: 'palette',
buttons: {
padding: {
__name: Ediphy.i18n.t('BasicVideo.padding'),
type: 'number',
value: 0,
min: 0,
units: 'px',
max: 100
},
borderStyle: {
__name: Ediphy.i18n.t('BasicVideo.border_style'),
type: 'select',
value: 'solid',
options: ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset', 'initial', 'inherit']
},
borderColor: {
__name: Ediphy.i18n.t('BasicVideo.border_color'),
type: 'color',
value: '#000000'
}
}
}
}
}
};
}
Como se puede ver, al igual que en getConfig aquí se devuelve un objeto con toda la estructura. A continuación se explicará su estructura y la de sus descendientes.
-
Este objeto va a tener como propiedades las pestañas (ya hemos dicho antes que de momento sólo puede haber una, que es la pestaña "main"). Obviamente, no puede haber dos pestañas con el mismo nombre
-
Cada pestaña será al mismo tiempo un objeto con dos propiedades,
__name(que es una cadena de texto y puede tener el valor que se desee, incluso sacado de los ficheros de traducción) yaccordions, que es un diccionario con todos los acordeones que contiene esta pestaña. -
Cada uno de los acordeones tendrá una clave única por el que se le identificará y una serie de propiedades:
-
__name: nombre que se mostrará en la barra de herramientas. -
icon: icono que se mostrará (se elegirá de la misma lista). -
buttons: objeto con todos los botones que contendrá el acordeón. -
accordions: objeto con los subacordeones que estarán contenidos. Serán iguales en forma excepto por el hecho de que no pueden contener subacordeones a su vez. -
order(opcional): array que contiene las claves de los botones y subacordeones en el orden en el que se desea que se muestren en la barra de herramientas.
-
-
Cada uno de los botones será también un objeto con las siguientes propiedades:
-
__name: nombre que se mostrará en la barra de herramientas. -
type: determinará el tipo de control mostrado en la barra de herramientas. Uno de los siguientes:-
color: Sobreescribe el input de color nativo de HTML para permitir la selección de colores con nivel de transparencia. -
radio: Input radio. A elegir una de N opciones. -
fancy_radio: DEPRECATED Igual que input*radio*pero con iconos como botones en lugar de texto. Requiere algunos campos adicionales exclusivos de este plugin: -
checkbox: Input checkbox. (Usacheckeden lugar devalue). -
external_provider: Permite buscar un archivo en la biblioteca y demás APIs de Ediphy. Se puede especificar el tipo MIME de los ficheros aceptados con un campo adicionalaccept. -
text: Input de texto. -
number: Input numérico. -
range: Input de rango (slider). -
select: Input select. Precisa el campooptionscon un array de los valores entre los que elegir.
-
-
options: las opciones que va a mostrar. Es necesario paraselect,radioyfancy_radio. -
tooltips: Texto a mostrar al pasar por encima de cada botón con el ratón. Exclusivo defancy_radio. -
icons: Nombre Material icon para cada opción (ej. 'warning'). Exclusivo defancy_radio. Se elegirá de la lista de material icons. Se especifica en forma de array de strings en el mismo orden que lasoptions. -
value: el valor que va a tener. En caso de ser de tipo "checkbox", en vez devalue, seráchecked. -
units: las unidades que va a tener, si es que tiene sentido (por ejemplo, "px" o "%" para unnumbero unrange). -
min: valor mínimo que puede tener, si es que tiene sentido (por ejemplo, para unnumbero unrange). -
max: valor máximo que puede tener, si es que tiene sentido (por ejemplo, para unnumbero unrange). -
step: cambio mínimo, si es que tiene sentido (por ejemplo, para unnumbero unrange).
Cada plugin tiene 3 partes principales: la estructura (
structure), el estilo (style) y el estado (state). La estructura tiene que ver con el tamaño y posicionamiento de la caja creada a partir del plugin. Por defecto se creará un acordeón llamado Estructura que contendrá los controles necesarios para este cometido, sin necesidad de definirlo a la hora de crear el plugin. El estilo tiene que ver con el estilo de la caja, no con el estilo de su interior. Ediphy se encarga de asociar las propiedades CSS incluidas en el acordeónstylecon el estilo de la caja. Por último, el estado contendrá la información relativa a cada instancia concreta del plugin, como se verá en el apartado siguiente. El desarrollador puede crear todas los acordeones y botones que necesite para que el usuario pueda configurar todos los campos del estado del plugin, espcificando con cuál de ellos está asociado, como se ve en el ejemplo anterior. -
Aquí se define el estado que se quiera que tenga el plugin. Ediphy se encarga de mantener los valores de cada instancia y de modificar el estado cada vez que se maneja la toolbar.
getInitialState: function () {
return {
url: 'http://nemanjakovacevic.net/wp-content/uploads/2013/07/placeholder.png'
};
}
Esta es, junto con getConfig la otra única función obligatoria (excepto si se configura para que necesite texto, que la genera automáticamente si no existiera).
En función del estado del plugin (recibido como parámetro), se puede devolver una interfaz u otra.
getRenderTemplate: function (state) {
return <img src={state.url}/>;
}
Su funcionamiento es el mismo que el de getRenderTemplate, solo que la interfaz que genera es para el diálogo de configuración del plugin.
Obtiene como parámetros los siguientes valores:
- id: Identificador de la caja que se está editando
- state: Estado actual de la caja.
- updateState: Función para editar el estado
- props: Serie de props adicionales, por ejemplo para acceder al modal de la biblioteca.
Esta función devuelve como resultado un objeto con dos claves:
- component: Componente de React que renderizará los controles de configuración de la instancia del plugin.
- n_steps: Número de pasos que supone la configuración de la instancia del plugin.
Si en cualquier momento se desea que una caja creada por un plugin pueda alojar a cajas creadas con otros plugins es necesario declarar la propiedad isComplex: true en la configuración del plugin y definir la zona destinada para este propósito en la interfaz del plugin. Para ello se emplea el componente PluginPlaceholder.
Dentro de la función getRenderTemplate se puede declarar uno o varios componentes PluginPlaceholder, que admiten las siguientes propiedades:
-
pluginContainerName: es el valor que se utiliza en la barra de herramientas para identificar a este contenedor en concreto y poder configurarlo. Si no se asigna ninguno, toma por defecto el valor "Contenedor". -
pluginDefaultContent(opcional): Array con el contenido por defecto que tendrá el contenedor. Puede estar vacío, pero si se desea que incluya contenido en el momento de su creación, es necesario definir un array con tantos elementos como cajas por defecto se desean incluir. Cada elemento del array será un objeto con dos claves:plugin, que contiene el nombre del plugin que se desea incluir por defecto, yinitialState, el estado inicial del plugin (solo es necesario incluir aquellos campos que difieran del estado por defecto del mismo. Un ejempo del contenido esperado es:[{ plugin: 'BasicText', initialState: { __text: '<p>Hola</p>' } }]. -
pluginContainer: String con la clave única (dentro del plugin) que recibe este contenedor. -
pluginDataInitialHeight: el valor de este atributo indica el tamaño inicial que se desea que tenga el contenedor. Puede darse en cualquier unidad, ya sea relativa o absoluta.
La creación del plugin para el visor no requiere de conocimientos adicionales a los ya vistos, si bien es cierto que las funciones auxiliares toman más relevancia aquí ya que es donde se pueden usar (en el modo edición no se puede interactuar con el interior de los plugins). Sin embargo, como no es obligatorio que un plugin tenga desarrollados ambos modos (por ejemplo, en una imagen no tiene sentido), se permite la inclusión de funciones auxiliares por si se quisiera algún tipo de interacción en la visualización (que abriera un enlace web, por poner un caso).
Las dependencias de un módulo de node habitualmente se definen en un package.json. Este es un archivo en formato JSON que incluye la versión, el nombre, elementos de configuración y las dependencias entre otras cosas.
{
"name": "nombre del plugin", //(campo obligatorio)
"version" : "version del plugin", //(campo obligatorio)
"dependencies": {
"nombre de dependencia de npm": "versión del paquete",
},
config:{
"localDependencies":{
"nombre": "ruta a la librería"
},
"aliases": {
"nombre de la dependencia": "nombre para exportar"
},
"css": {
"nombre" : "ruta al archivo css"
}
}
}
En un plugin de Ediphy se permiten tres tipos de dependencias:
-
Dependencia de npm: se pueden obtener los repositorios de npm, habitualmente son públicas pero pueden ser privadas, son librerías javascript empaquetadas dentro de un repositorio de npm. Dentro del package.json deben estar en el apartado dependencies. Si quiere utilizarse un nombre global para la dependencia utilizada debe utilizarse -> config -> aliases.
-
Dependencia local: para inyectar directamente una librería de javascript dentro de la aplicación se deben usar las dependencias locales. Dentro del JSON deben estar dentro de -> config -> dependencies.
-
Dependencia de estilos: son hojas de estilos css. Dentro del JSON deben estar dentro de -> config -> css.