Skip to content

Commit

Permalink
Merge pull request #155 from ckeditor/i/41
Browse files Browse the repository at this point in the history
Feature: Introduced the schema tab. Closes #41.
  • Loading branch information
pomek committed May 24, 2022
2 parents 3254232 + 7a14e99 commit 4d82ee7
Show file tree
Hide file tree
Showing 24 changed files with 1,115 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/assets/img/schema.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/ckeditorinspectorui.js
Expand Up @@ -23,6 +23,7 @@ import Button from './components/button';
import ModelPane from './model/pane';
import ViewPane from './view/pane';
import CommandsPane from './commands/pane';
import SchemaPane from './schema/pane';

import EditorQuickActions from './editorquickactions';
import ArrowDownIcon from './assets/img/arrow-down.svg';
Expand Down Expand Up @@ -97,6 +98,7 @@ class CKEditorInspectorUI extends Component {
<ModelPane label="Model" />
<ViewPane label="View" />
<CommandsPane label="Commands" />
<SchemaPane label="Schema" />
</Tabs>
</Rnd>;
}
Expand Down
1 change: 1 addition & 0 deletions src/components/objectinspector.css
Expand Up @@ -31,6 +31,7 @@
}

& > .ck-inspector-button {
flex-shrink: 0;
margin-left: .5em;
}

Expand Down
1 change: 1 addition & 0 deletions src/components/objectinspector.js
Expand Up @@ -30,6 +30,7 @@ export default class ObjectInspector extends PureComponent {
name={list.name}
itemDefinitions={list.itemDefinitions}
presentation={list.presentation}
onPropertyTitleClick={list.onPropertyTitleClick}
/>
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/propertylist.css
Expand Up @@ -61,6 +61,10 @@
border-radius: 2px;
border: 1px solid black;
}

&.ck-inspector-property-list__title_clickable label:hover {
text-decoration: underline;
}
}

& dt label {
Expand Down
10 changes: 9 additions & 1 deletion src/components/propertylist.js
Expand Up @@ -29,6 +29,7 @@ export default class PropertyList extends Component {
canCollapse={hasSubProperties}
colorBox={presentation.colorBox}
expandCollapsibles={expandCollapsibles}
onClick={this.props.onPropertyTitleClick}
/>,
<dd key={`${ this.props.name }-${ name }-value`}>
<input
Expand Down Expand Up @@ -95,10 +96,17 @@ class PropertyTitle extends PureComponent {
colorBox = <span className="ck-inspector-property-list__title__color-box" style={{ background: this.props.colorBox }}></span>;
}

if ( this.props.onClick ) {
classNames.push( 'ck-inspector-property-list__title_clickable' );
}

return <dt className={classNames.join( ' ' ).trim()}>
{collapseButton}
{colorBox}
<label htmlFor={`${ this.props.listUid }-${ this.props.name }-value-input`}>
<label
htmlFor={`${ this.props.listUid }-${ this.props.name }-value-input`}
onClick={this.props.onClick ? () => this.props.onClick( this.props.name ) : null}
>
{this.props.name}
</label>:
</dt>;
Expand Down
2 changes: 2 additions & 0 deletions src/data/reducer.js
Expand Up @@ -6,6 +6,7 @@
import { modelReducer } from '../model/data/reducer';
import { viewReducer } from '../view/data/reducer';
import commandsReducer from '../commands/data/reducer';
import schemaReducer from '../schema/data/reducer';

import {
TOGGLE_IS_COLLAPSED,
Expand Down Expand Up @@ -35,6 +36,7 @@ export function reducer( state, action ) {
newState.model = modelReducer( newState, newState.model, action );
newState.view = viewReducer( newState, newState.view, action );
newState.commands = commandsReducer( newState, newState.commands, action );
newState.schema = schemaReducer( newState, newState.schema, action );

return {
...state,
Expand Down
35 changes: 32 additions & 3 deletions src/model/nodeinspector.js
Expand Up @@ -6,25 +6,43 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';

import {
setActiveTab
} from '../data/actions';

import {
setSchemaCurrentDefinitionName
} from '../schema/data/actions';

import Button from '../components/button';
import Pane from '../components/pane';
import ObjectInspector from '../components/objectinspector';

import Logger from '../logger';

import ConsoleIcon from '../assets/img/console.svg';
import SchemaIcon from '../assets/img/schema.svg';

class ModelNodeInspector extends Component {
constructor( props ) {
super( props );

this.handleNodeLogButtonClick = this.handleNodeLogButtonClick.bind( this );
this.handleNodeSchemaButtonClick = this.handleNodeSchemaButtonClick.bind( this );
}

handleNodeLogButtonClick() {
Logger.log( this.props.currentNodeDefinition.editorNode );
}

handleNodeSchemaButtonClick() {
const schema = this.props.editors.get( this.props.currentEditorName ).model.schema;
const schemaDefinition = schema.getDefinition( this.props.currentNodeDefinition.editorNode );

this.props.setActiveTab( 'Schema' );
this.props.setSchemaCurrentDefinitionName( schemaDefinition.name );
}

render() {
const definition = this.props.currentNodeDefinition;

Expand All @@ -47,6 +65,12 @@ class ModelNodeInspector extends Component {
icon={<ConsoleIcon />}
text="Log in console"
onClick={this.handleNodeLogButtonClick}
/>,
<Button
key="schema"
icon={<SchemaIcon />}
text="Show in schema"
onClick={this.handleNodeSchemaButtonClick}
/>
]}
lists={[
Expand All @@ -65,8 +89,13 @@ class ModelNodeInspector extends Component {
}
}

const mapStateToProps = ( { model: { currentNodeDefinition } } ) => {
return { currentNodeDefinition };
const mapStateToProps = ( { editors, currentEditorName, model: { currentNodeDefinition } } ) => {
return { editors, currentEditorName, currentNodeDefinition };
};

const mapDispatchToProps = {
setActiveTab,
setSchemaCurrentDefinitionName
};

export default connect( mapStateToProps, {} )( ModelNodeInspector );
export default connect( mapStateToProps, mapDispatchToProps )( ModelNodeInspector );
11 changes: 11 additions & 0 deletions src/schema/data/actions.js
@@ -0,0 +1,11 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

export const SET_SCHEMA_CURRENT_DEFINITION_NAME = 'SET_SCHEMA_CURRENT_DEFINITION_NAME';

export function setSchemaCurrentDefinitionName( currentSchemaDefinitionName ) {
return { type: SET_SCHEMA_CURRENT_DEFINITION_NAME, currentSchemaDefinitionName };
}

70 changes: 70 additions & 0 deletions src/schema/data/reducer.js
@@ -0,0 +1,70 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

import {
SET_SCHEMA_CURRENT_DEFINITION_NAME
} from './actions';

import {
SET_EDITORS,
SET_CURRENT_EDITOR_NAME,
SET_ACTIVE_INSPECTOR_TAB
} from '../../data/actions';

import {
getSchemaTreeDefinition,
getSchemaDefinition
} from './utils';

export default function schemaReducer( globalState, schemaState, action ) {
// Performance optimization: don't create the schema state unless necessary.
if ( globalState.ui.activeTab !== 'Schema' ) {
return schemaState;
}

if ( !schemaState ) {
return getBlankSchemaState( globalState, schemaState );
}

switch ( action.type ) {
case SET_SCHEMA_CURRENT_DEFINITION_NAME:
return {
...schemaState,

currentSchemaDefinition: getSchemaDefinition( globalState, action.currentSchemaDefinitionName ),
currentSchemaDefinitionName: action.currentSchemaDefinitionName
};

// * SET_ACTIVE_INSPECTOR_TAB – Because of the performance optimization at the beginning, update the state
// if we're back in the commands tab.
// * UPDATE_MODEL_STATE – An action called by the editorEventObserver for the model document change.
case SET_ACTIVE_INSPECTOR_TAB:
return {
...schemaState,

currentSchemaDefinition: getSchemaDefinition( globalState, schemaState.currentSchemaDefinitionName ),
treeDefinition: getSchemaTreeDefinition( globalState, schemaState )
};

// Actions related to the external state.
case SET_EDITORS:
case SET_CURRENT_EDITOR_NAME:
return getBlankSchemaState( globalState, schemaState );

default:
return schemaState;
}
}

function getBlankSchemaState( globalState, schemaState = {} ) {
return {
...schemaState,

currentSchemaDefinitionName: null,
currentSchemaDefinition: null,
treeDefinition: getSchemaTreeDefinition( globalState, schemaState )
};
}

119 changes: 119 additions & 0 deletions src/schema/data/utils.js
@@ -0,0 +1,119 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

import {
stringifyPropertyList
} from '../../components/utils';

const definitionPropertyNames = [
'isBlock',
'isInline',
'isObject',
'isContent',
'isLimit',
'isSelectable'
];

const DOCS_PREFIX_URL = 'https://ckeditor.com/docs/ckeditor5/latest/api/';

export function getSchemaDefinition( { editors, currentEditorName }, currentSchemaDefinitionName ) {
if ( !currentSchemaDefinitionName ) {
return null;
}

const schema = editors.get( currentEditorName ).model.schema;
const definitions = schema.getDefinitions();
const definition = definitions[ currentSchemaDefinitionName ];
const properties = {};
const allowChildren = {};
const allowIn = {};
let allowAttributes = {};

for ( const propertyName of definitionPropertyNames ) {
// Include only those that are true.
if ( definition[ propertyName ] ) {
properties[ propertyName ] = {
value: definition[ propertyName ]
};
}
}

for ( const childName of definition.allowChildren.sort() ) {
allowChildren[ childName ] = {
value: true
};
}

for ( const parentName of definition.allowIn.sort() ) {
allowIn[ parentName ] = {
value: true
};
}

for ( const attributeName of definition.allowAttributes.sort() ) {
allowAttributes[ attributeName ] = {
value: true
};
}

allowAttributes = stringifyPropertyList( allowAttributes );

for ( const attributeName in allowAttributes ) {
const attributeProperties = schema.getAttributeProperties( attributeName );
const subPropertyDefinitions = {};

for ( const propertyName in attributeProperties ) {
subPropertyDefinitions[ propertyName ] = {
value: attributeProperties[ propertyName ]
};
}

allowAttributes[ attributeName ].subProperties = stringifyPropertyList( subPropertyDefinitions );
}

return {
currentSchemaDefinitionName,
type: 'SchemaCompiledItemDefinition',
urls: {
general: DOCS_PREFIX_URL + 'module_engine_model_schema-SchemaCompiledItemDefinition.html',
allowAttributes: DOCS_PREFIX_URL + 'module_engine_model_schema-SchemaItemDefinition.html#member-allowAttributes',
allowChildren: DOCS_PREFIX_URL + 'module_engine_model_schema-SchemaItemDefinition.html#member-allowChildren',
allowIn: DOCS_PREFIX_URL + 'module_engine_model_schema-SchemaItemDefinition.html#member-allowIn'
},
properties: stringifyPropertyList( properties ),
allowChildren: stringifyPropertyList( allowChildren ),
allowIn: stringifyPropertyList( allowIn ),
allowAttributes,
definition
};
}

export function getSchemaTreeDefinition( { editors, currentEditorName } ) {
const editor = editors.get( currentEditorName );

if ( !editor ) {
return [];
}

const list = [];
const definitions = editors.get( currentEditorName ).model.schema.getDefinitions();

for ( const definitionName in definitions ) {
list.push( {
name: definitionName,
type: 'element',
children: [],
node: definitionName,
attributes: [],

presentation: {
isEmpty: true,
cssClass: 'ck-inspector-tree-node_tagless'
}
} );
}

return list.sort( ( a, b ) => a.name > b.name ? 1 : -1 );
}

0 comments on commit 4d82ee7

Please sign in to comment.