mirror of
https://github.com/nasa/openmct.git
synced 2025-06-22 09:08:43 +00:00
feat: configurable Plan Views for reducing vertical scroll distance (#6415)
* refactor: convert Type API to ES6 module - Another AMD module bites the dust 🧹 * feat: add initial configurable plan type - Name change TBD * feat: add `clipActivityNames` property - refactor: initialize data to `null` * refactor: general code cleanup * feat(WIP): name clipping via clipPath elements * feat: compose a Gantt Chart using a Plan - Allows Plans to be dragged into Gantt Charts (name tentative) to create a configurable Activity View - Clip/Unclip activity names by editing domainObject property * feat: replace Plan if another is dragged in - SImilar to Gauges or Scatter Plots, launch a confirmation dialog to replace the existing Plan with another, if another Plan is dragged into the chart. * test: fix tests, add basic tests for gantt * tes(e2e): fix plan test * docs: add TODO * refactor: clean up more string literals * style: remove `rx`, increase min width - round widths to nearest integer * refactor: extract timeline creation logic - extracts the logic for creating the timeline into its own component, `ActivityTimeline.vue`. This will save us a lot of re-renders, as we were manually creating elements / clearing them on each tick * style: fix text y-pos and don't round * fix: make activities clickable again * docs: add copyright docs * feat: swimlane visibility - configure plan view from inspector fix: update plans when file changes - fix gantt chart display in time strips - code cleanup * fix: gantt chart embed in time strip * remove viewBox for now * fix: make `clipPath` ids more unique * refactor: more code cleanup * refactor: more code cleanup * test: fix existing Plan unit tests * refactor: rename variables * fix: respond to code review comments - Move config manipulation to PlanViewConfiguration.js/.vue - Variable renames, code refactoring * fix: unique, reproducible clipPathIds * fix: only mutate swimlaneVisibility once on init * fix: really make clipPathId unique this time * refactor: use default config * Closes #6113 - Refined CSS class naming and application. - Set cursor to pointer for Activity elements. - Added <title> node to Activity elements. - Styling for selected Activities. - Better Inspector tab name. * fix: make Plan creatability configurable and false by default * test: fix existing tests and add a couple new ones * Closes #6113 - Now uses SVG <symbol> instead of rect within Activity element. - Passes in `rowHeight` as a prop from Plan.vue. - SWIMLANE_PADDING const added and used to create margin at top and bottom edges of swimlanes. - Refined styling for selected activities. - New `$colorGanttSelectedBorder` theme constant. - Smoke tested in Espresso and Snow themes. * fix: default swimlaneWidth to clientWidth * test: fix test * feat: display selected activity name as header * fix: remove redundant listener * refactor: move `examplePlans.js` into `test-data/` * docs: remove copyright header * refactor: move `helper.js` into `helper/` * refactor: `helper.js` -> `planningUtils.js` * fix: update pathing * test: add tests for gantt/plan - add visual tests for gantt / plan - add test for clicking a single activity and verifying its contents in the inspector --------- Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
This commit is contained in:
@ -46,7 +46,7 @@ export default class Editor extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the application is in edit mode, false otherwise.
|
||||
* @returns {boolean} true if the application is in edit mode, false otherwise.
|
||||
*/
|
||||
isEditing() {
|
||||
return this.editing;
|
||||
|
@ -71,7 +71,7 @@ function (
|
||||
StatusAPI: StatusAPI.default,
|
||||
TelemetryAPI: TelemetryAPI,
|
||||
TimeAPI: TimeAPI.default,
|
||||
TypeRegistry: TypeRegistry,
|
||||
TypeRegistry: TypeRegistry.default,
|
||||
UserAPI: UserAPI.default,
|
||||
AnnotationAPI: AnnotationAPI.default
|
||||
};
|
||||
|
@ -62,6 +62,8 @@ import InMemorySearchProvider from './InMemorySearchProvider';
|
||||
* @property {Identifier[]} [composition] if
|
||||
* present, this will be used by the default composition provider
|
||||
* to load domain objects
|
||||
* @property {Object.<string, any>} [configuration] A key-value map containing configuration
|
||||
* settings for this domain object.
|
||||
* @memberof module:openmct.ObjectAPI~
|
||||
*/
|
||||
|
||||
|
@ -20,63 +20,25 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(function () {
|
||||
|
||||
/**
|
||||
* A Type describes a kind of domain object that may appear or be
|
||||
* created within Open MCT.
|
||||
*
|
||||
* @param {module:opemct.TypeRegistry~TypeDefinition} definition
|
||||
* @class Type
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
function Type(definition) {
|
||||
/**
|
||||
* A Type describes a kind of domain object that may appear or be
|
||||
* created within Open MCT.
|
||||
*
|
||||
* @param {module:opemct.TypeRegistry~TypeDefinition} definition
|
||||
* @class Type
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
export default class Type {
|
||||
constructor(definition) {
|
||||
this.definition = definition;
|
||||
if (definition.key) {
|
||||
this.key = definition.key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a domain object is an instance of this type.
|
||||
* @param domainObject
|
||||
* @returns {boolean} true if the domain object is of this type
|
||||
* @memberof module:openmct.Type#
|
||||
* @method check
|
||||
*/
|
||||
Type.prototype.check = function (domainObject) {
|
||||
// Depends on assignment from MCT.
|
||||
return domainObject.type === this.key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a definition for this type that can be registered using the
|
||||
* legacy bundle format.
|
||||
* @private
|
||||
*/
|
||||
Type.prototype.toLegacyDefinition = function () {
|
||||
const def = {};
|
||||
def.name = this.definition.name;
|
||||
def.cssClass = this.definition.cssClass;
|
||||
def.description = this.definition.description;
|
||||
def.properties = this.definition.form;
|
||||
|
||||
if (this.definition.initialize) {
|
||||
def.model = {};
|
||||
this.definition.initialize(def.model);
|
||||
}
|
||||
|
||||
if (this.definition.creatable) {
|
||||
def.features = ['creation'];
|
||||
}
|
||||
|
||||
return def;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a type definition from a legacy definition.
|
||||
*/
|
||||
Type.definitionFromLegacyDefinition = function (legacyDefinition) {
|
||||
static definitionFromLegacyDefinition(legacyDefinition) {
|
||||
let definition = {};
|
||||
definition.name = legacyDefinition.name;
|
||||
definition.cssClass = legacyDefinition.cssClass;
|
||||
@ -121,7 +83,39 @@ define(function () {
|
||||
}
|
||||
|
||||
return definition;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Check if a domain object is an instance of this type.
|
||||
* @param domainObject
|
||||
* @returns {boolean} true if the domain object is of this type
|
||||
* @memberof module:openmct.Type#
|
||||
* @method check
|
||||
*/
|
||||
check(domainObject) {
|
||||
// Depends on assignment from MCT.
|
||||
return domainObject.type === this.key;
|
||||
}
|
||||
/**
|
||||
* Get a definition for this type that can be registered using the
|
||||
* legacy bundle format.
|
||||
* @private
|
||||
*/
|
||||
toLegacyDefinition() {
|
||||
const def = {};
|
||||
def.name = this.definition.name;
|
||||
def.cssClass = this.definition.cssClass;
|
||||
def.description = this.definition.description;
|
||||
def.properties = this.definition.form;
|
||||
|
||||
return Type;
|
||||
});
|
||||
if (this.definition.initialize) {
|
||||
def.model = {};
|
||||
this.definition.initialize(def.model);
|
||||
}
|
||||
|
||||
if (this.definition.creatable) {
|
||||
def.features = ['creation'];
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
@ -19,35 +19,36 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
define(['./Type'], function (Type) {
|
||||
const UNKNOWN_TYPE = new Type({
|
||||
key: "unknown",
|
||||
name: "Unknown Type",
|
||||
cssClass: "icon-object-unknown"
|
||||
});
|
||||
import Type from './Type';
|
||||
|
||||
/**
|
||||
* @typedef TypeDefinition
|
||||
* @memberof module:openmct.TypeRegistry~
|
||||
* @property {string} label the name for this type of object
|
||||
* @property {string} description a longer-form description of this type
|
||||
* @property {function (object)} [initialize] a function which initializes
|
||||
* the model for new domain objects of this type
|
||||
* @property {boolean} [creatable] true if users should be allowed to
|
||||
* create this type (default: false)
|
||||
* @property {string} [cssClass] the CSS class to apply for icons
|
||||
*/
|
||||
const UNKNOWN_TYPE = new Type({
|
||||
key: "unknown",
|
||||
name: "Unknown Type",
|
||||
cssClass: "icon-object-unknown"
|
||||
});
|
||||
|
||||
/**
|
||||
* A TypeRegistry maintains the definitions for different types
|
||||
* that domain objects may have.
|
||||
* @interface TypeRegistry
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
function TypeRegistry() {
|
||||
/**
|
||||
* @typedef TypeDefinition
|
||||
* @memberof module:openmct.TypeRegistry~
|
||||
* @property {string} label the name for this type of object
|
||||
* @property {string} description a longer-form description of this type
|
||||
* @property {function (object)} [initialize] a function which initializes
|
||||
* the model for new domain objects of this type
|
||||
* @property {boolean} [creatable] true if users should be allowed to
|
||||
* create this type (default: false)
|
||||
* @property {string} [cssClass] the CSS class to apply for icons
|
||||
*/
|
||||
|
||||
/**
|
||||
* A TypeRegistry maintains the definitions for different types
|
||||
* that domain objects may have.
|
||||
* @interface TypeRegistry
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
export default class TypeRegistry {
|
||||
constructor() {
|
||||
this.types = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new object type.
|
||||
*
|
||||
@ -56,17 +57,16 @@ define(['./Type'], function (Type) {
|
||||
* @method addType
|
||||
* @memberof module:openmct.TypeRegistry#
|
||||
*/
|
||||
TypeRegistry.prototype.addType = function (typeKey, typeDef) {
|
||||
addType(typeKey, typeDef) {
|
||||
this.standardizeType(typeDef);
|
||||
this.types[typeKey] = new Type(typeDef);
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
* Takes a typeDef, standardizes it, and logs warnings about unsupported
|
||||
* usage.
|
||||
* @private
|
||||
*/
|
||||
TypeRegistry.prototype.standardizeType = function (typeDef) {
|
||||
standardizeType(typeDef) {
|
||||
if (Object.prototype.hasOwnProperty.call(typeDef, 'label')) {
|
||||
if (!typeDef.name) {
|
||||
typeDef.name = typeDef.label;
|
||||
@ -74,18 +74,16 @@ define(['./Type'], function (Type) {
|
||||
|
||||
delete typeDef.label;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
* List keys for all registered types.
|
||||
* @method listKeys
|
||||
* @memberof module:openmct.TypeRegistry#
|
||||
* @returns {string[]} all registered type keys
|
||||
*/
|
||||
TypeRegistry.prototype.listKeys = function () {
|
||||
listKeys() {
|
||||
return Object.keys(this.types);
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
* Retrieve a registered type by its key.
|
||||
* @method get
|
||||
@ -93,18 +91,15 @@ define(['./Type'], function (Type) {
|
||||
* @memberof module:openmct.TypeRegistry#
|
||||
* @returns {module:openmct.Type} the registered type
|
||||
*/
|
||||
TypeRegistry.prototype.get = function (typeKey) {
|
||||
get(typeKey) {
|
||||
return this.types[typeKey] || UNKNOWN_TYPE;
|
||||
};
|
||||
|
||||
TypeRegistry.prototype.importLegacyTypes = function (types) {
|
||||
}
|
||||
importLegacyTypes(types) {
|
||||
types.filter((t) => this.get(t.key) === UNKNOWN_TYPE)
|
||||
.forEach((type) => {
|
||||
let def = Type.definitionFromLegacyDefinition(type);
|
||||
this.addType(type.key, def);
|
||||
});
|
||||
};
|
||||
|
||||
return TypeRegistry;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,36 +20,36 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./TypeRegistry', './Type'], function (TypeRegistry, Type) {
|
||||
describe('The Type API', function () {
|
||||
let typeRegistryInstance;
|
||||
import TypeRegistry from './TypeRegistry';
|
||||
|
||||
beforeEach(function () {
|
||||
typeRegistryInstance = new TypeRegistry ();
|
||||
typeRegistryInstance.addType('testType', {
|
||||
name: 'Test Type',
|
||||
description: 'This is a test type.',
|
||||
creatable: true
|
||||
});
|
||||
});
|
||||
describe('The Type API', function () {
|
||||
let typeRegistryInstance;
|
||||
|
||||
it('types can be standardized', function () {
|
||||
typeRegistryInstance.addType('standardizationTestType', {
|
||||
label: 'Test Type',
|
||||
description: 'This is a test type.',
|
||||
creatable: true
|
||||
});
|
||||
typeRegistryInstance.standardizeType(typeRegistryInstance.types.standardizationTestType);
|
||||
expect(typeRegistryInstance.get('standardizationTestType').definition.label).toBeUndefined();
|
||||
expect(typeRegistryInstance.get('standardizationTestType').definition.name).toBe('Test Type');
|
||||
});
|
||||
|
||||
it('new types are registered successfully and can be retrieved', function () {
|
||||
expect(typeRegistryInstance.get('testType').definition.name).toBe('Test Type');
|
||||
});
|
||||
|
||||
it('type registry contains new keys', function () {
|
||||
expect(typeRegistryInstance.listKeys ()).toContain('testType');
|
||||
beforeEach(function () {
|
||||
typeRegistryInstance = new TypeRegistry ();
|
||||
typeRegistryInstance.addType('testType', {
|
||||
name: 'Test Type',
|
||||
description: 'This is a test type.',
|
||||
creatable: true
|
||||
});
|
||||
});
|
||||
|
||||
it('types can be standardized', function () {
|
||||
typeRegistryInstance.addType('standardizationTestType', {
|
||||
label: 'Test Type',
|
||||
description: 'This is a test type.',
|
||||
creatable: true
|
||||
});
|
||||
typeRegistryInstance.standardizeType(typeRegistryInstance.types.standardizationTestType);
|
||||
expect(typeRegistryInstance.get('standardizationTestType').definition.label).toBeUndefined();
|
||||
expect(typeRegistryInstance.get('standardizationTestType').definition.name).toBe('Test Type');
|
||||
});
|
||||
|
||||
it('new types are registered successfully and can be retrieved', function () {
|
||||
expect(typeRegistryInstance.get('testType').definition.name).toBe('Test Type');
|
||||
});
|
||||
|
||||
it('type registry contains new keys', function () {
|
||||
expect(typeRegistryInstance.listKeys ()).toContain('testType');
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user