Skip to content

Commit

Permalink
First working implementation of Date and Time pickers (CFM-107)
Browse files Browse the repository at this point in the history
  • Loading branch information
arlen committed Mar 30, 2022
1 parent 98fd866 commit b863899
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 68 deletions.
57 changes: 53 additions & 4 deletions app/src/View/Helper/FieldHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@

namespace App\View\Helper;

use Cake\I18n\FrozenTime;
use \Cake\Utility\Inflector;
use Cake\View\Helper;

class FieldHelper extends Helper {
public $helpers = ['Form', 'Html'];
public $helpers = ['Form', 'Html', 'Url'];

// Is this read-only or read-write?
protected $editable = true;
Expand Down Expand Up @@ -90,18 +91,66 @@ public function control(string $fieldName,
// Append the timezone to the label
$label = __d('field', $fieldName.".tz", [$this->_View->get('vv_tz')]);

// Render these fields as datepickers instead of plain text boxes
$coptions['class'] = 'datepicker-' . ($fieldName == 'valid_from' ? "f" : "u");
// A datetime field will be rendered as plain text input with adjacent date and time pickers
// that will interact with the field value. Allowing direct access to the input field is for
// accessibility purposes.
$coptions['class'] = 'form-control datepicker';
$coptions['placeholder'] = 'YYYY-MM-DD HH:MM:SS'; // TODO: test for date-only inputs and send only the date
$coptions['id'] = $fieldName;

$entity = $this->_View->get('vv_obj');

$pickerDate = '';
if(!empty($entity->$fieldName)) {
// Adjust the time back to the user's timezone
$coptions['value'] = $entity->$fieldName->i18nFormat("yyyy-MM-dd HH:mm:ss", $this->_View->get('vv_tz'));
$pickerDate = $entity->$fieldName->i18nFormat("yyyy-MM-dd", $this->_View->get('vv_tz'));
}

// Create a text field to hold our value.
$controlCode = $this->Form->text($fieldName, $coptions);
$liClass = " modelbox-data";

// Create a date/time picker. The yyyy-MM-dd format is set above in $pickerDate.
$pickerDateTime = ''; // for handling time
$pickerId = 'datepicker-' . $fieldName;
$pickerTarget = $fieldName;
$pickerTimed = true; // TODO: set false if date-only
$pickerAmPm = false; // TODO: allow change between AM/PM and 24-hour mode
if(!empty($coptions['value'])) {
$pickerDateTime = $coptions['value'];
}
$controlCode .= '
<script type="module">
import CmDateTimePicker from "' . $this->Url->script('comanage/components/datepicker/cm-datetimepicker.js') . '";
const app = Vue.createApp({
data() {
return {
id: "' . $pickerId . '",
target: "' . $pickerTarget . '",
date: "' . $pickerDate . '",
dateTime: "' . $pickerDateTime .'",
timed: true,
ampm: false,
core: {},
txt: {}
}
},
components: {
CmDateTimePicker
}
}).mount("#' . $pickerId . '-container");
</script>
<div id="' . $pickerId . '-container">
<cm-date-time-picker
:id="id"
:target="target"
:date="date"
:timed="timed"
:ampm="ampm">
</cm-date-time-picker>
</div>';

$liClass = "fields-datepicker";
} else {
if($fieldName != 'status'
&& !isset($options['empty'])
Expand Down
46 changes: 3 additions & 43 deletions app/templates/element/javascript.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,50 +169,10 @@
}
});

// Datepickers

<?php /* For all calls to datepicker, wrap the calling date field in a
container of class .modelbox-data: this allows us to show the datepicker next to
the appropriate field because jQuery drops the div at the bottom of the body and
that approach doesn't work well with Material Design Light (MDL). If you do not
do this, the datepicker will float up to the top of the browser window. See
app/View/CoGroupMembers for an example. */ ?>

/* XXX CFM-107 Hide datepickers until replaced with new approach
$(".datepicker-f").datepicker({
changeMonth: true,
changeYear: true,
dateFormat: "yy-mm-dd 00:00:00",
numberOfMonths: 1,
showButtonPanel: false,
showOtherMonths: true,
selectOtherMonths: true,
onSelect: function(selectedDate) {
$(this).closest('.mdl-textfield').addClass('is-dirty');
}
}).bind('click',function () {
$("#ui-datepicker-div").appendTo($(this).closest('.modelbox-data'));
});

$(".datepicker-u").datepicker({
changeMonth: true,
changeYear: true,
dateFormat: "yy-mm-dd 23:59:59",
numberOfMonths: 1,
showButtonPanel: false,
showOtherMonths: true,
selectOtherMonths: true,
onSelect: function(selectedDate) {
$(this).closest('.mdl-textfield').addClass('is-dirty');
}
}).bind('click',function () {
$("#ui-datepicker-div").appendTo($(this).closest('.modelbox-data'));
});
*/

// Add loading animation when a form is submitted, when any item with a "spin" class is clicked,
// or on any button or anchor tag lacking the .nospin class.
$("input[type='submit'], button:not(.nospin), a:not(.nospin), .spin").click(function() {
// or on any anchor tag lacking the .nospin class. We do not automatically add this to buttons
// because they are often on-page controls. Add a "spin" class to buttons that need it.
$("input[type='submit'], a:not('.nospin'), .spin").click(function() {

displaySpinner();

Expand Down
9 changes: 7 additions & 2 deletions app/templates/layout/default.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@
<?= $this->element('dialog'); ?>

<!-- Get timezone detection -->
<?php print $this->Html->script('jstimezonedetect/jstz.min.js'); ?>
<?= $this->Html->script('jstimezonedetect/jstz.min.js'); ?>
<script>
// Determines the time zone of the browser client
var tz = jstz.determine();
Expand All @@ -191,13 +191,18 @@
<!-- XXX Noty, js-cookie, and metisMenu should be deprecated -->
<?= $this->Html->script([
'bootstrap/bootstrap.bundle.min.js',
'vue/vue-3.2.31.global.prod.js',
'js-cookie/js.cookie-2.1.3.min.js',
'jquery/noty/jquery.noty.js',
'jquery/noty/layouts/topCenter.js',
'jquery/noty/themes/comanage.js',
'comanage.js'
'comanage/comanage.js'
]) . "\n"; ?>

<!-- Duet Datepicker should be loaded as a module -->
<?= $this->Html->script('duet-datepicker/duet/duet.esm.js',['type' => 'module']); ?>
<?= $this->Html->script('duet-datepicker/duet/duet.js',['nomodule' => '']); ?>

<!-- COmanage JavaScript onload scripts -->
<?php print $this->element('javascript'); ?>

Expand Down
124 changes: 105 additions & 19 deletions app/webroot/css/co-base.css
Original file line number Diff line number Diff line change
Expand Up @@ -1046,31 +1046,117 @@ ul.form-list li.field-stack textarea {
display: inline;
margin: 0;
}
/* Ensure datepicker renders properly.
We are using javascript to move it near its input field so
that it will scroll properly on mobile devices. */
#ui-datepicker-div table tr > *:first-child,
#ui-datepicker-div table tr > *:nth-child(2) {
min-width: 0;
#content .material-icons {
font-size: 17px;
margin-top: 1px;
vertical-align: top;
}
/* DATE and TIME PICKERS */
.cm-datetime-picker {
display: flex;
align-items: center;
margin-left: 2.8em;
}
.duet-date__toggle {
width: 30px;
height: 30px;
background-color: transparent;
border-radius: 0;
margin: -15px 0 0;
box-shadow: none;
}
.duet-date__toggle:focus {
border: 1px dotted var(--cmg-color-black);
box-shadow: 0 0 0 .25rem rgba(13,110,253,.25); /* override Duet to be same as Bootstrap */
}
.duet-date__dialog {
left: -3em;
}
table.duet-date__table tr th:first-child,
table.duet-date__table tr td:first-child,
table.duet-date__table th,
table.duet-date__table td {
padding: unset;
}
table.duet-date__table th.duet-date__table-header {
padding: 8px;
text-align: center;
}
#ui-datepicker-div table tr td:first-child {
padding-left: 1px;
.cm-time-picker button {
width: 30px;
margin: 0;
padding: 0;
}
#ui-datepicker-div {
top: 0 !important;
#content .cm-time-picker .material-icons {
font-size: 1.5em;
margin: 0;
}
.ui-datepicker select.ui-datepicker-month-year {
width: 100%;
.duet-date__input {
display: none;
}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
width: 49%;
ul.fields li.fields-datepicker {
overflow: unset;
}
#content .material-icons {
font-size: 17px;
margin-top: 1px;
vertical-align: top;
ul.fields li.fields-datepicker .field-info {
width: auto;
display: flex;
align-items: center;
}
.cm-time-picker-panel {
position: absolute;
z-index: 100;
right: -100%;
display: flex;
width: auto;
background-color: white;
border: 1px solid var(--cmg-color-lightgray-006);
text-align: center;
border-radius: 4px;
align-items: center;
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.1);
}
.cm-time-picker-title {
padding: 0.25em;
border-bottom: 1px solid var(--cmg-color-lightgray-005);
}
.cm-time-picker-vals ul {
display: grid;
grid-template-columns: repeat(12,40px);
grid-template-rows: repeat(2,40px);
padding: 0;
align-content: center;
}
.cm-time-picker-minutes .cm-time-picker-vals ul {
grid-template-columns: repeat(2,40px);
}
ul.form-list .cm-time-picker-vals li {
display: block;
padding: 4px 0 0 0;
}
.cm-time-picker-vals button {
background-color: transparent;
border: none;
width: 30px;
height: 30px;
font-size: 0.9rem;
margin: 0;
}
.cm-time-picker-vals button:focus {
background-color: var(--cmg-color-blue-003);
color: var(--cmg-color-white);
border-radius: 14px;
}
.cm-time-picker-colon {
padding: 0 1em;
}
/* Vue transitions */
.v-enter-active,
.v-leave-active {
transition: opacity 0.2s;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
/* DIALOG BOX */
#dialog .modal-header {
Expand Down
17 changes: 17 additions & 0 deletions app/webroot/css/co-color.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,21 @@

--cmg-color-white: #fff; /* white */
--cmg-color-black: #000; /* black */

/* Duet Date Picker Colors */
--duet-color-primary: #005fcc;
--duet-color-text: #333;
--duet-color-text-active: #fff;
--duet-color-placeholder: #666;
--duet-color-button: #f5f5f5;
--duet-color-surface: #fff;
--duet-color-overlay: rgba(0, 0, 0, 0.8);
--duet-color-border: #333;

--duet-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--duet-font-normal: 400;
--duet-font-bold: 600;

--duet-radius: 4px;
--duet-z-index: 600;
}
File renamed without changes.
Loading

0 comments on commit b863899

Please sign in to comment.