Skip to content

Commit

Permalink
Implement Density controls with general layout improvements, and impr…
Browse files Browse the repository at this point in the history
…ove Dark mode toggling (CFM-240)
  • Loading branch information
arlen committed Sep 21, 2023
1 parent 1af88ab commit 9c48a8a
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 143 deletions.
18 changes: 15 additions & 3 deletions app/resources/locales/en_US/menu.po
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,26 @@ msgstr "Advanced Menu"
msgid "menu.darkmode"
msgstr "Dark mode"

msgid "menu.darkmode.auto"
msgstr "auto"

msgid "menu.darkmode.dark"
msgstr "dark"

msgid "menu.darkmode.light"
msgstr "light"

msgid "menu.density"
msgstr "Density"

msgid "menu.density.small"
msgstr "Density small"
msgstr "small"

msgid "menu.density.medium"
msgstr "Density medium"
msgstr "medium"

msgid "menu.density.large"
msgstr "Density large"
msgstr "large"

msgid "menu.introduction"
msgstr "Please select an action from the menu."
Expand Down
4 changes: 2 additions & 2 deletions app/src/View/Helper/FieldHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ protected function formNameDiv(string $fieldName, string $labelText=null, string
. '<span class="visually-hidden">' . __d('field','required') . '</span>'
: '') . '
</div>
' . ($desc ? '<span class="field-desc">' . $desc . '</span>' : "") .'
' . ($desc ? '<div class="field-desc">' . $desc . '</div>' : "") .'
</div>';
}

Expand Down Expand Up @@ -509,7 +509,7 @@ public function statusControl(string $fieldName,

return $this->startLine()
. $this->formNameDiv($fieldName, $labelText, 'string', $labelIsTextOnly)
. $linkHtml
. $this->formInfoDiv($linkHtml)
. $this->endLine();
}

Expand Down
2 changes: 1 addition & 1 deletion app/templates/ApiUsers/fields.inc
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ if($vv_action == 'add' || $vv_action == 'edit') {
$vv_obj->id
],
'label' => __d('operation', 'api.key.generate'),
'class' => 'provisionbutton nospin',
'class' => 'provisionbutton nospin btn btn-primary btn-sm',
'confirm' => __d('operation', 'api.key.generate.confirm')
];

Expand Down
211 changes: 108 additions & 103 deletions app/templates/Standard/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,24 @@
<table id="<?= $tableName . '-table'; ?>" class="<?= $indexTableClasses; ?>">
<thead>
<tr>
<?php if(!empty($rowActions)): ?>
<td class="actions"></td>
<?php endif; ?>
<?php
// The first heading will get the bulk select all checkbox.
$firstHeading = true;
?>
<?php foreach($indexColumns as $col => $cfg): ?>
<th<?= !empty($cfg['cssClass']) ? ' class="' . $cfg['cssClass'] . '"' : ''; ?>>
<?php
$headingClass = '';
if(!empty($cfg['cssClass'])) {
$headingClass = $cfg['cssClass'];
}
if($firstHeading && !empty($rowActions)) {
if(!empty($headingClass)) {
$headingClass .= ' ';
}
$headingClass .= 'with-field-actions';
}
?>
<th<?= !empty($headingClass) ? ' class="' . $headingClass . '"' : ''; ?>>
<?php
if($firstHeading) {
print '<span class="row-link-heading">';
Expand All @@ -208,7 +217,7 @@
print '</span>';
}
?>
<?php if($firstHeading): ?>
<?php if($firstHeading && !empty($bulkActions)): ?>
<div class="form-check bulk-action-checkbox-container">
<input class="form-check-input" type="checkbox" value="" id="bulk-action-select-all">
<label class="form-check-label" for="bulk-action-select-all">
Expand All @@ -227,108 +236,97 @@
<tbody>
<?php foreach($$tableName as $entity): ?>
<tr>
<?php if(!empty($rowActions)): ?>
<?php
// Action list for command menu dropdown / button listing
$action_args = array();
$action_args['vv_attr_id'] = $entity->id;

// Insert actions as per the .inc file
// TODO: create an element or move this to MenuHelper so it can be used by topLinks as well as actions
$actionOrderDefault = $this->Menu->getMenuOrder('Default');
foreach($rowActions as $a) {
$ok = false;
if(!empty($a['controller'])) {
$tableName = Inflector::camelize($a['controller']);

if(isset($vv_permission_set[$entity->id][$tableName][ $a['action'] ])) {
$ok = $vv_permission_set[$entity->id][$tableName][ $a['action'] ];
}
} else {
$ok = $vv_permission_set[$entity->id][ $a['action'] ];
}

if($ok && !empty($a['if'])) {
// If there's a conditional on the field, test the entity
$f = $a['if'];

$ok = $entity->$f();
}

if($ok) {
$actionOrder = !empty($a['order']) ? $a['order'] : $actionOrderDefault++;
$actionIcon = !empty($a['icon']) ? $a['icon'] : $this->Menu->getMenuIcon('Default');
$actionIconClass = !empty($a['iconClass']) ? $a['iconClass'] : '';
$actionClass = !empty($a['class']) ? $a['class'] : '';
$actionUrl = ['action' => $a['action'], $entity->id];
$actionLabel = !empty($a['label']) ? $a['label'] : __d('operation', $a['action']);

<?php
if(!empty($rowActions)) {
// Action list for command menu dropdown / button listing
$action_args = array();
$action_args['vv_attr_id'] = $entity->id;

// Insert actions as per the .inc file
// TODO: create an element or move this to MenuHelper so it can be used by topLinks as well as actions
$actionOrderDefault = $this->Menu->getMenuOrder('Default');
foreach ($rowActions as $a) {
$ok = false;
if (!empty($a['controller'])) {
// We're linking into a related controller
$actionLabel = !empty($a['label']) ? $a['label'] : __d('controller', Inflector::camelize(Inflector::pluralize($a['controller'])), [99]);
$actionUrl = [
'controller' => $a['controller'],
'action' => $a['action'],
'?' => [$tableFK => $entity->id]
];
}

// Generate the link text and urls:

if(!empty($a['confirm'])) {
// Gather the default confirmation body text. By convention this is named
// [action].confirm in the operation.po file but can be overridden in the actions array.
$confirmKey = $a['action'].'.confirm';
$confirmTxt = __d('operation', $confirmKey, [$entity->id]);

// Gather the dialog text - we need to expand these (here) so we can provide the default confirmTxt when appropriate
$dialogBodyText = !empty($a['confirm']['dg_body_txt']) ? $a['confirm']['dg_body_txt'] : $confirmTxt;
$dialogTitle = !empty($a['confirm']['dg_title']) ? $a['confirm']['dg_title'] : $actionLabel;
$confirmButtonText = !empty($a['confirm']['dg_confirm_btn']) ? $a['confirm']['dg_confirm_btn'] : __d('operation','confirm');
$cancelButtonText = !empty($a['confirm']['dg_cancel_btn']) ? $a['confirm']['dg_cancel_btn'] : __d('operation','cancel');
$replacements = !empty($a['confirm']['dg_body_txt_replacements']) ? $a['confirm']['dg_body_txt_replacements'] : '';

$action_args['vv_actions'][] = array(
'order' => $actionOrder,
'icon' => $actionIcon,
'iconClass' => $actionIconClass,
'url' => $actionUrl,
'label' => $actionLabel,
'class' => !empty($actionClass) ? $actionClass . ' nospin' : 'nospin',
'confirm' => [
'dg_title' => $dialogTitle,
'dg_body_txt' => $dialogBodyText,
'dg_confirm_btn' => $confirmButtonText,
'dg_cancel_btn' => $cancelButtonText,
'dg_body_txt_replacements' => $replacements
]
);
$tableName = Inflector::camelize($a['controller']);

if (isset($vv_permission_set[$entity->id][$tableName][$a['action']])) {
$ok = $vv_permission_set[$entity->id][$tableName][$a['action']];
}
} else {
// Set the action link configuration
$action_args['vv_actions'][] = array(
'order' => $actionOrder,
'icon' => $actionIcon,
'iconClass' => $actionIconClass,
'url' => $actionUrl,
'label' => $actionLabel,
'class' => $actionClass
);
$ok = $vv_permission_set[$entity->id][$a['action']];
}

if ($ok && !empty($a['if'])) {
// If there's a conditional on the field, test the entity
$f = $a['if'];

$ok = $entity->$f();
}

if ($ok) {
$actionOrder = !empty($a['order']) ? $a['order'] : $actionOrderDefault++;
$actionIcon = !empty($a['icon']) ? $a['icon'] : $this->Menu->getMenuIcon('Default');
$actionIconClass = !empty($a['iconClass']) ? $a['iconClass'] : '';
$actionClass = !empty($a['class']) ? $a['class'] : '';
$actionUrl = ['action' => $a['action'], $entity->id];
$actionLabel = !empty($a['label']) ? $a['label'] : __d('operation', $a['action']);

if (!empty($a['controller'])) {
// We're linking into a related controller
$actionLabel = !empty($a['label']) ? $a['label'] : __d('controller', Inflector::camelize(Inflector::pluralize($a['controller'])), [99]);
$actionUrl = [
'controller' => $a['controller'],
'action' => $a['action'],
'?' => [$tableFK => $entity->id]
];
}

// Generate the link text and urls:

if (!empty($a['confirm'])) {
// Gather the default confirmation body text. By convention this is named
// [action].confirm in the operation.po file but can be overridden in the actions array.
$confirmKey = $a['action'] . '.confirm';
$confirmTxt = __d('operation', $confirmKey, [$entity->id]);

// Gather the dialog text - we need to expand these (here) so we can provide the default confirmTxt when appropriate
$dialogBodyText = !empty($a['confirm']['dg_body_txt']) ? $a['confirm']['dg_body_txt'] : $confirmTxt;
$dialogTitle = !empty($a['confirm']['dg_title']) ? $a['confirm']['dg_title'] : $actionLabel;
$confirmButtonText = !empty($a['confirm']['dg_confirm_btn']) ? $a['confirm']['dg_confirm_btn'] : __d('operation', 'confirm');
$cancelButtonText = !empty($a['confirm']['dg_cancel_btn']) ? $a['confirm']['dg_cancel_btn'] : __d('operation', 'cancel');
$replacements = !empty($a['confirm']['dg_body_txt_replacements']) ? $a['confirm']['dg_body_txt_replacements'] : '';

$action_args['vv_actions'][] = array(
'order' => $actionOrder,
'icon' => $actionIcon,
'iconClass' => $actionIconClass,
'url' => $actionUrl,
'label' => $actionLabel,
'class' => !empty($actionClass) ? $actionClass . ' nospin' : 'nospin',
'confirm' => [
'dg_title' => $dialogTitle,
'dg_body_txt' => $dialogBodyText,
'dg_confirm_btn' => $confirmButtonText,
'dg_cancel_btn' => $cancelButtonText,
'dg_body_txt_replacements' => $replacements
]
);
} else {
// Set the action link configuration
$action_args['vv_actions'][] = array(
'order' => $actionOrder,
'icon' => $actionIcon,
'iconClass' => $actionIconClass,
'url' => $actionUrl,
'label' => $actionLabel,
'class' => $actionClass
);
}
}
}
}

?>

<td class="actions">
<div class="field-actions">
<?php if(!empty($action_args['vv_actions'])): ?>
<?= $this->element('menuAction', $action_args); ?>
<?php endif; ?>
</div>
</td>
<?php endif; ?>

<?php
// We will set $isFirstLink to false after the first link is set. This is used to
// establish the row-link class (and thus click action) for the row. There can be only one.
// This is also used to determine which label will be assigned to the bulk action checkbox.
Expand All @@ -352,6 +350,13 @@
}
}

// Output the row actions if present
if($isFirstLink && !empty($rowActions)) {
print '<div class="field-actions">';
print $this->element('menuAction', $action_args);
print '</div>';
}

switch($cfg['type']) {
case 'boolean':
if(!empty($entity->$col) && $entity->$col) {
Expand Down Expand Up @@ -455,8 +460,8 @@

$linked = false;

// Output the bulk-action checkbox and label
if($isFirstLink) {
// Output the bulk-action checkbox and label if present
if($isFirstLink && !empty($bulkActions)) {
print '<div class="form-check bulk-action-checkbox-container">';
print '<input class="form-check-input" type="checkbox" value="" id="bulk-action-id-' . $entity->id . '" data-entity-id="' . $entity->id . '">';
print '<label class="form-check-label" for="bulk-action-id-' . $entity->id . '">';
Expand Down
31 changes: 25 additions & 6 deletions app/templates/element/javascript.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,18 +247,37 @@
});

// SETTINGS (from User Menu)
// Dark Mode toggle
$("#dark-mode-toggle").click(function(e) {
e.preventDefault();
$('html').toggleClass('dark-mode');
// Dark Mode toggles (auto is default)
$("#setting-darkmode-dark").click(function(e) {
$('html').removeClass('light-mode').addClass('dark-mode');
});
$("#setting-darkmode-light").click(function(e) {
$('html').removeClass('dark-mode').addClass('light-mode');
});
$("#setting-darkmode-auto").click(function(e) {
$('html').removeClass('dark-mode').removeClass('light-mode');
});

// Test for dark mode OS preference, and add the 'dark-mode' body class by default if it is present.
// XXX TODO: Replace this with setting an application variable (when available) so we do not flash the light color briefly on each page load
// XXX TODO: Once an application variable is set and the class is rendered at page build,
// there will be no flash of the light color on each page load if the setting is defined.
// We will need to investigate this for the OS setting, and send an ajax call to set the preference
// if the user has not set it so that no page flash occurs.
const darkModeOsEnabled = window.matchMedia("(prefers-color-scheme: dark)");
if(darkModeOsEnabled.matches) {
if(darkModeOsEnabled.matches && !($('html').hasClass('light-mode')) && !($('html').hasClass('dark-mode'))) {
$('html').addClass('dark-mode');
}

// Density toggles (medium is default)
$("#setting-density-small").click(function(e) {
$('html').removeClass('density-large').addClass('density-small');
});
$("#setting-density-medium").click(function(e) {
$('html').removeClass('density-small').removeClass('density-large');
});
$("#setting-density-large").click(function(e) {
$('html').removeClass('density-small').addClass('density-large');
});

});

Expand Down
Loading

0 comments on commit 9c48a8a

Please sign in to comment.