Skip to content

Commit

Permalink
Tab UI improvement (openemr#4626)
Browse files Browse the repository at this point in the history
* Reduced padding on the main menu
* Smaller font sizes on main menu and tabs
* Better spacing of various tab items
  • Loading branch information
robertdown committed Sep 15, 2021
1 parent 8d0be58 commit da49144
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 80 deletions.
124 changes: 67 additions & 57 deletions interface/main/tabs/main.php
Expand Up @@ -45,18 +45,19 @@
?>
<!DOCTYPE html>
<html>

<head>
<title><?php echo text($openemr_name); ?></title>

<script>
// This is to prevent users from losing data by refreshing or backing out of OpenEMR.
// (default behavior, however, this behavior can be turned off in the prevent_browser_refresh global)
<?php if ($GLOBALS['prevent_browser_refresh'] > 0) { ?>
window.addEventListener('beforeunload', (event) => {
if (!timed_out) {
event.returnValue = <?php echo xlj('Recommend not leaving or refreshing or you may lose data.'); ?>;
}
});
window.addEventListener('beforeunload', (event) => {
if (!timed_out) {
event.returnValue = <?php echo xlj('Recommend not leaving or refreshing or you may lose data.'); ?>;
}
});
<?php } ?>

<?php require($GLOBALS['srcdir'] . "/restoreSession.php"); ?>
Expand Down Expand Up @@ -132,14 +133,14 @@ function goRepeaterServices() {
}
// Always send reminder count text to model
app_view_model.application_data.user().messages(data.reminderText);
}).catch(function (error) {
}).catch(function(error) {
console.log('Request failed', error);
});

// run background-services
// delay 10 seconds to prevent both utility trigger at close to same time.
// Both call globals so that is my concern.
setTimeout(function () {
setTimeout(function() {
restoreSession();
request = new FormData;
request.append("skip_timeout_reset", "1");
Expand All @@ -153,7 +154,7 @@ function goRepeaterServices() {
if (response.status !== 200) {
console.log('Background Service start failed. Status Code: ' + response.status);
}
}).catch(function (error) {
}).catch(function(error) {
console.log('HTML Background Service start Request failed: ', error);
});
}, 10000);
Expand All @@ -164,26 +165,28 @@ function goRepeaterServices() {

function isEncounterLocked(encounterId) {
<?php if ($esignApi->lockEncounters()) { ?>
// If encounter locking is enabled, make a syncronous call (async=false) to check the
// DB to see if the encounter is locked.
// Call restore session, just in case
// @TODO next clean up pass, turn into await promise then modify tabs_view_model.js L-309
restoreSession();
let url = webroot_url + "/interface/esign/index.php?module=encounter&method=esign_is_encounter_locked";
$.ajax({
type: 'POST',
url: url,
data: {encounterId: encounterId},
success: function (data) {
encounter_locked = data;
},
dataType: 'json',
async: false
});
return encounter_locked;
// If encounter locking is enabled, make a syncronous call (async=false) to check the
// DB to see if the encounter is locked.
// Call restore session, just in case
// @TODO next clean up pass, turn into await promise then modify tabs_view_model.js L-309
restoreSession();
let url = webroot_url + "/interface/esign/index.php?module=encounter&method=esign_is_encounter_locked";
$.ajax({
type: 'POST',
url: url,
data: {
encounterId: encounterId
},
success: function(data) {
encounter_locked = data;
},
dataType: 'json',
async: false
});
return encounter_locked;
<?php } else { ?>
// If encounter locking isn't enabled then always return false
return false;
// If encounter locking isn't enabled then always return false
return false;
<?php } ?>
}
</script>
Expand All @@ -193,7 +196,7 @@ function isEncounterLocked(encounterId) {
// set up global translations for js
function setupI18n(lang_id) {
restoreSession();
return fetch(<?php echo js_escape($GLOBALS['webroot'])?> +"/library/ajax/i18n_generator.php?lang_id=" + encodeURIComponent(lang_id) + "&csrf_token_form=" + encodeURIComponent(csrf_token_js), {
return fetch(<?php echo js_escape($GLOBALS['webroot']) ?> + "/library/ajax/i18n_generator.php?lang_id=" + encodeURIComponent(lang_id) + "&csrf_token_form=" + encodeURIComponent(csrf_token_js), {
credentials: 'same-origin',
method: 'GET'
}).then(response => response.json())
Expand Down Expand Up @@ -254,29 +257,30 @@ function setupI18n(lang_id) {

<script>
<?php if (!empty($_SESSION['frame1url']) && !empty($_SESSION['frame1target'])) { ?>
// Use session variables and tabStatus object to set up initial/default first tab
app_view_model.application_data.tabs.tabsList.push(new tabStatus(<?php echo xlj("Loading"); ?> +"...",<?php echo json_encode("../" . $_SESSION['frame1url']); ?>,<?php echo json_encode($_SESSION['frame1target']); ?>,<?php echo xlj("Loading"); ?> +" " + <?php echo json_encode($_SESSION['frame1label']); ?>, true, true, false));
// Use session variables and tabStatus object to set up initial/default first tab
app_view_model.application_data.tabs.tabsList.push(new tabStatus(<?php echo xlj("Loading"); ?> + "...", <?php echo json_encode("../" . $_SESSION['frame1url']); ?>, <?php echo json_encode($_SESSION['frame1target']); ?>, <?php echo xlj("Loading"); ?> + " " + <?php echo json_encode($_SESSION['frame1label']); ?>, true, true, false));
<?php } ?>

<?php if (!empty($_SESSION['frame2url']) && !empty($_SESSION['frame2target'])) { ?>
// Use session variables and tabStatus object to set up initial/default second tab, if none is set in globals, this tab will not be displayed initially
app_view_model.application_data.tabs.tabsList.push(new tabStatus(<?php echo xlj("Loading"); ?> +"...",<?php echo json_encode("../" . $_SESSION['frame2url']); ?>,<?php echo json_encode($_SESSION['frame2target']); ?>,<?php echo xlj("Loading"); ?> +" " + <?php echo json_encode($_SESSION['frame2label']); ?>, true, false, false));
// Use session variables and tabStatus object to set up initial/default second tab, if none is set in globals, this tab will not be displayed initially
app_view_model.application_data.tabs.tabsList.push(new tabStatus(<?php echo xlj("Loading"); ?> + "...", <?php echo json_encode("../" . $_SESSION['frame2url']); ?>, <?php echo json_encode($_SESSION['frame2target']); ?>, <?php echo xlj("Loading"); ?> + " " + <?php echo json_encode($_SESSION['frame2label']); ?>, true, false, false));
<?php } ?>

app_view_model.application_data.user(new user_data_view_model(<?php echo json_encode($_SESSION["authUser"])
. ',' . json_encode($userQuery['fname'])
. ',' . json_encode($userQuery['lname'])
. ',' . json_encode($_SESSION['authProvider']); ?>));

. ',' . json_encode($userQuery['fname'])
. ',' . json_encode($userQuery['lname'])
. ',' . json_encode($_SESSION['authProvider']); ?>));
</script>
<style>
html, body {
width: max-content;
min-height: 100% !important;
height: 100% !important;
}
</style>
<style>
html,
body {
width: max-content;
min-height: 100% !important;
height: 100% !important;
}
</style>
</head>

<body class="min-vw-100">
<!-- Below iframe is to support logout, which needs to be run in an inner iframe to work as intended -->
<iframe name="logoutinnerframe" id="logoutinnerframe" style="visibility:hidden; position:absolute; left:0; top:0; height:0; width:0; border:none;" src="about:blank"></iframe>
Expand All @@ -294,7 +298,7 @@ function setupI18n(lang_id) {
}
}
?>
<div id="mainBox" <?php echo $disp_mainBox ?> >
<div id="mainBox" <?php echo $disp_mainBox ?>>
<nav class="navbar navbar-expand-xl navbar-light bg-light py-0">
<a class="navbar-brand mt-2 mt-xl-0 mr-3 mr-xl-2" href="https://www.open-emr.org" title="OpenEMR <?php echo xla("Website"); ?>" rel="noopener" target="_blank">
<?php echo file_get_contents($GLOBALS['images_static_absolute'] . "/menu-logo.svg"); ?>
Expand All @@ -303,45 +307,51 @@ function setupI18n(lang_id) {
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainMenu" data-bind="template: {name: 'menu-template', data: application_data}"></div>
<form name="frm_search_globals" class="form-inline">
<div class="input-group">
<input type="text" id="anySearchBox" class="form-control-sm <?php echo $any_search_class ?> form-control" name="anySearchBox" placeholder="<?php echo xla("Search by any demographics") ?>" autocomplete="off">
<div class="input-group-append">
<button type="button" id="search_globals" class="btn btn-sm btn-secondary <?php echo $search_globals_class ?>" title='<?php echo xla("Search for patient by entering whole or part of any demographics field information"); ?>' data-bind="event: {mousedown: viewPtFinder.bind( $data, '<?php echo xla("The search field cannot be empty. Please enter a search term") ?>', '<?php echo attr($search_any_type); ?>')}"><i class="fa fa-search">&nbsp;</i></button>
</div>
</div>
</form>
<span id="userData" data-bind="template: {name: 'user-data-template', data: application_data}"></span>
</nav>
<div id="attendantData" class="body_title acck" data-bind="template: {name: app_view_model.attendant_template_type, data: application_data}">
</div>

<div id="attendantData" class="body_title acck" data-bind="template: {name: app_view_model.attendant_template_type, data: application_data}"></div>
<div class="body_title" id="tabs_div" data-bind="template: {name: 'tabs-controls', data: application_data}"></div>

<div class="mainFrames d-flex flex-row" id="mainFrames_div">
<div id="framesDisplay" data-bind="template: {name: 'tabs-frames', data: application_data}"></div>
</div>
</div>
<script>
ko.applyBindings(app_view_model);

$(function () {
$(function() {
$('.dropdown-toggle').dropdown();
$('#patient_caret').click(function () {
$('#patient_caret').click(function() {
$('#attendantData').slideToggle();
$('#patient_caret').toggleClass('fa-caret-down').toggleClass('fa-caret-up');
});
if($('body').css('direction') == "rtl") {
$('.dropdown-menu-right').each(function() {
$(this).removeClass('dropdown-menu-right');
});
if ($('body').css('direction') == "rtl") {
$('.dropdown-menu-right').each(function() {
$(this).removeClass('dropdown-menu-right');
});
}
});
$(function () {
$(function() {
$('#logo_menu').focus();
});
$('#anySearchBox').keypress(function (event) {
$('#anySearchBox').keypress(function(event) {
if (event.which === 13 || event.keyCode === 13) {
event.preventDefault();
$('#search_globals').mousedown();
}
});
document.addEventListener('touchstart', {}); //specifically added for iOS devices, especially in iframes
$(function () {
$(function() {
goRepeaterServices();
});
</script>
</body>

</html>
12 changes: 0 additions & 12 deletions interface/main/tabs/templates/patient_data_template.php
Expand Up @@ -58,12 +58,7 @@ class="img-thumbnail"
<!-- /ko -->
</div>
<div class="form-group">
<!-- ko ifnot: patient -->
<span class="mx-2 font-weight-bold"><?php echo xlt("Patient"); ?>: <?php echo xlt("None{{Patient}}"); ?>
</span>
<!-- /ko -->
<!-- ko if: patient -->
<label><?php echo xlt("Patient"); ?>: </label>
<a class="ptName" data-bind="click:refreshPatient,with: patient" href="#">
<span data-bind="text: pname()"></span>
(<span data-bind="text: pubpid"></span>)
Expand Down Expand Up @@ -129,13 +124,6 @@ class="img-thumbnail"
<!-- /ko --><!-- patient -->
</div>
<div class="flex-column mx-2">
<div class="oe-expandable-search mr-auto" id="div-search-globals">
<?php //adapted from https://codepen.io/brandonkennedy/pen/yGjsi ?>
<form name="frm_search_globals">
<input type="text" id="anySearchBox" class="<?php echo $any_search_class ?> mr-1 mt-1" name="anySearchBox" placeholder="<?php echo xla("Search by any demographics") ?>" autocomplete="off">
<button type="button" id="search_globals" class="btn btn-secondary text-body btn-search btn-search1 mr-1 <?php echo $search_globals_class ?>" title='<?php echo xla("Search for patient by entering whole or part of any demographics field information"); ?>' data-bind="event: {mousedown: viewPtFinder.bind( $data, '<?php echo xla("The search field cannot be empty. Please enter a search term") ?>', '<?php echo attr($search_any_type); ?>')}"></button>
</form>
</div>
<!-- ko if: user -->
<!-- ko with: user -->
<!-- ko if:messages() -->
Expand Down
11 changes: 5 additions & 6 deletions interface/main/tabs/templates/tabs_template.php
Expand Up @@ -19,17 +19,16 @@
</div>
<!-- ko foreach: tabsList -->
<div class="tabSpan bgcolor2" data-bind="click: tabClicked, css: {tabsNoHover: !visible()}">
<span class="tabTitle" data-bind="text: title, click: tabClicked, css: {tabHidden: !visible()}"></span>
<span class="fa fa-fw fa-sync" data-bind="click: tabRefresh, class: spinner"></span>
<span class="tabTitle pr-2" data-bind="text: title, click: tabClicked, css: {tabHidden: !visible()}"></span>
<span class="fa fa-fw fa-xs mr-1 fa-sync" data-bind="click: tabRefresh, class: spinner"></span>
<!--ko if:!locked() -->
<span class="fa fa-fw fa-unlock" data-bind="click: tabLockToggle"></span>
<span class="fa fa-fw fa-xs mr-1 fa-unlock" data-bind="click: tabLockToggle"></span>
<!-- /ko -->
<!--ko if:locked() -->
<span class="fa fa-fw fa-lock" data-bind="click: tabLockToggle"></span>
<span class="fa fa-fw fa-xs mr-1 fa-lock" data-bind="click: tabLockToggle"></span>
<!-- /ko -->

<!-- ko if:closable-->
<span class="fa fa-fw fa-times" data-bind="click: tabClose"></span>
<span class="fa fa-fw fa-xs fa-times" data-bind="click: tabClose"></span>
<!-- /ko -->
</div>
<!-- /ko -->
Expand Down
4 changes: 2 additions & 2 deletions interface/main/tabs/templates/user_data_template.php
Expand Up @@ -18,9 +18,9 @@

<script type="text/html" id="user-data-template">
<!-- ko with: user -->
<div id="username" class="appMenu">
<div id="username" class="appMenu ml-5">
<div class='menuLabel dropdown' id="username" title="<?php echo xla('Current user') ?>" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<div><i class="fa fa-2x fa-user oe-show" aria-hidden="true" id="user_icon"></i></div>
<i class="fa fa-user mr-1" aria-hidden="true" id="user_icon"></i>
<span data-bind="text:fname"></span>
<span data-bind="text:lname"></span>
<ul id="userdropdown" class="userfunctions menuEntries dropdown-menu dropdown-menu-right menu-shadow-ovr rounded-0 border-0">
Expand Down
61 changes: 58 additions & 3 deletions interface/themes/oe-styles/style_light.scss
@@ -1,9 +1,9 @@
/* Bootstrap Code */
@import "../default-variables";

// @import "../default-variables";
$black: #222;
$close-color: $black;
$close-text-shadow: 0 1 0 $black;

$gray: #cdcdcd;
// Import Bootstrap 4 SCSS Files before doing anything
// This gets replaced in gulp now -- do not touch
// bs4import
Expand All @@ -22,13 +22,68 @@ $fa-font-path: "../assets/@fortawesome/fontawesome-free/webfonts";

@import "../navigation-slide/future-bootstrap";

:root {
--tabTopBorderWidth: 0.2em;
--gray300: #cdcdcd;
}

/* Overrides go here */
@if $compact-theme == false {
.btn-secondary {
background-color: rgba($black, 0.05) !important;
border-color: $gray-500 !important;
color: rgba($black, 0.87) !important;
}

.tabSpan {
border-top: var(--tabTopBorderWidth) solid var(--primary) !important;
font-weight: 600 !important;
padding: 0.25em 0.4em !important;

.fa {
color: $gray-500 !important;

&:hover {
color: $gray-700 !important;
}
}
}

.tabsNoHover {
border-top: var(--tabTopBorderWidth) solid transparent !important;
font-weight: normal !important;
}

// MAIN NAV
// The top level of the menu
.menuLabel {
font-size: 0.9em !important;
font-weight: normal !important;
padding: 0.2em 0.6rem !important;
transition-duration: 0ms !important;
}

.dropdown .dropdown-menu {
top: auto !important;
}

.dropdown ul.dropdown-menu .dropdown ul.dropdown-menu {
top: 0 !important;
}

.menuLabel:hover,
.menuEntries li .menuLabel:hover,
.menuSection:hover {
background-color: lighten(theme-color("primary"), 40%) !important;
}

.acck {
font-size: 0.9em !important;
}

.tabSpan.tabsNoHover:hover {
background-color: $gray-300 !important;
}
}

/* search any */
Expand Down

0 comments on commit da49144

Please sign in to comment.