From 8f4044a344e77cb20931e0d0c644c5435f8acd3d Mon Sep 17 00:00:00 2001 From: damylen Date: Tue, 19 Apr 2022 08:30:31 +0200 Subject: [PATCH 01/40] cs-form: use prop icon for optional properties --- packages/cs-form/src/components/cs-form/cs-form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cs-form/src/components/cs-form/cs-form.html b/packages/cs-form/src/components/cs-form/cs-form.html index bc485f58..418a957b 100644 --- a/packages/cs-form/src/components/cs-form/cs-form.html +++ b/packages/cs-form/src/components/cs-form/cs-form.html @@ -31,7 +31,7 @@ From 50e55f6a316167fa75e8d5a7ff229309f02479bc Mon Sep 17 00:00:00 2001 From: damylen Date: Tue, 19 Apr 2022 08:31:16 +0200 Subject: [PATCH 02/40] cs-form: wrap group buttons --- .../cs-form/src/components/fields/group-buttons-editor.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cs-form/src/components/fields/group-buttons-editor.vue b/packages/cs-form/src/components/fields/group-buttons-editor.vue index 35cf033f..2bcfc53e 100644 --- a/packages/cs-form/src/components/fields/group-buttons-editor.vue +++ b/packages/cs-form/src/components/fields/group-buttons-editor.vue @@ -4,7 +4,7 @@ v-model="target[field._key]" tile :hint="field.hint" - + class="group-btn-toggle" @change="fieldUpdated(field)" group > @@ -31,6 +31,9 @@ - - \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/element-card-extension.ts b/packages/cs-graph/src/components/document/plugins/element-card-extension.ts deleted file mode 100644 index 1becbef6..00000000 --- a/packages/cs-graph/src/components/document/plugins/element-card-extension.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { mergeAttributes } from '@tiptap/core' -import { VueNodeViewRenderer } from '@tiptap/vue-2' -import ElementCardComponent from './element-card-component.vue' -import { GraphElement } from '@csnext/cs-data' -import Paragraph from '@tiptap/extension-paragraph'; - -declare module '@tiptap/core' { - interface Commands { - elementCard: { - /** - * Add a text entity - */ - setElementCard: (entity?: GraphElement) => any, - toggleElementCard: (attributes?: any) => ReturnType; - } - } -} - - -export default Paragraph.extend({ - name: 'element-card', - - draggable: true, - group: 'block', - atom: true, - - addAttributes() { - return { - elementId: { default: undefined} - } - - }, - - parseHTML() { - return [ - { - tag: 'element-card', - }, - ] - }, - - - renderHTML({ HTMLAttributes }) { - return ['element-card', mergeAttributes(HTMLAttributes), 0] - }, - - addCommands() { - return { - toggleElementCard: attributes => ({ commands }) => { - return commands.toggleNode('element-card', 'paragraph', attributes) - }, - setElementCard: (entity?: GraphElement) => ({ tr, dispatch }) => { - // debugger; - if (dispatch) { - - // } else { - // // debugger; - // } - } - - return true - }, - } - }, - - // onUpdate(a: any) { - // console.log(a) - // // debugger; - // }, - - - addNodeView() { - return VueNodeViewRenderer(ElementCardComponent) - }, -}) \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/paragraph-component.vue b/packages/cs-graph/src/components/document/plugins/paragraph-component.vue deleted file mode 100644 index d2d22c01..00000000 --- a/packages/cs-graph/src/components/document/plugins/paragraph-component.vue +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - diff --git a/packages/cs-graph/src/components/document/plugins/paragraph-extension.ts b/packages/cs-graph/src/components/document/plugins/paragraph-extension.ts deleted file mode 100644 index f7c444e8..00000000 --- a/packages/cs-graph/src/components/document/plugins/paragraph-extension.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { mergeAttributes } from '@tiptap/core' -import { VueNodeViewRenderer } from '@tiptap/vue-2' -import ParagraphComponent from './paragraph-component.vue' -import { GraphElement } from '@csnext/cs-data' -import Paragraph from '@tiptap/extension-paragraph'; - -declare module '@tiptap/core' { - interface Commands { - nodeParagraph: { - /** - * Add a text entity - */ - setNodeParagraph: (entity?: GraphElement) => any, - toggleNodeParagraph: (attributes?: any) => ReturnType; - } - } -} - - -export default Paragraph.extend({ - name: 'node-paragraph', - - draggable: true, - group: 'block', - content: 'inline*', - // content: 'inline*', - - addAttributes() { - return { - language_code: { default: 'en' }, - language_score: { default: 0 } - } - - }, - - parseHTML() { - return [ - { - tag: 'node-paragraph', - }, - ] - }, - - - renderHTML({ HTMLAttributes }) { - return ['node-paragraph', mergeAttributes(HTMLAttributes), 0] - }, - - addCommands() { - return { - toggleNodeParagraph: attributes => ({ commands }) => { - return commands.toggleNode('node-paragraph', 'paragraph', attributes) - }, - setNodeParagraph: (entity?: GraphElement) => ({ tr, dispatch }) => { - // debugger; - if (dispatch) { - - // } else { - // // debugger; - // } - } - - return true - }, - } - }, - - // onUpdate(a: any) { - // console.log(a) - // // debugger; - // }, - - - addNodeView() { - return VueNodeViewRenderer(ParagraphComponent) - }, -}) \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/text-entity-component.vue b/packages/cs-graph/src/components/document/plugins/text-entity-component.vue deleted file mode 100644 index c25b945a..00000000 --- a/packages/cs-graph/src/components/document/plugins/text-entity-component.vue +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/text-extension.ts b/packages/cs-graph/src/components/document/plugins/text-extension.ts deleted file mode 100644 index 18d025a3..00000000 --- a/packages/cs-graph/src/components/document/plugins/text-extension.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { mergeAttributes } from '@tiptap/core' -import { VueNodeViewRenderer } from '@tiptap/vue-2' -import EntityComponent from './text-entity-component.vue' -import { TextSelection } from 'prosemirror-state' -import { guidGenerator } from '@csnext/cs-core' -import { FeatureType, TextEntity } from '@csnext/cs-data' -import Text from '@tiptap/extension-text'; - -declare module '@tiptap/core' { - interface Commands { - textEntity: { - /** - * Add a text entity - */ - setTextEntity: (entity?: TextEntity) => any, - setTextEntityType: (entity?: FeatureType) => any, - } - } -} - -function getSelection(selection: any) { - - -} - -export default Text.extend({ - name: 'text-entity', - - group: 'inline', - - draggable: true, - // content: 'inline*', - - inline: true, - - atom: true, - - addAttributes() { - - return { - id: { default: null }, - type: { default: 'person' }, - text: { default: '' }, - kg_id: { default: undefined }, - label: { default: null}, - spacy_label: { default: null} - } - }, - - parseHTML() { - return [ - { - tag: 'text-entity', - }, - ] - }, - - renderText({ node }) { - return `${node.attrs.text}` - }, - - - - addCommands() { - return { - setTextEntity: (entity?: TextEntity) => ({ tr, dispatch }) => { - if (dispatch) { - const node = tr.doc.nodeAt(tr.selection.from); - let text = entity?.text ?? (tr.doc as any).textBetween(tr.selection.from, tr.selection.to -1); - // const text = (node.text as string).substring(); - if (entity?.text !== text) { - tr.selection.$from.pos = tr.selection.from - 1; - text = (tr.doc as any).textBetween(tr.selection.from, tr.selection.to).trim(); - } - - // if (entity?.text !== text) { - // tr.selection.$from.pos = tr.selection.from + 2; - // text = tr.doc.textBetween(tr.selection.from , tr.selection.to); - // } - - // if (entity?.text === text) { - - // console.log(text); - - const { parent, pos } = tr.selection.$from - const posAfter = pos + 1; - const nodeAfter = tr.doc.nodeAt(posAfter) - const id = entity?.id ?? guidGenerator(); - const type = entity?.spacy_label ?? 'node'; - - const newEntity = this.type.create(); - newEntity.attrs = entity as any; // {...{ id : id, type, text: text }, ...entity}; - newEntity.text = text; - tr.replaceSelectionWith(newEntity) - - // end of document - if (!nodeAfter) { - const node = parent.type.contentMatch.defaultType ?.create(); - if (node) { - tr.insert(posAfter, node) - tr.setSelection(TextSelection.create(tr.doc, posAfter)) - } - } - tr.scrollIntoView(); - - // } else { - // // debugger; - // } - } - - return true - }, - } - }, - - // onUpdate(a: any) { - // console.log(a) - // // debugger; - // }, - - - - renderHTML({ node, HTMLAttributes }) { - return ['text-entity', mergeAttributes(HTMLAttributes)] - }, - - addNodeView() { - return VueNodeViewRenderer(EntityComponent) - }, -}) \ No newline at end of file diff --git a/packages/cs-graph/src/components/element-context-menu.vue b/packages/cs-graph/src/components/element-context-menu.vue index 4ae59390..4692a18a 100644 --- a/packages/cs-graph/src/components/element-context-menu.vue +++ b/packages/cs-graph/src/components/element-context-menu.vue @@ -146,6 +146,12 @@ export default class ElementContextMenu extends Vue { // this.listUpdated(); }, }); + for (const tool of this.source.tools) { + const actions = tool.elementActions(this.element); + if (actions && actions.length > 0) { + this.contextMenuitems = this.contextMenuitems.concat(actions); + } + } this.contextMenuitems.push({ title: 'rename', icon: 'mdi-form-textbox', From 4af09b5349006e30a1bf3e35bde18aab41abc3ca Mon Sep 17 00:00:00 2001 From: damylen Date: Tue, 19 Apr 2022 08:34:15 +0200 Subject: [PATCH 04/40] cs-graph: added support for element actions for tools --- packages/cs-graph/src/classes/tool.ts | 6 +++--- .../cs-graph/src/components/element/element-actions.ts | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/cs-graph/src/classes/tool.ts b/packages/cs-graph/src/classes/tool.ts index 198724ab..788eb27e 100644 --- a/packages/cs-graph/src/classes/tool.ts +++ b/packages/cs-graph/src/classes/tool.ts @@ -1,6 +1,6 @@ -import { IWidget } from '@csnext/cs-core'; +import { IMenu, IWidget } from '@csnext/cs-core'; import { Component } from 'vue'; - +import { GraphElement} from '@csnext/cs-data'; export interface ITool { title: string; subtitle?: string; @@ -9,7 +9,7 @@ export interface ITool { icon?: string; component?: Component; disbled?: boolean; - + elementActions?: (element: GraphElement) => IMenu[] | undefined; busy?: boolean; widget?: IWidget; action?: () => Promise; diff --git a/packages/cs-graph/src/components/element/element-actions.ts b/packages/cs-graph/src/components/element/element-actions.ts index 7c6b20e3..dbc32fa7 100644 --- a/packages/cs-graph/src/components/element/element-actions.ts +++ b/packages/cs-graph/src/components/element/element-actions.ts @@ -34,6 +34,14 @@ export class ElementActions { callAfter(i); }, }); + for (const tool of source.tools) { + if (tool.elementActions) { + const actions = tool.elementActions(element); + if (actions && actions.length > 0) { + menuItems = menuItems.concat(actions); + } + } + } menuItems.push({ title: 'add to visualisation', icon: 'mdi-playlist-plus', From 8b74a1867ae7f8d2b802d534ae857b31324bbacf Mon Sep 17 00:00:00 2001 From: damylen Date: Tue, 19 Apr 2022 08:35:07 +0200 Subject: [PATCH 05/40] cs-graph: support for text-actions in documents, restructure document folders --- .../components/document/document-viewer.vue | 182 +++----- .../element-card/element-card-component.vue | 148 ++++++ .../element-card/element-card-extension.ts | 75 +++ .../element-property-component.vue | 102 +++++ .../element-property-extension.ts | 79 ++++ .../node-paragraph/paragraph-component.vue | 102 +++++ .../node-paragraph/paragraph-extension.ts | 77 ++++ .../text-entity/text-entity-component.vue | 275 +++++++++++ .../nodes/text-entity/text-extension.ts | 130 ++++++ .../document/plugins/entity-list.vue | 119 ----- .../src/components/document/plugins/entity.ts | 110 ----- .../document/plugins/snippet-list.vue | 95 ---- .../components/document/plugins/snippet.ts | 91 ---- .../document/plugins/text-action/sub-menu.vue | 18 +- .../plugins/text-action/text-action-list.vue | 430 ++++++++++-------- .../plugins/text-action/text-action.ts | 36 +- .../suggestion-list.vue | 100 +--- .../document/plugins/text-mention.ts | 6 - 18 files changed, 1331 insertions(+), 844 deletions(-) create mode 100644 packages/cs-graph/src/components/document/nodes/element-card/element-card-component.vue create mode 100644 packages/cs-graph/src/components/document/nodes/element-card/element-card-extension.ts create mode 100644 packages/cs-graph/src/components/document/nodes/element-property/element-property-component.vue create mode 100644 packages/cs-graph/src/components/document/nodes/element-property/element-property-extension.ts create mode 100644 packages/cs-graph/src/components/document/nodes/node-paragraph/paragraph-component.vue create mode 100644 packages/cs-graph/src/components/document/nodes/node-paragraph/paragraph-extension.ts create mode 100644 packages/cs-graph/src/components/document/nodes/text-entity/text-entity-component.vue create mode 100644 packages/cs-graph/src/components/document/nodes/text-entity/text-extension.ts delete mode 100644 packages/cs-graph/src/components/document/plugins/entity-list.vue delete mode 100644 packages/cs-graph/src/components/document/plugins/entity.ts delete mode 100644 packages/cs-graph/src/components/document/plugins/snippet-list.vue delete mode 100644 packages/cs-graph/src/components/document/plugins/snippet.ts delete mode 100644 packages/cs-graph/src/components/document/plugins/text-mention.ts diff --git a/packages/cs-graph/src/components/document/document-viewer.vue b/packages/cs-graph/src/components/document/document-viewer.vue index 119e12d5..03de79f9 100644 --- a/packages/cs-graph/src/components/document/document-viewer.vue +++ b/packages/cs-graph/src/components/document/document-viewer.vue @@ -89,6 +89,12 @@
+ mdi-label + mdi-pencil + mdi-card + mdi-format-paragraph mdi-format-bold @@ -139,11 +145,7 @@ mdi-undo mdi-redo - mdi-label - mdi-format-paragraph - mdi-card +
@@ -155,7 +157,15 @@
- {{ currentDocument.properties.name }} + + + mdi-content-save + mdi-pencil + + + {{ currentDocument.properties.name }} + +
Created {{ publishedDate() }} ago, @@ -221,22 +231,24 @@ import StarterKit from '@tiptap/starter-kit'; import { BubbleMenu, Editor, EditorContent, FloatingMenu, Node } from '@tiptap/vue-2'; import simplebar from 'simplebar-vue'; import { Component, Prop, Ref, Vue } from 'vue-property-decorator'; -import { IImportPlugin } from '../..'; +import { IImportPlugin, suggestion } from '../..'; import { DocUtils } from '../../utils/doc-utils'; import { DocDatasource, GraphDocument, ITool } from './../../'; import Commands from './plugins/commands/commands'; import commandSuggestion from './plugins/commands/commands-suggestion'; -import ElementCardExtension from './plugins/element-card-extension'; -import ParagraphExtension from './plugins/paragraph-extension'; -import SnippetList from './plugins/snippet-list.vue'; -import SuggestionList from './plugins/text-entity-suggestion/suggestion-list.vue'; -import suggestion from './plugins/text-entity-suggestion/suggestion'; +import ElementCardExtension from './nodes/element-card/element-card-extension'; + +import ParagraphExtension from './nodes/node-paragraph/paragraph-extension'; +import ElementPropertyExtension from './nodes/element-property/element-property-extension'; + +import TaskList from '@tiptap/extension-task-list' +import TaskItem from '@tiptap/extension-task-item' + import ActionList from './plugins/text-action/text-action-list.vue'; import textAction from './plugins/text-action/text-action'; -import TextExtension from './plugins/text-extension'; -import { TextMention } from './plugins/text-mention'; +import TextExtension from './nodes/text-entity/text-extension'; import SelectionPopup from './selection-popup.vue'; @@ -246,10 +258,8 @@ import SelectionPopup from './selection-popup.vue'; EditorContent, BubbleMenu, FloatingMenu, - SimpleRelationLineSection, - SuggestionList, + SimpleRelationLineSection, ActionList, - SnippetList, // EditorMenuBubble, SelectionPopup, }, @@ -274,6 +284,13 @@ export default class DocumentViewer extends WidgetBase { return ((this.widget?.options) as any).hideHeader || false; } + public toggleTitle() { + this.editTitle = !this.editTitle; + if (!this.editTitle && this.source && this.currentDocument) { + this.source.saveNode(this.currentDocument); + } + } + @Prop() public content?: any; @Ref() @@ -290,6 +307,7 @@ export default class DocumentViewer extends WidgetBase { public isLoading?: boolean; public showPopup = true; public extendedToolbar = false; + public editTitle = false; // public document?: GraphDocument; public node?: Node; // v-if="selectionTo !== undefined && selectionFrom !== undefined" @@ -306,8 +324,6 @@ export default class DocumentViewer extends WidgetBase { this.editor.chain().focus().setTextEntity({ spacy_label: this.entityBubbleSelection?.type }).run(); this.syncDocumentState(); DocUtils.syncEntities(this.currentDocument, this.source, this.currentDocument.properties?.doc?.content, true); - - // alert(this.entityBubbleSelection?.title); } public setEditorMode(mode: string) { @@ -315,8 +331,7 @@ export default class DocumentViewer extends WidgetBase { if (this.editor) { this.editor!.setEditable(mode === 'EDIT'); } - this.$forceUpdate(); - // this.currentDocument!.properties!.edit_mode = mode; + this.$forceUpdate(); } public setElementCard() { @@ -339,23 +354,9 @@ export default class DocumentViewer extends WidgetBase { return; } - this.editor.chain().focus().toggleNodeParagraph().run(); - // this.syncDocumentState(); - // this.source.syncEntities( - // this.currentDocument, - // this.currentDocument.doc.content, - // true - // ); - - // alert(this.entityBubbleSelection?.title); + this.editor.chain().focus().toggleNodeParagraph().run(); } - // public get document(): GraphDocument | undefined { - // if (this.currentDocument) { - // return this.currentDocument; - // } - // } - public updateContextMenu() { this.contextMenuitems = []; this.contextMenuitems.push({ @@ -395,19 +396,6 @@ export default class DocumentViewer extends WidgetBase { DocUtils.syncEntities(this.currentDocument, this.source, this.currentDocument.properties?.doc?.content, true); } - // public setTextParagraph() { - // if (!this.currentDocument || !this.editor) { - // return; - // } - // this.editor.chain().focus().setTextParagraph().run(); - // this.syncDocumentState(); - // this.source.syncEntities( - // this.currentDocument, - // this.currentDocument.doc.content, - // true - // ); - // } - public beforeDestroy() { if (!this.editor) { return; @@ -493,14 +481,6 @@ export default class DocumentViewer extends WidgetBase { return; } - - - - // this.highlight = new Highlight({ - // disableRegex: false, - // // entities: this.currentDocument?.entities - // }); - if (this.editor && destroy) { this.editor.destroy(); this.source.editor = undefined; @@ -515,19 +495,20 @@ export default class DocumentViewer extends WidgetBase { Dropcursor, ParagraphExtension, ElementCardExtension, + ElementPropertyExtension, + TaskList, + TaskItem, Highlight, Placeholder, Commands.configure({ commandSuggestion, }), Mention.extend({ + document: this.currentDocument, name: 'text-action'}).configure({ suggestion: textAction }), - TextMention.configure({ - // renderLabel: (props) => { - // return 'text-entity' - // }, + Mention.configure({ HTMLAttributes: { class: 'text-entity', name: 'text-entity', @@ -535,25 +516,8 @@ export default class DocumentViewer extends WidgetBase { suggestion, }), ], - onTransaction({ transaction }) { - // transaction. - // if (transaction.steps) { - // for (const step of transaction.steps) { - // console.log(step); - // } - // } - // console.log(transaction); - // debugger; - }, - // content: ` - //

- // This is still the text editor you’re used to, but enriched with node views. - //

- // component 1 - //

- // Did you see that? That’s a Vue component. We are really living in the future. - //

- // `, + onTransaction({ transaction }) { + }, content: this.currentDocument?.properties?.doc, editable: true, editorProps: { @@ -581,7 +545,7 @@ export default class DocumentViewer extends WidgetBase { type: 'doc', content: [ { - type: 'paragraph', + type: 'node-paragraph', content: [ { type: 'text', @@ -596,24 +560,7 @@ export default class DocumentViewer extends WidgetBase { this.state.element = doc; - this.loaded = true; - - // this.source.activateDocument(doc).then(() => { - // this.widget.options!.title = doc.name; - // if (this.currentDocument) { - // // this.source?.openDocumentDetails(this.currentDocument, false); - // if (this.currentDocument._entities) { - // for (const ent of this.currentDocument._entities) { - // if (ent._node) { - // // ent._node._included = true; - // } - // } - // } - - // this.loaded = true; - // this.busManager.subscribe(this.source!.bus, 'document-entities', () => {}); - // } - // }); + this.loaded = true; } } @@ -658,16 +605,6 @@ export default class DocumentViewer extends WidgetBase { this.updateEditor(); } - if (this.currentDocument.properties?.doc) { - // this.currentDocument.properties.doc = JSON.parse( - // JSON.stringify(this.currentDocument.properties.doc, (key, value: any) => { - // if (value === null) { - // return undefined; - // } - // return value; - // })) - } - this.editor?.chain().clearContent().setContent(this.currentDocument.properties?.doc, false).run(); this.updateEntityTypes(); }); @@ -1158,6 +1095,33 @@ export default class DocumentViewer extends WidgetBase { } + + + + + + + \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/nodes/element-card/element-card-extension.ts b/packages/cs-graph/src/components/document/nodes/element-card/element-card-extension.ts new file mode 100644 index 00000000..1becbef6 --- /dev/null +++ b/packages/cs-graph/src/components/document/nodes/element-card/element-card-extension.ts @@ -0,0 +1,75 @@ +import { mergeAttributes } from '@tiptap/core' +import { VueNodeViewRenderer } from '@tiptap/vue-2' +import ElementCardComponent from './element-card-component.vue' +import { GraphElement } from '@csnext/cs-data' +import Paragraph from '@tiptap/extension-paragraph'; + +declare module '@tiptap/core' { + interface Commands { + elementCard: { + /** + * Add a text entity + */ + setElementCard: (entity?: GraphElement) => any, + toggleElementCard: (attributes?: any) => ReturnType; + } + } +} + + +export default Paragraph.extend({ + name: 'element-card', + + draggable: true, + group: 'block', + atom: true, + + addAttributes() { + return { + elementId: { default: undefined} + } + + }, + + parseHTML() { + return [ + { + tag: 'element-card', + }, + ] + }, + + + renderHTML({ HTMLAttributes }) { + return ['element-card', mergeAttributes(HTMLAttributes), 0] + }, + + addCommands() { + return { + toggleElementCard: attributes => ({ commands }) => { + return commands.toggleNode('element-card', 'paragraph', attributes) + }, + setElementCard: (entity?: GraphElement) => ({ tr, dispatch }) => { + // debugger; + if (dispatch) { + + // } else { + // // debugger; + // } + } + + return true + }, + } + }, + + // onUpdate(a: any) { + // console.log(a) + // // debugger; + // }, + + + addNodeView() { + return VueNodeViewRenderer(ElementCardComponent) + }, +}) \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/nodes/element-property/element-property-component.vue b/packages/cs-graph/src/components/document/nodes/element-property/element-property-component.vue new file mode 100644 index 00000000..8988dd37 --- /dev/null +++ b/packages/cs-graph/src/components/document/nodes/element-property/element-property-component.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/packages/cs-graph/src/components/document/nodes/element-property/element-property-extension.ts b/packages/cs-graph/src/components/document/nodes/element-property/element-property-extension.ts new file mode 100644 index 00000000..a5001067 --- /dev/null +++ b/packages/cs-graph/src/components/document/nodes/element-property/element-property-extension.ts @@ -0,0 +1,79 @@ +import { mergeAttributes } from '@tiptap/core' +import { VueNodeViewRenderer } from '@tiptap/vue-2' +import ElementPropertyComponent from './element-property-component.vue'; +import { GraphElement } from '@csnext/cs-data' +import Paragraph from '@tiptap/extension-paragraph'; + +declare module '@tiptap/core' { + interface Commands { + elementProperty: { + /** + * Add a text entity + */ + setElementProperty: (entity?: GraphElement) => any, + toggleElementProperty: (attributes?: any) => ReturnType; + } + } +} + + +export default Paragraph.extend({ + name: 'element-property', + + draggable: true, + group: 'inline', + content: 'inline*', + inline: true, + atom: true, + // content: 'inline*', + + addAttributes() { + return { + key: { default: null }, + value: { default: undefined } + } + + }, + + parseHTML() { + return [ + { + tag: 'element-property', + }, + ] + }, + + + renderHTML({ HTMLAttributes }) { + return ['element-property', mergeAttributes(HTMLAttributes), 0] + }, + + addCommands() { + return { + toggleElementProperty: attributes => ({ commands }) => { + return commands.setNode('element-property', attributes) + }, + setElementProperty: (entity?: GraphElement, key?: string, value?: any) => ({ tr, dispatch }) => { + // debugger; + if (dispatch) { + + // } else { + // // debugger; + // } + } + + return true + }, + } + }, + + // onUpdate(a: any) { + // console.log(a) + // // debugger; + // }, + + + addNodeView() { + return VueNodeViewRenderer(ElementPropertyComponent) + }, +}) \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/nodes/node-paragraph/paragraph-component.vue b/packages/cs-graph/src/components/document/nodes/node-paragraph/paragraph-component.vue new file mode 100644 index 00000000..6e884c80 --- /dev/null +++ b/packages/cs-graph/src/components/document/nodes/node-paragraph/paragraph-component.vue @@ -0,0 +1,102 @@ + + + + + + + diff --git a/packages/cs-graph/src/components/document/nodes/node-paragraph/paragraph-extension.ts b/packages/cs-graph/src/components/document/nodes/node-paragraph/paragraph-extension.ts new file mode 100644 index 00000000..f7c444e8 --- /dev/null +++ b/packages/cs-graph/src/components/document/nodes/node-paragraph/paragraph-extension.ts @@ -0,0 +1,77 @@ +import { mergeAttributes } from '@tiptap/core' +import { VueNodeViewRenderer } from '@tiptap/vue-2' +import ParagraphComponent from './paragraph-component.vue' +import { GraphElement } from '@csnext/cs-data' +import Paragraph from '@tiptap/extension-paragraph'; + +declare module '@tiptap/core' { + interface Commands { + nodeParagraph: { + /** + * Add a text entity + */ + setNodeParagraph: (entity?: GraphElement) => any, + toggleNodeParagraph: (attributes?: any) => ReturnType; + } + } +} + + +export default Paragraph.extend({ + name: 'node-paragraph', + + draggable: true, + group: 'block', + content: 'inline*', + // content: 'inline*', + + addAttributes() { + return { + language_code: { default: 'en' }, + language_score: { default: 0 } + } + + }, + + parseHTML() { + return [ + { + tag: 'node-paragraph', + }, + ] + }, + + + renderHTML({ HTMLAttributes }) { + return ['node-paragraph', mergeAttributes(HTMLAttributes), 0] + }, + + addCommands() { + return { + toggleNodeParagraph: attributes => ({ commands }) => { + return commands.toggleNode('node-paragraph', 'paragraph', attributes) + }, + setNodeParagraph: (entity?: GraphElement) => ({ tr, dispatch }) => { + // debugger; + if (dispatch) { + + // } else { + // // debugger; + // } + } + + return true + }, + } + }, + + // onUpdate(a: any) { + // console.log(a) + // // debugger; + // }, + + + addNodeView() { + return VueNodeViewRenderer(ParagraphComponent) + }, +}) \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/nodes/text-entity/text-entity-component.vue b/packages/cs-graph/src/components/document/nodes/text-entity/text-entity-component.vue new file mode 100644 index 00000000..16c85afd --- /dev/null +++ b/packages/cs-graph/src/components/document/nodes/text-entity/text-entity-component.vue @@ -0,0 +1,275 @@ + + + + + + + \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/nodes/text-entity/text-extension.ts b/packages/cs-graph/src/components/document/nodes/text-entity/text-extension.ts new file mode 100644 index 00000000..18d025a3 --- /dev/null +++ b/packages/cs-graph/src/components/document/nodes/text-entity/text-extension.ts @@ -0,0 +1,130 @@ +import { mergeAttributes } from '@tiptap/core' +import { VueNodeViewRenderer } from '@tiptap/vue-2' +import EntityComponent from './text-entity-component.vue' +import { TextSelection } from 'prosemirror-state' +import { guidGenerator } from '@csnext/cs-core' +import { FeatureType, TextEntity } from '@csnext/cs-data' +import Text from '@tiptap/extension-text'; + +declare module '@tiptap/core' { + interface Commands { + textEntity: { + /** + * Add a text entity + */ + setTextEntity: (entity?: TextEntity) => any, + setTextEntityType: (entity?: FeatureType) => any, + } + } +} + +function getSelection(selection: any) { + + +} + +export default Text.extend({ + name: 'text-entity', + + group: 'inline', + + draggable: true, + // content: 'inline*', + + inline: true, + + atom: true, + + addAttributes() { + + return { + id: { default: null }, + type: { default: 'person' }, + text: { default: '' }, + kg_id: { default: undefined }, + label: { default: null}, + spacy_label: { default: null} + } + }, + + parseHTML() { + return [ + { + tag: 'text-entity', + }, + ] + }, + + renderText({ node }) { + return `${node.attrs.text}` + }, + + + + addCommands() { + return { + setTextEntity: (entity?: TextEntity) => ({ tr, dispatch }) => { + if (dispatch) { + const node = tr.doc.nodeAt(tr.selection.from); + let text = entity?.text ?? (tr.doc as any).textBetween(tr.selection.from, tr.selection.to -1); + // const text = (node.text as string).substring(); + if (entity?.text !== text) { + tr.selection.$from.pos = tr.selection.from - 1; + text = (tr.doc as any).textBetween(tr.selection.from, tr.selection.to).trim(); + } + + // if (entity?.text !== text) { + // tr.selection.$from.pos = tr.selection.from + 2; + // text = tr.doc.textBetween(tr.selection.from , tr.selection.to); + // } + + // if (entity?.text === text) { + + // console.log(text); + + const { parent, pos } = tr.selection.$from + const posAfter = pos + 1; + const nodeAfter = tr.doc.nodeAt(posAfter) + const id = entity?.id ?? guidGenerator(); + const type = entity?.spacy_label ?? 'node'; + + const newEntity = this.type.create(); + newEntity.attrs = entity as any; // {...{ id : id, type, text: text }, ...entity}; + newEntity.text = text; + tr.replaceSelectionWith(newEntity) + + // end of document + if (!nodeAfter) { + const node = parent.type.contentMatch.defaultType ?.create(); + if (node) { + tr.insert(posAfter, node) + tr.setSelection(TextSelection.create(tr.doc, posAfter)) + } + } + tr.scrollIntoView(); + + // } else { + // // debugger; + // } + } + + return true + }, + } + }, + + // onUpdate(a: any) { + // console.log(a) + // // debugger; + // }, + + + + renderHTML({ node, HTMLAttributes }) { + return ['text-entity', mergeAttributes(HTMLAttributes)] + }, + + addNodeView() { + return VueNodeViewRenderer(EntityComponent) + }, +}) \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/entity-list.vue b/packages/cs-graph/src/components/document/plugins/entity-list.vue deleted file mode 100644 index 37d08531..00000000 --- a/packages/cs-graph/src/components/document/plugins/entity-list.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/entity.ts b/packages/cs-graph/src/components/document/plugins/entity.ts deleted file mode 100644 index 061750fd..00000000 --- a/packages/cs-graph/src/components/document/plugins/entity.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { Node, mergeAttributes } from '@tiptap/core' -import Suggestion, { SuggestionOptions } from '@tiptap/suggestion' -import Vue from 'vue'; - -export type EntityOptions = { - HTMLAttributes: Record, - suggestion: Omit, -} - -export const Entity = Node.create({ - name: 'entity', - - defaultOptions: { - HTMLAttributes: {}, - suggestion: { - char: '#', - command: ({ editor, range, props }) => { - (editor - .chain() - .focus() as any) - .replaceRange(range, 'entity', props) - .insertContent(' ') - .run() - }, - allow: ({ editor, range }) => { - return (editor.can() as any).replaceRange(range, 'entity') - }, - }, - }, - - group: 'inline', - - inline: true, - - selectable: false, - - atom: true, - - addAttributes() { - return { - id: { - default: null, - parseHTML: element => { - return { - id: element.getAttribute('data-entity'), - } - }, - renderHTML: attributes => { - if (!attributes.id) { - return {} - } - - return { - 'data-entity': attributes.id, - } - }, - }, - } - }, - - parseHTML() { - return [ - { - tag: 'span[data-entity]', - }, - ] - }, - - renderHTML({ node, HTMLAttributes }) { - return ['span', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), `#${node.attrs.id}`] - }, - - renderText({ node }) { - return `@${node.attrs.id}` - }, - - addKeyboardShortcuts() { - return { - Backspace: () => this.editor.commands.command(({ tr, state }) => { - let isEntity = false - const { selection } = state - const { empty, anchor } = selection - - if (!empty) { - return false - } - - state.doc.nodesBetween(anchor - 1, anchor, (node: any, pos: any) => { - if (node.type.name === 'entity') { - isEntity = true - tr.insertText(this.options.suggestion.char || '', pos, pos + node.nodeSize) - - return false - } - }) - - return isEntity - }), - } - }, - - addProseMirrorPlugins() { - return [ - Suggestion({ - editor: this.editor, - ...this.options.suggestion, - }), - ] - }, -}) \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/snippet-list.vue b/packages/cs-graph/src/components/document/plugins/snippet-list.vue deleted file mode 100644 index b81fed91..00000000 --- a/packages/cs-graph/src/components/document/plugins/snippet-list.vue +++ /dev/null @@ -1,95 +0,0 @@ - - - - \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/snippet.ts b/packages/cs-graph/src/components/document/plugins/snippet.ts deleted file mode 100644 index 8ac16bf8..00000000 --- a/packages/cs-graph/src/components/document/plugins/snippet.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { VueRenderer } from '@tiptap/vue-2' -import tippy from 'tippy.js' -import Vue from 'vue' -import { DocDatasource, DocUtils, GraphDocument, Suggestions } from '../../..' -import SnippetList from './snippet-list.vue' - -export default { - name: 'element-card', - char: '@', - items: (props) => { - const source = props.editor.view._props.source as DocDatasource; - const query = props.query; - let res : any[]= []; - if (query && source?.fuse) { - res = source.fuse.search(query).slice(0, 10); - console.log(res.length) - return res; - } else - { - return []; - } - }, - - command: (a) => { - console.log(a); - Vue.nextTick(async ()=>{ - a.editor.chain().focus().setTextSelection(a.range).setElementCard(a.props).run(); - const source = a?.editor?.view?._props?.source; - if (source && a?.editor?.view?._props?.document) { - DocUtils.syncEntities(a.editor.view._props.document as GraphDocument, source, undefined, true); - await source.entityParser.callDocument(a.editor.view._props.document, source); - } - }) - return; - }, - - render: () => { - let component: VueRenderer - let popup - let source: DocDatasource - - return { - - onStart: props => { - - - source = props.editor.view._props.source as DocDatasource; - props.source = source; - component = new VueRenderer(SnippetList, { - parent: this, - propsData: props, - }) - - popup = tippy('body', { - getReferenceClientRect: props.clientRect, - appendTo: () => document.getElementById('app') as HTMLElement, - content: component.element, - showOnCreate: true, - interactive: true, - trigger: 'manual', - placement: 'bottom-start', - }) - }, - - onUpdate(props) { - component.updateProps(props) - - popup[0].setProps({ - getReferenceClientRect: props.clientRect - }) - }, - - onKeyDown(props) { - if (props.event.key === 'Escape') { - popup[0].hide() - - return true - } - return (component.ref! as any).onKeyDown(props) - - }, - - onExit() { - popup[0].destroy() - if (component) { - component.destroy() - } - }, - } - }, -} \ No newline at end of file diff --git a/packages/cs-graph/src/components/document/plugins/text-action/sub-menu.vue b/packages/cs-graph/src/components/document/plugins/text-action/sub-menu.vue index 57d60221..585dce82 100644 --- a/packages/cs-graph/src/components/document/plugins/text-action/sub-menu.vue +++ b/packages/cs-graph/src/components/document/plugins/text-action/sub-menu.vue @@ -12,14 +12,11 @@ \ No newline at end of file + diff --git a/packages/cs-graph/src/components/document/plugins/text-action/text-action.ts b/packages/cs-graph/src/components/document/plugins/text-action/text-action.ts index c7094dfb..1372c9f0 100644 --- a/packages/cs-graph/src/components/document/plugins/text-action/text-action.ts +++ b/packages/cs-graph/src/components/document/plugins/text-action/text-action.ts @@ -9,33 +9,28 @@ export default { name: 'text-action', char: '/', pluginKey: new PluginKey('text-action'), - items: (props) => { - const source = props.editor.view._props.source as DocDatasource; - const query = props.query; - let res: any[] = []; - if (query && source?.fuse) { - res = source.fuse.search(query).slice(0, 10); - console.log(res.length); - return res; - } else { - return []; - } + + items: (props) => { + return []; }, command: (a) => { console.log(a); Vue.nextTick(async () => { - a.editor.chain().focus().setTextSelection(a.range).setTextEntity(a.props).run(); - const source = a?.editor?.view?._props?.source; - if (source && a?.editor?.view?._props?.document) { - DocUtils.syncEntities(a.editor.view._props.document as GraphDocument, source, undefined, true); - await source.entityParser.callDocument(a.editor.view._props.document, source); - } + + // a.editor.chain().focus().setTextSelection(a.range).setTextEntity(a.props).run(); + // const source = a?.editor?.view?._props?.source; + // if (source && a?.editor?.view?._props?.document) { + // DocUtils.syncEntities(a.editor.view._props.document as GraphDocument, source, undefined, true); + // await source.entityParser.callDocument(a.editor.view._props.document, source); + // } }); return; }, + + render: () => { let component: VueRenderer; let popup; @@ -45,9 +40,12 @@ export default { onStart: (props) => { source = props.editor.view._props.source as DocDatasource; props.source = source; + props.document = props.editor.view._props.document as GraphDocument; + component = new VueRenderer(ActionList, { parent: this, propsData: props, + $vuetify: $cs.vuetify }); popup = tippy('body', { @@ -62,7 +60,9 @@ export default { }, onUpdate(props) { - component.updateProps(props); + if (component) { + component.updateProps(props); + } popup[0].setProps({ getReferenceClientRect: props.clientRect, diff --git a/packages/cs-graph/src/components/document/plugins/text-entity-suggestion/suggestion-list.vue b/packages/cs-graph/src/components/document/plugins/text-entity-suggestion/suggestion-list.vue index 5ec31734..88a5588f 100644 --- a/packages/cs-graph/src/components/document/plugins/text-entity-suggestion/suggestion-list.vue +++ b/packages/cs-graph/src/components/document/plugins/text-entity-suggestion/suggestion-list.vue @@ -41,48 +41,6 @@ export default class MentionList extends Vue { this.selectedIndex! = 0; } - // props: { - // items: { - // type: Array, - // required: true, - // }, - - // command: { - // type: Function, - // required: true, - // }, - // }, - - // data() { - // return { - // selectedIndex: 0, - // }; - // }, - - // watch: { - // items() { - // this.selectedIndex! = 0; - // }, - // }, - // methods: { - // onKeyDown({ event }) { - // if (event.key === 'ArrowUp') { - // this.upHandler(); - // return true; - // } - - // if (event.key === 'ArrowDown') { - // this.downHandler(); - // return true; - // } - - // if (event.key === 'Enter') { - // this.enterHandler(); - // return true; - // } - - // return false; - // }, private onKeyDown({event}) { if (event.key === 'ArrowUp') { this.upHandler(); @@ -116,67 +74,11 @@ export default class MentionList extends Vue { } selectItem(index) { - const item = this.items[index]; - // this.$options.propsData.editor.chain().focus().setTextEntity({ spacy_label: this.entityBubbleSelection?.type }).run(); - // setTextEntity({ spacy_label: this.entityBubbleSelection?.type }); - // console.log(item); + const item = this.items[index]; if (item) { this.command({ name:'text-entity', id: `entity-${idGenerator()}`, label: item.item.properties.name, text: item.item.properties.name, spacy_label: item.item._featureType.type, kg_id: item.item.id }); } } - - // public selectedIndex = 0; - // public testitems = ['Foo', 'Bar', 'Fizz', 'Buzz']; - - // onKeyDown({ event }) { - // if (event.key === 'ArrowUp') { - // this.upHandler() - // return true - // } - - // if (event.key === 'ArrowDown') { - // this.downHandler() - // return true - // } - - // if (event.key === 'Enter') { - // this.enterHandler() - // return true - // } - - // return false - // } - - // upHandler() { - // this.selectedIndex! = ((this.selectedIndex! + this.items.length) - 1) % this.items.length - // } - - // downHandler() { - // this.selectedIndex! = (this.selectedIndex! + 1) % this.items.length - // } - - // enterHandler() { - // this.selectItem(this.selectedIndex!) - // } - - // selectItem(index) { - // const item = this.items[index] - - // // if (item) { - // // this.command({ id: item }) - // // } - // } - - // mounted() { - // console.log(this); - - // console.log(this.$options.propsData); - // console.log('init mention list'); - // // setInterval(()=> { - // // this.$forceUpdate(); - // // }, 2000) - // // alert('added'); - // } } diff --git a/packages/cs-graph/src/components/document/plugins/text-mention.ts b/packages/cs-graph/src/components/document/plugins/text-mention.ts deleted file mode 100644 index c1f769aa..00000000 --- a/packages/cs-graph/src/components/document/plugins/text-mention.ts +++ /dev/null @@ -1,6 +0,0 @@ -import Mention from '@tiptap/extension-mention'; - -export const TextMention = Mention; - -export const SnippetMention = Mention; - From 20f89fad8a0c8a851ea96ab68cd7d792453386ca Mon Sep 17 00:00:00 2001 From: damylen Date: Tue, 19 Apr 2022 08:37:28 +0200 Subject: [PATCH 06/40] cs-graph: added element context menu to element info --- .../src/components/element/element-info.vue | 91 +++++++++---------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/packages/cs-graph/src/components/element/element-info.vue b/packages/cs-graph/src/components/element/element-info.vue index 27d7eaaf..bdf076b7 100644 --- a/packages/cs-graph/src/components/element/element-info.vue +++ b/packages/cs-graph/src/components/element/element-info.vue @@ -36,53 +36,56 @@ mdi-folder-outline - + - + - - - - - - - + + - -
timeline vertical
+
timeline vertical
-
- - Today - - mdi-chevron-left - - - mdi-chevron-right - - - {{ $refs.calendar.title }} - - - - +
@@ -614,7 +680,7 @@ :source="rows" :columns="columns" > --> -
+ p.type === PropertyValueType.options); + this.filterProperties.forEach((p) => { + const panel = new FilterPanel(); + panel.title = p.label; + panel.propertyType = p; + panel.selectedOptions = []; + this.filterPanels.push(panel); + }); + } + } + private openContextMenu(e: any) { e.preventDefault(); const elementId = e.currentTarget?.dataset.elementid || e.path[3].dataset.elementid; @@ -1099,9 +1189,18 @@ export default class ElementDataGrid extends WidgetBase { return; } const rows = this.gridApi.getSelectedRows(); + this.selectedElements = []; + if (!rows) { + return; + } if (rows.length === 1) { this.selectEntity(rows[0]); } + if (rows.length > 0) { + for (const row of rows) { + this.selectedElements.push(row); + } + } } public resize() { @@ -1263,6 +1362,52 @@ export default class ElementDataGrid extends WidgetBase { } } + public exportCsv() { + if (!this.source || !this.items) { + return; + } + // const csv = this.source.exportCsv(); + const exportItems: any[] = []; + for (const i of this.items) { + let flat = GraphElement.getFlat(i); + if (!flat.properties) { return; } + delete flat.properties.id; + delete flat.properties.classId; + let id = flat.id; + let classId = flat.classId; + if (i._featureType?.properties) { + for (const pt of i._featureType.properties) { + if (pt.required && pt.key) { + if (pt.type === PropertyValueType.relation) { + if (i._outgoing) { + const rel = i._outgoing.filter((r) => r.classId === pt.relation!.type); + if (!rel || rel.length === 0) { + flat.properties[pt.key] = flat.properties[pt.key]?.id; + } else { + flat.properties[pt.key] = rel.map((r) => r.to?.properties?.name).join(','); + } + } else { + flat.properties[pt.key] = flat.properties[pt.key]?.id; + } + } else { + if (!flat.properties.hasOwnProperty(pt.key)) { + flat.properties[pt.key] = ''; + } + } + } + } + } + + exportItems.push({ ...{ id, classId }, ...flat.properties }); + } + + let data = Papa.unparse(exportItems, { + header: true, + }); + + $cs.triggerFileDownload(this.options?.defaultView, data, 'applications/csv'); + } + public get splitWidgetLayout(): any { if (this.options.splitView) { switch (this.options.splitView) { @@ -1291,6 +1436,13 @@ export default class ElementDataGrid extends WidgetBase { this.$forceUpdate(); } + public toggleSettings() { + this.settingsOpen = !this.settingsOpen; + if (this.settingsOpen) { + this.updateFilterPanels(); + } + } + public openSearch() { this.searchEnabled = true; this.$nextTick(() => { @@ -1431,6 +1583,24 @@ export default class ElementDataGrid extends WidgetBase { this.$forceUpdate(); } + public unselectAll() { + if (this.gridApi) { + this.gridApi.deselectAll(); + } + } + + public deleteSelection() { + $cs.triggerYesNoQuestionDialog('Delete selected items?', 'Are you sure you want to delete the selected items?').then(async (s: string) => { + if (this.source && s === 'YES' && this.selectedElements) { + for (const el of this.selectedElements) { + await this.source.removeNodeById(el.id!); + } + this.updateEntities(true); + // this.source?.deleteSelection(); + } + }); + } + public updateHeaders() { if (!this.featureType?.properties || !this.options?.tableOptions) { return; @@ -1525,7 +1695,7 @@ export default class ElementDataGrid extends WidgetBase { this.columnDefs.push(column); } } - if (this.options.canDelete || this.options.canGraph) { + if (this.options.canDelete || this.options.canEdit) { this.columnDefs.push({ floatingFilter: false, sortable: false, @@ -1533,9 +1703,9 @@ export default class ElementDataGrid extends WidgetBase { cellRenderer: 'grid-row-actions', cellRendererParams: { options: this.options, - graphNode: (row: GraphElement) => { - this.graphNode(row); - }, + // graphNode: (row: GraphElement) => { + // this.graphNode(row); + // }, editNode: (row: GraphElement) => { this.editNode(row); }, @@ -1619,6 +1789,8 @@ export default class ElementDataGrid extends WidgetBase { public async addChildEntity(type: FeatureType, parent?: GraphElement) { await this.addEntity(type, parent); + this.updateEntities(true); + this.$forceUpdate(); } public async addEntity(type: FeatureType, parent?: GraphElement, properties?: any) { @@ -1709,7 +1881,9 @@ export default class ElementDataGrid extends WidgetBase { } public editEntity(element: GraphElement) { - if (!this.source) { return; } + if (!this.source) { + return; + } this.source.startEditElement(element); } @@ -2109,16 +2283,13 @@ export default class ElementDataGrid extends WidgetBase { }; if (this.source && this.options.nodeRules) { - - this.localPreset = { classId: 'graph_preset', properties: { graphLayout: { nodeRules: this.options.nodeRules }}} as FilterGraphElement; - this.source.applyGraphPresetRules(this.localPreset as FilterGraphElement, this.options.nodeRules); + this.localPreset = { classId: 'graph_preset', properties: { graphLayout: { nodeRules: this.options.nodeRules } } } as FilterGraphElement; + this.source.applyGraphPresetRules(this.localPreset as FilterGraphElement, this.options.nodeRules); this.items = this.localPreset._visibleNodes; filterItems(); this.update(); this.$forceUpdate(); return; - - } if (this.source && this.options?.preset && this.options.syncMode === 'follow') { @@ -2338,6 +2509,9 @@ export default class ElementDataGrid extends WidgetBase { } this.update(); + if (this.options.defaultView === 'table') { + this.onGridSelection(); + } this.$forceUpdate(); } @@ -2388,7 +2562,7 @@ export default class ElementDataGrid extends WidgetBase { this.update(); this.registerWidgetConfig(); - if (this.source?.events) { + if (this.source?.events) { this.source.events.subscribe(GraphDatasource.GRAPH_EVENTS, (action: string, el: GraphElement) => { if (action === GraphDatasource.ELEMENT_UPDATED) { this.updateEntities(true); @@ -2409,6 +2583,8 @@ export default class ElementDataGrid extends WidgetBase { // this.options.defaultView = GridView.table; } + this.updateFilterPanels(); + // let selectionSizePlugin = new Plugin({ // view(editorView) { // return new SelectionSizeTooltip(editorView); @@ -2425,6 +2601,9 @@ export default class ElementDataGrid extends WidgetBase { if (element.properties && element.properties.hasTimeseries) { return ElementCardManager.cards['indicator']; } + if (ElementCardManager.cards?.hasOwnProperty('node')) { + return ElementCardManager.cards['node']; + } return 'default-element-card'; } @@ -2437,12 +2616,18 @@ export default class ElementDataGrid extends WidgetBase { data: { title: 'Element data grid', }, - options: { ...(this.widget.options || {}), ...{ nodeRules: [ - { - type: 'ELEMENT', - elementIds: (this.items) ? this.items.map((i) => i.id) : [] - } - ]}, hideHeader: true } as DataGridOptions, + options: { + ...(this.widget.options || {}), + ...{ + nodeRules: [ + { + type: 'ELEMENT', + elementIds: this.items ? this.items.map((i) => i.id) : [], + }, + ], + }, + hideHeader: true, + } as DataGridOptions, }; this.source.addSlideConfig(w); } diff --git a/packages/cs-graph/src/components/element/element-info.vue b/packages/cs-graph/src/components/element/element-info.vue index bdf076b7..ba43a9de 100644 --- a/packages/cs-graph/src/components/element/element-info.vue +++ b/packages/cs-graph/src/components/element/element-info.vue @@ -203,11 +203,12 @@ } .info-tab-items { - position: absolute; + /* position: absolute; */ top: 0; bottom: 0; left: 0; right: 0; + margin-bottom: 300px; } .feature-property-value { diff --git a/packages/cs-graph/src/datasources/doc-datasource.ts b/packages/cs-graph/src/datasources/doc-datasource.ts index 5d3c84f0..89e4f085 100644 --- a/packages/cs-graph/src/datasources/doc-datasource.ts +++ b/packages/cs-graph/src/datasources/doc-datasource.ts @@ -488,7 +488,7 @@ export class DocDatasource extends GraphDatasource { public getGraphPreset(id: string): FilterGraphElement | undefined { const preset = this.getElement(id) as FilterGraphElement; if (preset) { - preset.classId = 'graph_preset'; + // preset.classId = 'graph_preset'; if (!preset.properties) { preset.properties = {}; } From 7515dbb31576ba63dc468c33d8dee4ac6de42996 Mon Sep 17 00:00:00 2001 From: damylen Date: Wed, 20 Apr 2022 20:01:20 +0200 Subject: [PATCH 10/40] cs-graph: table bug fixes --- .../components/data-grid/table/grid-row-actions.vue | 11 ++++++----- .../data-grid/table/node-link-cell-editor.vue | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/cs-graph/src/components/data-grid/table/grid-row-actions.vue b/packages/cs-graph/src/components/data-grid/table/grid-row-actions.vue index 2249a5bb..fb5325da 100644 --- a/packages/cs-graph/src/components/data-grid/table/grid-row-actions.vue +++ b/packages/cs-graph/src/components/data-grid/table/grid-row-actions.vue @@ -3,17 +3,18 @@ mdi-delete - mdi-scatter-plot - --> + + mdi-pencil - + +function async(arg0: (e: any) => { icon: string; title: string; action: any; }[]): (element: +import("@csnext/cs-data").GraphElement) => import("@csnext/cs-core").IMenu[] { throw new +Error('Function not implemented.'); } From 86e22e46d8d51c3b54f51ba17ab7027734cb0f8e Mon Sep 17 00:00:00 2001 From: damylen Date: Thu, 21 Apr 2022 10:57:18 +0200 Subject: [PATCH 15/40] cs-graph: added support for select all in data grid in table view --- .../data-grid/element-data-grid.vue | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/cs-graph/src/components/data-grid/element-data-grid.vue b/packages/cs-graph/src/components/data-grid/element-data-grid.vue index 168212e2..82b3b93a 100644 --- a/packages/cs-graph/src/components/data-grid/element-data-grid.vue +++ b/packages/cs-graph/src/components/data-grid/element-data-grid.vue @@ -35,15 +35,19 @@ {{ $cs.Translate('NEW_ITEM') }} @@ -309,6 +331,8 @@ inset :label="$cs.Translate(field.title)" :hint="field.hint" + :filled="field.filled" + :rounded="field.rounded" :persistentHint="field.persistentHint" v-model="target[field._key]" :disabled="field.readonly" @@ -323,6 +347,8 @@ :key="option" :label="option" :value="option" + :filled="field.filled" + :rounded="field.rounded" @change="fieldUpdated(field)" :disabled="field.readonly" v-model="target[field._key]" @@ -334,6 +360,8 @@ v-if="field._key" v-model.number="target[field._key]" type="number" + :filled="field.filled" + :rounded="field.rounded" :rules="[rules.valueMin, rules.valueMax, rules.required]" :label="$cs.Translate(field.title)" :clearable="field.clearable" @@ -351,6 +379,8 @@ :required="field.required" :clearable="field.clearable" :hint="field.hint" + :filled="field.filled" + :rounded="field.rounded" :persistentHint="field.persistentHint" :disabled="field.readonly" @change="fieldUpdated(field)" diff --git a/packages/cs-form/src/components/cs-form/cs-form.css b/packages/cs-form/src/components/cs-form/cs-form.css index eabdbc47..50895b1f 100644 --- a/packages/cs-form/src/components/cs-form/cs-form.css +++ b/packages/cs-form/src/components/cs-form/cs-form.css @@ -6,4 +6,10 @@ .form-title { font-weight: 600; font-size: 125%; - } \ No newline at end of file + } + +.optional-field-title { + font-weight: 600; + + +} \ No newline at end of file diff --git a/packages/cs-form/src/components/cs-form/cs-form.html b/packages/cs-form/src/components/cs-form/cs-form.html index 418a957b..17f22957 100644 --- a/packages/cs-form/src/components/cs-form/cs-form.html +++ b/packages/cs-form/src/components/cs-form/cs-form.html @@ -30,9 +30,10 @@ - +
+
mdi-plus Add optional field
+ {{field.icon}}mdi-plus{{field.title || field._key}} +
diff --git a/packages/cs-form/src/components/fields/chips-editor.vue b/packages/cs-form/src/components/fields/chips-editor.vue index cc5232e8..2e511090 100644 --- a/packages/cs-form/src/components/fields/chips-editor.vue +++ b/packages/cs-form/src/components/fields/chips-editor.vue @@ -10,6 +10,8 @@ :hint="field.hint" :persistentHint="field.persistentHint" :clearable="field.clearable" + :filled="field.filled" + :rounded="field.rounded" solo multiple text diff --git a/packages/cs-form/src/components/fields/string-editor.vue b/packages/cs-form/src/components/fields/string-editor.vue index 1c55fa51..7827e327 100644 --- a/packages/cs-form/src/components/fields/string-editor.vue +++ b/packages/cs-form/src/components/fields/string-editor.vue @@ -11,6 +11,8 @@ :placeholder="field.placeholder" :append-outer-icon="field._appendIcon" @update:error="fieldError" + :filled="field.filled" + :rounded="field.rounded" @input="fieldInput(field)" :hint="field.hint" :persistentHint="field.persistentHint" @@ -21,6 +23,8 @@ v-if="field._index" v-model="target[field._index]" :label="$cs.Translate(field.title)" + :filled="field.filled" + :rounded="field.rounded" :required="field.required" :disabled="field.readonly" :clearable="field.clearable" diff --git a/packages/cs-form/src/components/fields/url-editor.vue b/packages/cs-form/src/components/fields/url-editor.vue index 1950e97d..fdf8a1b8 100644 --- a/packages/cs-form/src/components/fields/url-editor.vue +++ b/packages/cs-form/src/components/fields/url-editor.vue @@ -10,6 +10,8 @@ :disabled="field.readonly" :placeholder="field.placeholder" append-outer-icon="mdi-open-in-new" + :filled="field.filled" + :rounded="field.rounded" @update:error="fieldError" @input="fieldInput(field)" :hint="field.hint" @@ -24,6 +26,8 @@ :required="field.required" :disabled="field.readonly" :clearable="field.clearable" + :filled="field.filled" + :rounded="field.rounded" @change="fieldUpdated(field)" >
From 41e973e8fdb697b222102d1f95fcb6af7111cec2 Mon Sep 17 00:00:00 2001 From: damylen Date: Thu, 28 Apr 2022 12:56:02 +0200 Subject: [PATCH 19/40] cs-graph: added fuse search filter option, added _included to meta entity --- packages/cs-data/src/classes/types/meta-entity.ts | 5 +++-- .../cs-data/src/data-sources/graph-datasource.ts | 13 ++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/cs-data/src/classes/types/meta-entity.ts b/packages/cs-data/src/classes/types/meta-entity.ts index f518f713..1abecc86 100644 --- a/packages/cs-data/src/classes/types/meta-entity.ts +++ b/packages/cs-data/src/classes/types/meta-entity.ts @@ -5,9 +5,10 @@ export class MetaEntity { public text?: string; public id?: string; public kg_id?: string; + public source?: string; public _node?: GraphElement; public _edge?: GraphElement; - public _included?: boolean; - public _approved?: boolean; + // public _included?: boolean; + public _linked?: boolean; // public _relations?: TextRelation[]; } \ No newline at end of file diff --git a/packages/cs-data/src/data-sources/graph-datasource.ts b/packages/cs-data/src/data-sources/graph-datasource.ts index 483c6a76..337218f0 100644 --- a/packages/cs-data/src/data-sources/graph-datasource.ts +++ b/packages/cs-data/src/data-sources/graph-datasource.ts @@ -542,7 +542,8 @@ export class GraphDatasource extends DataSource { public getClassElements( classId: string, traversal?: boolean, - filter?: GraphFilter + filter?: GraphFilter, + state?: any ): GraphElement[] { let res: GraphElement[] = []; if (traversal) { @@ -617,8 +618,8 @@ export class GraphDatasource extends DataSource { ) ) { const value = filter.hasIncomingTypeRelation[field]; - const v = typeof value === 'function' ? value() : value; - if (r.classId === field && r.from?.id === v) { + const v = typeof value === 'function' ? value(r, state) : value; + if (r.classId === field && r.fromId === v) { return true; } } @@ -1019,13 +1020,15 @@ export class GraphDatasource extends DataSource { ); } - public searchFuse(query: string, featureType?: string, traversal = false) { + public searchFuse(query: string, featureType?: string, traversal = false, options?: { nlp?: string}) { if (!this.fuse) { return []; } let res = this.fuse.search(query); - if (featureType) { + if (options?.nlp) { + res = res.filter(r => r.item._featureType?.attributes && r.item._featureType.attributes['nlp:entity_class'] && r.item._featureType.attributes['nlp:entity_class'] === options.nlp); + } else if (featureType) { res = res.filter(r => r.item._featureType?._inheritedTypes && r.item._featureType._inheritedTypes.includes(featureType)); } return res; From 42e768b03abd93f30e98250a8922cb7789ba32d6 Mon Sep 17 00:00:00 2001 From: damylen Date: Thu, 28 Apr 2022 12:56:41 +0200 Subject: [PATCH 20/40] cs-split-panel: updated split panel gutter hover layout --- packages/cs-split-panel/src/split-comp.ts | 2 +- packages/cs-split-panel/src/split-panel.css | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/cs-split-panel/src/split-comp.ts b/packages/cs-split-panel/src/split-comp.ts index e757266a..d77fcd7c 100644 --- a/packages/cs-split-panel/src/split-comp.ts +++ b/packages/cs-split-panel/src/split-comp.ts @@ -21,7 +21,7 @@ export class SplitComp extends Vue { @Prop({ default: null }) private dashboard?: IDashboard | null; public splitGridOptions = { - gutterSize: 3, + gutterSize: 5, }; public allHidden(element: SplitPanelOptions) { diff --git a/packages/cs-split-panel/src/split-panel.css b/packages/cs-split-panel/src/split-panel.css index b26dd2d7..74d98e95 100644 --- a/packages/cs-split-panel/src/split-panel.css +++ b/packages/cs-split-panel/src/split-panel.css @@ -8,7 +8,14 @@ } .vsg_gutter { - background-color: #80808078 !important; + background-color: #cacaca78 !important; +} + +.vsg_gutter:hover { + background-color: #5a5a5a78 !important; + border-style: solid; + border-width: 10px; + border-color: var(--v-primary-base); } .hide-vertical-scroll { From 1354434e4bc4e1926e93d0aa7efa12c8d483c185 Mon Sep 17 00:00:00 2001 From: damylen Date: Thu, 28 Apr 2022 12:57:28 +0200 Subject: [PATCH 21/40] cs-map: added null check in node-chip --- packages/cs-map/src/components/data/data-sections/node-link.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cs-map/src/components/data/data-sections/node-link.ts b/packages/cs-map/src/components/data/data-sections/node-link.ts index 464b83cd..9eadcbe8 100644 --- a/packages/cs-map/src/components/data/data-sections/node-link.ts +++ b/packages/cs-map/src/components/data/data-sections/node-link.ts @@ -55,7 +55,7 @@ export class NodeLink extends Vue { props: ['node', 'source', 'light'], components: {NodeSpan}, template: ` - + From f0f29149d128786c3773368cf70ac1c8c16c01e1 Mon Sep 17 00:00:00 2001 From: damylen Date: Thu, 28 Apr 2022 12:58:34 +0200 Subject: [PATCH 22/40] cs-graph: moved getElementCard to ElementCardManager, added card-size style sheets --- packages/cs-graph/src/assets/cards.css | 14 ++++++++++++++ .../data-grid/cards/element-card-manager.ts | 13 +++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 packages/cs-graph/src/assets/cards.css diff --git a/packages/cs-graph/src/assets/cards.css b/packages/cs-graph/src/assets/cards.css new file mode 100644 index 00000000..1bc281b4 --- /dev/null +++ b/packages/cs-graph/src/assets/cards.css @@ -0,0 +1,14 @@ +.card-small-size { + width: 200px; + height: 100px; +} + +.card-medium-size { + width: 300px; + height: 120px; +} + +.card-large-size { + width: 300px; + height: 200px; +} diff --git a/packages/cs-graph/src/components/data-grid/cards/element-card-manager.ts b/packages/cs-graph/src/components/data-grid/cards/element-card-manager.ts index 08f4c251..b697f6e6 100644 --- a/packages/cs-graph/src/components/data-grid/cards/element-card-manager.ts +++ b/packages/cs-graph/src/components/data-grid/cards/element-card-manager.ts @@ -1,5 +1,18 @@ import { Component } from "vue"; +import DefaultElementCard from "./default-element-card.vue"; +import { GraphElement } from '@csnext/cs-data'; export class ElementCardManager { public static cards: { [type: string] : Component} = {}; + + public static getElementCard(element: GraphElement ) : string | Component | Vue { + const id = element.classId; + if (id && ElementCardManager.cards?.hasOwnProperty(id)) { + return ElementCardManager.cards[id]; + } + if (ElementCardManager.cards?.hasOwnProperty('node')) { + return ElementCardManager.cards['node']; + } + return DefaultElementCard; + } } \ No newline at end of file From 0c50c306ddc87b4467ff8c989ace585a0c731d71 Mon Sep 17 00:00:00 2001 From: damylen Date: Thu, 28 Apr 2022 14:28:12 +0200 Subject: [PATCH 23/40] cs-graph: element data grid cards includes grouping, card size selection --- .../components/data-grid/data-grid-options.ts | 28 +- .../data-grid/element-data-grid.vue | 391 ++++++++++++++---- .../data-grid/suggestions/suggestions.ts | 16 + 3 files changed, 349 insertions(+), 86 deletions(-) create mode 100644 packages/cs-graph/src/components/data-grid/suggestions/suggestions.ts diff --git a/packages/cs-graph/src/components/data-grid/data-grid-options.ts b/packages/cs-graph/src/components/data-grid/data-grid-options.ts index 532f171e..73e0b3e2 100644 --- a/packages/cs-graph/src/components/data-grid/data-grid-options.ts +++ b/packages/cs-graph/src/components/data-grid/data-grid-options.ts @@ -1,5 +1,6 @@ -import { IMenu, IWidget, WidgetOptions } from '@csnext/cs-core'; +import { IMenu, IWidget, WidgetOptions, CardSize } from '@csnext/cs-core'; import { GraphElement, GraphFilter, IGraphFilter, NodeRule } from '@csnext/cs-data'; +import { DocDatasource } from '../..'; export enum GridView { list = 'list', @@ -82,7 +83,10 @@ export class DataGridTableOptions { public showRowIcon? = true; } - +export class GroupOptions { + public enabled?: boolean; + public property?: string; +} export class DataGridTreeOptions { public treeStructure?: string[]; @@ -90,6 +94,10 @@ export class DataGridTreeOptions { public baseTreeItem?: GraphElement; } +export class DataGridCardsOptions { + public cardSize?: 'small' | 'medium' | 'large'; +} + export class DataGridOptions extends WidgetOptions { public baseType?: string; public addNodesWithTimeseries?: boolean; @@ -106,15 +114,18 @@ export class DataGridOptions extends WidgetOptions { public onAfterAdded?: (element: GraphElement) => Promise; public defaultView: GridView = GridView.table; - public graphPresetId?: string; public customSort?: (a: GraphElement, b: GraphElement) => number; - public groupId?: string; + public grouping?: GroupOptions; + public canDelete? = true; public canAdd?: boolean; + public editNewItem?: boolean; + public editorDialog?: IWidget; public canEdit? = true; public canSearch? = true; + public canLink? = false; public searchFilter?: string; public canSort? = false; public canGraph? = true; @@ -129,6 +140,7 @@ export class DataGridOptions extends WidgetOptions { public timelineOptions?: TimelineOptions; public treeOptions?: DataGridTreeOptions; public gridOptions?: DataGridGridOptions; + public cardOptions?: DataGridCardsOptions; public hideHeader?: boolean = false; @@ -146,9 +158,15 @@ export class DataGridOptions extends WidgetOptions { public filter?: GraphFilter; public newItem?: any; public newRelations?: NewRelationDefinition[]; - public additionalActions?: IMenu[]; + // ids of actions that will be visible on card or list + public prominentActions?: string[]; + + // list of additional actions available within datagrid + public additionalActions?: IMenu[] | getActions; } +export type getActions = (e: GraphElement, src: DocDatasource) => IMenu[]; + export class DataGridHeader { public key?: string; public width?: string | number; diff --git a/packages/cs-graph/src/components/data-grid/element-data-grid.vue b/packages/cs-graph/src/components/data-grid/element-data-grid.vue index 82b3b93a..93c283d3 100644 --- a/packages/cs-graph/src/components/data-grid/element-data-grid.vue +++ b/packages/cs-graph/src/components/data-grid/element-data-grid.vue @@ -1,5 +1,5 @@