Skip to content

Commit ac9553b

Browse files
committed
Refactor theme page and make it work with htmx
Via vanilla color input, users can easily select colors and even extract colors from screen, and thus farbtastic is not needed any more. Besides that, now the color input value automatically changes to current value that's specified in "Custom CSS" when a section is selected.
1 parent 27442d0 commit ac9553b

File tree

11 files changed

+51
-561
lines changed

11 files changed

+51
-561
lines changed

devel/third-party/README

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,6 @@ Origin: https://github.com/NicolasCARPi/dropzone
4747
License: MIT
4848
Note that this version is in a new, maintained fork from the original project.
4949

50-
51-
* eyedropper.svg
52-
Description: color picker icon
53-
Origin: http://thenounproject.com/noun/eye-dropper/ (The Noun Project)
54-
Author: The Noun Project
55-
License: CC BY 3.0
56-
5750
* htmx-2.0.3
5851
Description: htmx gives you access to AJAX, CSS Transitions, WebSockets and Server Sent Events directly in HTML, using attributes, so you can build modern user interfaces with the simplicity and power of hypertext
5952
Origin: https://github.com/bigskysoftware/htmx

devel/third-party/eyedropper.svg

Lines changed: 0 additions & 13 deletions
This file was deleted.

lib/RT/Interface/Web/MenuBuilder.pm

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,9 +1485,6 @@ sub _BuildAdminTopMenu {
14851485
title => loc('Theme'),
14861486
description => loc('Customize the look of your RT'),
14871487
path => '/Admin/Tools/Theme.html',
1488-
attributes => {
1489-
'hx-boost' => 'false',
1490-
},
14911488
);
14921489
if (RT->Config->Get('StatementLog')
14931490
&& $current_user->HasRight( Right => 'SuperUser', Object => RT->System )) {

share/html/Admin/Tools/Theme.html

Lines changed: 50 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,12 @@
5151
<& /Elements/Tabs &>
5252
<& /Elements/ListActions, actions => \@results &>
5353

54-
<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/static/js/farbtastic.js"></script>
55-
5654
<div class="row">
5755

5856
<div id="simple-customize" class="col-6">
5957
<div id="upload-logo">
60-
<h2>Logo</h2>
58+
<h3>Logo</h3>
6159
<& /Elements/Logo, id => 'logo-theme-editor', ShowName => 0 &>
62-
<button onclick="setLogoSize()" class="button btn btn-primary form-control" id="logo-size"><&|/l&>Full Size</&></button>
6360

6461
<form method="POST" enctype="multipart/form-data" action="">
6562
<div class="row mt-2">
@@ -82,7 +79,7 @@ <h2>Logo</h2>
8279
</div>
8380

8481
<div id="custom-css" class="col-6">
85-
<h2><&|/l&>Custom CSS (Advanced)</&></h2>
82+
<h3><&|/l&>Custom CSS (Advanced)</&></h3>
8683

8784
<form method="POST" id="custom-css-form" action="">
8885
<textarea class="form-control mb-2" rows="20" id="user_css" name="user_css" wrap="off"><% $user_css %></textarea>
@@ -98,21 +95,19 @@ <h2><&|/l&>Custom CSS (Advanced)</&></h2>
9895

9996
<div class="row">
10097
<div id="customize-theme" class="col-6">
101-
<h2><&|/l&>Customize the RT theme</&></h2>
102-
<ol class="list-group-compact list-group">
103-
<li class="list-group-item">
104-
<label for="section"><&|/l&>Select a section</&>:</label>
105-
<select class="selectpicker" id="section"></select>
106-
</li>
107-
<li class="list-group-item">
108-
<div class="description">
109-
<&|/l&>Select a color for the section</&>:
110-
<div id="logo-picker-hint" style="display: none;">
111-
<&|/l&>You can also click on the logo above to get colors!</&>
112-
</div>
113-
</div>
98+
<h3><&|/l&>Customize the RT theme</&></h3>
99+
<&| /Elements/LabeledValue, Label => loc('Select a section') &>
100+
<select class="form-select selectpicker" id="section" name="section">
101+
% for my $section ( @sections ) {
102+
<option value="<% $section->[0] %>"><% $section->[0] %></option>
103+
% };
104+
</select>
105+
</&>
106+
107+
<&| /Elements/LabeledValue, Label => loc('Select a color for the section') &>
108+
<div class="row">
114109
% if ($colors) {
115-
<div class="button btn btn-primary primary-colors">
110+
<div class="col button btn btn-primary primary-colors">
116111
% for (@$colors) {
117112
% my $fg = $_->{l} >= $text_threshold ? 'black' : 'white';
118113
<button type="button" class="button btn btn-primary color-template form-control"
@@ -122,10 +117,11 @@ <h2><&|/l&>Customize the RT theme</&></h2>
122117
% }
123118
</div>
124119
% }
125-
<div id="color-picker"></div>
126-
<canvas id="logo-color-picker" title="<&|/l&>Click to choose a color</&>"></canvas>
127-
</li>
128-
</ol>
120+
<div class="col">
121+
<input type="color" class="form-control" id="color-picker" />
122+
</div>
123+
</div>
124+
</&>
129125
</div>
130126
</div>
131127

@@ -146,25 +142,11 @@ <h2><&|/l&>Customize the RT theme</&></h2>
146142
</%ONCE>
147143
<script type="text/javascript">
148144
var section_css_mapping = <% JSON(\@sections) |n%>;
149-
150145
jQuery(function($) {
151-
152-
jQuery.each(section_css_mapping, function(i,v){
153-
$('select#section').append($("<option/>")
154-
.attr('value', v[0])
155-
.text(v[0]));
156-
});
157-
// Refresh selectpicker so we have a default value, at least for Firefox
158-
$('select#section').val(section_css_mapping[0][0]);
159-
160146
function update_sitecss(text) {
161147
if (!text)
162148
text = $('#user_css').val();
163-
164-
// IE 8 doesn't let us update the innerHTML of <style> tags (with jQuery.text())
165-
// see: http://stackoverflow.com/questions/2692770/style-style-textcss-appendtohead-does-not-work-in-ie/2692861#2692861
166-
$("style#sitecss").remove();
167-
$("<style id='sitecss' type='text/css' media='all'>" + text + "</style>").appendTo('head');
149+
$("style#sitecss").text(text);
168150
}
169151

170152
update_sitecss();
@@ -225,73 +207,43 @@ <h2><&|/l&>Customize the RT theme</&></h2>
225207
update_sitecss(css);
226208
}
227209

228-
$('#color-picker').farbtastic(function(color){ change_color(color, this.hsl[2] > <% $text_threshold %> ? '#000' : '#fff') });
210+
document.querySelector('#color-picker').addEventListener('change', function() {
211+
const red = parseInt('0x' + this.value.substr(1,2));
212+
const green = parseInt('0x' + this.value.substr(3,2));
213+
const blue = parseInt('0x' + this.value.substr(5,2));
214+
const lightness = (Math.max(red, green, blue) + Math.min(red, green, blue))/2/255;
215+
change_color(this.value, lightness > <% $text_threshold %> ? '#000' : '#fff');
216+
});
229217

230218
$('button.color-template').click(function() {
231219
change_color($(this).css('background-color'), $(this).css('color'));
232-
});
233-
234-
});
235-
236-
// Setup the canvas color picker
237-
jQuery(window).on('load',function() {
238-
var $ = jQuery;
239-
var logo = $("#logo-theme-editor img");
240-
var canvas = $("#logo-color-picker");
241-
var el_canvas = canvas.get(0);
220+
});
242221

243-
if (!el_canvas.getContext) return;
244222

245-
setLogoSize();
223+
document.getElementById('section').addEventListener('change', event => {
224+
const section = event.target.value;
225+
const selector = section_css_mapping.filter( a => { return a[0] == section })[0][1][0];
226+
const specials = new RegExp("([.*+?|()\\[\\]{}\\\\])", "g");
227+
const escaped_selector = selector.replace(specials, "\\$1");
228+
const rule = new RegExp('^'+escaped_selector+'\\s*\{\\s*background:\\s*(#\\w+|rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\))', "m");
229+
const css = document.getElementById('user_css').value;
230+
const result = css.match(rule);
231+
if ( result ) {
232+
if ( result[2] ) {
233+
document.getElementById('color-picker').value =
234+
'#' + result.slice(2, 5).map(num => parseInt(num).toString(16).padStart(2, '0')).join('');
235+
}
236+
else {
237+
document.getElementById('color-picker').value = result[1];
238+
}
239+
}
240+
else {
241+
document.getElementById('color-picker').value = '#ffffff';
242+
}
243+
});
244+
document.getElementById('section').dispatchEvent(new Event('change'));
246245
});
247246

248-
function setLogoSize() {
249-
var logo = jQuery("#logo-theme-editor img");
250-
var canvas = jQuery("#logo-color-picker");
251-
var el_canvas = canvas.get(0);
252-
253-
if (!el_canvas.getContext) return;
254-
// Set logo to same size as CSS input
255-
var container = jQuery('#user_css');
256-
257-
var context = el_canvas.getContext("2d");
258-
var button = jQuery('#logo-size');
259-
260-
if ( button.text() == '<&|/l&>Preview</&>' ) {
261-
var ratio = Math.min(container.width() / logo.width(), container.height() / logo.height());
262-
263-
el_canvas.width = logo.width()*ratio;
264-
el_canvas.height = logo.height()*ratio;
265-
266-
// Re-draw our scaled down image
267-
context.drawImage(logo.get(0), 0, 0, logo.width()*ratio, logo.height()*ratio );
268-
269-
button.text( "<&|/l&>Full Size</&>" );
270-
}
271-
else {
272-
el_canvas.width = logo.width();
273-
el_canvas.height = logo.height();
274-
275-
context.drawImage(logo.get(0), 0, 0);
276-
button.text( "<&|/l&>Preview</&>" );
277-
}
278-
279-
logo.hide().after(canvas);
280-
canvas.show().click(function(ev) {
281-
ev.preventDefault();
282-
var R = 0,
283-
G = 1,
284-
B = 2,
285-
A = 3;
286-
var pixel = this.getContext("2d").getImageData(ev.offsetX, ev.offsetY, 1, 1).data;
287-
// Farbtastic expects values in the range of 0..1
288-
var rgba = jQuery.makeArray(pixel).map(function(v,i) { return v / 255 });
289-
var wheel = jQuery.farbtastic("#color-picker");
290-
wheel.setHSL( wheel.RGBToHSL( rgba.slice(R,A) ) );
291-
// XXX TODO factor in the alpha channel too
292-
});
293-
jQuery('#logo-picker-hint').show();
294-
}
295247
</script>
296248
<%INIT>
297249
unless ($session{'CurrentUser'}->HasRight( Object=> RT->System, Right => 'SuperUser')) {

share/static/css/elevator/farbtastic.css

Lines changed: 0 additions & 51 deletions
This file was deleted.

share/static/css/elevator/main.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@import "jquery-ui.css";
22
@import "jquery-ui-timepicker-addon.css";
33
@import "tablesorter.css";
4-
@import "farbtastic.css";
54
@import "bootstrap.css";
65
@import "bootstrap-combobox.css";
76
@import "dropzone.css";

share/static/css/elevator/theme-editor.css

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,13 @@
55

66
#logo-theme-editor a img {
77
border: none;
8+
max-width: 100%;
89
}
910

1011
#custom-css textarea {
1112
width: 100%;
1213
}
1314

14-
#customize-theme ol li {
15-
font-size: 1.2em;
16-
font-weight: bold;
17-
}
18-
19-
#customize-theme ol li .description,
20-
#customize-theme ol li label
21-
{
22-
font-weight: normal;
23-
}
24-
25-
#customize-theme #section {
26-
font-size: 1em;
27-
}
28-
29-
.color-template {
30-
height: 2em;
31-
margin: 0 0 0.5em 0;
32-
}
33-
34-
.primary-colors, #color-picker {
35-
margin-top: 0.5em;
36-
}
37-
38-
.primary-colors {
39-
float: left;
40-
width: 20%;
41-
}
42-
43-
#color-picker {
44-
float: right;
45-
width: 78%;
46-
}
47-
48-
#logo-color-picker {
49-
display: none; /* unhidden by javascript */
50-
cursor: url(../../../static/images/eyedropper.png), crosshair;
51-
z-index: 1;
52-
position: relative;
53-
}
54-
5515
#custom-css-form {
5616
z-index: 2;
5717
position: relative;
-652 Bytes
Binary file not shown.
-1.97 KB
Binary file not shown.
-11.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)