marcuscalidus-svg-panel icon indicating copy to clipboard operation
marcuscalidus-svg-panel copied to clipboard

Custom CSS

Open nmoreaud opened this issue 5 years ago • 7 comments

Could you add an editor to add static CSS to the panel ? Also, it could be nice to load js files from url (aka CDN, jquery-ui, bootstrap, or other things) Thanks for your good work

nmoreaud avatar Nov 28 '19 08:11 nmoreaud

that's an interesting idea. I'll give thought to that. Thank you.

MarcusCalidus avatar Dec 02 '19 17:12 MarcusCalidus

You can load any js library you like via following script during onInit() like so:

//this example loads the moment.js library and prints the current timestamp into the browser's console

function injectScript(src) {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.async = true;
        script.src = src;
        script.addEventListener('load', resolve);
        script.addEventListener('error', () => reject('Error loading script.'));
        script.addEventListener('abort', () => reject('Script loading aborted.'));
        document.head.appendChild(script);
    });
}

injectScript('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js')
    .then(() => {
        console.log('Script loaded!');
        console.log(moment().format());
    }).catch(error => {
        console.log(error);
    });

MarcusCalidus avatar Dec 11 '19 06:12 MarcusCalidus

to inject custom css you can do the following inside onInit()

function addCss(rule) {
  let css = document.createElement('style');
  css.type = 'text/css';
  if (css.styleSheet) css.styleSheet.cssText = rule; // Support for IE
  else css.appendChild(document.createTextNode(rule)); // Support for the rest
  document.getElementsByTagName("head")[0].appendChild(css);
}


// CSS rules
let rule  = '.red {background-color: red}';
    rule += '.blue {background-color: blue}';

addCss(rule);

be aware though, that the above script will add the css node multiple times. One time for every created control. You could tweak the routine to detect if the given node was already added by maybe setting a global variable in window or similar hack.

MarcusCalidus avatar Dec 11 '19 06:12 MarcusCalidus

Thanks for your response. I currently use the init function to put all shared generic code between panels, that way I can copy-paste it. Ex : open modals, tooltips, etc. I use the same trick as you to add custom CSS.

(function() {
	"use strict";
	
	// generic code (lib)
	
	// All temporary data should be stored in the controller to be able to display multiple panel.
	// Moreover, the front is an SPA, we don't want to share data between pages
	let lib = {};
	ctrl.companyscope = { lib: lib };
		
	lib.isLightColor = function(color) {
		let ctx = document.createElement("canvas").getContext("2d");
		ctx.fillStyle = color;
		let hexColor = ctx.fillStyle.replace("#", "");
		let r = parseInt(hexColor.substr(0,2),16);
		let g = parseInt(hexColor.substr(2,2),16);
		let b = parseInt(hexColor.substr(4,2),16);
		let yiq = ((r*299)+(g*587)+(b*114))/1000;
		return (yiq >= 128);
	};
	
	lib.showModal = function(title, description) {
		// Grafana uses SystemJs to share it's typescript SDK : https://grafana.com/docs/plugins/developing/development/#buildscript
		// We use appEvents pour display a modal : https://github.com/grafana/grafana/blob/e9d5e037e817b57303c4497b42b28ee9403dd65f/public/app/features/alerting/alert_list_ctrl.ts#L64
		System.import('app/core/core').then(function(core) {
			// https://github.com/grafana/grafana/blob/master/public/app/features/alerting/partials/alert_howto.html
			const modalHtml = `
			<div class="modal-body">
				<div class="modal-header">
					<h2 class="modal-header-title">
						<i class="fa fa-info-circle"></i>
						<span class="p-l-1">${title}</span>
					</h2>

					<a class="modal-header-close" ng-click="dismiss();">
						<i class="fa fa-remove"></i>
					</a>
				</div>

				<div class="modal-content">
					<p class="p-a-2 text-center offset-lg-1 col-lg-10">
						${description}
					</p>
				</div>
			</div>
			`;

			core.appEvents.emit('show-modal', {
				modalClass: 'modal--narrow',
				templateHtml: modalHtml,
				model: {}
			});
		});
	};

	lib.zabbixSeverityToColor = function(severity) {
	    if (severity === 'Not classified') {
	        return 'YellowGreen';
	    }
		if (severity === 'Information') {
			return 'MediumTurquoise';
		}
		else if (severity === 'Warning') {
			return 'orange';
		}
		else if (severity === 'Average') {
		    return 'gold';
		}
		else if (severity === 'High' || severity == 'Disaster') {
			return 'red';
		}
		else {
			return 'green';
		}
	};
	
	lib.$tooltip = $('<div class="graph-tooltip">');
	lib.bindTooltipOnSvgHover = function(svgnode, genTooltipHTMLCallback) {
		svgnode.hover(function(e) {
			let html = genTooltipHTMLCallback(e);
			if (html !== null && html !== '') {
				lib.$tooltip
				  .html(html)
				  .place_tt(e.clientX + 20, e.clientY);
			}			
		}, function(e) {
            lib.$tooltip.detach();
		});
	};
	
	lib.makeTooltipsFollowMouse = function() {
		svgnode.onmousemove = function(e) {
			if (document.contains(lib.$tooltip[0])) {
				lib.$tooltip.place_tt(e.clientX + 20, e.clientY);
			}
		};
	};
})();

nmoreaud avatar Dec 11 '19 09:12 nmoreaud

I see... so you still think it is necessary to add CSS and 3rdParty Library support inputs?

MarcusCalidus avatar Dec 11 '19 09:12 MarcusCalidus

I think it would be great addition. With 3rd party JS support, I could host a lib somewhere and when I update it every panel would be updated as well. CSS panel/file is just to separate contents, though for big stylesheets it would improve readability.

nmoreaud avatar Dec 11 '19 09:12 nmoreaud

acknowledged ... I'll see what I can do.

MarcusCalidus avatar Dec 11 '19 10:12 MarcusCalidus