@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering.
357 lines • 14.7 kB
JavaScript
import { FunctionExt } from '../../util';
import { grid } from '../../layout/grid';
import { Node } from '../../model/node';
import { Model } from '../../model/model';
import { View } from '../../view/view';
import { Graph } from '../../graph/graph';
import { Dnd } from '../dnd';
export class Stencil extends View {
constructor(options) {
super();
this.graphs = {};
this.$groups = {};
this.options = Object.assign(Object.assign({}, Stencil.defaultOptions), options);
this.dnd = new Dnd(this.options);
this.onSearch = FunctionExt.debounce(this.onSearch, 200);
this.container = document.createElement('div');
this.$container = this.$(this.container)
.addClass(this.prefixClassName(ClassNames.base))
.attr('data-not-found-text', this.options.notFoundText || 'No matches found');
this.options.collapsable =
options.collapsable &&
options.groups &&
options.groups.some((group) => group.collapsable !== false);
if (this.options.collapsable) {
this.$container.addClass('collapsable');
const collapsed = options.groups &&
options.groups.every((group) => group.collapsed || group.collapsable === false);
if (collapsed) {
this.$container.addClass('collapsed');
}
}
this.$('<div/>')
.addClass(this.prefixClassName(ClassNames.title))
.html(this.options.title)
.appendTo(this.$container);
if (options.search) {
this.$container.addClass('searchable').append(this.renderSearch());
}
this.$content = this.$('<div/>')
.addClass(this.prefixClassName(ClassNames.content))
.appendTo(this.$container);
const globalGraphOptions = options.stencilGraphOptions || {};
if (options.groups && options.groups.length) {
options.groups.forEach((group) => {
const $group = this.$('<div/>')
.addClass(this.prefixClassName(ClassNames.group))
.attr('data-name', group.name);
if ((group.collapsable == null && options.collapsable) ||
group.collapsable !== false) {
$group.addClass('collapsable');
}
$group.toggleClass('collapsed', group.collapsed === true);
const $title = this.$('<h3/>')
.addClass(this.prefixClassName(ClassNames.groupTitle))
.html(group.title || group.name);
const $content = this.$('<div/>').addClass(this.prefixClassName(ClassNames.groupContent));
const graphOptionsInGroup = group.graphOptions;
const graph = new Graph(Object.assign(Object.assign(Object.assign({}, globalGraphOptions), graphOptionsInGroup), { container: document.createElement('div'), model: globalGraphOptions.model || new Model(), width: group.graphWidth || options.stencilGraphWidth, height: group.graphHeight || options.stencilGraphHeight, interacting: false, preventDefaultBlankAction: false }));
$content.append(graph.container);
$group.append($title, $content).appendTo(this.$content);
this.$groups[group.name] = $group;
this.graphs[group.name] = graph;
});
}
else {
const graph = new Graph(Object.assign(Object.assign({}, globalGraphOptions), { container: document.createElement('div'), model: globalGraphOptions.model || new Model(), width: options.stencilGraphWidth, height: options.stencilGraphHeight, interacting: false, preventDefaultBlankAction: false }));
this.$content.append(graph.container);
this.graphs[Private.defaultGroupName] = graph;
}
this.startListening();
return this;
}
get targetScroller() {
const target = this.options.target;
return Graph.isGraph(target) ? target.scroller.widget : target;
}
get targetGraph() {
const target = this.options.target;
return Graph.isGraph(target) ? target : target.graph;
}
get targetModel() {
return this.targetGraph.model;
}
renderSearch() {
return this.$('<div/>')
.addClass(this.prefixClassName(ClassNames.search))
.append(this.$('<input/>')
.attr({
type: 'search',
placeholder: this.options.placeholder || 'Search',
})
.addClass(this.prefixClassName(ClassNames.searchText)));
}
startListening() {
const title = this.prefixClassName(ClassNames.title);
const searchText = this.prefixClassName(ClassNames.searchText);
const groupTitle = this.prefixClassName(ClassNames.groupTitle);
this.delegateEvents({
[`click .${title}`]: 'onTitleClick',
[`touchstart .${title}`]: 'onTitleClick',
[`click .${groupTitle}`]: 'onGroupTitleClick',
[`touchstart .${groupTitle}`]: 'onGroupTitleClick',
[`input .${searchText}`]: 'onSearch',
[`focusin .${searchText}`]: 'onSearchFocusIn',
[`focusout .${searchText}`]: 'onSearchFocusOut',
});
Object.keys(this.graphs).forEach((groupName) => {
const graph = this.graphs[groupName];
graph.on('cell:mousedown', this.onDragStart, this);
});
}
stopListening() {
this.undelegateEvents();
Object.keys(this.graphs).forEach((groupName) => {
const graph = this.graphs[groupName];
graph.off('cell:mousedown', this.onDragStart, this);
});
}
load(data, groupName) {
if (Array.isArray(data)) {
this.loadGroup(data, groupName);
}
else if (this.options.groups) {
Object.keys(this.options.groups).forEach((groupName) => {
if (data[groupName]) {
this.loadGroup(data[groupName], groupName);
}
});
}
return this;
}
loadGroup(cells, groupName) {
const model = this.getModel(groupName);
if (model) {
const nodes = cells.map((cell) => Node.isNode(cell) ? cell : Node.create(cell));
model.resetCells(nodes);
}
const group = this.getGroup(groupName);
let height = this.options.stencilGraphHeight;
if (group && group.graphHeight != null) {
height = group.graphHeight;
}
const layout = (group && group.layout) || this.options.layout;
if (layout && model) {
FunctionExt.call(layout, this, model, group);
}
if (!height) {
const graph = this.getGraph(groupName);
graph.fitToContent({
minWidth: graph.options.width,
gridHeight: 1,
padding: (group && group.graphPadding) ||
this.options.stencilGraphPadding ||
10,
});
}
return this;
}
onDragStart(args) {
const { e, node } = args;
this.dnd.start(node, e);
}
filter(keyword, filter) {
const found = Object.keys(this.graphs).reduce((memo, groupName) => {
const graph = this.graphs[groupName];
const name = groupName === Private.defaultGroupName ? null : groupName;
const items = graph.model.getNodes().filter((cell) => {
let matched = false;
if (typeof filter === 'function') {
matched = FunctionExt.call(filter, this, cell, keyword, name, this);
}
else if (typeof filter === 'boolean') {
matched = filter;
}
else {
matched = this.isCellMatched(cell, keyword, filter, keyword.toLowerCase() !== keyword);
}
const view = graph.renderer.findViewByCell(cell);
if (view) {
view.$(view.container).toggleClass('unmatched', !matched);
}
return matched;
});
const found = items.length > 0;
const options = this.options;
const model = new Model();
model.resetCells(items);
if (options.layout) {
FunctionExt.call(options.layout, this, model, this.getGroup(groupName));
}
if (this.$groups[groupName]) {
this.$groups[groupName].toggleClass('unmatched', !found);
}
graph.fitToContent({
gridWidth: 1,
gridHeight: 1,
padding: options.stencilGraphPadding || 10,
});
return memo || found;
}, false);
this.$container.toggleClass('not-found', !found);
}
isCellMatched(cell, keyword, filters, ignoreCase) {
if (keyword && filters) {
return Object.keys(filters).some((shape) => {
if (shape === '*' || cell.shape === shape) {
const filter = filters[shape];
if (typeof filter === 'boolean') {
return filter;
}
const paths = Array.isArray(filter) ? filter : [filter];
return paths.some((path) => {
let val = cell.getPropByPath(path);
if (val != null) {
val = `${val}`;
if (!ignoreCase) {
val = val.toLowerCase();
}
return val.indexOf(keyword) >= 0;
}
return false;
});
}
return false;
});
}
return true;
}
onSearch(evt) {
this.filter(evt.target.value, this.options.search);
}
onSearchFocusIn() {
this.$container.addClass('is-focused');
}
onSearchFocusOut() {
this.$container.removeClass('is-focused');
}
onTitleClick() {
if (this.options.collapsable) {
this.$container.toggleClass('collapsed');
if (this.$container.hasClass('collapsed')) {
this.collapseGroups();
}
else {
this.expandGroups();
}
}
}
onGroupTitleClick(evt) {
const $group = this.$(evt.target).closest(`.${this.prefixClassName(ClassNames.group)}`);
this.toggleGroup($group.attr('data-name') || '');
const allCollapsed = Object.keys(this.$groups).every((name) => {
const group = this.getGroup(name);
const $group = this.$groups[name];
return ((group && group.collapsable === false) || $group.hasClass('collapsed'));
});
this.$container.toggleClass('collapsed', allCollapsed);
}
getModel(groupName) {
const graph = this.getGraph(groupName);
return graph ? graph.model : null;
}
getGraph(groupName) {
return this.graphs[groupName || Private.defaultGroupName];
}
getGroup(groupName) {
const groups = this.options.groups;
if (groupName != null && groups && groups.length) {
return groups.find((group) => group.name === groupName);
}
return null;
}
toggleGroup(groupName) {
if (this.isGroupCollapsed(groupName)) {
this.expandGroup(groupName);
}
else {
this.collapseGroup(groupName);
}
return this;
}
collapseGroup(groupName) {
if (this.isGroupCollapsable(groupName)) {
const $group = this.$groups[groupName];
if ($group && !this.isGroupCollapsed(groupName)) {
this.trigger('group:collapse', { name: groupName });
$group.addClass('collapsed');
}
}
return this;
}
expandGroup(groupName) {
if (this.isGroupCollapsable(groupName)) {
const $group = this.$groups[groupName];
if ($group && this.isGroupCollapsed(groupName)) {
this.trigger('group:expand', { name: groupName });
$group.removeClass('collapsed');
}
}
return this;
}
isGroupCollapsable(groupName) {
const $group = this.$groups[groupName];
return $group.hasClass('collapsable');
}
isGroupCollapsed(groupName) {
const $group = this.$groups[groupName];
return $group && $group.hasClass('collapsed');
}
collapseGroups() {
Object.keys(this.$groups).forEach((groupName) => this.collapseGroup(groupName));
return this;
}
expandGroups() {
Object.keys(this.$groups).forEach((groupName) => this.expandGroup(groupName));
return this;
}
onRemove() {
Object.keys(this.graphs).forEach((groupName) => {
const graph = this.graphs[groupName];
graph.view.remove();
delete this.graphs[groupName];
});
this.dnd.remove();
this.stopListening();
this.undelegateDocumentEvents();
}
}
(function (Stencil) {
Stencil.defaultOptions = Object.assign({ stencilGraphWidth: 200, stencilGraphHeight: 800, title: 'Stencil', collapsable: false, placeholder: 'Search', notFoundText: 'No matches found', layout(model, group) {
const options = {
columnWidth: this.options.stencilGraphWidth / 2 - 10,
columns: 2,
rowHeight: 80,
resizeToFit: false,
dx: 10,
dy: 10,
};
grid(model, Object.assign(Object.assign(Object.assign({}, options), this.options.layoutOptions), (group ? group.layoutOptions : {})));
} }, Dnd.defaults);
})(Stencil || (Stencil = {}));
var ClassNames;
(function (ClassNames) {
ClassNames.base = 'widget-stencil';
ClassNames.title = `${ClassNames.base}-title`;
ClassNames.search = `${ClassNames.base}-search`;
ClassNames.searchText = `${ClassNames.search}-text`;
ClassNames.content = `${ClassNames.base}-content`;
ClassNames.group = `${ClassNames.base}-group`;
ClassNames.groupTitle = `${ClassNames.group}-title`;
ClassNames.groupContent = `${ClassNames.group}-content`;
})(ClassNames || (ClassNames = {}));
var Private;
(function (Private) {
Private.defaultGroupName = '__default__';
})(Private || (Private = {}));
//# sourceMappingURL=index.js.map