@@ -, +, @@ - Log into the OPAC and check that date pickers on the following pages work correctly, including month selection, year selection, forward and back arrows, and "Clear date" controls: - Go to the "Your personal details" page. - Check the "Date of birth" field. - With SuspendHoldsOpac enabled, view the "Your summary" page for a patron with pending holds. - Click "Suspend" to confirm that the "Suspend until" datepicker works correctly for more than one hold. - From the catalog search results page, select multiple results and click the "Place hold" link. - On the hold confirmation page, click "Show more options" to test the "Hold starts on date" (with OPACAllowHoldDateInFuture enabled) and the "Hold not needed after" fields. --- .../opac-tmpl/bootstrap/css/src/_common.scss | 32 +- .../bootstrap/css/src/_flatpickr.scss | 965 ++++++++++++++++++ .../bootstrap/css/src/_flatpickr_vars.scss | 37 + .../opac-tmpl/bootstrap/css/src/opac.scss | 5 + .../bootstrap/en/includes/calendar.inc | 211 ++-- .../bootstrap/en/includes/holds-table.inc | 2 +- .../bootstrap/en/includes/opac-bottom.inc | 3 - .../bootstrap/en/modules/opac-memberentry.tt | 13 +- .../bootstrap/en/modules/opac-reserve.tt | 46 +- .../bootstrap/en/modules/opac-user.tt | 16 - .../bootstrap/lib/jquery/jquery-ui-1.12.1.css | 2 - .../lib/jquery/jquery-ui-1.12.1.min.css | 2 +- .../lib/jquery/jquery-ui-rtl-1.12.1.css | 2 - .../lib/jquery/jquery-ui-rtl-1.12.1.min.css | 2 +- .../opac-tmpl/lib/flatpickr/flatpickr.min.js | 2 + 15 files changed, 1155 insertions(+), 185 deletions(-) create mode 100644 koha-tmpl/opac-tmpl/bootstrap/css/src/_flatpickr.scss create mode 100644 koha-tmpl/opac-tmpl/bootstrap/css/src/_flatpickr_vars.scss create mode 100644 koha-tmpl/opac-tmpl/lib/flatpickr/flatpickr.min.js --- a/koha-tmpl/opac-tmpl/bootstrap/css/src/_common.scss +++ a/koha-tmpl/opac-tmpl/bootstrap/css/src/_common.scss @@ -80,6 +80,8 @@ $high-contrast-grey: #666666; @import "../../../../../node_modules/bootstrap/scss/type"; @import "../../../../../node_modules/bootstrap/scss/utilities"; +@import "flatpickr"; + /* Koha imports */ @import "fonts"; @@ -650,7 +652,6 @@ div.dt-button-collection button.dt-button.buttons-print:not(.disabled):before { .ui-widget select, .ui-widget textarea, .ui-widget button { - font-family: 'NotoSans'; font-size: inherit; } @@ -828,35 +829,6 @@ ul { /* End jQueryUI tab styles */ -/* jQuery UI Datepicker */ - -.ui-datepicker { - @include shadowed; - table { - border: 0; - border-collapse: collapse; - font-size: .9em; - margin: 0 0 .4em; - width: 100%; - } - - th { - background: transparent none; - border: 0; - font-weight: bold; - padding: .7em .3em; - text-align: center; - } -} - -.ui-datepicker-trigger { - margin: 0 3px; - vertical-align: middle; -} - -/* End jQueryUI datepicker styles */ - - /* jQueryUI Core */ .ui-widget-content { --- a/koha-tmpl/opac-tmpl/bootstrap/css/src/_flatpickr.scss +++ a/koha-tmpl/opac-tmpl/bootstrap/css/src/_flatpickr.scss @@ -0,0 +1,965 @@ +@import "flatpickr_vars"; + +@-webkit-keyframes fpFadeInDown { + from { + opacity: 0; + transform: translate3d(0, -20px, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@-moz-keyframes fpFadeInDown { + from { + opacity: 0; + transform: translate3d(0, -20px, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@-ms-keyframes fpFadeInDown { + from { + opacity: 0; + transform: translate3d(0, -20px, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@-o-keyframes fpFadeInDown { + from { + opacity: 0; + transform: translate3d(0, -20px, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes fpFadeInDown { + from { + opacity: 0; + transform: translate3d(0, -20px, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +.flatpickr-calendar { + animation: none; + background: transparent; + border-radius: 5px; + border: 0; + box-sizing: border-box; + direction: ltr; + display: none; + font-size: 14px; + gap: 2px; + line-height: 24px; + opacity: 0; + padding: 0; + position: absolute; + text-align: center; + touch-action: manipulation; + visibility: hidden; + width: $calendarWidth; + + @if variable-exists( "noCalendarBorder" ) { + box-shadow: 0 3px 13px rgba(black, 0.08); + } + + @else { + + background: $calendarBackground; + box-shadow: 1px 1px 3px 0 #666; + } + + &.open, + &.inline { + opacity: 1; + max-height: 640px; + visibility: visible; + } + + &.open { + display: inline-block; + z-index: 99999; + } + + &.animate.open { + animation: fpFadeInDown 300ms $bezier; + } + + &.inline { + display: block; + position: relative; + top: 2px; + } + + &.static { + position: absolute; + top: calc(100% + 2px); + + &.open { + z-index: 999; + display: block; + } + } + + &.multiMonth { + .flatpickr-days .dayContainer:nth-child(n+1) { + & .flatpickr-day.inRange:nth-child(7n+7) { + box-shadow: none !important; + } + } + + .flatpickr-days .dayContainer:nth-child(n+2) { + & .flatpickr-day.inRange:nth-child(7n+1) { + box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6; + } + } + } + + .hasWeeks, + .hasTime { + .dayContainer { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + } + } + + @if variable-exists( "noCalendarBorder" ) { + .hasWeeks .dayContainer { + border-left: 0; + } + } + + &.hasTime { + .flatpickr-time { + height: $timeHeight; + border-top: 1px solid $calendarBorderColor; + } + + @if variable-exists( "noCalendarBorder" ) { + .flatpickr-innerContainer { + border-bottom: 0; + } + + .flatpickr-time { + border: 1px solid $calendarBorderColor; + } + } + } + + &.noCalendar.hasTime { + .flatpickr-time { + height: auto; + } + } + + &:before, + &:after { + position: absolute; + display: block; + pointer-events: none; + border: solid transparent; + content: ''; + height: 0; + width: 0; + left: 22px; + } + + &.rightMost, + &.arrowRight { + + &:before, + &:after { + left: auto; + right: 22px; + } + } + + &.arrowCenter { + + &:before, + &:after { + left: 50%; + right: 50%; + } + } + + &:before { + border-width: 5px; + margin: 0 -5px; + } + + &:after { + border-width: 4px; + margin: 0 -4px; + } + + &.arrowTop { + + &:before, + &:after { + bottom: 100%; + } + + &:before { + border-bottom-color: $calendarBorderColor; + } + + &:after { + @if variable-exists( "noCalendarBorder" ) { + border-bottom-color: $monthBackground; + } + + @else { + border-bottom-color: $calendarBackground; + } + } + } + + &.arrowBottom { + + &:before, + &:after { + top: 100%; + } + + &:before { + border-top-color: $calendarBorderColor; + } + + &:after { + @if variable-exists( "noCalendarBorder" ) { + border-top-color: $monthBackground; + } + + @else { + border-top-color: $calendarBackground; + } + } + } + + &:focus { + outline: 0; + } +} + +.flatpickr-wrapper { + position: relative; + display: inline-block; +} + +.flatpickr-months { + display: flex; + + .flatpickr-month { + @if variable-exists( "noCalendarBorder" ) { + border-radius: 5px 5px 0 0; + } + + background: $monthBackground; + color: $monthForeground; + fill: $monthForeground; + height: $monthNavHeight; + line-height: 1; + text-align: center; + position: relative; + user-select: none; + overflow: hidden; + flex: 1; + } + + .flatpickr-prev-month, + .flatpickr-next-month { + text-decoration: none; + cursor: pointer; + position: absolute; + top: 0; + height: $monthNavHeight; + padding: 10px; + z-index: 3; + color: $monthForeground; + fill: $monthForeground; + + &.flatpickr-disabled { + display: none; + } + + i { + position: relative; + } + + &.flatpickr-prev-month { + /*! + /*rtl:begin:ignore*/ + /* + */ + left: 0; + /*! + /*rtl:end:ignore*/ + /* + */ + } + + &.flatpickr-next-month { + /*! + /*rtl:begin:ignore*/ + /* + */ + right: 0; + /*! + /*rtl:end:ignore*/ + /* + */ + } + + &:hover { + color: $todayColor; + + svg { + @if variable-exists( "arrow_hover_color" ){ + fill: $arrow_hover_color; + } + + @else { + + fill: $todayColor; + } + } + } + + svg { + width: 14px; + height: 14px; + + path { + transition: fill 0.1s; + fill: inherit; + } + } + } +} + +.numInputWrapper { + position: relative; + height: auto; + + input, + span { + display: inline-block; + } + + input { + width: 100%; + + &::-ms-clear { + display: none; + } + + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + margin: 0; + -webkit-appearance: none; + } + } + + span { + position: absolute; + right: 0; + width: $timecontrols; + padding: 0 4px 0 2px; + height: 50%; + line-height: 50%; + opacity: 0; + cursor: pointer; + border: 1px solid rgba($dayForeground, 0.15); + box-sizing: border-box; + + &:hover { + background: rgba($invertedBg, 0.1); + } + + &:active { + background: rgba($invertedBg, 0.2); + } + + &:after { + display: block; + content: ""; + position: absolute; + } + + &.arrowUp { + top: 0; + border-bottom: 0; + + &:after { + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-bottom: 4px solid rgba($dayForeground, 0.6); + top: 26%; + } + } + + &.arrowDown { + top: 50%; + + &:after { + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid rgba($dayForeground, 0.6); + top: 40%; + } + } + + svg { + width: inherit; + height: auto; + + path { + fill: rgba($monthForeground, 0.5); + } + } + } + + &:hover { + background: rgba($invertedBg, 0.05); + + span { + opacity: 1; + } + } +} + +.flatpickr-current-month { + font-size: 135%; + line-height: inherit; + font-weight: 300; + color: inherit; + position: absolute; + width: 75%; + left: 12.5%; + padding: 0.22 * $monthNavHeight 0 0 0; + line-height: 1; + height: $monthNavHeight; + display: inline-block; + text-align: center; + transform: translate3d(0px, 0px, 0px); + + span.cur-month { + font-family: inherit; + font-weight: 700; + color: inherit; + display: inline-block; + margin-left: 0.5ch; + padding: 0; + + &:hover { + background: rgba($invertedBg, 0.05); + } + } + + .numInputWrapper { + width: 8ch; + display: inline-block; + + span.arrowUp:after { + border-bottom-color: $monthForeground; + } + + span.arrowDown:after { + border-top-color: $monthForeground; + } + } + + input.cur-year { + background: transparent; + box-sizing: border-box; + color: inherit; + cursor: text; + padding: 0 0 0 0.5ch; + margin: 0; + display: inline-block; + font-size: inherit; + font-family: inherit; + font-weight: 300; + line-height: inherit; + height: auto; + border: 0; + border-radius: 0; + vertical-align: initial; + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield; + + &:focus { + outline: 0; + } + + &[disabled], + &[disabled]:hover { + font-size: 100%; + color: rgba($monthForeground, 0.5); + background: transparent; + pointer-events: none; + } + } + + .flatpickr-monthDropdown-months { + appearance: none; + background-image: url('data:image/svg+xml;charset=utf8,%3csvg fill="%23000000" fill-opacity="0.54" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"%3e%3cpath d="M7 10l5 5 5-5z"/%3e%3cpath d="M0 0h24v24H0z" fill="none"/%3e%3c/svg%3e'); + background-position: 100% 50%; + background-repeat: no-repeat; + background-size: 24px 24px; + border: none; + border-radius: 0; + box-sizing: border-box; + color: inherit; + cursor: pointer; + font-size: 90%; + font-family: inherit; + font-weight: 300; + height: auto; + line-height: 120%; + margin: -1px 0 0 0; + outline: none; + padding: 4px 28px 4px 4px; + position: relative; + vertical-align: initial; + width: auto; + + &:focus, + &:active { + outline: none; + } + + .flatpickr-monthDropdown-month { + background-color: $monthBackground; + outline: none; + padding: 0; + } + } +} + +.flatpickr-weekdays { + background: $weekdaysBackground; + text-align: center; + overflow: hidden; + width: 100%; + display: flex; + align-items: center; + height: $weekdaysHeight; + + .flatpickr-weekdaycontainer { + display: flex; + flex: 1; + } +} + + + +span.flatpickr-weekday { + cursor: default; + font-size: 90%; + background: $monthBackground; + color: $weekdaysForeground; + line-height: 1; + margin: 0; + text-align: center; + display: block; + flex: 1; + font-weight: bolder; +} + +.dayContainer, +.flatpickr-weeks { + padding: 1px 0 0 0; +} + +.flatpickr-days { + position: relative; + overflow: hidden; + display: flex; + align-items: flex-start; + width: $daysWidth; + + &:focus { + outline: 0; + } + + @if variable-exists( "noCalendarBorder" ) { + border-left: 1px solid $calendarBorderColor; + border-right: 1px solid $calendarBorderColor; + } +} + +.dayContainer { + padding: 0; + outline: 0; + text-align: left; + width: $daysWidth; + min-width: $daysWidth; + max-width: $daysWidth; + box-sizing: border-box; + display: inline-block; + display: -ms-flexbox; + display: flex; + flex-wrap: wrap; + -ms-flex-wrap: wrap; + -ms-flex-pack: justify; + justify-content: space-around; + transform: translate3d(0px, 0px, 0px); + opacity: 1; + + &+.dayContainer { + box-shadow: -1px 0 0 $calendarBorderColor; + } +} + +.flatpickr-day { + background: none; + border: 1px solid transparent; + border-radius: 150px; + box-sizing: border-box; + color: $dayForeground; + cursor: pointer; + + font-weight: 400; + width: 14.2857143%; + flex-basis: 14.2857143%; + max-width: $daySize; + height: $daySize; + line-height: $daySize; + margin: 0; + + display: inline-block; + position: relative; + justify-content: center; + text-align: center; + + &, + &.prevMonthDay, + &.nextMonthDay { + + &.inRange, + &.today.inRange, + &:hover, + &:focus { + cursor: pointer; + outline: 0; + background: $dayHoverBackground; + border-color: $dayHoverBackground; + } + } + + &.today { + border-color: $todayColor; + + &:hover, + &:focus { + border-color: $todayColor; + background: $todayColor; + + @if variable-exists( "today_fg_color" ){ + color: $today_fg_color; + } + + @else { + color: white; + } + } + } + + &.selected, + &.startRange, + &.endRange { + + &, + &.inRange, + &:focus, + &:hover, + &.prevMonthDay, + &.nextMonthDay { + background: $selectedDayBackground; + box-shadow: none; + + @if variable-exists( "selectedDayForeground" ){ + color: $selectedDayForeground; + } + + @else { + color: white; + } + + border-color: $selectedDayBackground; + } + + &.startRange { + border-radius: 50px 0 0 50px; + } + + &.endRange { + border-radius: 0 50px 50px 0; + } + + &.startRange+.endRange:not(:nth-child(7n+1)) { + box-shadow: -5 * $dayMargin 0 0 $selectedDayBackground; + } + + &.startRange.endRange { + border-radius: 50px; + } + } + + &.inRange { + border-radius: 0; + box-shadow: -2.5 * $dayMargin 0 0 $dayHoverBackground, 2.5 * $dayMargin 0 0 $dayHoverBackground; + } + + &.flatpickr-disabled, + &.flatpickr-disabled:hover, + &.prevMonthDay, + &.nextMonthDay, + &.notAllowed, + &.notAllowed.prevMonthDay, + &.notAllowed.nextMonthDay { + color: rgba($dayForeground, 0.3); + background: transparent; + + @if variable-exists( "disabled_border_color" ){ + border-color: $disabled_border_color; + } + + @else { + + border-color: transparent; + } + + cursor: default; + } + + &.flatpickr-disabled, + &.flatpickr-disabled:hover { + cursor: not-allowed; + color: rgba($dayForeground, 0.1); + } + + &.week.selected { + border-radius: 0; + box-shadow: -2.5 * $dayMargin 0 0 $selectedDayBackground, 2.5 * $dayMargin 0 0 $selectedDayBackground; + } + + &.hidden { + visibility: hidden; + } +} + +.rangeMode .flatpickr-day { + margin-top: 1px; +} + +.flatpickr-weekwrapper { + float: left; + + .flatpickr-weeks { + padding: 0 12px; + + @if variable-exists( "noCalendarBorder" ) { + border-left: 1px solid $calendarBorderColor; + } + + @else { + box-shadow: 1px 0 0 $calendarBorderColor; + } + } + + .flatpickr-weekday { + float: none; + width: 100%; + line-height: $weekdaysHeight; + } + + span.flatpickr-day { + + &, + &:hover { + display: block; + width: 100%; + max-width: none; + color: rgba($dayForeground, 0.3); + background: transparent; + cursor: default; + border: none; + } + } +} + +.flatpickr-innerContainer { + display: block; + display: flex; + box-sizing: border-box; + overflow: hidden; + + @if variable-exists( "noCalendarBorder" ) { + background: $calendarBackground; + border-bottom: 1px solid $calendarBorderColor; + } +} + +.flatpickr-rContainer { + display: inline-block; + padding: 0; + box-sizing: border-box; +} + +.flatpickr-time { + text-align: center; + outline: 0; + display: block; + height: 0; + line-height: $timeHeight; + max-height: $timeHeight; + box-sizing: border-box; + overflow: hidden; + display: flex; + + @if variable-exists( "noCalendarBorder" ) { + background: $calendarBackground; + border-radius: 0 0 5px 5px; + } + + &:after { + content: ""; + display: table; + clear: both; + } + + .numInputWrapper { + flex: 1; + width: 40%; + height: $timeHeight; + float: left; + + span.arrowUp:after { + border-bottom-color: $dayForeground; + } + + span.arrowDown:after { + border-top-color: $dayForeground; + } + } + + &.hasSeconds .numInputWrapper { + width: 26%; + } + + &.time24hr .numInputWrapper { + width: 49%; + } + + input { + background: transparent; + box-shadow: none; + border: 0; + border-radius: 0; + text-align: center; + margin: 0; + padding: 0; + height: inherit; + line-height: inherit; + color: $dayForeground; + font-size: 14px; + position: relative; + box-sizing: border-box; + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield; + + &.flatpickr-hour { + font-weight: bold; + } + + &.flatpickr-minute, + &.flatpickr-second { + font-weight: 400; + } + + &:focus { + outline: 0; + border: 0; + } + } + + .flatpickr-time-separator, + .flatpickr-am-pm { + height: inherit; + float: left; + line-height: inherit; + color: $dayForeground; + font-weight: bold; + width: 2%; + user-select: none; + align-self: center; + } + + .flatpickr-am-pm { + outline: 0; + width: 18%; + cursor: pointer; + text-align: center; + font-weight: 400; + } + + input, + .flatpickr-am-pm { + + &:hover, + &:focus { + background: lighten($dayHoverBackground, 3); + } + } +} + +.flatpickr-input { + background-repeat: no-repeat; + background-position: 3px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='#369' class='bi bi-calendar3'%3E%3Cg stroke-width='1.333'%3E%3Cpath d='M10.5 0h-9A1.5 1.5 0 0 0 0 1.5v9A1.5 1.5 0 0 0 1.5 12h9a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 10.5 0zM.75 2.893c0-.355.336-.643.75-.643h9c.414 0 .75.288.75.643v7.714c0 .355-.336.643-.75.643h-9c-.414 0-.75-.288-.75-.643z'/%3E%3Cpath d='M4.875 5.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zM2.625 7.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm-6.75 2.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5z'/%3E%3C/g%3E%3C/svg%3E"); + border-style: inset; + border-width: 1px; + padding: 3px 3px 3px 20px; + + &:focus { + border-radius: 0; + } + + &[readonly] { + cursor: pointer; + } +} + +.flatpickr-day { + border-radius: 0; +} + +.flatpickr_wrapper { + white-space: nowrap; +} --- a/koha-tmpl/opac-tmpl/bootstrap/css/src/_flatpickr_vars.scss +++ a/koha-tmpl/opac-tmpl/bootstrap/css/src/_flatpickr_vars.scss @@ -0,0 +1,37 @@ +$bezier: cubic-bezier(0.23, 1, 0.32, 1); +$slideTime: 400ms; + + +$daySize: 39px !default; +$padding: $daySize / 16; +$dayMargin: 2px !default; +$daysWidth: $daySize * 7 + $dayMargin * 14 + $padding * 2 + 2; +$calendarWidth: $daysWidth; + +$monthNavHeight: 34px !default; +$weekdaysHeight: 28px !default; +$timeHeight: 40px; + + + +$calendarBackground: #ffffff !default; +$calendarBorderColor: #e6e6e6 !default; + +$monthForeground: rgba(black, 0.9) !default; +$arrow_hover_color: #f64747 !default; + +$monthBackground: transparent !default; + +$weekdaysBackground: transparent !default; +$weekdaysForeground: rgba(black, 0.54) !default; + +$dayForeground: #000 !default; +$dayHoverBackground: #e6e6e6 !default; + +$todayColor: #538200 !default; +$selectedDayBackground: #FFF4C6 !default; +$selectedDayForeground: #000; + +$invertedBg: invert($calendarBackground); + +$timecontrols: 25px; --- a/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss +++ a/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss @@ -77,6 +77,11 @@ a { padding-left: 1em; } + &.clear_date { + color: #c33; + font-size: 130%; + } + &.title { font-size: 108%; font-weight: bold; --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/calendar.inc +++ a/koha-tmpl/opac-tmpl/bootstrap/en/includes/calendar.inc @@ -1,98 +1,139 @@ +[% USE Asset %] [% USE Koha %] +[% USE raw %] +[% Asset.js("lib/flatpickr/flatpickr.min.js") | $raw %] + --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/holds-table.inc +++ a/koha-tmpl/opac-tmpl/bootstrap/en/includes/holds-table.inc @@ -157,7 +157,7 @@