Skip to content

Commit 71d356f

Browse files
author
Benedikt Artelt
committed
Implementation of a modular layout for the editor/implement page
1 parent 2191b59 commit 71d356f

File tree

10 files changed

+232
-77
lines changed

10 files changed

+232
-77
lines changed

app/assets/javascripts/editor.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,59 @@ $(document).on('turbolinks:load', function(event) {
2020
CodeOceanEditor.initializeEverything();
2121
}
2222

23+
let isMouseDownHorizontal = 0
24+
$('#resizerHorizontal').on('mousedown', mouseDownHorizontal)
25+
26+
function mouseDownHorizontal(event) {
27+
isMouseDownHorizontal = 1
28+
document.body.addEventListener('mousemove', mouseMoveHorizontal)
29+
document.body.addEventListener('mouseup', mouseUpHorizontal)
30+
}
31+
32+
function mouseMoveHorizontal(event) {
33+
if (isMouseDownHorizontal === 1 && event.clientX <= 0.7 * window.innerWidth && event.clientX >= 0.2 * window.innerWidth) {
34+
event.preventDefault();
35+
$('#panel-left').css('width', (event.clientX - $('#panel-left').offset().left) + "px")
36+
CodeOceanEditor.resizeSidebars()
37+
CodeOceanEditor.resizeHorizontalResizer()
38+
} else {
39+
mouseUpHorizontal()
40+
}
41+
}
42+
43+
function mouseUpHorizontal(event) {
44+
isMouseDownHorizontal = 0
45+
document.body.removeEventListener('mouseup', mouseUpHorizontal)
46+
resizerHorizontal.removeEventListener('mousemove', mouseMoveHorizontal)
47+
}
48+
49+
let isMouseDownVertical = 0
50+
$('#resizerVertical').on('mousedown', mouseDownVertical)
51+
52+
function mouseDownVertical(event) {
53+
isMouseDownVertical = 1
54+
document.body.addEventListener('mousemove', mouseMoveVertical)
55+
document.body.addEventListener('mouseup', mouseUpVertical)
56+
}
57+
58+
function mouseMoveVertical(event) {
59+
if (isMouseDownVertical === 1) {
60+
event.preventDefault();
61+
$('.panel-top').css('height', (event.clientY - $('.panel-top').offset().top - $('#statusbar').height()) + "px")
62+
$('.panel-bottom').height(CodeOceanEditor.calculateEditorHeight('.panel-bottom', false));
63+
CodeOceanEditor.resizeSidebars()
64+
CodeOceanEditor.resizeHorizontalResizer()
65+
} else {
66+
mouseUpVertical()
67+
}
68+
}
69+
70+
function mouseUpVertical(event) {
71+
isMouseDownVertical = 0
72+
document.body.removeEventListener('mouseup', mouseUpVertical)
73+
resizerVertical.removeEventListener('mousemove', mouseMoveVertical)
74+
}
75+
2376
function handleThemeChangeEvent(event) {
2477
if (CodeOceanEditor) {
2578
CodeOceanEditor.THEME = event.detail.currentTheme === 'dark' ? 'ace/theme/tomorrow_night' : 'ace/theme/tomorrow';

app/assets/javascripts/editor/editor.js.erb

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,10 @@ var CodeOceanEditor = {
241241
$('#content-right-sidebar').height(this.calculateEditorHeight('#content-right-sidebar', false));
242242
},
243243

244+
resizeHorizontalResizer: function () {
245+
$('#resizerHorizontal').height(this.calculateEditorHeight('#resizerHorizontal', false));
246+
},
247+
244248
calculateEditorHeight: function (element, considerStatusbar) {
245249
const jqueryElement = $(element);
246250
if (jqueryElement.length === 0) {
@@ -457,11 +461,18 @@ var CodeOceanEditor = {
457461
handleSideBarToggle: function () {
458462
const sidebar = $('#sidebar');
459463
sidebar.toggleClass('sidebar-col').toggleClass('sidebar-col-collapsed');
460-
if (sidebar.hasClass('w-25') || sidebar.hasClass('restore-to-w-25')) {
461-
sidebar.toggleClass('w-25').toggleClass('restore-to-w-25');
462-
}
463464
$('#sidebar-collapsed').toggleClass('d-none');
464465
$('#sidebar-uncollapsed').toggleClass('d-none');
466+
if ($('#sidebar-uncollapsed').hasClass('d-none')) {
467+
if ($(window).width() >= 992) {
468+
$('#panel-left').css('width', $('#sidebar-collapsed').outerWidth(true));
469+
}
470+
$('#panel-left').css('padding-bottom', '4px');
471+
} else {
472+
$('#panel-left').css('width', '');
473+
$('#panel-left').css('padding-bottom', '');
474+
}
475+
$('#resizerHorizontal').toggleClass('d-lg-block');
465476
},
466477

467478
initializeRegexes: function () {
@@ -541,6 +552,15 @@ var CodeOceanEditor = {
541552
},
542553

543554
populateCard: function (card, result, index) {
555+
if (result.stderr && !result.score || result.score < 1) {
556+
card.find('.card-body').toggleClass('d-none');
557+
card.find('i').toggleClass('fa-chevron-down fa-chevron-right')
558+
}
559+
card.find('.card-header').on('click', ()=>{
560+
card.find('.card-body').toggleClass('d-none');
561+
card.find('i').toggleClass('fa-chevron-down fa-chevron-right')
562+
});
563+
card.addClass(this.getCardClass(result));
544564
card.addClass(`card border-${this.getCardClass(result)}`);
545565
card.find('.card-header').addClass(`bg-${this.getCardClass(result)} text-white`);
546566
card.find('.card-title .filename').text(result.filename);
@@ -927,12 +947,30 @@ var CodeOceanEditor = {
927947
$('#output_sidebar_collapsed').addClass('d-none');
928948
$('#output_sidebar_uncollapsed').removeClass('d-none');
929949
$('#output_sidebar').removeClass('output-col-collapsed').addClass('output-col');
950+
if (window.matchMedia('(min-width: 992px)').matches) {
951+
$('.panel-top').css('height', '50vh');
952+
$('.panel-bottom').css('height', this.calculateEditorHeight('.panel-bottom', false));
953+
this.resizeSidebars();
954+
$('#resizerVertical').removeClass('d-none');
955+
} else {
956+
$('.panel-bottom').css('height', '50vh');
957+
$('html, body').animate({scrollTop: $(document).height() - $(window).height()}, 250);
958+
}
959+
this.resizeAceEditors();
930960
},
931961

932962
hideOutputBar: function () {
933963
$('#output_sidebar_collapsed').removeClass('d-none');
934964
$('#output_sidebar_uncollapsed').addClass('d-none');
935965
$('#output_sidebar').removeClass('output-col').addClass('output-col-collapsed');
966+
$('#resizerVertical').addClass('d-none');
967+
$('.panel-bottom').css('height', $('#output_sidebar_collapsed').height() + 'px');
968+
if (window.matchMedia('(min-width: 992px)').matches) {
969+
$('.panel-top').css('height', ($('#editor-column').height() - $('.panel-bottom').height()) + "px");
970+
} else {
971+
$('html, body').animate({scrollTop: $(document).height() - $(window).height()}, 1);
972+
}
973+
this.resizeAceEditors();
936974
},
937975

938976
initializeSideBarTooltips: function () {

app/assets/javascripts/editor/evaluation.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ CodeOceanEditorEvaluation = {
2424
$('#score_div').removeClass('d-none');
2525
await this.socketScoreCode(submission.id);
2626
});
27+
this.showOutputBar();
28+
$('html, body').animate({scrollTop: $(document).height() - $(window).height()}, 500);
2729
},
2830

2931
handleScoringResponse: function (results) {

app/assets/javascripts/editor/submissions.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ CodeOceanEditorSubmissions = {
189189

190190
await this.runSubmission(submission);
191191
});
192+
this.showOutputBar();
193+
$('html, body').animate({scrollTop: $(document).height() - $(window).height()}, 500);
192194
},
193195

194196
runSubmission: async function (submission) {

app/assets/stylesheets/editor.css.scss

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,65 @@
11
.editor {
22
height: 100%;
3+
max-height: 100%;
34
width: 100%;
5+
display: flex;
6+
}
7+
8+
9+
10+
.btn, #description, #content-left-sidebar, #content-right-sidebar {
11+
border-radius: 5px !important;
12+
}
13+
14+
#editor-buttons .btn {
15+
margin-left: 2px;
16+
}
17+
18+
#editor-buttons span {
19+
border-radius: 5px !important;
20+
}
21+
22+
#editor-column {
23+
max-height: 85vh;
24+
}
25+
26+
#panel-left {
27+
position: relative;
28+
max-height: 85vh;
29+
overflow-y: scroll;
30+
}
31+
32+
#panel-right {
33+
position: relative;
34+
}
35+
36+
#resizerHorizontal {
37+
position: relative;
38+
background: rgb(215, 215, 215);
39+
width: 2px;
40+
display: inline-block;
41+
cursor: col-resize;
42+
padding: 0;
43+
}
44+
45+
#resizerVertical {
46+
position: relative;
47+
background: rgb(215, 215, 215);
48+
height: 2px;
49+
cursor: row-resize;
50+
margin-bottom: 4px;
51+
}
52+
53+
.panel-bottom {
54+
overflow: auto;
55+
position: relative;
56+
}
57+
58+
.panel-top {
59+
overflow: auto;
60+
position: relative;
61+
min-height: 30vh;
62+
max-height: 60vh;
463
}
564

665
.own-editor {
@@ -116,20 +175,17 @@ html[data-bs-theme="light"] {
116175
-webkit-transition: width .2s;
117176
transition: width .2s;
118177
width:67px;
119-
float:left;
120178
min-height: 1px;
121-
padding-left: 15px;
122-
padding-right: 15px;
179+
padding-left: 3px;
180+
padding-right: 3px;
123181
}
124182

125183
.sidebar-col {
126184
-webkit-transition: width .2s;
127185
transition: width .2s;
128-
width:20%;
129-
float:left;
130186
min-height: 1px;
131-
padding-left: 15px;
132-
padding-right: 15px;
187+
padding-left: 3px;
188+
padding-right: 3px;
133189
}
134190

135191
.editor-col {
@@ -142,13 +198,14 @@ html[data-bs-theme="light"] {
142198
.output-col {
143199
-webkit-transition: width .2s;
144200
transition: width .2s;
145-
width:40%;
201+
width:100%;
146202
float:right;
147203
min-height: 1px;
148-
padding-left: 15px;
149-
padding-right: 15px;
204+
padding-left: 3px;
205+
padding-right: 3px;
150206
box-sizing: border-box;
151207
margin-left: auto;
208+
min-height: 40vh;
152209
}
153210

154211
.output-col-collapsed {
@@ -157,8 +214,8 @@ html[data-bs-theme="light"] {
157214
width:67px;
158215
float:right;
159216
min-height: 1px;
160-
padding-left: 15px;
161-
padding-right: 15px;
217+
padding-left: 3px;
218+
padding-right: 3px;
162219
box-sizing: border-box;
163220
margin-left: auto;
164221
}

app/assets/stylesheets/exercises.css.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ input[type='file'] {
1616
border-radius: 3px;
1717
box-shadow: 0 2px 4px 0 rgba(var(--bs-black-rgb), 0.2);
1818
padding: 1px 10px 1px 10px;
19-
margin-bottom: 10px;
19+
margin: 3px 3px 10px 3px;
2020

2121
a#toggle {
2222
margin-bottom: 5px;

app/views/exercises/_editor.html.slim

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,41 @@
55
- hide_rfc_button = @hide_rfc_button || false
66

77
#editor.row data-exercise-id[email protected] data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-message-out-of-memory=t('exercises.editor.out_of_memory', memory_limit: @exercise.execution_environment.memory_limit) data-submissions-url=submissions_path data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-tips-interventions=show_tips_interventions
8-
- unless @embed_options[:hide_sidebar]
9-
- additional_classes = 'sidebar-col'
10-
- if @tips.blank?
11-
- if @exercise.hide_file_tree
12-
- additional_classes = 'sidebar-col-collapsed'
13-
- else
14-
- additional_classes = 'sidebar-col w-25'
15-
#sidebar class=additional_classes = render('editor_file_tree', exercise: @exercise, files: @files)
8+
div id="panel-left" class="col-12 col-lg-3"
9+
- unless @embed_options[:hide_sidebar]
10+
- additional_classes = 'sidebar-col row'
11+
- if @tips.blank?
12+
- if @exercise.hide_file_tree
13+
- additional_classes = 'sidebar-col-collapsed row'
14+
- else
15+
- additional_classes = 'sidebar-col row'
16+
#sidebar class=additional_classes = render('editor_file_tree', exercise: @exercise, files: @files)
1617

17-
.editor-col.col.p-0#frames
18-
#editor-buttons.btn-group.enforce-bottom-margin
19-
= render('editor_button', disabled: true, icon: 'fa-solid fa-ban', id: 'dummy', label: t('exercises.editor.dummy'))
20-
= render('editor_button', icon: 'fa-solid fa-desktop', id: 'render', label: t('exercises.editor.render')) unless @embed_options[:hide_run_button]
21-
span.flex-grow-1.d-inline-flex#run-stop-button-group data-bs-placement='top' data-bs-toggle='tooltip' data-bs-container='body' title=t('shared.tooltips.shortcut', shortcut: 'ALT + r')
22-
= render('editor_button', data: {'data-message-failure': t('exercises.editor.run_failure'), 'data-message-network': t('exercises.editor.network'), 'data-message-success': t('exercises.editor.run_success')}, icon: 'fa-solid fa-play', id: 'run', label: t('exercises.editor.run'), classes: 'w-100 h-100 btn-primary') unless @embed_options[:disable_run]
23-
= render('editor_button', icon: 'fa-solid fa-stop', id: 'stop', label: t('exercises.editor.stop'), classes: 'w-100 h-100 btn-primary') unless @embed_options[:disable_run]
24-
= render('editor_button', data: {'data-bs-placement': 'top', 'data-bs-toggle': 'tooltip', 'data-bs-container': 'body'}, icon: 'fa-solid fa-rocket', id: 'test', label: t('exercises.editor.test'), title: t('shared.tooltips.shortcut', shortcut: 'ALT + t')) unless @embed_options[:disable_run]
25-
= render('editor_button', data: {'data-bs-placement': 'top', 'data-bs-toggle': 'tooltip', 'data-bs-container': 'body'}, icon: 'fa-solid fa-trophy', id: 'assess', label: t('exercises.editor.score'), title: t('shared.tooltips.shortcut', shortcut: 'ALT + s')) unless @embed_options[:disable_score]
26-
- unless hide_rfc_button
27-
= render('editor_button', data: {'data-bs-placement': 'top', 'data-bs-toggle': 'tooltip', 'data-bs-container': 'body'}, icon: 'fa-solid fa-comment', id: 'requestComments', label: t('exercises.editor.requestComments'), title: t('exercises.editor.requestCommentsTooltip'))
18+
#resizerHorizontal.d-none.d-lg-block
19+
div.col.p-0#frames
20+
#panel-right.col
21+
#editor-buttons.btn-group.enforce-bottom-margin
22+
= render('editor_button', disabled: true, icon: 'fa-solid fa-ban', id: 'dummy', label: t('exercises.editor.dummy'))
23+
= render('editor_button', icon: 'fa-solid fa-desktop', id: 'render', label: t('exercises.editor.render')) unless @embed_options[:hide_run_button]
24+
span.flex-grow-1.overflow-hidden.d-inline-flex#run-stop-button-group data-bs-placement='top' data-bs-toggle='tooltip' data-bs-container='body' title=t('shared.tooltips.shortcut', shortcut: 'ALT + r')
25+
= render('editor_button', data: {'data-message-failure': t('exercises.editor.run_failure'), 'data-message-network': t('exercises.editor.network'), 'data-message-success': t('exercises.editor.run_success')}, icon: 'fa-solid fa-play', id: 'run', label: t('exercises.editor.run'), classes: 'w-100 h-100 btn-primary') unless @embed_options[:disable_run]
26+
= render('editor_button', icon: 'fa-solid fa-stop', id: 'stop', label: t('exercises.editor.stop'), classes: 'w-100 h-100 btn-primary') unless @embed_options[:disable_run]
27+
= render('editor_button', data: {'data-bs-placement': 'top', 'data-bs-toggle': 'tooltip', 'data-bs-container': 'body'}, icon: 'fa-solid fa-rocket', id: 'test', label: t('exercises.editor.test'), title: t('shared.tooltips.shortcut', shortcut: 'ALT + t')) unless @embed_options[:disable_run]
28+
= render('editor_button', data: {'data-bs-placement': 'top', 'data-bs-toggle': 'tooltip', 'data-bs-container': 'body'}, icon: 'fa-solid fa-trophy', id: 'assess', label: t('exercises.editor.score'), title: t('shared.tooltips.shortcut', shortcut: 'ALT + s')) unless @embed_options[:disable_score]
29+
- unless hide_rfc_button
30+
= render('editor_button', data: {'data-bs-placement': 'top', 'data-bs-toggle': 'tooltip', 'data-bs-container': 'body'}, icon: 'fa-solid fa-comment', id: 'requestComments', label: t('exercises.editor.requestComments'), title: t('exercises.editor.requestCommentsTooltip'))
31+
.panel-top
32+
- @files.each do |file|
33+
- file.read_only = true if @embed_options[:read_only]
34+
= render('editor_frame', exercise:, file:)
2835

29-
- @files.each do |file|
30-
- file.read_only = true if @embed_options[:read_only]
31-
= render('editor_frame', exercise:, file:)
32-
33-
#statusbar.d-flex.justify-content-between
34-
div
35-
- if !@embed_options[:disable_download] && @exercise.hide_file_tree?
36-
button#download.p-0.border-0.btn-link.visible.bg-body.text-primary
37-
i.fa-solid.fa-arrow-down
38-
= t('exercises.editor.download')
39-
40-
div
36+
#statusbar.d-flex.justify-content-between
37+
div
38+
- if !@embed_options[:disable_download] && @exercise.hide_file_tree?
39+
button#download.p-0.border-0.btn-link.visible.bg-body.text-primary
40+
i.fa-solid.fa-arrow-down
41+
= t('exercises.editor.download')
42+
div
4143
ruby:
4244
if current_contributor.programming_group?
4345
current_contributor.users.each do |user|
@@ -53,15 +55,16 @@
5355
= t('exercises.editor.lastsaved')
5456
span#autosave
5557

56-
= ' | '
57-
58-
button#start-over-active-file.p-0.border-0.btn-link.bg-body.text-primary data-message-confirm=t('exercises.editor.confirm_start_over_active_file') data-url=reload_exercise_path(@exercise)
59-
i.fa-solid.fa-circle-notch.fa-spin.d-none
60-
i.fa-solid.fa-clock-rotate-left
61-
= t('exercises.editor.start_over_active_file')
58+
= ' | '
6259

63-
- unless @embed_options[:disable_run] && @embed_options[:disable_score]
64-
.output-col-collapsed#output_sidebar = render('exercises/editor_output')
60+
button#start-over-active-file.p-0.border-0.btn-link.bg-body.text-primary data-message-confirm=t('exercises.editor.confirm_start_over_active_file') data-url=reload_exercise_path(@exercise)
61+
i.fa-solid.fa-circle-notch.fa-spin.d-none
62+
i.fa-solid.fa-clock-rotate-left
63+
= t('exercises.editor.start_over_active_file')
64+
#resizerVertical.d-none
65+
.panel-bottom
66+
- unless @embed_options[:disable_run] && @embed_options[:disable_score]
67+
.output-col-collapsed#output_sidebar = render('exercises/editor_output')
6568

6669
- unless @embed_options[:disable_rfc]
6770
= render 'shared/modal',

0 commit comments

Comments
 (0)