Browse Source

Sidebar plugin. (#581)

* Sidebar plugin.
pull/584/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
542fca6d76
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      backend/i18n/frontend_en.json
  2. 5
      backend/i18n/frontend_it.json
  3. 5
      backend/i18n/frontend_nl.json
  4. 5
      backend/i18n/source/frontend_en.json
  5. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs
  6. 10
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs
  7. 10
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs
  8. 28
      backend/src/Squidex/wwwroot/scripts/context-editor.html
  9. 4
      backend/src/Squidex/wwwroot/scripts/editor-combined.html
  10. 47
      backend/src/Squidex/wwwroot/scripts/editor-context.html
  11. 3
      backend/src/Squidex/wwwroot/scripts/editor-json-schema.html
  12. 9
      backend/src/Squidex/wwwroot/scripts/editor-log.html
  13. 146
      backend/src/Squidex/wwwroot/scripts/editor-sdk.js
  14. 4
      backend/src/Squidex/wwwroot/scripts/editor-simple.html
  15. 49
      backend/src/Squidex/wwwroot/scripts/sidebar-context.html
  16. 118
      backend/src/Squidex/wwwroot/scripts/sidebar-search.html
  17. 1
      frontend/app/features/content/declarations.ts
  18. 15
      frontend/app/features/content/module.ts
  19. 10
      frontend/app/features/content/pages/comments/comments-page.component.html
  20. 11
      frontend/app/features/content/pages/content/content-field.component.ts
  21. 4
      frontend/app/features/content/pages/content/content-page.component.html
  22. 11
      frontend/app/features/content/pages/content/content-page.component.ts
  23. 4
      frontend/app/features/content/pages/contents/contents-page.component.html
  24. 4
      frontend/app/features/content/pages/contents/contents-page.component.ts
  25. 15
      frontend/app/features/content/pages/sidebar/sidebar-page.component.html
  26. 5
      frontend/app/features/content/pages/sidebar/sidebar-page.component.scss
  27. 113
      frontend/app/features/content/pages/sidebar/sidebar-page.component.ts
  28. 8
      frontend/app/features/rules/pages/rules/rules-page.component.html
  29. 20
      frontend/app/features/schemas/pages/schema/common/schema-edit-form.component.html
  30. 2
      frontend/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.html
  31. 14
      frontend/app/framework/angular/forms/editors/iframe-editor.component.ts
  32. 47
      frontend/app/shared/components/comments/comments.component.html
  33. 2
      frontend/app/shared/guards/content-must-exist.guard.spec.ts
  34. 26
      frontend/app/shared/services/schemas.service.spec.ts
  35. 19
      frontend/app/shared/services/schemas.service.ts
  36. 2
      frontend/app/shared/state/schemas.forms.ts
  37. 2
      frontend/app/theme/_panels.scss
  38. 2
      frontend/app/theme/icomoon/demo-files/demo.css
  39. 556
      frontend/app/theme/icomoon/demo.html
  40. BIN
      frontend/app/theme/icomoon/fonts/icomoon.eot
  41. 1
      frontend/app/theme/icomoon/fonts/icomoon.svg
  42. BIN
      frontend/app/theme/icomoon/fonts/icomoon.ttf
  43. BIN
      frontend/app/theme/icomoon/fonts/icomoon.woff
  44. 2
      frontend/app/theme/icomoon/selection.json
  45. 161
      frontend/app/theme/icomoon/style.css

5
backend/i18n/frontend_en.json

@ -301,6 +301,7 @@
"common.searchResults": "Search Results", "common.searchResults": "Search Results",
"common.separateByLine": "Separate by line", "common.separateByLine": "Separate by line",
"common.settings": "Settings", "common.settings": "Settings",
"common.sidebar": "Sidebar Extension",
"common.sidebarTour": "The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time.", "common.sidebarTour": "The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time.",
"common.slug": "Slug", "common.slug": "Slug",
"common.stars.max": "Must not have more more than 15 stars", "common.stars.max": "Must not have more more than 15 stars",
@ -622,6 +623,10 @@
"schemas.addNestedField": "Add Nested Field", "schemas.addNestedField": "Add Nested Field",
"schemas.changeCategoryFailed": "Failed to change category. Please reload.", "schemas.changeCategoryFailed": "Failed to change category. Please reload.",
"schemas.clone": "Clone Schema", "schemas.clone": "Clone Schema",
"schemas.contentSidebarUrl": "Content Sidebar Extension",
"schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.",
"schemas.contentsSidebarUrl": "Contents Sidebar Extension",
"schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.",
"schemas.contextMenuTour": "Open the context menu to delete the schema or to create some scripts for content changes.", "schemas.contextMenuTour": "Open the context menu to delete the schema or to create some scripts for content changes.",
"schemas.create": "Create Schema", "schemas.create": "Create Schema",
"schemas.createCategory": "Create new category...", "schemas.createCategory": "Create new category...",

5
backend/i18n/frontend_it.json

@ -301,6 +301,7 @@
"common.searchResults": "Risultati di ricerca", "common.searchResults": "Risultati di ricerca",
"common.separateByLine": "Separato dalla linea", "common.separateByLine": "Separato dalla linea",
"common.settings": "Impostazioni", "common.settings": "Impostazioni",
"common.sidebar": "Sidebar Extension",
"common.sidebarTour": "La barra di navigazione laterale contiene specifici utili collegamenti per il contesto. Qui puoi visualizzare la cronologia dei cambiamenti di questo schema.", "common.sidebarTour": "La barra di navigazione laterale contiene specifici utili collegamenti per il contesto. Qui puoi visualizzare la cronologia dei cambiamenti di questo schema.",
"common.slug": "Slug", "common.slug": "Slug",
"common.stars.max": "Non deve avere più di 15 stelle", "common.stars.max": "Non deve avere più di 15 stelle",
@ -622,6 +623,10 @@
"schemas.addNestedField": "Aggiungi un campo annidato", "schemas.addNestedField": "Aggiungi un campo annidato",
"schemas.changeCategoryFailed": "Non è stato possibile cambiare la categoria. Per favore ricarica.", "schemas.changeCategoryFailed": "Non è stato possibile cambiare la categoria. Per favore ricarica.",
"schemas.clone": "Clona lo Schema", "schemas.clone": "Clona lo Schema",
"schemas.contentSidebarUrl": "Content Sidebar Extension",
"schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.",
"schemas.contentsSidebarUrl": "Contents Sidebar Extension",
"schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.",
"schemas.contextMenuTour": "Apri il menu per cancellare lo schema o per inserire alcuni script che modificano il contenuto.", "schemas.contextMenuTour": "Apri il menu per cancellare lo schema o per inserire alcuni script che modificano il contenuto.",
"schemas.create": "Crea uno Schema", "schemas.create": "Crea uno Schema",
"schemas.createCategory": "Crea una nuova categoria...", "schemas.createCategory": "Crea una nuova categoria...",

5
backend/i18n/frontend_nl.json

@ -301,6 +301,7 @@
"common.searchResults": "Zoekresultaten", "common.searchResults": "Zoekresultaten",
"common.separateByLine": "Scheiden op regel", "common.separateByLine": "Scheiden op regel",
"common.settings": "Instellingen", "common.settings": "Instellingen",
"common.sidebar": "Sidebar Extension",
"common.sidebarTour": "De zijbalknavigatie bevat nuttige contextspecifieke links. Hier kun je de geschiedenis bekijken hoe dit schema in de loop van de tijd is veranderd.", "common.sidebarTour": "De zijbalknavigatie bevat nuttige contextspecifieke links. Hier kun je de geschiedenis bekijken hoe dit schema in de loop van de tijd is veranderd.",
"common.slug": "Slug", "common.slug": "Slug",
"common.stars.max": "Mag niet meer dan 15 sterren hebben", "common.stars.max": "Mag niet meer dan 15 sterren hebben",
@ -622,6 +623,10 @@
"schemas.addNestedField": "Voeg genest veld toe", "schemas.addNestedField": "Voeg genest veld toe",
"schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.", "schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.",
"schemas.clone": "Clone Schema", "schemas.clone": "Clone Schema",
"schemas.contentSidebarUrl": "Content Sidebar Extension",
"schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.",
"schemas.contentsSidebarUrl": "Contents Sidebar Extension",
"schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.",
"schemas.contextMenuTour": "Open het contextmenu om het schema te verwijderen of om scripts te maken voor wijzigingen in de inhoud.", "schemas.contextMenuTour": "Open het contextmenu om het schema te verwijderen of om scripts te maken voor wijzigingen in de inhoud.",
"schemas.create": "Schema maken", "schemas.create": "Schema maken",
"schemas.createCategory": "Nieuwe categorie maken ...", "schemas.createCategory": "Nieuwe categorie maken ...",

5
backend/i18n/source/frontend_en.json

@ -301,6 +301,7 @@
"common.searchResults": "Search Results", "common.searchResults": "Search Results",
"common.separateByLine": "Separate by line", "common.separateByLine": "Separate by line",
"common.settings": "Settings", "common.settings": "Settings",
"common.sidebar": "Sidebar Extension",
"common.sidebarTour": "The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time.", "common.sidebarTour": "The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time.",
"common.slug": "Slug", "common.slug": "Slug",
"common.stars.max": "Must not have more more than 15 stars", "common.stars.max": "Must not have more more than 15 stars",
@ -622,6 +623,10 @@
"schemas.addNestedField": "Add Nested Field", "schemas.addNestedField": "Add Nested Field",
"schemas.changeCategoryFailed": "Failed to change category. Please reload.", "schemas.changeCategoryFailed": "Failed to change category. Please reload.",
"schemas.clone": "Clone Schema", "schemas.clone": "Clone Schema",
"schemas.contentSidebarUrl": "Content Sidebar Extension",
"schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.",
"schemas.contentsSidebarUrl": "Contents Sidebar Extension",
"schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.",
"schemas.contextMenuTour": "Open the context menu to delete the schema or to create some scripts for content changes.", "schemas.contextMenuTour": "Open the context menu to delete the schema or to create some scripts for content changes.",
"schemas.create": "Create Schema", "schemas.create": "Create Schema",
"schemas.createCategory": "Create new category...", "schemas.createCategory": "Create new category...",

4
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs

@ -15,6 +15,10 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
public ReadOnlyCollection<string>? Tags { get; set; } public ReadOnlyCollection<string>? Tags { get; set; }
public string? ContentsSidebarUrl { get; set; }
public string? ContentSidebarUrl { get; set; }
public bool DeepEquals(SchemaProperties properties) public bool DeepEquals(SchemaProperties properties)
{ {
return SimpleEquals.IsEquals(this, properties); return SimpleEquals.IsEquals(this, properties);

10
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs

@ -24,6 +24,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
[LocalizedStringLength(1000)] [LocalizedStringLength(1000)]
public string? Hints { get; set; } public string? Hints { get; set; }
/// <summary>
/// The url to a the sidebar plugin for content lists.
/// </summary>
public string? ContentsSidebarUrl { get; set; }
/// <summary>
/// The url to a the sidebar plugin for content items.
/// </summary>
public string? ContentSidebarUrl { get; set; }
/// <summary> /// <summary>
/// Tags for automation processes. /// Tags for automation processes.
/// </summary> /// </summary>

10
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs

@ -27,6 +27,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
[LocalizedStringLength(1000)] [LocalizedStringLength(1000)]
public string? Hints { get; set; } public string? Hints { get; set; }
/// <summary>
/// The url to a the sidebar plugin for content lists.
/// </summary>
public string? ContentsSidebarUrl { get; set; }
/// <summary>
/// The url to a the sidebar plugin for content items.
/// </summary>
public string? ContentSidebarUrl { get; set; }
/// <summary> /// <summary>
/// Tags for automation processes. /// Tags for automation processes.
/// </summary> /// </summary>

28
backend/src/Squidex/wwwroot/scripts/context-editor.html

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js -->
<script src="editor-sdk.js"></script>
</head>
<body>
<textarea style="width: 100%; box-sizing: border-box; height: 100px;" name="content" id="editor"></textarea>
<script>
var element = document.getElementById('editor');
// When the field is instantiated it notified the UI that it has been loaded.
var field = new SquidexFormField();
field.onInit(function (context) {
if (context) {
element.innerHTML = JSON.stringify(context, null, 2);
}
});
</script>
</body>
</html>

4
backend/src/Squidex/wwwroot/scripts/combined-editor.html → backend/src/Squidex/wwwroot/scripts/editor-combined.html

@ -14,7 +14,9 @@
<script> <script>
var element = document.getElementById('editor'); var element = document.getElementById('editor');
// When the field is instantiated it notified the UI that it has been loaded. // When the field is instantiated it notifies the UI that it has been loaded.
//
// Furthermore it sends the current size to the parent.
var field = new SquidexFormField(); var field = new SquidexFormField();
// Handle the value change event and set the text to the editor. // Handle the value change event and set the text to the editor.

47
backend/src/Squidex/wwwroot/scripts/editor-context.html

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js -->
<script src="editor-sdk.js"></script>
<style>
textarea {
box-sizing: border-box;
resize: none;
overflow: hidden;
width: 100%;
}
</style>
</head>
<body>
<script>
function grow(element) {
element.style.height = "5px";
element.style.height = (element.scrollHeight)+"px";
}
</script>
<textarea oninput="grow(this)" name="content" id="editor"></textarea>
<script>
var element = document.getElementById('editor');
// When the field is instantiated it notifies the UI that it has been loaded.
//
// Furthermore it sends the current size to the parent.
var field = new SquidexFormField();
// Init is called once with a context that contains the app name, schema name and authentication information.
field.onInit(function (context) {
element.innerHTML = JSON.stringify(context, null, 2);
grow(element);
});
</script>
</body>
</html>

3
backend/src/Squidex/wwwroot/scripts/editor-json-schema.html

@ -29,6 +29,9 @@
<script> <script>
const Form = JSONSchemaForm.default; const Form = JSONSchemaForm.default;
// When the field is instantiated it notifies the UI that it has been loaded.
//
// Furthermore it sends the current size to the parent.
const field = new SquidexFormField(); const field = new SquidexFormField();
function Editor() { function Editor() {

9
backend/src/Squidex/wwwroot/scripts/simple-log.html → backend/src/Squidex/wwwroot/scripts/editor-log.html

@ -16,13 +16,16 @@
var button = document.getElementById('button'); var button = document.getElementById('button');
// When the field is instantiated it notifies the UI that it has been loaded.
//
// Furthermore it sends the current size to the parent.
var field = new SquidexFormField(); var field = new SquidexFormField();
function logState(message) { function logState(message) {
console.log(`${message}. Value: <${JSON.stringify(field.getValue(), 2)}>, Form Value: <${JSON.stringify(field.getFormValue())}>`); console.log(`${message}. Value: <${JSON.stringify(field.getValue(), 2)}>, Form Value: <${JSON.stringify(field.getFormValue())}>`);
} }
logState('Init'); logState('Setup');
if (button) { if (button) {
button.addEventListener('click', function () { button.addEventListener('click', function () {
@ -34,6 +37,10 @@
}); });
} }
field.onInit(function () {
logState('Init');
});
// Handle the value change event and set the text to the editor. // Handle the value change event and set the text to the editor.
field.onValueChanged(function (value) { field.onValueChanged(function (value) {
logState(`Value changed: <${JSON.stringify(value, 2)}>`); logState(`Value changed: <${JSON.stringify(value, 2)}>`);

146
backend/src/Squidex/wwwroot/scripts/editor-sdk.js

@ -1,3 +1,123 @@
function measureAndNotifyParent() {
var height = 0;
document.body.style.margin = '0';
document.body.style.padding = '0';
window.parent.postMessage({ type: 'started' }, '*');
function notifySize() {
var newHeight = document.body.offsetHeight;
if (height !== newHeight) {
height = newHeight;
if (window.parent) {
window.parent.postMessage({ type: 'resize', height: height }, '*');
}
}
window.parent.postMessage({ type: 'resize', height: height }, '*');
}
notifySize();
return setInterval(function () {
notifySize();
}, 50);
}
function SquidexPlugin() {
var initHandler;
var initCalled = false;
var contentHandler;
var content;
var context;
var timer;
function raiseContentChanged() {
if (contentHandler && content) {
contentHandler(content);
}
}
function raiseInit() {
if (initHandler && !initCalled && context) {
initHandler(context);
initCalled = true;
}
}
function eventListener(event) {
if (event.source !== window) {
var type = event.data.type;
if (type === 'contentChanged') {
content = event.data.content;
raiseContentChanged();
} else if (type === 'init') {
context = event.data.context;
raiseInit();
}
}
}
window.addEventListener('message', eventListener, false);
timer = measureAndNotifyParent();
var editor = {
/**
* Get the current value.
*/
getContext: function () {
return context;
},
/*
* Notifies the parent to navigate to the path.
*/
navigate: function (url) {
if (window.parent) {
window.parent.postMessage({ type: 'navigate', url: url }, '*');
}
},
/**
* Register the init handler.
*/
onInit: function (callback) {
initHandler = callback;
raiseInit();
},
/**
* Register the content changed handler.
*/
onContentChanged: function (callback) {
contentHandler = callback;
raiseContentChanged();
},
/**
* Clean the editor SDK.
*/
clean: function () {
if (timer) {
window.removeEventListener('message', eventListener);
timer();
}
}
};
return editor;
}
function SquidexFormField() { function SquidexFormField() {
var initHandler; var initHandler;
@ -10,7 +130,6 @@ function SquidexFormField() {
var formValue; var formValue;
var context; var context;
var timer; var timer;
var height = document.body.offsetHeight;
function raiseDisabled() { function raiseDisabled() {
if (disabledHandler) { if (disabledHandler) {
@ -63,23 +182,9 @@ function SquidexFormField() {
} }
} }
document.body.style.margin = '0';
document.body.style.padding = '0';
window.addEventListener('message', eventListener, false); window.addEventListener('message', eventListener, false);
window.parent.postMessage({ type: 'started' }, '*'); timer = measureAndNotifyParent();
window.parent.postMessage({ type: 'resize', height: height }, '*');
timer = setInterval(function () {
var newHeight = document.body.offsetHeight;
if (height !== newHeight) {
height = newHeight;
window.parent.postMessage({ type: 'resize', height: height }, '*');
}
}, 500);
var editor = { var editor = {
/** /**
@ -112,6 +217,15 @@ function SquidexFormField() {
} }
}, },
/*
* Notifies the parent to navigate to the path.
*/
navigate: function (url) {
if (window.parent) {
window.parent.postMessage({ type: 'navigate', url: url }, '*');
}
},
/** /**
* Notifies the control container that the value has been changed. * Notifies the control container that the value has been changed.
*/ */

4
backend/src/Squidex/wwwroot/scripts/simple-editor.html → backend/src/Squidex/wwwroot/scripts/editor-simple.html

@ -27,7 +27,9 @@
console.error(error); console.error(error);
}) })
.then(editor => { .then(editor => {
// When the field is instantiated it notified the UI that it has been loaded. // When the field is instantiated it notifies the UI that it has been loaded.
//
// Furthermore it sends the current size to the parent.
var field = new SquidexFormField(); var field = new SquidexFormField();
// Handle the value change event and set the text to the editor. // Handle the value change event and set the text to the editor.

49
backend/src/Squidex/wwwroot/scripts/sidebar-context.html

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js -->
<script src="editor-sdk.js"></script>
<style>
textarea {
box-sizing: border-box;
border: 0;
border-radius: 0;
resize: none;
overflow: hidden;
width: 100%;
}
</style>
</head>
<body>
<script>
function grow(element) {
element.style.height = "5px";
element.style.height = (element.scrollHeight)+"px";
}
</script>
<textarea oninput="grow(this)" name="content" id="editor"></textarea>
<script>
var element = document.getElementById('editor');
// When the field is instantiated it notifies the UI that it has been loaded.
//
// Furthermore it sends the current size to the parent.
var plugin = new SquidexPlugin();
// Init is called once with a context that contains the app name, schema name and authentication information.
plugin.onInit(function (context) {
element.innerHTML = JSON.stringify(context, null, 2);
grow(element);
});
</script>
</body>
</html>

118
backend/src/Squidex/wwwroot/scripts/sidebar-search.html

@ -0,0 +1,118 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- Load the editor sdk from the local folder or https://cloud.squidex.io/scripts/editor-sdk.js -->
<script src="editor-sdk.js"></script>
<script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.0.0/dist/algoliasearch-lite.umd.js" integrity="sha256-MfeKq2Aw9VAkaE9Caes2NOxQf6vUa8Av0JqcUXUGkd0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.0.0/dist/instantsearch.production.min.js" integrity="sha256-6S7q0JJs/Kx4kb/fv0oMjS855QTz5Rc2hh9AkIUjUsk=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@7.3.1/themes/algolia-min.css" integrity="sha256-HB49n/BZjuqiCtQQf49OdZn63XuKFaxcIHWf0HNKte8=" crossorigin="anonymous">
<style>
.container {
min-height: 400px;
}
.ais-Hits {
margin-top: 1rem;
}
.ais-Hits-item {
margin-right: 0;
margin-top: 5px;
width: 100%;
}
.button-click {
margin-top: 5px;
color: #3389ff;
cursor: pointer;
display: inline-block;
}
.button-click:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<script>
function grow(element) {
element.style.height = "5px";
element.style.height = (element.scrollHeight)+"px";
}
</script>
<div class="container">
<div id="searchbox"></div>
<div id="hits"></div>
</div>
<script>
var element = document.getElementById('editor');
// When the field is instantiated it notifies the UI that it has been loaded.
//
// Furthermore it sends the current size to the parent.
var plugin = new SquidexPlugin();
// Init is called once with a context that contains the app name, schema name and authentication information.
plugin.onInit(function (context) {
var searchClient = algoliasearch('CFNTEE51PJ', 'afa3a7605277b85348c6fa160fb5cecc');
var search = instantsearch({ indexName: 'test', searchClient });
document.addEventListener('click', event => {
if (event.target.matches('.button-click')) {
var id = event.target.getAttribute('data-object-id');
// We cannot directly navigate to in the iframe because it would only change the URL of the iframe.
plugin.navigate(`/app/${context.appName}/content/${context.schemaName}`);
// plugin.navigate(`/app/${context.appName}/content/${context.schemaName}/${id}`);
}
});
search.addWidgets([
instantsearch.widgets.searchBox({
container: '#searchbox',
}),
instantsearch.widgets.hits({
container: '#hits',
templates: {
item(hit, bindEvent) {
return `
<div>
<div class="hit-name">
${instantsearch.highlight({
attribute: 'firstname',
hit,
})}
${instantsearch.highlight({
attribute: 'lastname',
hit,
})}
<div>
<a data-object-id="${hit.objectID}" class="button-click">EDIT</a>
</div>
</div>
</div>
`;
}
},
})
]);
search.start();
});
</script>
</body>
</html>

1
frontend/app/features/content/declarations.ts

@ -16,6 +16,7 @@ export * from './pages/contents/contents-filters-page.component';
export * from './pages/contents/contents-page.component'; export * from './pages/contents/contents-page.component';
export * from './pages/contents/custom-view-editor.component'; export * from './pages/contents/custom-view-editor.component';
export * from './pages/schemas/schemas-page.component'; export * from './pages/schemas/schemas-page.component';
export * from './pages/sidebar/sidebar-page.component';
export * from './shared/content-status.component'; export * from './shared/content-status.component';
export * from './shared/due-time-selector.component'; export * from './shared/due-time-selector.component';
export * from './shared/forms/array-editor.component'; export * from './shared/forms/array-editor.component';

15
frontend/app/features/content/module.ts

@ -10,7 +10,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { CanDeactivateGuard, ContentMustExistGuard, LoadLanguagesGuard, SchemaMustExistPublishedGuard, SchemaMustNotBeSingletonGuard, SqxFrameworkModule, SqxSharedModule, UnsetContentGuard } from '@app/shared'; import { CanDeactivateGuard, ContentMustExistGuard, LoadLanguagesGuard, SchemaMustExistPublishedGuard, SchemaMustNotBeSingletonGuard, SqxFrameworkModule, SqxSharedModule, UnsetContentGuard } from '@app/shared';
import { ArrayEditorComponent, ArrayItemComponent, ArraySectionComponent, AssetsEditorComponent, CommentsPageComponent, ContentComponent, ContentCreatorComponent, ContentEventComponent, ContentFieldComponent, ContentHistoryPageComponent, ContentListCellDirective, ContentListFieldComponent, ContentListHeaderComponent, ContentListWidthPipe, ContentPageComponent, ContentSectionComponent, ContentSelectorComponent, ContentSelectorItemComponent, ContentsFiltersPageComponent, ContentsPageComponent, ContentStatusComponent, ContentValueComponent, ContentValueEditorComponent, CustomViewEditorComponent, DueTimeSelectorComponent, FieldEditorComponent, FieldLanguagesComponent, PreviewButtonComponent, ReferenceItemComponent, ReferencesEditorComponent, SchemasPageComponent, StockPhotoEditorComponent } from './declarations'; import { ArrayEditorComponent, ArrayItemComponent, ArraySectionComponent, AssetsEditorComponent, CommentsPageComponent, ContentComponent, ContentCreatorComponent, ContentEventComponent, ContentFieldComponent, ContentHistoryPageComponent, ContentListCellDirective, ContentListFieldComponent, ContentListHeaderComponent, ContentListWidthPipe, ContentPageComponent, ContentSectionComponent, ContentSelectorComponent, ContentSelectorItemComponent, ContentsFiltersPageComponent, ContentsPageComponent, ContentStatusComponent, ContentValueComponent, ContentValueEditorComponent, CustomViewEditorComponent, DueTimeSelectorComponent, FieldEditorComponent, FieldLanguagesComponent, PreviewButtonComponent, ReferenceItemComponent, ReferencesEditorComponent, SchemasPageComponent, SidebarPageComponent, StockPhotoEditorComponent } from './declarations';
const routes: Routes = [ const routes: Routes = [
{ {
@ -28,12 +28,16 @@ const routes: Routes = [
{ {
path: '', path: '',
component: ContentsPageComponent, component: ContentsPageComponent,
canActivate: [SchemaMustNotBeSingletonGuard], canActivate: [SchemaMustNotBeSingletonGuard, UnsetContentGuard],
canDeactivate: [CanDeactivateGuard], canDeactivate: [CanDeactivateGuard],
children: [ children: [
{ {
path: 'filters', path: 'filters',
component: ContentsFiltersPageComponent component: ContentsFiltersPageComponent
},
{
path: 'sidebar',
component: SidebarPageComponent
} }
] ]
}, },
@ -59,7 +63,11 @@ const routes: Routes = [
{ {
path: 'comments', path: 'comments',
component: CommentsPageComponent component: CommentsPageComponent
} },
{
path: 'sidebar',
component: SidebarPageComponent
}
] ]
} }
] ]
@ -105,6 +113,7 @@ const routes: Routes = [
ReferenceItemComponent, ReferenceItemComponent,
ReferencesEditorComponent, ReferencesEditorComponent,
SchemasPageComponent, SchemasPageComponent,
SidebarPageComponent,
StockPhotoEditorComponent StockPhotoEditorComponent
] ]
}) })

10
frontend/app/features/content/pages/comments/comments-page.component.html

@ -1 +1,9 @@
<sqx-comments [commentsId]="commentsId | async"></sqx-comments> <sqx-panel desiredWidth="20rem" isBlank="true" [isLazyLoaded]="false" grid="true">
<ng-container title>
{{ 'comments.title' | sqxTranslate }}
</ng-container>
<ng-container content>
<sqx-comments [commentsId]="commentsId | async"></sqx-comments>
</ng-container>
</sqx-panel>

11
frontend/app/features/content/pages/content/content-field.component.ts

@ -7,8 +7,8 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { AppLanguageDto, AppsState, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, StringFieldPropertiesDto, TranslationsService, Types, value$ } from '@app/shared'; import { AppLanguageDto, AppsState, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, StringFieldPropertiesDto, TranslationsService, Types, value$ } from '@app/shared';
import { Observable } from 'rxjs'; import { combineLatest, Observable } from 'rxjs';
import { combineLatest } from 'rxjs/operators'; import { map } from 'rxjs/operators';
@Component({ @Component({
selector: 'sqx-content-field', selector: 'sqx-content-field',
@ -78,9 +78,10 @@ export class ContentFieldComponent implements OnChanges {
if ((changes['formModel'] || changes['formModelCompare']) && this.formModelCompare) { if ((changes['formModel'] || changes['formModelCompare']) && this.formModelCompare) {
this.isDifferent = this.isDifferent =
value$(this.formModel.form).pipe( combineLatest([
combineLatest(value$(this.formModelCompare!.form), value$(this.formModel.form),
(lhs, rhs) => !Types.equals(lhs, rhs, true))); value$(this.formModelCompare!.form)
]).pipe(map(([lhs, rhs]) => !Types.equals(lhs, rhs, true)));
} }
} }

4
frontend/app/features/content/pages/content/content-page.component.html

@ -105,6 +105,10 @@
<i class="icon-comments"></i> <i class="icon-comments"></i>
</a> </a>
<a class="panel-link" routerLink="sidebar" routerLinkActive="active" queryParamsHandling="preserve" title="i18n:common.sidebar" titlePosition="left" *ngIf="schema.properties.contentSidebarUrl">
<i class="icon-plugin"></i>
</a>
<sqx-onboarding-tooltip helpId="history" [for]="linkHistory" position="left-top" after="120000"> <sqx-onboarding-tooltip helpId="history" [for]="linkHistory" position="left-top" after="120000">
{{ 'common.sidebarTour' | sqxTranslate }} {{ 'common.sidebarTour' | sqxTranslate }}
</sqx-onboarding-tooltip> </sqx-onboarding-tooltip>

11
frontend/app/features/content/pages/content/content-page.component.ts

@ -9,7 +9,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ApiUrlConfig, AppLanguageDto, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, ContentDto, ContentsState, DialogService, EditContentForm, fadeAnimation, FieldForm, FieldSection, LanguagesState, ModalModel, ResourceOwner, RootFieldDto, SchemaDetailsDto, SchemasState, TempService, valueAll$, Version } from '@app/shared'; import { ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, ContentDto, ContentsState, DialogService, EditContentForm, fadeAnimation, FieldForm, FieldSection, LanguagesState, ModalModel, ResourceOwner, RootFieldDto, SchemaDetailsDto, SchemasState, TempService, valueAll$, Version } from '@app/shared';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { debounceTime, filter, onErrorResumeNext, tap } from 'rxjs/operators'; import { debounceTime, filter, onErrorResumeNext, tap } from 'rxjs/operators';
@ -39,7 +39,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
public language: AppLanguageDto; public language: AppLanguageDto;
public languages: ReadonlyArray<AppLanguageDto>; public languages: ReadonlyArray<AppLanguageDto>;
constructor(apiUrl: ApiUrlConfig, authService: AuthService, constructor(apiUrl: ApiUrlConfig, authService: AuthService, appsState: AppsState,
public readonly contentsState: ContentsState, public readonly contentsState: ContentsState,
private readonly autoSaveService: AutoSaveService, private readonly autoSaveService: AutoSaveService,
private readonly dialogs: DialogService, private readonly dialogs: DialogService,
@ -51,7 +51,12 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
) { ) {
super(); super();
this.formContext = { user: authService.user, apiUrl: apiUrl.buildUrl('api') }; this.formContext = {
apiUrl: apiUrl.buildUrl('api'),
appId: appsState.snapshot.selectedApp!.id,
appName: appsState.snapshot.selectedApp!.name,
user: authService.user
};
} }
public ngOnInit() { public ngOnInit() {

4
frontend/app/features/content/pages/contents/contents-page.component.html

@ -130,6 +130,10 @@
<a class="panel-link" routerLink="filters" routerLinkActive="active" queryParamsHandling="preserve" title="i18n:common.filters" titlePosition="left"> <a class="panel-link" routerLink="filters" routerLinkActive="active" queryParamsHandling="preserve" title="i18n:common.filters" titlePosition="left">
<i class="icon-filter"></i> <i class="icon-filter"></i>
</a> </a>
<a class="panel-link" routerLink="sidebar" routerLinkActive="active" queryParamsHandling="preserve" title="i18n:common.sidebar" titlePosition="left" *ngIf="schema.properties.contentsSidebarUrl">
<i class="icon-plugin"></i>
</a>
</div> </div>
</ng-container> </ng-container>
</sqx-panel> </sqx-panel>

4
frontend/app/features/content/pages/contents/contents-page.component.ts

@ -65,11 +65,11 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit {
public ngOnInit() { public ngOnInit() {
this.own( this.own(
combineLatest( combineLatest([
this.schemasState.selectedSchema, this.schemasState.selectedSchema,
this.languagesState.languages, this.languagesState.languages,
this.contentsState.statuses this.contentsState.statuses
).subscribe(([schema, languages, statuses]) => { ]).subscribe(([schema, languages, statuses]) => {
this.queryModel = queryModelFromSchema(schema, languages.map(x => x.language), statuses); this.queryModel = queryModelFromSchema(schema, languages.map(x => x.language), statuses);
})); }));

15
frontend/app/features/content/pages/sidebar/sidebar-page.component.html

@ -0,0 +1,15 @@
<sqx-panel desiredWidth="20rem" isBlank="true" [isLazyLoaded]="false">
<ng-container title>
{{ 'common.sidebar' | sqxTranslate }}
</ng-container>
<ng-container content>
<div>
<iframe #iframe scrolling="no" width="100%"></iframe>
</div>
</ng-container>
</sqx-panel>

5
frontend/app/features/content/pages/sidebar/sidebar-page.component.scss

@ -0,0 +1,5 @@
iframe {
background: 0;
border: 0;
overflow: hidden;
}

113
frontend/app/features/content/pages/sidebar/sidebar-page.component.ts

@ -0,0 +1,113 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Renderer2, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { ApiUrlConfig, ResourceOwner, Types } from '@app/framework/internal';
import { AppsState, AuthService, ContentsState, SchemasState } from '@app/shared';
import { combineLatest } from 'rxjs';
@Component({
selector: 'sqx-sidebar-page',
styleUrls: ['./sidebar-page.component.scss'],
templateUrl: './sidebar-page.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SidebarPageComponent extends ResourceOwner implements AfterViewInit {
private isInitialized = false;
private context: any;
private content: any;
@ViewChild('iframe', { static: false })
public iframe: ElementRef<HTMLIFrameElement>;
constructor(apiUrl: ApiUrlConfig, authService: AuthService, appsState: AppsState,
private readonly contentsState: ContentsState,
private readonly schemasState: SchemasState,
private readonly renderer: Renderer2,
private readonly router: Router
) {
super();
this.context = {
apiUrl: apiUrl.buildUrl('api'),
appId: appsState.snapshot.selectedApp!.id,
appName: appsState.snapshot.selectedApp!.name,
user: authService.user
};
}
public ngAfterViewInit() {
this.own(
combineLatest([
this.schemasState.selectedSchema,
this.contentsState.selectedContent
]).subscribe(([schema, content]) => {
const url =
content ?
schema.properties.contentSidebarUrl :
schema.properties.contentsSidebarUrl;
this.context['schemaName'] = schema.name;
this.context['schemaId'] = schema.id;
this.iframe.nativeElement.src = url || '';
}));
this.own(
this.contentsState.selectedContent
.subscribe(content => {
this.content = content;
this.sendContent();
}));
this.own(
this.renderer.listen('window', 'message', (event: MessageEvent) => {
if (event.source === this.iframe.nativeElement.contentWindow) {
const { type } = event.data;
if (type === 'started') {
this.isInitialized = true;
this.sendInit();
this.sendContent();
} else if (type === 'resize') {
const { height } = event.data;
this.iframe.nativeElement.height = height + 'px';
} else if (type === 'navigate') {
const { url } = event.data;
this.router.navigateByUrl(url);
}
}
}));
}
private sendInit() {
this.sendMessage('init', { context: this.context });
}
private sendContent() {
this.sendMessage('contentChanged', { content: this.content });
}
private sendMessage(type: string, payload: any) {
if (!this.iframe) {
return;
}
const iframe = this.iframe.nativeElement;
if (this.isInitialized && iframe.contentWindow && Types.isFunction(iframe.contentWindow.postMessage)) {
const message = { type, ...payload };
iframe.contentWindow.postMessage(message, '*');
}
}
}

8
frontend/app/features/rules/pages/rules/rules-page.component.html

@ -71,11 +71,9 @@
<ng-container sidebar> <ng-container sidebar>
<div class="panel-nav"> <div class="panel-nav">
<ng-container *ngIf="rulesState.canReadEvents | async"> <a class="panel-link panel-link-gray" routerLink="events" routerLinkActive="active" title="i18n:common.history" titlePosition="left" *ngIf="rulesState.canReadEvents | async">
<a class="panel-link panel-link-gray" routerLink="events" routerLinkActive="active" title="i18n:common.history" titlePosition="left"> <i class="icon-time"></i>
<i class="icon-time"></i> </a>
</a>
</ng-container>
<a class="panel-link" routerLink="help" routerLinkActive="active" queryParamsHandling="preserve" title="i18n:common.help" titlePosition="left" #helpLink> <a class="panel-link" routerLink="help" routerLinkActive="active" queryParamsHandling="preserve" title="i18n:common.help" titlePosition="left" #helpLink>
<i class="icon-help2"></i> <i class="icon-help2"></i>

20
frontend/app/features/schemas/pages/schema/common/schema-edit-form.component.html

@ -30,6 +30,26 @@
<sqx-form-hint>{{ 'schemas.schemaHintsHint' | sqxTranslate }}</sqx-form-hint> <sqx-form-hint>{{ 'schemas.schemaHintsHint' | sqxTranslate }}</sqx-form-hint>
</div> </div>
<div class="form-group">
<label for="hints">{{ 'schemas.contentsSidebarUrl' | sqxTranslate }}</label>
<sqx-control-errors for="contentsSidebarUrl"></sqx-control-errors>
<input type="url" class="form-control" id="contentsSidebarUrl" formControlName="contentsSidebarUrl">
<sqx-form-hint>{{ 'schemas.contentsSidebarUrl' | sqxTranslate }}</sqx-form-hint>
</div>
<div class="form-group">
<label for="hints">{{ 'schemas.contentSidebarUrl' | sqxTranslate }}</label>
<sqx-control-errors for="contentSidebarUrl"></sqx-control-errors>
<input type="url" class="form-control" id="contentSidebarUrl" formControlName="contentSidebarUrl">
<sqx-form-hint>{{ 'schemas.contentSidebarUrlHint' | sqxTranslate }}</sqx-form-hint>
</div>
<div class="form-group"> <div class="form-group">
<label for="tags">{{ 'common.tags' | sqxTranslate }}</label> <label for="tags">{{ 'common.tags' | sqxTranslate }}</label>

2
frontend/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.html

@ -3,7 +3,7 @@
<label class="col-3 col-form-label" for="{{field.fieldId}}_editorUrl">{{ 'schemas.field.editorUrl' | sqxTranslate }}</label> <label class="col-3 col-form-label" for="{{field.fieldId}}_editorUrl">{{ 'schemas.field.editorUrl' | sqxTranslate }}</label>
<div class="col-6"> <div class="col-6">
<input type="text" class="form-control" id="{{field.fieldId}}_editorUrl" formControlName="editorUrl"> <input type="url" class="form-control" id="{{field.fieldId}}_editorUrl" formControlName="editorUrl">
<sqx-form-hint> <sqx-form-hint>
{{ 'schemas.field.editorUrlHint' | sqxTranslate }} {{ 'schemas.field.editorUrlHint' | sqxTranslate }}

14
frontend/app/framework/angular/forms/editors/iframe-editor.component.ts

@ -7,6 +7,7 @@
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnChanges, Renderer2, SimpleChanges, ViewChild } from '@angular/core'; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnChanges, Renderer2, SimpleChanges, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { Router } from '@angular/router';
import { StatefulControlComponent, Types } from '@app/framework/internal'; import { StatefulControlComponent, Types } from '@app/framework/internal';
export const SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { export const SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
@ -40,7 +41,8 @@ export class IFrameEditorComponent extends StatefulControlComponent<any, any> im
public url: string; public url: string;
constructor(changeDetector: ChangeDetectorRef, constructor(changeDetector: ChangeDetectorRef,
private readonly renderer: Renderer2 private readonly renderer: Renderer2,
private readonly router: Router
) { ) {
super(changeDetector, {}); super(changeDetector, {});
} }
@ -72,7 +74,7 @@ export class IFrameEditorComponent extends StatefulControlComponent<any, any> im
if (type === 'started') { if (type === 'started') {
this.isInitialized = true; this.isInitialized = true;
this.sendMessage('init', { context: this.context || {} }); this.sendInit();
this.sendFormValue(); this.sendFormValue();
this.sendDisabled(); this.sendDisabled();
this.sendValue(); this.sendValue();
@ -80,6 +82,10 @@ export class IFrameEditorComponent extends StatefulControlComponent<any, any> im
const { height } = event.data; const { height } = event.data;
this.iframe.nativeElement.height = height + 'px'; this.iframe.nativeElement.height = height + 'px';
} else if (type === 'navigate') {
const { url } = event.data;
this.router.navigateByUrl(url);
} else if (type === 'valueChanged') { } else if (type === 'valueChanged') {
const { value } = event.data; const { value } = event.data;
@ -107,6 +113,10 @@ export class IFrameEditorComponent extends StatefulControlComponent<any, any> im
this.sendDisabled(); this.sendDisabled();
} }
private sendInit() {
this.sendMessage('init', { context: this.context || {} });
}
private sendValue() { private sendValue() {
this.sendMessage('valueChanged', { value: this.value }); this.sendMessage('valueChanged', { value: this.value });
} }

47
frontend/app/shared/components/comments/comments.component.html

@ -1,27 +1,28 @@
<sqx-panel desiredWidth="20rem" isBlank="true" [isLazyLoaded]="false" grid="true"> <ng-container *ngIf="mentionUsers | async; let users">
<ng-container title> <div class="comments-list" #commentsList>
{{ 'comments.title' | sqxTranslate }} <div (sqxResized)="scrollDown()">
</ng-container> <sqx-comment *ngFor="let comment of commentsState.comments | async; trackBy: trackByComment"
[comment]="comment"
<ng-container content> [commentsState]="commentsState"
<ng-container *ngIf="mentionUsers | async; let users"> [mentionUsers]="users"
[canEdit]="true"
<div class="comments-list" #commentsList> [canFollow]="false"
<div (sqxResized)="scrollDown()"> [userToken]="userToken">
<sqx-comment *ngFor="let comment of commentsState.comments | async; trackBy: trackByComment" [comment]="comment" [commentsState]="commentsState" [mentionUsers]="users" [canEdit]="true" [canFollow]="false" [userToken]="userToken"> </sqx-comment>
</sqx-comment> </div>
</div> </div>
</div>
<div class="comments-footer">
<form [formGroup]="commentForm.form" (ngSubmit)="comment()">
<input class="form-control" name="text" formControlName="text" placeholder="{{ 'comments.create' | sqxTranslate }}" [mention]="users" [mentionConfig]="mentionConfig" autocomplete="off" autocorrect="off" autocapitalize="off">
</form>
</div>
</ng-container>
</ng-container>
</sqx-panel>
<div class="comments-footer">
<form [formGroup]="commentForm.form" (ngSubmit)="comment()">
<input class="form-control" name="text" formControlName="text" placeholder="{{ 'comments.create' | sqxTranslate }}"
[mention]="users"
[mentionConfig]="mentionConfig"
autocomplete="off"
autocorrect="off"
autocapitalize="off">
</form>
</div>
</ng-container>

2
frontend/app/shared/guards/content-must-exist.guard.spec.ts

@ -18,8 +18,8 @@ describe('ContentMustExistGuard', () => {
} }
}; };
let contentsState: IMock<ContentsState>;
let router: IMock<Router>; let router: IMock<Router>;
let contentsState: IMock<ContentsState>;
let contentGuard: ContentMustExistGuard; let contentGuard: ContentMustExistGuard;
beforeEach(() => { beforeEach(() => {

26
frontend/app/shared/services/schemas.service.spec.ts

@ -613,8 +613,12 @@ describe('SchemasService', () => {
lastModifiedBy: `modifier${id}`, lastModifiedBy: `modifier${id}`,
properties: { properties: {
label: `label${id}${suffix}`, label: `label${id}${suffix}`,
hints: `hints${id}${suffix}`, contentsSidebarUrl: `url/to/contents/${id}${suffix}`,
tags: [`tags${id}${suffix}`] contentSidebarUrl: `url/to/content/${id}${suffix}`,
tags: [
`tags${id}${suffix}`
],
hints: `hints${id}${suffix}`
}, },
version: `${id}`, version: `${id}`,
_links: { _links: {
@ -637,6 +641,8 @@ describe('SchemasService', () => {
version: `${id}`, version: `${id}`,
properties: { properties: {
label: `label${id}${suffix}`, label: `label${id}${suffix}`,
contentsSidebarUrl: `url/to/contents/${id}${suffix}`,
contentSidebarUrl: `url/to/content/${id}${suffix}`,
tags: [ tags: [
`tags${id}${suffix}` `tags${id}${suffix}`
], ],
@ -811,6 +817,18 @@ describe('SchemasService', () => {
} }
}); });
function createSchemaProperties(id: number, suffix = '') {
return new SchemaPropertiesDto(
`label${id}${suffix}`,
`hints${id}${suffix}`,
`url/to/contents/${id}${suffix}`,
`url/to/content/${id}${suffix}`,
[
`tags${id}${suffix}`
]
);
}
export function createSchema(id: number, suffix = '') { export function createSchema(id: number, suffix = '') {
const links: ResourceLinks = { const links: ResourceLinks = {
update: { method: 'PUT', href: `/schemas/${id}` } update: { method: 'PUT', href: `/schemas/${id}` }
@ -820,7 +838,7 @@ export function createSchema(id: number, suffix = '') {
`schema-id${id}`, `schema-id${id}`,
`schema-name${id}${suffix}`, `schema-name${id}${suffix}`,
`category${id}${suffix}`, `category${id}${suffix}`,
new SchemaPropertiesDto(`label${id}${suffix}`, `hints${id}${suffix}`, [`tags${id}${suffix}`]), createSchemaProperties(id, suffix),
id % 2 === 0, id % 2 === 0,
id % 3 === 0, id % 3 === 0,
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`, DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`,
@ -837,7 +855,7 @@ export function createSchemaDetails(id: number, suffix = '') {
`schema-id${id}`, `schema-id${id}`,
`schema-name${id}${suffix}`, `schema-name${id}${suffix}`,
`category${id}${suffix}`, `category${id}${suffix}`,
new SchemaPropertiesDto(`label${id}${suffix}`, `hints${id}${suffix}`, [`tags${id}${suffix}`]), createSchemaProperties(id, suffix),
id % 2 === 0, id % 2 === 0,
id % 3 === 0, id % 3 === 0,
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`, DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`,

19
frontend/app/shared/services/schemas.service.ts

@ -323,6 +323,8 @@ export class SchemaPropertiesDto {
constructor( constructor(
public readonly label?: string, public readonly label?: string,
public readonly hints?: string, public readonly hints?: string,
public readonly contentsSidebarUrl?: string,
public readonly contentSidebarUrl?: string,
public readonly tags?: ReadonlyArray<string> public readonly tags?: ReadonlyArray<string>
) { ) {
} }
@ -362,6 +364,8 @@ export interface SynchronizeSchemaDto {
export interface UpdateSchemaDto { export interface UpdateSchemaDto {
readonly label?: string; readonly label?: string;
readonly hints?: string; readonly hints?: string;
readonly contentsSidebarUrl?: string;
readonly contentSidebarUrl?: string;
readonly tags?: ReadonlyArray<string>; readonly tags?: ReadonlyArray<string>;
} }
@ -698,7 +702,7 @@ function parseSchemas(response: any) {
item.id, item.id,
item.name, item.name,
item.category, item.category,
new SchemaPropertiesDto(item.properties.label, item.properties.hints, item.properties.tags), parseProperties(item.properties),
item.isSingleton, item.isSingleton,
item.isPublished, item.isPublished,
DateTime.parseISO(item.created), item.createdBy, DateTime.parseISO(item.created), item.createdBy,
@ -713,13 +717,11 @@ function parseSchemas(response: any) {
function parseSchemaWithDetails(response: any) { function parseSchemaWithDetails(response: any) {
const fields = response.fields.map((item: any) => parseField(item)); const fields = response.fields.map((item: any) => parseField(item));
const properties = new SchemaPropertiesDto(response.properties.label, response.properties.hints, response.properties.tags);
return new SchemaDetailsDto(response._links, return new SchemaDetailsDto(response._links,
response.id, response.id,
response.name, response.name,
response.category, response.category,
properties, parseProperties(response.properties),
response.isSingleton, response.isSingleton,
response.isPublished, response.isPublished,
DateTime.parseISO(response.created), response.createdBy, DateTime.parseISO(response.created), response.createdBy,
@ -733,6 +735,15 @@ function parseSchemaWithDetails(response: any) {
response.previewUrls || {}); response.previewUrls || {});
} }
function parseProperties(response: any) {
return new SchemaPropertiesDto(
response.label,
response.hints,
response.contentsSidebarUrl,
response.contentSidebarUrl,
response.tags);
}
export function parseField(item: any) { export function parseField(item: any) {
const propertiesDto = const propertiesDto =
createProperties( createProperties(

2
frontend/app/shared/state/schemas.forms.ts

@ -232,6 +232,8 @@ export class EditSchemaForm extends Form<FormGroup, UpdateSchemaDto, SchemaPrope
Validators.maxLength(1000) Validators.maxLength(1000)
] ]
], ],
contentsSidebarUrl: '',
contentSidebarUrl: '',
tags: [] tags: []
})); }));
} }

2
frontend/app/theme/_panels.scss

@ -106,6 +106,8 @@
&-blank { &-blank {
background: $panel-light-background; background: $panel-light-background;
border: 0; border: 0;
border-radius: 0;
padding-top: 0;
} }
&-blank-bordered { &-blank-bordered {

2
frontend/app/theme/icomoon/demo-files/demo.css

@ -150,7 +150,7 @@ p {
font-size: 24px; font-size: 24px;
} }
.fs2 { .fs2 {
font-size: 28px; font-size: 24px;
} }
.fs3 { .fs3 {
font-size: 24px; font-size: 24px;

556
frontend/app/theme/icomoon/demo.html

File diff suppressed because it is too large

BIN
frontend/app/theme/icomoon/fonts/icomoon.eot

Binary file not shown.

1
frontend/app/theme/icomoon/fonts/icomoon.svg

@ -140,6 +140,7 @@
<glyph unicode="&#xe982;" glyph-name="zoom_out" d="M298 554.667h214v-42h-214v42zM406 340.667q80 0 136 56t56 136-56 136-136 56-136-56-56-136 56-136 136-56zM662 340.667l212-212-64-64-212 212v34l-12 12q-76-66-180-66-116 0-197 80t-81 196 81 197 197 81 196-81 80-197q0-42-20-95t-46-85l12-12h34z" /> <glyph unicode="&#xe982;" glyph-name="zoom_out" d="M298 554.667h214v-42h-214v42zM406 340.667q80 0 136 56t56 136-56 136-136 56-136-56-56-136 56-136 136-56zM662 340.667l212-212-64-64-212 212v34l-12 12q-76-66-180-66-116 0-197 80t-81 196 81 197 197 81 196-81 80-197q0-42-20-95t-46-85l12-12h34z" />
<glyph unicode="&#xe983;" glyph-name="zoom_in" d="M512 512.667h-86v-86h-42v86h-86v42h86v86h42v-86h86v-42zM406 340.667q80 0 136 56t56 136-56 136-136 56-136-56-56-136 56-136 136-56zM662 340.667l212-212-64-64-212 212v34l-12 12q-76-66-180-66-116 0-197 80t-81 196 81 197 197 81 196-81 80-197q0-42-20-95t-46-85l12-12h34z" /> <glyph unicode="&#xe983;" glyph-name="zoom_in" d="M512 512.667h-86v-86h-42v86h-86v42h86v86h42v-86h86v-42zM406 340.667q80 0 136 56t56 136-56 136-136 56-136-56-56-136 56-136 136-56zM662 340.667l212-212-64-64-212 212v34l-12 12q-76-66-180-66-116 0-197 80t-81 196 81 197 197 81 196-81 80-197q0-42-20-95t-46-85l12-12h34z" />
<glyph unicode="&#xe984;" glyph-name="enter" d="M470 554.667l60-60-154-154h392v428h86v-512h-478l154-154-60-60-256 256z" /> <glyph unicode="&#xe984;" glyph-name="enter" d="M470 554.667l60-60-154-154h392v428h86v-512h-478l154-154-60-60-256 256z" />
<glyph unicode="&#xe985;" glyph-name="plugin" horiz-adv-x="839" d="M456.158 914.575c-12.145 0.032-24.18-1.32-35.941-3.974-23.536-5.313-45.906-15.872-65.238-31.342-77.826-61.631-80.817-179.217-6.302-244.772 0.028-0.028 0.084-0.086 0.113-0.114 0.020-0.018 0.038-0.039 0.058-0.057 0.037-0.039 0.074-0.076 0.112-0.112l0.001-0.001c4.727-4.044 7.287-9.964 7.097-16.182-0.003-0.018 0.003-0.038 0-0.057-0.008-0.085-0.012-0.184-0.012-0.284s0.004-0.199 0.013-0.296l-0.001 0.013c0-10.503-8.234-18.737-18.737-18.737h-290.707c-5.436 0.020-10.354-4.898-10.334-10.334v-192.479c0.079-40.118 25.509-70.246 57.289-81.875 31.779-11.628 70.726-5.088 96.694 25.494 48.914 57.384 140.83 35.582 158.809-37.644 6.312-27.401 0.010-56.217-17.261-78.411-35.813-45.415-103.666-46.801-141.265-2.839-0.153 0.167-0.32 0.317-0.501 0.448l-0.010 0.007c-0.073 0.080-0.147 0.154-0.224 0.224l-0.003 0.002c-25.873 30.135-64.41 36.684-96.012 25.21-31.887-11.577-57.465-41.733-57.517-81.988v-191.003c-0.020-5.436 4.898-10.354 10.334-10.334h745.955c5.436-0.020 10.354 4.898 10.334 10.334l-0.852 616.898c0.020 5.436-4.898 10.354-10.334 10.334h-222.401c-10.503 0-18.737 8.234-18.737 18.737-0.019 5.72 2.466 11.020 6.87 14.592 0.080 0.073 0.154 0.147 0.224 0.224l0.002 0.003 0.227 0.227c97.336 84.032 59.622 244.251-65.125 275.83-0.169 0.034-0.365 0.054-0.566 0.057h-0.002c-0.018 0.004-0.038-0.004-0.058 0h-0.113c-11.884 2.753-23.91 4.17-35.884 4.202zM454.682 845.816c6.891-0.035 13.821-0.856 20.667-2.441 73.201-17.648 95.396-109.369 38.439-158.582-30.669-26.067-37.154-65.105-25.38-96.921 11.773-31.813 42.084-57.204 82.329-57.006h164.090v-499.65h-629.673l-0.795 133.884c0.026 10.44 8.193 18.619 18.623 18.68 5.89-0.174 11.341-2.823 15.103-7.324 0.055-0.060 0.111-0.116 0.169-0.17l0.002-0.002c0.028-0.028 0.086-0.087 0.114-0.113 65.15-75.665 183.646-73.404 245.737 4.769l0.058 0.058c0.076 0.085 0.151 0.178 0.22 0.275l0.007 0.010c30.825 38.562 42.246 89.164 31.058 137.234-0.010 0.168-0.030 0.324-0.062 0.475l0.004-0.021c-30.581 126.005-192.369 164.654-276.681 66.145-0.019-0.019-0.037-0.038-0.055-0.057l-0.001-0.001c-0.175-0.215-0.297-0.397-0.454-0.624-5.948-8.251-13.969-9.414-21.519-6.699-7.485 2.692-12.925 8.644-12.378 18.623v0.172c0.003 0.041-0.003 0.073 0 0.113 0.017 0.131 0.045 0.267 0.057 0.397 0.002 0.043 0.003 0.092 0.003 0.142s-0.001 0.1-0.003 0.149v-0.007 133.259h233.53c40.255 0.052 70.451 25.629 82.045 57.517 11.514 31.669 4.916 70.308-25.38 96.183 0 0.008 0 0.018 0 0.028s0 0.020 0 0.030v-0.002c-0.064 0.053-0.108 0.117-0.172 0.17-0.106 0.123-0.218 0.234-0.337 0.337l-0.004 0.003c-43.965 37.601-42.63 105.454 2.782 141.265 11.117 8.651 23.938 14.535 37.36 17.488 6.716 1.478 13.596 2.249 20.497 2.214z" />
<glyph unicode="&#xe9ca;" glyph-name="earth" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-0.002c-62.958 0-122.872 13.012-177.23 36.452l233.148 262.29c5.206 5.858 8.082 13.422 8.082 21.26v96c0 17.674-14.326 32-32 32-112.99 0-232.204 117.462-233.374 118.626-6 6.002-14.14 9.374-22.626 9.374h-128c-17.672 0-32-14.328-32-32v-192c0-12.122 6.848-23.202 17.69-28.622l110.31-55.156v-187.886c-116.052 80.956-192 215.432-192 367.664 0 68.714 15.49 133.806 43.138 192h116.862c8.488 0 16.626 3.372 22.628 9.372l128 128c6 6.002 9.372 14.14 9.372 22.628v77.412c40.562 12.074 83.518 18.588 128 18.588 70.406 0 137.004-16.26 196.282-45.2-4.144-3.502-8.176-7.164-12.046-11.036-36.266-36.264-56.236-84.478-56.236-135.764s19.97-99.5 56.236-135.764c36.434-36.432 85.218-56.264 135.634-56.26 3.166 0 6.342 0.080 9.518 0.236 13.814-51.802 38.752-186.656-8.404-372.334-0.444-1.744-0.696-3.488-0.842-5.224-81.324-83.080-194.7-134.656-320.142-134.656z" /> <glyph unicode="&#xe9ca;" glyph-name="earth" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-0.002c-62.958 0-122.872 13.012-177.23 36.452l233.148 262.29c5.206 5.858 8.082 13.422 8.082 21.26v96c0 17.674-14.326 32-32 32-112.99 0-232.204 117.462-233.374 118.626-6 6.002-14.14 9.374-22.626 9.374h-128c-17.672 0-32-14.328-32-32v-192c0-12.122 6.848-23.202 17.69-28.622l110.31-55.156v-187.886c-116.052 80.956-192 215.432-192 367.664 0 68.714 15.49 133.806 43.138 192h116.862c8.488 0 16.626 3.372 22.628 9.372l128 128c6 6.002 9.372 14.14 9.372 22.628v77.412c40.562 12.074 83.518 18.588 128 18.588 70.406 0 137.004-16.26 196.282-45.2-4.144-3.502-8.176-7.164-12.046-11.036-36.266-36.264-56.236-84.478-56.236-135.764s19.97-99.5 56.236-135.764c36.434-36.432 85.218-56.264 135.634-56.26 3.166 0 6.342 0.080 9.518 0.236 13.814-51.802 38.752-186.656-8.404-372.334-0.444-1.744-0.696-3.488-0.842-5.224-81.324-83.080-194.7-134.656-320.142-134.656z" />
<glyph unicode="&#xf00a;" glyph-name="grid" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857z" /> <glyph unicode="&#xf00a;" glyph-name="grid" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857z" />
<glyph unicode="&#xf0c9;" glyph-name="list1" horiz-adv-x="878" d="M877.714 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 768v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" /> <glyph unicode="&#xf0c9;" glyph-name="list1" horiz-adv-x="878" d="M877.714 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 768v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" />

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 103 KiB

BIN
frontend/app/theme/icomoon/fonts/icomoon.ttf

Binary file not shown.

BIN
frontend/app/theme/icomoon/fonts/icomoon.woff

Binary file not shown.

2
frontend/app/theme/icomoon/selection.json

File diff suppressed because one or more lines are too long

161
frontend/app/theme/icomoon/style.css

@ -1,10 +1,10 @@
@font-face { @font-face {
font-family: 'icomoon'; font-family: 'icomoon';
src: url('fonts/icomoon.eot?epjxtr'); src: url('fonts/icomoon.eot?osg65x');
src: url('fonts/icomoon.eot?epjxtr#iefix') format('embedded-opentype'), src: url('fonts/icomoon.eot?osg65x#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?epjxtr') format('truetype'), url('fonts/icomoon.ttf?osg65x') format('truetype'),
url('fonts/icomoon.woff?epjxtr') format('woff'), url('fonts/icomoon.woff?osg65x') format('woff'),
url('fonts/icomoon.svg?epjxtr#icomoon') format('svg'); url('fonts/icomoon.svg?osg65x#icomoon') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: block; font-display: block;
@ -25,80 +25,8 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-enter:before { .icon-plugin:before {
content: "\e984"; content: "\e985";
}
.icon-zoom_out:before {
content: "\e982";
}
.icon-zoom_in:before {
content: "\e983";
}
.icon-flip:before {
content: "\e97f";
}
.icon-rotate_right:before {
content: "\e980";
}
.icon-rotate_left:before {
content: "\e981";
}
.icon-create_new_folder:before {
content: "\e97b";
}
.icon-folder:before {
content: "\e97c";
}
.icon-help2:before {
content: "\e97a";
}
.icon-trigger-Manual:before {
content: "\e979";
}
.icon-play-line:before {
content: "\e979";
}
.icon-corner-down-right:before {
content: "\e977";
}
.icon-info-outline:before {
content: "\e974";
}
.icon-upload-2:before {
content: "\e972";
}
.icon-translate:before {
content: "\e96f";
}
.icon-arrow_back:before {
content: "\e96e";
}
.icon-external-link:before {
content: "\e96d";
}
.icon-minus-square:before {
content: "\e969";
}
.icon-plus-square:before {
content: "\e968";
}
.icon-drag2:before {
content: "\e961";
}
.icon-comments:before {
content: "\e95f";
}
.icon-backup:before {
content: "\e95b";
}
.icon-support:before {
content: "\e95a";
}
.icon-control-RichText:before {
content: "\e939";
}
.icon-download:before {
content: "\e93e";
} }
.icon-angle-double-right:before { .icon-angle-double-right:before {
content: "\e97d"; content: "\e97d";
@ -214,6 +142,81 @@
.icon-search-Rule:before { .icon-search-Rule:before {
content: "\e947"; content: "\e947";
} }
.icon-enter:before {
content: "\e984";
}
.icon-zoom_out:before {
content: "\e982";
}
.icon-zoom_in:before {
content: "\e983";
}
.icon-flip:before {
content: "\e97f";
}
.icon-rotate_right:before {
content: "\e980";
}
.icon-rotate_left:before {
content: "\e981";
}
.icon-create_new_folder:before {
content: "\e97b";
}
.icon-folder:before {
content: "\e97c";
}
.icon-help2:before {
content: "\e97a";
}
.icon-trigger-Manual:before {
content: "\e979";
}
.icon-play-line:before {
content: "\e979";
}
.icon-corner-down-right:before {
content: "\e977";
}
.icon-info-outline:before {
content: "\e974";
}
.icon-upload-2:before {
content: "\e972";
}
.icon-translate:before {
content: "\e96f";
}
.icon-arrow_back:before {
content: "\e96e";
}
.icon-external-link:before {
content: "\e96d";
}
.icon-minus-square:before {
content: "\e969";
}
.icon-plus-square:before {
content: "\e968";
}
.icon-drag2:before {
content: "\e961";
}
.icon-comments:before {
content: "\e95f";
}
.icon-backup:before {
content: "\e95b";
}
.icon-support:before {
content: "\e95a";
}
.icon-control-RichText:before {
content: "\e939";
}
.icon-download:before {
content: "\e93e";
}
.icon-type-UI:before { .icon-type-UI:before {
content: "\e975"; content: "\e975";
} }

Loading…
Cancel
Save