2020-10-16 15:17:33 +02:00
2021-09-10 14:52:28 +02:00
import Strings from 'zero/helpers/strings.js' ;
2020-11-01 23:23:36 +01:00
import EditorField from './editor-field.ts' ;
2020-10-18 01:21:11 +02:00
2020-10-16 15:17:33 +02:00
class Editor
{
2021-05-08 15:20:23 +02:00
_alias ;
_prefix ;
2020-10-18 23:47:59 +02:00
2021-05-08 15:20:23 +02:00
_preview = {
2021-01-22 14:17:39 +01:00
icon : null ,
template : null ,
hideLabel : false
};
2020-10-18 23:47:59 +02:00
/**
* Overrides the string generation for the label
*/
2021-05-08 15:20:23 +02:00
templateLabel = field => this . _prefix + field ;
2020-10-18 23:47:59 +02:00
/**
* Overrides the string generation for the description
*/
2021-05-08 15:20:23 +02:00
templateDescription = field => this . _prefix + field + '_text' ;
2020-10-16 15:17:33 +02:00
tabs = [];
2020-10-18 01:21:11 +02:00
fields = [];
2020-10-16 15:17:33 +02:00
2021-11-15 15:59:25 +01:00
blueprintAlias = null ;
2020-10-23 13:22:41 +02:00
options = {
2020-11-17 15:44:04 +01:00
disabled : false ,
2021-01-14 16:09:39 +01:00
display : 'tabs' ,
2021-09-23 11:29:10 +02:00
coreDatabase : false
2020-10-23 13:22:41 +02:00
};
2020-10-16 15:17:33 +02:00
2020-10-19 14:48:21 +02:00
constructor ( alias , prefix )
2020-10-16 15:17:33 +02:00
{
2021-05-08 15:20:23 +02:00
this . _alias = alias ;
this . _prefix = prefix || '' ;
2020-10-16 15:17:33 +02:00
}
get alias ()
{
2021-05-08 15:20:23 +02:00
return this . _alias ;
2020-10-16 15:17:33 +02:00
}
2021-01-22 14:17:39 +01:00
get previewOptions ()
{
2021-05-08 15:20:23 +02:00
return this . _preview ;
2021-01-22 14:17:39 +01:00
}
2020-10-16 15:17:33 +02:00
2020-10-18 01:21:11 +02:00
/**
* A tab within an editor
* @typedef {object} EditorTab
* @param {string} alias - Alias for the tab
* @param {string} name - Name of the tab (can be a translation)
* @param {number|function} [count] - Output a count indicator
* @param {boolean|function} [disabled] - Conditionally disable the tab and its content
*/
2020-10-16 15:17:33 +02:00
/**
2020-11-17 15:44:04 +01:00
* Add a new tab to the editor or returns the tab in case it was already added
2020-10-16 15:17:33 +02:00
* @param {string} alias - Alias for the tab
* @param {string} name - Name of the tab (can be a translation)
* @param {number|function} [count] - Output a count indicator
* @param {boolean|function} [disabled] - Conditionally disable the tab and its content
2021-01-09 19:09:31 +01:00
* @param {string} [classes] - Append HTML class to the generated tab
* @param {object} [component] - Render a custom vue component instead of editor fields
2020-10-18 01:21:11 +02:00
* @returns {EditorTab}
2020-10-16 15:17:33 +02:00
*/
2021-01-09 19:09:31 +01:00
tab ( alias , name , count , disabled , classes , component )
2020-10-16 15:17:33 +02:00
{
2021-09-04 16:58:11 +02:00
if ( typeof disabled !== 'undefined' && disabled != null && typeof disabled !== 'boolean' && typeof disabled !== 'function' )
2020-10-16 15:17:33 +02:00
{
console . warn ( `[zero] editor.tab: the disabled property has to be of type [boolean, function, undefined]` );
return ;
}
2021-09-04 16:58:11 +02:00
if ( typeof count !== 'undefined' && count != null && typeof count !== 'number' && typeof count !== 'function' )
2020-10-16 15:17:33 +02:00
{
console . warn ( `[zero] editor.tab: the count property has to be of type [number, function, undefined]` );
return ;
}
2020-11-17 15:44:04 +01:00
let tab = this . tabs . find ( x => x . alias === alias );
2020-10-16 15:17:33 +02:00
2020-11-17 15:44:04 +01:00
if ( ! tab )
{
2021-01-09 19:09:31 +01:00
tab = this . _createTab ( alias , name , disabled , count , classes , component );
2020-11-17 15:44:04 +01:00
this . tabs . push ( tab );
}
2020-10-16 15:17:33 +02:00
return tab ;
}
/**
2020-11-17 15:44:04 +01:00
* Add a new field to the editor or returns the field in case it was already added
2020-10-16 15:17:33 +02:00
* @param {string} path - Model path
* @param {object} [options] - Custom options
* @param {string} [options.label] - A custom label for this field (otherwise it's generated via `onLabelCreate`)
2020-10-18 23:47:59 +02:00
* @param {string} [options.hideLabel] - Hide the field label and make the content full-width
2020-10-16 15:17:33 +02:00
* @param {string} [options.description] - A custom description for this field (otherwise it's generated via `onDescriptionCreate`)
* @param {string} [options.helpText] - Display a help text below the field
* @param {boolean|function} [options.condition] - Conditionally hide the field
* @param {boolean|function} [options.disabled=false] - Conditionally disable the field
2020-11-19 18:09:59 +01:00
* @param {boolean} [options.coreDatabase] - Operate on the core database for this field (default is set by Editor.options.coreDatabase)
2020-10-18 01:21:11 +02:00
* @param {string|object} [options.tab] - Add this field to a tab (by passing the alias or the tab instance)
2021-01-13 13:51:55 +01:00
* @param {boolean} [options.allTabs] - Add this field to all defined tabs (could be an information property for instance)
2021-01-09 19:09:31 +01:00
* @param {string} [options.classes] - Append HTML class to the generated property
2020-10-18 01:21:11 +02:00
* @returns {EditorField}
2020-10-16 15:17:33 +02:00
*/
2020-10-18 01:21:11 +02:00
field ( path , options )
2020-10-16 15:17:33 +02:00
{
2020-11-19 18:09:59 +01:00
options = options || {};
2021-01-10 17:11:00 +01:00
//let field = this.fields.find(x => x.path === path);
// TODO we need another method to retrieve existing fields
// but we can't do it this way anymore as sometimes a field is defined multiple times with a condition
2020-11-17 15:44:04 +01:00
2021-01-09 19:09:31 +01:00
if ( this . tabs . length < 1 )
{
this . tab ( 'content' , '@ui.tab_content' , x => 0 , x => false , null , null );
}
2021-01-10 17:11:00 +01:00
//if (!field)
//{
2020-11-19 18:09:59 +01:00
if ( typeof options . coreDatabase === 'undefined' )
{
options . coreDatabase = this . options . coreDatabase ;
}
2021-01-09 19:09:31 +01:00
if ( ! options . tab )
{
options . tab = 'content' ;
}
2021-01-10 17:11:00 +01:00
let field = new EditorField ( path , options );
2020-11-17 15:44:04 +01:00
this . fields . push ( field );
2021-01-10 17:11:00 +01:00
//}
//else
//{
// field.options = { ...field.options, ...options };
//}
2020-11-17 15:44:04 +01:00
2020-10-18 01:21:11 +02:00
return field ;
}
2021-08-29 14:45:37 +02:00
/**
* Add a new fieldset to the editor or returns the tab in case it was already added
* A fieldset combines properties in a row (side-by-side)
2021-09-10 14:52:28 +02:00
* @param {function} [configure] - Configures the fieldset
2021-08-29 14:45:37 +02:00
*/
2021-09-10 14:52:28 +02:00
fieldset ( configure )
{
let set = this . _createFieldset ();
configure ( set );
}
2021-08-29 14:45:37 +02:00
2021-01-09 19:09:31 +01:00
/**
2021-05-04 17:23:52 +02:00
* Adds an info tab to the editor which outputs data for ZeroEntity.
2021-01-09 19:09:31 +01:00
* This won't work as expected for other entities as they probably do not have required properties defined.
* @returns {EditorTab}
*/
infoTab ()
{
2021-03-24 20:54:52 +01:00
return this . tab ( 'infos' , '@ui.tab_infos' , x => 0 , false , 'is-blank' , () => import ( '../editor/editor-infos.vue' ));
2021-01-09 19:09:31 +01:00
}
2020-11-17 15:44:04 +01:00
/**
* Conditionally disable this editor
* @param {function|boolean} [condition] - Optionally only disable this editor when a condition is fulfilled or reset the required state with true/false
*/
disabled ( condition )
{
this . options . disabled = condition ;
return this ;
}
2020-10-18 23:47:59 +02:00
/**
* Get fields for the specified tab
* @param {string|EditorTab} [tab] - Pass the tab or its alias
* @returns {EditorField[]}
*/
getFields ( tab )
{
const alias = typeof tab === 'undefined' ? null : ( typeof tab === 'string' ? tab : tab.alias );
2021-09-10 14:52:28 +02:00
return this . fields . filter ( x => ! alias || x . options . allTabs ? true : x . options . tab === alias );;
}
/**
* Get fields grouped in fieldsets for the specified tab
* @param {string|EditorTab} [tab] - Pass the tab or its alias
* @returns {EditorField[]}
*/
getFieldsets ( tab )
{
let fields = this . getFields ( tab );
let currentFieldset = "__undefined" ;
let fieldsets = [];
fields . forEach ( field =>
{
if ( field . options . fieldset != currentFieldset || ! field . options . fieldset )
{
currentFieldset = field . options . fieldset ;
fieldsets . push ({
fields : [],
cols : []
});
}
fieldsets [ fieldsets . length - 1 ]. fields . push ( field );
fieldsets [ fieldsets . length - 1 ]. cols . push ( field . options . fieldsetColumns );
});
fieldsets . forEach ( fieldset =>
{
fieldset . count = fieldset . fields . length ;
let reserved = fieldset . cols . reduce (( acc , x ) => acc + ( x || 0 ), 0 );
let rest = reserved < 1 ? 12 : ( 12 - reserved ) % 12 ;
let columnsToFill = fieldset . cols . filter ( x => ! x ). length ;
let perColumn = Math . floor ( rest / columnsToFill );
fieldset . fields . forEach ( field =>
{
if ( ! field . options . fieldsetColumns )
{
field . options . fieldsetColumns = perColumn ;
}
});
});
return fieldsets ;
2020-10-18 23:47:59 +02:00
}
2020-10-22 15:45:34 +02:00
/**
* Removes a field by path name
* @param {string} path - Model path
*/
2020-10-23 13:07:00 +02:00
removeField ( path )
2020-10-22 15:45:34 +02:00
{
const field = this . fields . find ( x => x . path === path );
if ( field != null )
{
const index = this . fields . indexOf ( field );
this . fields . splice ( index , 1 );
}
}
2020-10-23 13:07:00 +02:00
/**
* Removes a tab from the editor
* @param {EditorTab|string} tab - The tab to remove
*/
removeTab ( tab )
{
const alias = typeof tab === 'object' ? tab.alias : tab ;
const foundTab = this . tabs . find ( x => x . alias === alias );
if ( foundTab != null )
{
const index = this . tabs . indexOf ( foundTab );
this . tabs . splice ( index , 1 );
this . fields . filter ( x => x . tab === alias ). forEach ( field => this . removeField ( field . path ));
}
}
2020-10-22 11:52:22 +02:00
/**
* Set another editor as the base for this editor (copies fields and tabs)
* @param {Editor} editor - Base editor
* @returns {Editor}
*/
setBase ( editor )
{
this . fields = editor . fields . map ( x => new EditorField ( x . path ). setBase ( x ));
2021-01-09 19:09:31 +01:00
this . tabs = editor . tabs . map ( x => this . _createTab ( x . alias , x . name , x . disabled , x . count , x . component ));
2021-05-08 15:20:23 +02:00
this . _preview = { ... editor . previewOptions };
2021-01-22 14:17:39 +01:00
return this ;
}
/**
* Create a preview for this editor
* This is primarly used for <ui-modules /> module previews
* @param {string|object} template - A vue template string (`model` is passed as property for the module content) or a custom vue component
* @param {object} [options] - Custom options
* @param {number} [options.icon] - Custom icon
* @param {number} [options.hideLabel=false] - Whether to hide the displayed module-type label in the preview
* @returns {Editor}
*/
preview ( template , options )
{
2021-05-08 15:20:23 +02:00
this . _preview = { ... this . _preview , template , ... options };
2020-10-22 11:52:22 +02:00
return this ;
}
2020-10-18 01:21:11 +02:00
/**
* Create a new tab instance
* @returns {EditorTab}
*/
2021-01-09 19:09:31 +01:00
_createTab ( alias , name , disabled , count , classes , component )
2020-10-18 01:21:11 +02:00
{
return {
alias ,
name ,
disabled ,
count ,
2021-01-09 19:09:31 +01:00
class : classes ,
component ,
2020-10-18 01:21:11 +02:00
field : ( path , options ) =>
{
options = options || {};
options . tab = alias ;
return this . field ( path , options );
2020-10-22 15:45:34 +02:00
},
2021-09-10 14:52:28 +02:00
fieldset : configure =>
{
let set = this . _createFieldset ( alias );
configure ( set );
},
removeField : path => this . removeField ( path )
};
}
/**
* Create a new fieldset
*/
_createFieldset ( tab )
{
let id = Strings . guid ();
return {
id ,
field : ( path , options ) =>
{
options = options || {};
options . fieldset = id ;
options . tab = tab ;
return this . field ( path , options );
},
2020-10-23 13:07:00 +02:00
removeField : path => this . removeField ( path )
2020-10-18 01:21:11 +02:00
};
2020-10-16 15:17:33 +02:00
}
2020-10-21 01:04:49 +02:00
2020-10-16 15:17:33 +02:00
};
export default Editor ;