mirror of
https://github.com/sismics/docs.git
synced 2024-12-22 11:23:48 +01:00
Closes #236: onboarding wizard
This commit is contained in:
parent
d4d1c35264
commit
940b365447
@ -7,7 +7,7 @@ angular.module('docs',
|
||||
// Dependencies
|
||||
['ui.router', 'ui.bootstrap', 'dialog', 'ngProgress', 'monospaced.qrcode', 'yaru22.angular-timeago', 'ui.validate',
|
||||
'ui.sortable', 'restangular', 'ngSanitize', 'ngTouch', 'colorpicker.module', 'ngFileUpload', 'pascalprecht.translate',
|
||||
'tmh.dynamicLocale']
|
||||
'tmh.dynamicLocale', 'ngOnboarding']
|
||||
)
|
||||
|
||||
/**
|
||||
@ -514,7 +514,7 @@ angular.module('docs',
|
||||
/**
|
||||
* Initialize ngProgress.
|
||||
*/
|
||||
.run(function($rootScope, ngProgressFactory, $http) {
|
||||
.run (function ($rootScope, ngProgressFactory, $http) {
|
||||
$rootScope.ngProgress = ngProgressFactory.createInstance();
|
||||
|
||||
// Watch for the number of XHR running
|
||||
@ -527,6 +527,12 @@ angular.module('docs',
|
||||
$rootScope.ngProgress.start();
|
||||
}
|
||||
});
|
||||
})
|
||||
/**
|
||||
* Initialize ngOnboarding.
|
||||
*/
|
||||
.run (function ($rootScope) {
|
||||
$rootScope.onboardingEnabled = false;
|
||||
});
|
||||
|
||||
if (location.search.indexOf("protractor") > -1) {
|
||||
|
@ -142,4 +142,47 @@ angular.module('docs').controller('DocumentDefault', function ($scope, $rootScop
|
||||
}).then(function (data) {
|
||||
$scope.documentsWorkflow = data.documents;
|
||||
});
|
||||
|
||||
// Onboarding
|
||||
$translate('onboarding.step1.title').then(function () {
|
||||
if (localStorage.onboardingDisplayed || $(window).width() < 1000) {
|
||||
return;
|
||||
}
|
||||
localStorage.onboardingDisplayed = true;
|
||||
|
||||
$rootScope.onboardingEnabled = true;
|
||||
|
||||
$rootScope.onboardingSteps = [
|
||||
{
|
||||
title: $translate.instant('onboarding.step1.title'),
|
||||
description: $translate.instant('onboarding.step1.description'),
|
||||
position: "centered",
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step2.title'),
|
||||
description: $translate.instant('onboarding.step2.description'),
|
||||
attachTo: "#document-add-btn",
|
||||
position: "right"
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step3.title'),
|
||||
description: $translate.instant('onboarding.step3.description'),
|
||||
attachTo: "#quick-upload-zone",
|
||||
position: "left"
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step4.title'),
|
||||
description: $translate.instant('onboarding.step4.description'),
|
||||
attachTo: "#search-box",
|
||||
position: "right"
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step5.title'),
|
||||
description: $translate.instant('onboarding.step5.description'),
|
||||
attachTo: "#navigation-tag",
|
||||
position: "right"
|
||||
}
|
||||
];
|
||||
});
|
||||
});
|
@ -13,6 +13,7 @@
|
||||
<link rel="stylesheet" href="style/fontawesome.css" type="text/css" />
|
||||
<link rel="stylesheet" href="style/colorpicker.css" type="text/css" />
|
||||
<link rel="stylesheet" href="style/pell.css" type="text/css" />
|
||||
<link rel="stylesheet" href="style/ng-onboarding.css" type="text/css" />
|
||||
<link rel="stylesheet/less" href="style/main.less" type="text/css" />
|
||||
<!-- endref -->
|
||||
<link rel="stylesheet" href="../api/theme/stylesheet" type="text/css" id="theme-stylesheet" />
|
||||
@ -49,6 +50,7 @@
|
||||
<script src="lib/angular.ngprogress.js" type="text/javascript"></script>
|
||||
<script src="lib/angular.qrcode.js" type="text/javascript"></script>
|
||||
<script src="lib/angular.timeago.js" type="text/javascript"></script>
|
||||
<script src="lib/angular.ng-onboarding.js" type="text/javascript"></script>
|
||||
<script src="app/docs/app.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/Login.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/Main.js" type="text/javascript"></script>
|
||||
@ -106,6 +108,8 @@
|
||||
<!-- endref -->
|
||||
</head>
|
||||
<body translate-cloak ng-cloak>
|
||||
<onboarding-popover ng-if="onboardingEnabled" enabled="onboardingEnabled" steps="onboardingSteps"></onboarding-popover>
|
||||
|
||||
<nav class="navbar navbar-default" role="navigation" ng-controller="Navigation">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle"
|
||||
@ -134,7 +138,7 @@
|
||||
<li ui-sref-active="{ active: 'document.**' }">
|
||||
<a href="#/document"><span class="fas fa-book"></span> {{ 'index.nav_documents' | translate }}</a>
|
||||
</li>
|
||||
<li ui-sref-active="{ active: 'tag.**' }">
|
||||
<li ui-sref-active="{ active: 'tag.**' }" id="navigation-tag">
|
||||
<a href="#/tag"><span class="fas fa-tags"></span> {{ 'index.nav_tags' | translate }}</a>
|
||||
</li>
|
||||
<li ui-sref-active="{ active: 'user.**', active2: 'group.**' }">
|
||||
|
185
docs-web/src/main/webapp/src/lib/angular.ng-onboarding.js
Normal file
185
docs-web/src/main/webapp/src/lib/angular.ng-onboarding.js
Normal file
@ -0,0 +1,185 @@
|
||||
(function() {
|
||||
var app;
|
||||
|
||||
app = angular.module("ngOnboarding", []);
|
||||
|
||||
app.provider("ngOnboardingDefaults", function() {
|
||||
return {
|
||||
options: {
|
||||
overlay: true,
|
||||
overlayOpacity: 0.4,
|
||||
overlayClass: 'onboarding-overlay',
|
||||
popoverClass: 'onboarding-popover',
|
||||
titleClass: 'onboarding-popover-title',
|
||||
contentClass: 'onboarding-popover-content',
|
||||
arrowClass: 'onboarding-arrow',
|
||||
buttonContainerClass: 'onboarding-button-container',
|
||||
buttonClass: "btn",
|
||||
showButtons: true,
|
||||
nextButtonText: 'Next →',
|
||||
previousButtonText: '← Previous',
|
||||
showDoneButton: true,
|
||||
doneButtonText: 'Done',
|
||||
closeButtonClass: 'onboarding-close-button',
|
||||
closeButtonText: 'X',
|
||||
stepClass: 'onboarding-step-info',
|
||||
showStepInfo: true
|
||||
},
|
||||
$get: function() {
|
||||
return this.options;
|
||||
},
|
||||
set: function(keyOrHash, value) {
|
||||
var k, v, _results;
|
||||
if (typeof keyOrHash === 'object') {
|
||||
_results = [];
|
||||
for (k in keyOrHash) {
|
||||
v = keyOrHash[k];
|
||||
_results.push(this.options[k] = v);
|
||||
}
|
||||
return _results;
|
||||
} else {
|
||||
return this.options[keyOrHash] = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
app.directive('onboardingPopover', [
|
||||
'ngOnboardingDefaults', '$sce', '$timeout', function(ngOnboardingDefaults, $sce, $timeout) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
enabled: '=',
|
||||
steps: '=',
|
||||
onFinishCallback: '&onFinishCallback',
|
||||
index: '=?stepIndex'
|
||||
},
|
||||
replace: true,
|
||||
link: function(scope, element, attrs) {
|
||||
var attributesToClear, curStep, setupOverlay, setupPositioning;
|
||||
curStep = null;
|
||||
attributesToClear = ['title', 'top', 'right', 'bottom', 'left', 'width', 'height', 'position'];
|
||||
scope.stepCount = scope.steps.length;
|
||||
scope.next = function() {
|
||||
return scope.index = scope.index + 1;
|
||||
};
|
||||
scope.previous = function() {
|
||||
return scope.index = scope.index - 1;
|
||||
};
|
||||
scope.close = function() {
|
||||
scope.enabled = false;
|
||||
setupOverlay(false);
|
||||
if (scope.onFinishCallback) {
|
||||
return scope.onFinishCallback();
|
||||
}
|
||||
};
|
||||
scope.$watch('index', function(newVal, oldVal) {
|
||||
var attr, k, v, _i, _len;
|
||||
if (newVal === null) {
|
||||
scope.enabled = false;
|
||||
setupOverlay(false);
|
||||
return;
|
||||
}
|
||||
curStep = scope.steps[scope.index];
|
||||
scope.lastStep = scope.index + 1 === scope.steps.length;
|
||||
scope.showNextButton = scope.index + 1 < scope.steps.length;
|
||||
scope.showPreviousButton = scope.index > 0;
|
||||
for (_i = 0, _len = attributesToClear.length; _i < _len; _i++) {
|
||||
attr = attributesToClear[_i];
|
||||
scope[attr] = null;
|
||||
}
|
||||
for (k in ngOnboardingDefaults) {
|
||||
v = ngOnboardingDefaults[k];
|
||||
if (curStep[k] === void 0) {
|
||||
scope[k] = v;
|
||||
}
|
||||
}
|
||||
for (k in curStep) {
|
||||
v = curStep[k];
|
||||
scope[k] = v;
|
||||
}
|
||||
scope.description = $sce.trustAsHtml(scope.description);
|
||||
scope.nextButtonText = $sce.trustAsHtml(scope.nextButtonText);
|
||||
scope.previousButtonText = $sce.trustAsHtml(scope.previousButtonText);
|
||||
scope.doneButtonText = $sce.trustAsHtml(scope.doneButtonText);
|
||||
scope.closeButtonText = $sce.trustAsHtml(scope.closeButtonText);
|
||||
setupOverlay();
|
||||
return setupPositioning();
|
||||
});
|
||||
setupOverlay = function(showOverlay) {
|
||||
if (showOverlay == null) {
|
||||
showOverlay = true;
|
||||
}
|
||||
$('.onboarding-focus').removeClass('onboarding-focus');
|
||||
if (showOverlay) {
|
||||
if (curStep['attachTo'] && scope.overlay) {
|
||||
return $(curStep['attachTo']).addClass('onboarding-focus');
|
||||
}
|
||||
}
|
||||
};
|
||||
setupPositioning = function() {
|
||||
var attachTo, bottom, left, right, top, xMargin, yMargin;
|
||||
attachTo = curStep['attachTo'];
|
||||
scope.position = curStep['position'];
|
||||
xMargin = 15;
|
||||
yMargin = 15;
|
||||
if (attachTo) {
|
||||
if (!(scope.left || scope.right)) {
|
||||
left = null;
|
||||
right = null;
|
||||
if (scope.position === 'right') {
|
||||
left = $(attachTo).offset().left + $(attachTo).outerWidth() + xMargin;
|
||||
} else if (scope.position === 'left') {
|
||||
right = $(window).width() - $(attachTo).offset().left + xMargin;
|
||||
} else if (scope.position === 'top' || scope.position === 'bottom') {
|
||||
left = $(attachTo).offset().left;
|
||||
}
|
||||
if (curStep['xOffset']) {
|
||||
if (left !== null) {
|
||||
left = left + curStep['xOffset'];
|
||||
}
|
||||
if (right !== null) {
|
||||
right = right - curStep['xOffset'];
|
||||
}
|
||||
}
|
||||
scope.left = left;
|
||||
scope.right = right;
|
||||
}
|
||||
if (!(scope.top || scope.bottom)) {
|
||||
top = null;
|
||||
bottom = null;
|
||||
if (scope.position === 'left' || scope.position === 'right') {
|
||||
top = $(attachTo).offset().top + $(attachTo).outerHeight() / 2 - 14;
|
||||
} else if (scope.position === 'bottom') {
|
||||
top = $(attachTo).offset().top + $(attachTo).outerHeight() + yMargin;
|
||||
} else if (scope.position === 'top') {
|
||||
bottom = $(window).height() - $(attachTo).offset().top + yMargin;
|
||||
}
|
||||
if (curStep['yOffset']) {
|
||||
if (top !== null) {
|
||||
top = top + curStep['yOffset'];
|
||||
}
|
||||
if (bottom !== null) {
|
||||
bottom = bottom - curStep['yOffset'];
|
||||
}
|
||||
}
|
||||
scope.top = top;
|
||||
scope.bottom = bottom;
|
||||
}
|
||||
}
|
||||
if (scope.position && scope.position.length) {
|
||||
return scope.positionClass = "onboarding-" + scope.position;
|
||||
} else {
|
||||
return scope.positionClass = null;
|
||||
}
|
||||
};
|
||||
if (scope.steps.length && !scope.index) {
|
||||
return scope.index = 0;
|
||||
}
|
||||
},
|
||||
template: "<div class='onboarding-container' ng-show='enabled'>\n <div class='{{overlayClass}}' ng-style='{opacity: overlayOpacity}', ng-show='overlay'></div>\n <div class='{{popoverClass}} {{positionClass}}' ng-style=\"{width: width, height: height, left: left, top: top, right: right, bottom: bottom}\">\n <div class='{{arrowClass}}'></div>\n <h3 class='{{titleClass}}' ng-show='title' ng-bind='title'></h3>\n <a href='' ng-click='close()' class='{{closeButtonClass}}' ng-bind-html='closeButtonText'></a>\n <div class='{{contentClass}}'>\n <p ng-bind-html='description'></p>\n </div>\n <div class='{{buttonContainerClass}}' ng-show='showButtons'>\n <span ng-show='showStepInfo' class='{{stepClass}}'>{{index + 1}}/{{stepCount}}</span>\n <a href='' ng-click='previous()' ng-show='showPreviousButton' class='{{buttonClass}}' ng-bind-html='previousButtonText'></a>\n <a href='' ng-click='next()' ng-show='showNextButton' class='{{buttonClass}}' ng-bind-html='nextButtonText'></a>\n <a href='' ng-click='close()' ng-show='showDoneButton && lastStep' class='{{buttonClass}}' ng-bind-html='doneButtonText'></a>\n </div>\n </div>\n</div>"
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
}).call(this);
|
@ -564,6 +564,28 @@
|
||||
"first": "First",
|
||||
"last": "Last"
|
||||
},
|
||||
"onboarding": {
|
||||
"step1": {
|
||||
"title": "First time?",
|
||||
"description": "If it's your first time on Sismics Docs, click the Next button, otherwise feel free to close me."
|
||||
},
|
||||
"step2": {
|
||||
"title": "Documents",
|
||||
"description": "Sismics Docs is organized in documents and each document contains multiple files."
|
||||
},
|
||||
"step3": {
|
||||
"title": "Files",
|
||||
"description": "You can add files after creating a document or before using this Quick upload area."
|
||||
},
|
||||
"step4": {
|
||||
"title": "Search",
|
||||
"description": "This is the main way to find your documents back. There is also an advanced search with the magnifier button."
|
||||
},
|
||||
"step5": {
|
||||
"title": "Tags",
|
||||
"description": "Documents can be organized in tags (which are like super-folders). Create them here."
|
||||
}
|
||||
},
|
||||
"ok": "OK",
|
||||
"cancel": "Cancel",
|
||||
"share": "Share",
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="well well-3d" style="background-color: #f6f9fc">
|
||||
<div class="well well-3d" style="background-color: #f6f9fc" id="quick-upload-zone">
|
||||
<div class="pull-right" ng-show="files.length > 0">
|
||||
<a href class="btn btn-default" ng-init="selectAll = true" ng-click="changeChecked(selectAll); selectAll = !selectAll">
|
||||
<span class="far" ng-class="{ 'fa-check-square': selectAll, 'fa-square': !selectAll }"></span>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="well well-3d">
|
||||
<!-- Main new document button -->
|
||||
<div class="text-center mb-19">
|
||||
<div class="btn-group" uib-dropdown>
|
||||
<div class="btn-group" uib-dropdown id="document-add-btn">
|
||||
<a href="#/document/add" class="btn btn-primary">
|
||||
<span class="fas fa-plus"></span> {{ 'document.add_document' | translate }}
|
||||
</a>
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
<!-- Search (simple and advanced) -->
|
||||
<div class="row search-dropdown-anchor">
|
||||
<div class="col-xs-12 input-group">
|
||||
<div class="col-xs-12 input-group" id="search-box">
|
||||
<input type="search" class="form-control"
|
||||
uib-typeahead="suggestion for suggestion in suggestions"
|
||||
typeahead-focus-first="false"
|
||||
|
196
docs-web/src/main/webapp/src/style/ng-onboarding.css
Normal file
196
docs-web/src/main/webapp/src/style/ng-onboarding.css
Normal file
@ -0,0 +1,196 @@
|
||||
.onboarding-popover {
|
||||
position: absolute;
|
||||
z-index: 100001;
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 6px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
background-clip: padding-box;
|
||||
max-width: 800px;
|
||||
max-height: 800px;
|
||||
min-width: 100px;
|
||||
min-height: 50px;
|
||||
}
|
||||
.onboarding-popover.onboarding-top {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.onboarding-popover.onboarding-right {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.onboarding-popover.onboarding-bottom {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.onboarding-popover.onboarding-left {
|
||||
margin-left: -10px;
|
||||
}
|
||||
.onboarding-popover.onboarding-centered {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
-moz-transform: translate(-50%, -50%);
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
-o-transform: translate(-50%, -50%);
|
||||
}
|
||||
.onboarding-arrow,
|
||||
.onboarding-arrow:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.onboarding-arrow {
|
||||
border-width: 11px;
|
||||
}
|
||||
.onboarding-arrow:after {
|
||||
border-width: 10px;
|
||||
content: "";
|
||||
}
|
||||
.onboarding-popover.onboarding-bottom .onboarding-arrow {
|
||||
top: -11px;
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
border-bottom-color: #cccccc;
|
||||
border-top-width: 0;
|
||||
}
|
||||
.onboarding-popover.onboarding-bottom .onboarding-arrow:after {
|
||||
content: " ";
|
||||
top: 1px;
|
||||
margin-left: -10px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #ffffff;
|
||||
}
|
||||
.onboarding-popover.onboarding-top .onboarding-arrow {
|
||||
bottom: -11px;
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
border-top-color: #cccccc;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.onboarding-popover.onboarding-top .onboarding-arrow:after {
|
||||
content: " ";
|
||||
bottom: 1px;
|
||||
margin-left: -10px;
|
||||
border-bottom-width: 0;
|
||||
border-top-color: #ffffff;
|
||||
}
|
||||
.onboarding-popover.onboarding-left .onboarding-arrow {
|
||||
top: 14px;
|
||||
right: -11px;
|
||||
margin-top: -11px;
|
||||
border-left-color: #cccccc;
|
||||
border-right-width: 0;
|
||||
}
|
||||
.onboarding-popover.onboarding-left .onboarding-arrow:after {
|
||||
content: " ";
|
||||
right: 1px;
|
||||
border-right-width: 0;
|
||||
border-left-color: #ffffff;
|
||||
bottom: -10px;
|
||||
}
|
||||
.onboarding-popover.onboarding-right .onboarding-arrow {
|
||||
top: 14px;
|
||||
left: -11px;
|
||||
margin-top: -11px;
|
||||
border-right-color: #cccccc;
|
||||
border-left-width: 0;
|
||||
}
|
||||
.onboarding-popover.onboarding-right .onboarding-arrow:after {
|
||||
content: " ";
|
||||
left: 1px;
|
||||
bottom: -10px;
|
||||
border-left-width: 0;
|
||||
border-right-color: #ffffff;
|
||||
}
|
||||
.onboarding-popover-title {
|
||||
position: relative;
|
||||
padding: 8px 14px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 18px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
.onboarding-close-button {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 5px;
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
font-size: 110%;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
.onboarding-close-button,
|
||||
.onboarding-close-button:hover,
|
||||
.onboarding-close-button:visited {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
.onboarding-popover-content {
|
||||
padding: 9px 14px;
|
||||
}
|
||||
.onboarding-button-container {
|
||||
text-align: right;
|
||||
padding: 7px;
|
||||
display: block;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.onboarding-button {
|
||||
padding: 5px 10px;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
}
|
||||
.onboarding-button:hover,
|
||||
.onboarding-button:active {
|
||||
color: #333;
|
||||
background-color: #ebebeb;
|
||||
border-color: #adadad;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
.onboarding-overlay {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1000%;
|
||||
background-color: #000000;
|
||||
}
|
||||
.onboarding-focus {
|
||||
/*z-index: 100000 !important;*/
|
||||
}
|
||||
.onboarding-step-info {
|
||||
float: left;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-top: 7px;
|
||||
font-size: 80%;
|
||||
font-style: italic;
|
||||
}
|
Loading…
Reference in New Issue
Block a user