diff --git a/app/resources/locales/en_US/menu.po b/app/resources/locales/en_US/menu.po index 2512352b0..60bdcfedc 100644 --- a/app/resources/locales/en_US/menu.po +++ b/app/resources/locales/en_US/menu.po @@ -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." diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php index 93a112a24..b9f31c759 100644 --- a/app/src/View/Helper/FieldHelper.php +++ b/app/src/View/Helper/FieldHelper.php @@ -461,7 +461,7 @@ protected function formNameDiv(string $fieldName, string $labelText=null, string . '' . __d('field','required') . '' : '') . ' - ' . ($desc ? '' . $desc . '' : "") .' + ' . ($desc ? '
' . $desc . '
' : "") .' '; } @@ -509,7 +509,7 @@ public function statusControl(string $fieldName, return $this->startLine() . $this->formNameDiv($fieldName, $labelText, 'string', $labelIsTextOnly) - . $linkHtml + . $this->formInfoDiv($linkHtml) . $this->endLine(); } diff --git a/app/templates/ApiUsers/fields.inc b/app/templates/ApiUsers/fields.inc index f2c9682cc..2c3887e6e 100644 --- a/app/templates/ApiUsers/fields.inc +++ b/app/templates/ApiUsers/fields.inc @@ -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') ]; diff --git a/app/templates/Standard/index.php b/app/templates/Standard/index.php index 6725109f6..1fd4e2d07 100644 --- a/app/templates/Standard/index.php +++ b/app/templates/Standard/index.php @@ -178,15 +178,24 @@ - - - $cfg): ?> - > + + > '; @@ -208,7 +217,7 @@ print ''; } ?> - +
- - 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']); - + 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 + ); + } } } } - - ?> - - - - '; + print $this->element('menuAction', $action_args); + print ''; + } + switch($cfg['type']) { case 'boolean': if(!empty($entity->$col) && $entity->$col) { @@ -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 '
'; print ''; print '
1): // More than one CO is available, so present the switch button ?> diff --git a/app/webroot/css/co-base.css b/app/webroot/css/co-base.css index e5a73246d..de4903d8e 100644 --- a/app/webroot/css/co-base.css +++ b/app/webroot/css/co-base.css @@ -243,10 +243,40 @@ body.cos.select #top-bar { #top-menu .dropdown-item:hover { background-color: var(--cmg-color-bg-003); } -#top-menu .dropdown-item .material-icons { +#top-menu .dropdown-item .material-icons, +#top-menu .dropdown-item .material-icons-outlined { margin-right: 0.25em; vertical-align: middle; } +#top-menu .menu-grouping h4 .material-icons, +#top-menu .menu-grouping h4 .material-icons-outlined { + font-size: 1.1rem; + margin-right: 0.5rem; +} +.menu-grouping { + padding: 1em; + font-size: 0.9em; +} +.menu-grouping h4 { + display: flex; + align-items: center; + font-size: 1rem; + padding: 0; + margin-bottom: 0.2em; +} +.menu-grouping-group { + display: flex; + gap: 0.75em; + padding-left: 2em; +} +.menu-grouping-group .form-check { + display: flex; + gap: 0.25em; + align-items: center; +} +.menu-grouping-group .form-check .form-check-input { + float: none; +} #user-panel { color: var(--cmg-color-body-txt); background-color: var(--cmg-color-bg-002); @@ -911,8 +941,14 @@ ul.form-list li.alert-banner .co-alert { padding-bottom: 1em; } /* INDEX ACTION COMMAND MENUS */ -a.action-menu-toggle { - padding: 0.5rem; +th.with-field-actions { + padding-left: 2.75em; +} +.field-actions { + float: left; + padding-left: 1.5em; + margin-left: -1.5em; + margin-right: 0.75em; } .field-actions .dropdown-menu { padding: 0; @@ -943,11 +979,14 @@ a.dropdown-item.deletebutton { table.bulk-edit-mode a.row-link, table.bulk-edit-mode .read-only-link-container, table.bulk-edit-mode .row-link-heading, -table.bulk-edit-mode td.actions .field-actions { +table.bulk-edit-mode .field-actions { display: none; } +table.bulk-edit-mode th.with-field-actions { + padding-left: 0.75em; +} table.list-mode a.row-link, -table.list-mode td.actions .field-actions { +table.list-mode .field-actions { display: block; } table.list-mode .bulk-action-checkbox-container { @@ -957,6 +996,9 @@ table.index-table .form-check { margin-bottom: 0; min-height: unset; } +.bulk-action-checkbox-container .form-check-input { + margin-right: 1em; +} /* PAGINATION */ #pagination { margin: 0; @@ -1176,6 +1218,9 @@ ul.form-list li .field { margin: 0; padding: 0.75em; } +ul.form-list li .field:hover { + background-color: var(--cmg-color-bg-001); +} ul.form-list li.fields-submit { background-color: unset; border-left: none; @@ -1190,6 +1235,7 @@ ul.form-list .field-info { ul.form-list .field-desc { font-size: 0.9em; font-style: italic; + padding-bottom: 1em; } ul.form-list .fields-header { background-color: var(--cmg-color-body-bg); @@ -1207,12 +1253,16 @@ ul.form-list input[type="password"] { color: var(--cmg-color-body-txt); background-color: var(--cmg-color-body-bg); } +ul.form-list input[type="number"] { + width: 6em; +} ul.form-list input[type="text"]:focus, ul.form-list input[type="number"]:focus, ul.form-list input[type="password"]:focus { background-color: var(--cmg-color-highlight-002); } ul.form-list select { + width: auto; /* select boxes should fit to their content, typically */ font-size: 0.9em; color: var(--cmg-color-body-txt); background-color: var(--cmg-color-body-bg); @@ -1325,6 +1375,9 @@ ul.fields li.fields-datepicker .field-info { display: flex; align-items: center; } +ul.form-list input.datepicker { + width: auto; +} .field-info .input-group { flex-wrap: unset; } @@ -1337,6 +1390,9 @@ ul.fields li.fields-datepicker .field-info { line-height: inherit; padding: .25rem .75rem; } +.cm-time-picker { + position: relative; +} .cm-time-picker-panel { position: absolute; z-index: 100; @@ -1572,13 +1628,8 @@ thead td.actions { td { border-bottom: 1px solid var(--cmg-color-bg-005); } -th:first-child, -td:first-child { - padding-left: 1rem; -} table.list-mode th.actions:first-child, table.list-mode td.actions:first-child { - padding-left: 0; text-align: center; } table.bulk-edit-mode th.actions, @@ -1757,6 +1808,9 @@ html.dark-mode .btn-default:active { color: var(--cmg-color-btn-bg-001); border: none; } +#edit_ApiUsers .provisionbutton { + margin-left: 1em; +} /*Bootstrap badge*/ .badge { font-weight: normal; @@ -1845,6 +1899,50 @@ html.dark-mode .btn-default:active { transition-duration: 0.2s; transition-timing-function: ease-out; } +/* DENSITY SETTINGS (from User Settings Menu; Medium is default) */ +/* Density "small" */ +html.density-small th, +html.density-small td { + padding: 0.5em; +} +html.density-small th.with-field-actions { + padding-left: 2.5em; +} +html.density-small table.bulk-edit-mode th.with-field-actions { + padding-left: 0.5em; +} +html.density-small .bulk-action-checkbox-container .form-check-input { + margin-right: 0; +} +html.density-small .co-grid .col { + padding: 0.75em; +} +html.density-small ul.form-list li .field { + padding: 0.5em; +} +html.density-small a.menu-panel-toggle { + font-size: 0.9em; +} +/* Density "large" */ +html.density-large th, +html.density-large td { + padding: 1.25em; +} +html.density-large th.with-field-actions { + padding-left: 2.75em; +} +html.density-large table.bulk-edit-mode th.with-field-actions { + padding-left: 1.25em; +} +html.density-large .co-grid .col { + padding: 1.5em; +} +html.density-large ul.form-list li .field { + padding: 1.5em; +} +html.density-large .field-data { + padding: 0.75em 1em; +} /* FOOTER */ footer { text-align: center; diff --git a/app/webroot/css/co-responsive.css b/app/webroot/css/co-responsive.css index 22fc96c68..6e8429896 100644 --- a/app/webroot/css/co-responsive.css +++ b/app/webroot/css/co-responsive.css @@ -69,17 +69,16 @@ top: 0; right: 0; } + /* FORMS */ + ul.form-list li .field { + display: grid; + column-gap: 1em; + align-items: center; + grid-template-columns: 1fr 2fr; + } ul.form-list .field-name { - display: inline-block; - min-width: 80px; - vertical-align: middle; - width: 30%; padding-left: 0.5em; } - ul.form-list .field-info { - display: inline-block; - width: 70%; - } ul.form-list-admin .field-name { width: 50%; padding-right: 1em; @@ -87,10 +86,8 @@ ul.form-list-admin .field-info { width: 40%; } - ul.form-list li .field { - display: flex; - column-gap: 1em; - align-items: center; + ul.form-list .field-desc { + padding: 0.5em 0 0 0.5em; } ul.form-list li.field-stack textarea { margin: 0.5em 0 0.5em 0.5em; @@ -119,6 +116,11 @@ .duet-date__dialog { left: -20em; } + /* DENSITY SETTINGS (from User Settings Menu; Medium is default) */ + /* Density "small" */ + html.density-small h1 { + font-size: 2.4em; + } } /* MEDIUM - Primary breakpoint */ @@ -347,7 +349,6 @@ th.actions, td.actions { width: 40px; /* this is pushed wider by the nowrap on .field-actions .dropdown-menu */ - padding: 0; } /* EDIT / VIEW / INDEX ACTION TOP-LINKS MENU */ /* Convert the dropdown menus to be visible on wider screens when .actions-expanded class exists. @@ -408,9 +409,13 @@ /**************************************************************************************************************/ /* Medium devices (desktops, 992px and up) */ @media only screen and (min-width: 992px) { + /* FORMS */ + ul.form-list li .field { + column-gap: 2em; + } /* DATE / TIME PICKER */ .cm-time-picker-panel { - right: -100%; + right: -250px; display: flex; } .cm-time-picker-vals ul { @@ -431,7 +436,10 @@ /**************************************************************************************************************/ /* Extra large devices (desktops, 1200px and up) */ @media only screen and (min-width: 1200px) { - + /* FORMS */ + ul.form-list li .field { + grid-template-columns: 1fr 3fr; + } } /* SPECIAL */
-
- - element('menuAction', $action_args); ?> - -
-