+
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index e5e31fe..30185a8 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,81 +1,90 @@
import { AppComponent } from './app.component';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CdkTableModule } from '@angular/cdk/table';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { FormsModule, ReactiveFormsModule} from '@angular/forms';
import { NgModule } from '@angular/core';
import { MatDialogModule, MatToolbarModule} from '@angular/material';
import { MatExpansionModule } from '@angular/material/expansion';
import {MatIconModule} from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import {MatMenuModule} from '@angular/material/menu';
-import { MatPaginatorModule, MatSortModule, MatSidenavModule, MatCheckboxModule } from '@angular/material';
+import { MatPaginatorModule, MatSortModule, MatSidenavModule, MatCheckboxModule, MatRadioModule } from '@angular/material';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import {MatTabsModule} from '@angular/material/tabs';
import { DataListViewComponent } from './data-list-view/data-list-view.component';
import { DataListViewTableComponent, HighlightPipe } from './data-list-view/data-list-view-table/data-list-view-table.component';
import { DialogComponent } from './dialog-component/dialog.component';
import { InfoBoxComponent } from './info-box-component/info-box.component';
import { TextFieldComponent} from './textfield-component/textfield.component';
import { WordPositionDirective } from './textfield-component/word-position.directive';
import { TextfieldOptionsComponentComponent } from './textfield-options-component/textfield-options-component.component';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { RdfDataBrowserComponentComponent } from './rdf-data-browser-component/rdf-data-browser-component.component';
import { QueryService } from './services/query.service';
import {AnnotationService, TextStyleService} from './services/annotation.service';
import { DisplayedCollumnsService } from './data-list-view/data-list-view-services/table-data.service';
import {DataListViewSettings} from './data-list-view/data-list-view-settings/data-list-view-settings.service';
import { MarkupTextComponentComponent } from './markup-text-component/markup-text-component.component';
import { BergwerkeComponent } from './bergwerke/bergwerke.component';
+import { SubmenuComponentComponent } from './markup-text-component/submenu-component/submenu-component.component';
+import { MarkupTextButtonsComponentComponent } from './markup-text-component/markup-text-buttons-component/markup-text-buttons-component.component';
+import { OptionsComponent } from './options-component/options-component.component';
+import { MarkupHyperlinkComponentComponent } from './markup-text-component/markup-hyperlink-component/markup-hyperlink-component.component';
@NgModule({
declarations: [
AppComponent,
TextFieldComponent,
WordPositionDirective,
InfoBoxComponent,
TextfieldOptionsComponentComponent,
DataListViewComponent,
DataListViewTableComponent,
DialogComponent,
HighlightPipe,
RdfDataBrowserComponentComponent,
MarkupTextComponentComponent,
- BergwerkeComponent
+ BergwerkeComponent,
+ SubmenuComponentComponent,
+ MarkupTextButtonsComponentComponent,
+ OptionsComponent,
+ MarkupHyperlinkComponentComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
CdkTableModule,
CommonModule,
DragDropModule,
HttpClientModule,
MatCheckboxModule,
MatDialogModule,
MatExpansionModule,
MatIconModule,
MatInputModule,
MatListModule,
MatMenuModule,
MatPaginatorModule,
+ MatRadioModule,
MatSelectModule,
MatSidenavModule,
MatSortModule,
MatTableModule,
MatTabsModule,
MatToolbarModule,
FormsModule,
ReactiveFormsModule
],
providers: [ QueryService, DisplayedCollumnsService, DataListViewSettings, TextStyleService, AnnotationService ],
bootstrap: [AppComponent],
entryComponents: [DialogComponent]
})
export class AppModule { }
diff --git a/src/app/bergwerke/bergwerke.component.html b/src/app/bergwerke/bergwerke.component.html
index eda2cf8..d85a029 100644
--- a/src/app/bergwerke/bergwerke.component.html
+++ b/src/app/bergwerke/bergwerke.component.html
@@ -1,4 +1,5 @@
This is a Text app styled with markup component
Please take your time to also read this gorgeous text. It is one of the most beautiful texts of all time: The "Unverhofftes Wiedersehen" of Johann Peter Hebel
+
Unverhofftes Wiedersehen
diff --git a/src/app/bergwerke/bergwerke.component.ts b/src/app/bergwerke/bergwerke.component.ts
index 6db34f9..7673dc8 100644
--- a/src/app/bergwerke/bergwerke.component.ts
+++ b/src/app/bergwerke/bergwerke.component.ts
@@ -1,49 +1,50 @@
import { Component, OnInit } from '@angular/core';
+import markupDefaultSettings from '../markup-text-component/markup-text-settings.json';
+import {MarkupOptionsService, MarkupSetting} from '../markup-text-component/markup-options.service';
@Component({
selector: 'app-bergwerke',
templateUrl: './bergwerke.component.html',
styleUrls: ['./bergwerke.component.scss']
})
export class BergwerkeComponent implements OnInit {
myText = 'In Falun in Schweden küßte vor guten fünfzig Jahren und mehr ein junger Bergmann seine junge hübsche Braut und sagte zu ihr: »Auf Sankt Luciä wird unsere Liebe von des Priesters Hand gesegnet. Dann sind wir Mann und Weib, und bauen uns ein eigenes Nestlein.« – »Und Friede und Liebe soll darin wohnen«, sagte die schöne Braut mit holdem Lächeln, »denn du bist mein einziges und alles, und ohne dich möchte ich lieber im Grab sein, als an einem andern Ort.« Als sie aber vor St. Luciä der Pfarrer zum zweitenmal in der Kirche ausgerufen hatte: »So nun jemand Hindernis wüßte anzuzeigen, warum diese Personen nicht möchten ehelich zusammenkommen« – da meldete sich der Tod. Denn als der Jüngling den andern Morgen in seiner schwarzen Bergmannskleidung an ihrem Haus vorbeiging, der Bergmann hat sein Totenkleid immer an, da klopfte er zwar noch einmal an ihrem Fenster, und sagte ihr guten Morgen, aber keinen guten Abend mehr. Er kam nimmer aus dem Bergwerk zurück, und sie saumte vergeblich selbigen Morgen ein schwarzes Halstuch mit rotem Rand für ihn zum Hochzeittag, sondern als er nimmer kam, legte sie es weg, und weinte um ihn und vergaß ihn nie. Unterdessen wurde die Stadt Lissabon in Portugal durch ein Erdbeben zerstört, und der Siebenjährige Krieg ging vorüber, und Kaiser Franz der Erste starb, und der Jesuitenorden wurde aufgehoben und Polen geteilt, und die Kaiserin Maria Theresia starb, und der Struensee wurde hingerichtet, Amerika wurde frei, und die vereinigte französische und spanische Macht konnte Gibraltar nicht erobern. Die Türken schlossen den General Stein in der Veteraner Höhle in Ungarn ein, und der Kaiser Joseph starb auch. Der König Gustav von Schweden eroberte russisch Finnland, und die Französische Revolution und der lange Krieg fing an, und der Kaiser Leopold der Zweite ging auch ins Grab. Napoleon eroberte Preußen, und die Engländer bombardierten Kopenhagen, und die Ackerleute säeten und schnitten. Der Müller mahlte, und die Schmiede hämmerten, und die Bergleute gruben nach den Metalladern in ihrer unterirdischen Werkstatt. Als aber die Bergleute in Falun im Jahr 1809 etwas vor oder nach Johannis zwischen zwei Schachten eine Öffnung durchgraben wollten, gute dreihundert Ellen tief unter dem Boden gruben sie aus dem Schutt und Vitriolwasser den Leichnam eines Jünglings heraus, der ganz mit Eisenvitriol durchdrungen, sonst aber unverwest und unverändert war; also daß man seine Gesichtszüge und sein Alter noch völlig erkennen konnte, als wenn er erst vor einer Stunde gestorben, oder ein wenig eingeschlafen wäre, an der Arbeit. Als man ihn aber zu Tag ausgefördert hatte, Vater und Mutter, Gefreundte und Bekannte waren schon lange tot, kein Mensch wollte den schlafenden Jüngling kennen oder etwas von seinem Unglück wissen, bis die ehemalige Verlobte des Bergmanns kam, der eines Tages auf die Schicht gegangen war und nimmer zurückkehrte. Grau und zusammengeschrumpft kam sie an einer Krücke an den Platz und erkannte ihren Bräutigam; und mehr mit freudigem Entzücken als mit Schmerz sank sie auf die geliebte Leiche nieder, und erst als sie sich von einer langen heftigen Bewegung des Gemüts erholt hatte, »es ist mein Verlobter«, sagte sie endlich, »um den ich fünfzig Jahre lang getrauert hatte, und den mich Gott noch einmal sehen läßt vor meinem Ende. Acht Tage vor der Hochzeit ist er unter die Erde gegangen und nimmer heraufgekommen.« Da wurden die Gemüter aller Umstehenden von Wehmut und Tränen ergriffen, als sie sahen die ehemalige Braut jetzt in der Gestalt des hingewelkten kraftlosen Alters und den Bräutigam noch in seiner jugendlichen Schöne, und wie in ihrer Brust nach 50 Jahren die Flamme der jugendlichen Liebe noch einmal erwachte; aber er öffnete den Mund nimmer zum Lächeln oder die Augen zum Wiedererkennen; und wie sie ihn endlich von den Bergleuten in ihr Stüblein tragen ließ, als die einzige, die ihm angehöre, und ein Recht an ihn habe, bis sein Grab gerüstet sei auf dem Kirchhof. Den andern Tag, als das Grab gerüstet war auf dem Kirchhof und ihn die Bergleute holten, schloß sie ein Kästlein auf, legte sie ihm das schwarzseidene Halstuch mit roten Streifen um, und begleitete ihn alsdann in ihrem Sonntagsgewand, als wenn es ihr Hochzeittag und nicht der Tag seiner Beerdigung wäre. Denn als man ihn auf dem Kirchhof ins Grab legte, sagte sie: »Schlafe nun wohl, noch einen Tag oder zehen im kühlen Hochzeitbett, und laß dir die Zeit nicht lange werden. Ich habe nur noch wenig zu tun, und komme bald, und bald wird\'s wieder Tag. – Was die Erde einmal wiedergegeben hat, wird sie zum zweitenmal auch nicht behalten«, sagte sie, als sie fortging, und noch einmal umschaute.';
+ defaultSettings = JSON.parse(JSON.stringify(markupDefaultSettings));
+
myStandoffData: Array> = [
[ "stoff:SemanticAnnotation", 3, 8, 'humgeo:City', "https://www.wikidata.org/wiki/Q26509"],
[ "stoff:SemanticAnnotation", 12, 19, 'humgeo:State', "https://www.wikidata.org/wiki/Q34"],
[ "stoff:SemanticAnnotation", 1662, 1666, 'human:Organization', "https://de.wikipedia.org/wiki/K%C3%B6nig"],
[ "stoff:SemanticAnnotation", 1679, 1686, 'humgeo:State', "https://de.wikipedia.org/wiki/Schweden"],
[ "stoff:SemanticAnnotation", 1662, 1686, 'human:Person', "https://de.wikipedia.org/wiki/Karl_X._Gustav"],
[ "stoff:CssStyle", 125, 301, "font-style: italic;"],
[ "stoff:CssStyle", 346, 457, "font-style: italic;"],
[ "stoff:CssStyle", 544, 645, "font-style: italic;"],
[ "stoff:CssStyle", 3163, 3186, "font-style: italic;"],
[ "stoff:CssStyle", 3207, 3398, "font-style: italic;"],
[ "stoff:CssStyle", 4334, 4605, "font-style: italic;"],
[ "stoff:CssStyle", 2097, 2102, "font-weight: bold;"],
[ "stoff:CssStyle", 2097, 2102, "color: blue;"],
[ "stoff:CssStyle", 2097, 2102, "text-decoration: underline;"],
[ "stoff:CssStyle", 1182, 1190, "text-decoration: underline;"],
[ "stoff:CssStyle", 1182, 1190, "font-weight: bold;"],
[ "stoff:CssStyle", 1182, 1190, "color: blue;"],
[ "stoff:CssStyle", 1443, 1450, "text-decoration: underline;"],
[ "stoff:CssStyle", 1443, 1450, "font-weight: bold;"],
[ "stoff:CssStyle", 1443, 1450, "color: blue;"],
[ "stoff:CssStyle", 1285, 1300, "text-decoration: underline;"],
[ "stoff:CssStyle", 1285, 1300, "font-weight: bold;"],
[ "stoff:CssStyle", 1285, 1300, "color: red;"],
[ "stoff:CssStyle", 1316, 1329, "text-decoration: underline;"],
[ "stoff:CssStyle", 1316, 1329, "color: green;"],
[ "stoff:CssStyle", 1383, 1397, "text-decoration: underline;"],
[ "stoff:CssStyle", 1383, 1397, "font-weight: bold;"],
[ "stoff:CssStyle", 1383, 1397, "color: red;"],
[ "stoff:CssStyle", 1639, 1645, "text-decoration: underline;"],
[ "stoff:CssStyle", 1639, 1645, "font-weight: bold;"],
[ "stoff:CssStyle", 1639, 1645, "color: red;"]
];
- constructor() { }
-
ngOnInit() {
}
-
}
diff --git a/src/app/markup-text-component/markup-hyperlink-component/markup-hyperlink-component.component.html b/src/app/markup-text-component/markup-hyperlink-component/markup-hyperlink-component.component.html
new file mode 100644
index 0000000..738d596
--- /dev/null
+++ b/src/app/markup-text-component/markup-hyperlink-component/markup-hyperlink-component.component.html
@@ -0,0 +1,17 @@
+{{segment[1]}}
+
+
+ 1"
+ class = "annotation-button"
+ [matMenuTriggerFor]="polyMenuOpen"
+ [matMenuTriggerData]= segment[3]>
+ {{segment[1]}}
+
+
+
+
diff --git a/src/app/markup-text-component/markup-text-component.component.scss b/src/app/markup-text-component/markup-text-component.component.scss
index d1d0b9f..70bdfd4 100644
--- a/src/app/markup-text-component/markup-text-component.component.scss
+++ b/src/app/markup-text-component/markup-text-component.component.scss
@@ -1,48 +1,62 @@
body {
}
a {
color: inherit; /* no colors for links. Color will be defined by semantic classes */
text-decoration: inherit; /* no underline */
}
::ng-deep .poly-menu {
width: 400px;
}
+.annotation-button {
+ padding: 0;
+ margin-right: 0.25em; // needed as a text with white spaces within a button does not make the button have white spaces around ...
+ border: 2px;
+ border-color: transparent;
+ background-color: transparent;
+ white-space: normal;
+}
+
+.annotation-button:hover {
+ background-color: rgba(10, 104, 24, 0.21);
+ border-color: rgba(10, 104, 24, 0.21);
+}
+
.style-markup {
}
.semantic-markup {
}
.semantic-markup.polysemantic {
text-decoration: underline;
cursor: pointer;
color: #0c5593;
font-weight: bold;
}
.semantic-markup.humgeo {
font-weight: bold;
}
.humgeo.city {
color: blue;
}
.humgeo.state {
color: #a4a4a4;
}
.semantic-markup.human {
}
.human.organization {
color: #fa1cbc;
}
.human.person{
color: #ffbb5c;
}
diff --git a/src/app/markup-text-component/markup-text-component.component.ts b/src/app/markup-text-component/markup-text-component.component.ts
index 324f272..1efd59b 100644
--- a/src/app/markup-text-component/markup-text-component.component.ts
+++ b/src/app/markup-text-component/markup-text-component.component.ts
@@ -1,153 +1,168 @@
import {Component, Input, OnChanges} from '@angular/core';
-import {RESOURCE_CACHE_PROVIDER} from '@angular/platform-browser-dynamic';
+import {MarkupOptionsService, MarkupSetting} from './markup-options.service';
+import markupDefaultSettings from './markup-text-settings.json';
@Component({
selector: 'app-markup-text-component',
templateUrl: './markup-text-component.component.html',
styleUrls: ['./markup-text-component.component.scss'] // TODO: implement dynamic css from files/assets/server or somewhere
})
/** TODO: change documentation
* Simple and leightweight component marking up/styling a text input textToStyle with standoff markupData.
* @param StandoffData: markup data.
* @param textToStyle: the word for which the markup information should be applied as styles in the template.
* @param startIndices: the startindices of all final text segments to style
* @param styleSegments: the resulting style segments with their css styles which are then applied by ngStyle in the template
*/
export class MarkupTextComponentComponent implements OnChanges {
@Input() standoffData: Array>; // [[rdfs:type, startIndex:number, endIndex:number, cssStyle:string], [...], ...]
@Input() textToStyle: string;
+ @Input() markupSettings?: MarkupSetting;
+ defaultSettings = JSON.parse(JSON.stringify(markupDefaultSettings));
+
// temporary mockup data
markupData: Array> = [
["stoff:CssStyle", 0, 1, "font-weight: bold;"],
["stoff:CssStyle", 1, 4, "font-style: italic;"],
["stoff:CssStyle", 0, 6, "text-decoration: underline;"],
["stoff:CssStyle", 4, 4, "color: blue;"],
["stoff:CssStyle", 4, 5, "text-decoration: line-through;"]
];
- polyMenuOpen = false; // the status of the menu for polysemantics
-
startIndices: Array = []; // the startindices of all style segments
templateSegments: Array = []; // the resulting style segments with their css styles
-
- constructor() {
+ constructor( private optionService: MarkupOptionsService) {
+ if (!this.markupSettings ) {
+ this.markupSettings = this.optionService.loadDefaultSettings(this.defaultSettings);
+ }
}
ngOnChanges() {
+ this.optionService.optionsChange.subscribe(
+ options => { this.markupSettings = options; console.log('new markup settings coming in: ' + this.markupSettings); }
+ );
// TODO: maybe go for data stream as an observable/promise
if (this.standoffData && this.standoffData.length > 0 ) { this.markupData = this.standoffData; }
this.templateSegments = []; // needed reset, if not ngOnChanges appends the new spans to the old ones in the template instead of replacing!
this.startIndices = this.getStartIndices(this.markupData);
this.createSegments();
}
/**
* Creates the final text segments and styles (this.templateSegments) which will
* be *ngFored as and styled with *ngStyle in the template.
* Iterates through all startindices defined, pushes the corresponding substring
* of our text to style together with its matching styles to this.templateSegments
*
*/
private createSegments() {
let c = 1; // counter for getting the end index of the substring() via this.startIndices[c]
for (const startIndex of this.startIndices) {
const textSegment = this.textToStyle.substring(startIndex, this.startIndices[c]);
const markupDef = this.getMarkupDef(startIndex);
if (markupDef) {
this.templateSegments.push([markupDef[0], textSegment, markupDef[1], markupDef[2]]);
}
c += 1;
}
console.log('this.templateSegments: ', this.templateSegments);
}
/**
* Gets the styles that apply for a passed start index. Hence the styles of a segment
* apply to every character within that segment equally, we only need to get the styles
* for the startindex, so all those styles apply to the whole segment correctly.
* Iterates through all defined styles/ranges ranges of this.markupData.
* For every range covering the startIndex, its style is added as property to the styles object.
*
* @param startIndex: The startindex of a new range to style
* @return styles: An object with all matching css styles as properties
*/
private getMarkupDef(startIndex) {
const markup = {};
const resource = []; // more than one linked resource possible at overlaps
let markuptype = '';
for (const entry of this.markupData) {
// if our startIndex is within a given range, the style of that range will be added to styles.
if (entry[1] <= startIndex && startIndex <= entry[2]) {
if (entry[0] === 'stoff:CssStyle') {
markuptype = entry[0].split(':')[1]; // CssStyle
// Adds css property/value as an object: Splits the style string at ':',
// removes leading and ending spaces, deletes ";" and assigns it as an object as css property:"value"
markup[entry[3].split(':')[0].trim()] = entry[3].split(':')[1].trim().replace(';', '');
}
if (entry[0] === 'stoff:SemanticAnnotation') {
markuptype = entry[0].split(':')[1]; // SemanticAnnotation
// adds css class
markup[(entry[3].trim().replace(':', ' ' ).toLowerCase())] = true;
resource.push( { linkText: this.textToStyle.substring(entry[1], entry[2] + 1),
link: entry[4],
cssClass: entry[3].trim().replace(':', ' ' ).toLowerCase(),
oClass: entry[3].trim().split(':')[1] }); // links
}
}
}
if (Object.keys(markup).length === 0 && markup.constructor === Object) {
// If there is no range applying, e.g. no style at all defined for a startindex we simply define font-style: normal.
return ['CssStyle', {'font-style': 'normal'}, ''];
} else { return [markuptype, markup, resource ];
}
}
/**
* Creates an array of startindices defining the final text/style segments.
* Hence every end index can be described as a startindex minus one (an end
* index equals a start index of sth. new -1), end indices are also covered implicitly.
* So every endIndex +1 is also pushed to the startIndices array if not yet existing.
* If a defined range is ending at the very last character, there is obviously not a new
* style starting and an additive startIndex is wrong. Therefore the last startIndex
* is simply popped from the array if its value exceeds textToStyle.length.
*
* @return startIndices: Array of all distinct startIndices of the final text segments.
*/
private getStartIndices(standoffData) {
const startIndices: Array = [];
// Push every startindex to startIndices if not yet there (distinct).
// Hence every endIndex is itself equals a (startIndex -1), we simply add also every endIndex +1 to
// the Array of startIndices if there is not yet a start defined for that character.
standoffData.forEach(range => {
if (startIndices.indexOf(range[1]) === -1) {
startIndices.push(range[1]);
}
if (startIndices.indexOf(range[2] + 1) === -1) {
startIndices.push(range[2] + 1);
}
});
// if there is no startindex of 0, we have to add it to define the first text segment.
if (startIndices.indexOf(0) === -1) {
startIndices.push(0); }
// sort it
startIndices.sort((n1, n2) => n1 - n2);
// If the last endIndex is the very last character of the string to style, we must not generate a startIndex at endIndex+1.
// In any other case we have to start a new style.
// If the last generated startIndex is bigger than this.textToStyle.length
// we have to pop that last startIndex (from the right) from our startIndices.
// If the textToStyle.length is bigger or equals the last startIndex, that last startIndex simply means the end of the style
// one character before. In theese cases a last style range with font style undefined (style:normal) has to start,
// so the last end ist also defined.
if (this.textToStyle.length < startIndices[startIndices.length - 1]) {
console.log('popping last startIndex: ', startIndices[startIndices.length - 1]);
startIndices.pop();
}
return startIndices;
}
+ // runs some action
+ // TODO: Add actions
+ private getHRef(action, target) {
+ console.log('action = ', action);
+ if (action === 'external') {
+ return target;
+ }
+ }
}
diff --git a/src/app/markup-text-component/markup-text-settings.json b/src/app/markup-text-component/markup-text-settings.json
new file mode 100644
index 0000000..37ead40
--- /dev/null
+++ b/src/app/markup-text-component/markup-text-settings.json
@@ -0,0 +1,85 @@
+{
+ "settings":[
+ {
+ "label":"semanticMarkup",
+ "description":"display semantic annotations as",
+ "entries":[
+ {
+ "label":"styleType",
+ "description":"render semantic annotations as",
+ "options":[
+ {
+ "id":"button",
+ "description":"buttons"
+ },
+ {
+ "id":"icon",
+ "description":"icons"
+ },
+ {
+ "id":"hyperlink",
+ "description":"text with hyperlinks"
+ },
+ {
+ "id":"markup",
+ "description":"styled text/markup only"
+ },
+ {
+ "id":"none",
+ "description":"plane unstyled text"
+ }
+ ],
+ "value":"button",
+ "type":"select"
+ },
+ {
+ "label":"position",
+ "description":"position them",
+ "options":[
+ {
+ "id":"before",
+ "description":"before the text"
+ },
+ {
+ "id":"after",
+ "description":"after the text"
+ }
+ ],
+ "value":"after",
+ "type":"radio",
+ "condition": [{"option":"semanticMarkup.styleType", "value":"icon"}]
+ },
+ {
+ "label":"actions",
+ "description":"action to take place on click at a semantic annotation",
+ "options":[
+ {
+ "id":"external",
+ "description":"open external link"
+ },
+ {
+ "id":"pannel",
+ "description":"display resource in pannel"
+ },
+ {
+ "id":"menu",
+ "description":"open menu"
+ }
+ ],
+ "value":"menu",
+ "type":"select",
+ "condition": [
+ {"option": "semanticMarkup.styleType", "value": "icon"},
+ {"option": "semanticMarkup.styleType", "value": "button"},
+ {"option": "semanticMarkup.styleType", "value": "hyperlink"}
+ ]
+ },
+ {
+ "label":"tooltip",
+ "value":true,
+ "type":"checkbox"
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/app/markup-text-component/submenu-component/submenu-component.component.html b/src/app/markup-text-component/submenu-component/submenu-component.component.html
new file mode 100644
index 0000000..75fadeb
--- /dev/null
+++ b/src/app/markup-text-component/submenu-component/submenu-component.component.html
@@ -0,0 +1,10 @@
+
+