diff --git a/assets/css/part/ckeditor.css b/assets/css/_layout/ckeditor-overrides.css similarity index 54% rename from assets/css/part/ckeditor.css rename to assets/css/_layout/ckeditor-overrides.css index 0c8573b2..14df683f 100644 --- a/assets/css/part/ckeditor.css +++ b/assets/css/_layout/ckeditor-overrides.css @@ -1,47 +1,59 @@ +textarea.ckeditor { + height: 268px; +} + .cke_chrome { + border-radius: 7px !important; + border: 1px solid #cacaca !important; + background: #f7f7f7 !important; + outline: 0 solid transparent !important; + transition: linear 0.1s outline !important; + box-shadow: none !important; - border: none !important; } -.cke_chrome.cke_focus { - outline: 1px solid #F07F1F !important; +.cke_chrome:focus-within { + outline: 2px solid rgb(240,127,31, 0.6) !important; + border-color: #F07F1F !important; } .cke_reset_all, .cke_reset_all *, .cke_reset_all a { font-family: inherit !important; } .cke_toolgroup { - background-image: linear-gradient(to bottom,#fff,#efefef) !important; + background: #fff !important; +} +.cke_toolgroup .cke_button:hover, .cke_toolgroup .cke_button:focus { + background: #eaeaea !important; +} +.cke_toolgroup .cke_button_on { + background: #cdcdcd !important; + + box-shadow: none !important; } .cke_chrome .cke_inner { - background: rgba(224,224,224, 0.3) !important; + background: transparent !important; } .cke_chrome .cke_wysiwyg_div { padding: 2px 11px !important; - background: #F3F3F3 !important; - border: 1px solid #C2C2C2 !important; - box-shadow: inset 2px 2px 2px #DEDEDE !important; + background: #fff !important; + border-top: 1px solid #cacaca !important; + border-bottom: 1px solid #cacaca !important; } .cke_chrome span.cke_top, .cke_chrome span.cke_bottom { box-shadow: none !important; - border: 1px solid #C2C2C2 !important; background: none !important; -} -.cke_chrome span.cke_top { - border-bottom: none !important; -} -.cke_chrome span.cke_bottom { - border-top: none !important; + border: none !important; } .cke_chrome .cke_wordcount { text-transform: lowercase; - padding: 1px 0.7em 0 0; + padding: 1px 12px 0 0; } .cke_chrome .cke_wordcount span { cursor: default !important; } .cke_chrome .cke_wysiwyg_div p, .cke_chrome .cke_wysiwyg_div ul, .cke_chrome .cke_wysiwyg_div ol { - margin: 0.8em 0; + margin: 15px 0; } .cke_dialog_body { diff --git a/assets/css/_layout/form-action-button.css b/assets/css/_layout/form-action-button.css new file mode 100644 index 00000000..7025c695 --- /dev/null +++ b/assets/css/_layout/form-action-button.css @@ -0,0 +1,42 @@ +.form-action-button { + margin-top: 10px; + position: sticky; + bottom: 0; + padding: 30px 0; + + text-align: center; +} +.form-action-button::before, .form-action-button::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + + /* cover shadow from .ui-block */ + width: 20px; +} +.form-action-button::before { + left: 0; + transform: translateX(-100%); +} +.form-action-button::after { + right: 0; + transform: translateX(100%); +} +.form-action-button, .form-action-button::before, .form-action-button::after { + /* keep color in sync with body background */ + background: linear-gradient(rgba(255,255,255, 0), #f7f6f5 40%); +} +.form-action-button button { + margin: 0 auto; + padding-left: 30px; + padding-right: 30px; +} + +@media (max-width: 767px) +{ + /* keep in sync with .site-width padding */ + .form-action-button::before, .form-action-button::after { + width: 5vw; + } +} diff --git a/assets/css/_layout/form-layout.css b/assets/css/_layout/form-layout.css new file mode 100644 index 00000000..eaf906c7 --- /dev/null +++ b/assets/css/_layout/form-layout.css @@ -0,0 +1,53 @@ +.form-item + .form-item, +.form-columns + .form-columns, +.form-item + .form-columns, .form-columns + .form-item, +.form-item + .form-group, .form-group + .form-item { + margin-top: 20px; +} + +.form-item label { + display: inline-block; + margin-bottom: 7px; +} + +@media (min-width: 768px) +{ + .form-columns { + display: grid; + grid-auto-columns: 1fr; + grid-auto-flow: column; + grid-gap: 20px; + } + + .form-columns .form-item + .form-item { + margin-top: 0; + } +} + +.form-item-checkbox { + position: relative; + + /* keep in sync with input/checkbox width */ + padding-left: calc(19px + 10px); +} +.form-item-checkbox label { + display: inline; + margin-bottom: 0; +} +.form-item-checkbox input { + position: absolute; + left: 0; + top: 1px; +} + +.form-error-message { + color: #ff0000; + margin: 7px 0 0; + font-weight: bold; +} +.form-help-message { + font-size: 0.9em; + font-style: italic; + margin: 7px 0 0; + opacity: 0.8; +} diff --git a/assets/css/_layout/form-widgets-extra.css b/assets/css/_layout/form-widgets-extra.css new file mode 100644 index 00000000..d0e17a40 --- /dev/null +++ b/assets/css/_layout/form-widgets-extra.css @@ -0,0 +1,33 @@ +.form-widget-number-unit { + position: relative; +} +.form-widget-number-unit input { + /* keep in sync with input's padding right */ + padding-right: calc(11px + 70px); +} +.form-widget-number-unit .unit-label { + position: absolute; + + /* keep in sync with input's border width */ + right: 1px; + top: 1px; + bottom: 1px; + + /* keep in sync with input's border radius */ + border-top-right-radius: 7px; + border-bottom-right-radius: 7px; + + /* keep in sync with input's border color */ + border-left: 1px solid #cacaca; + + background: #f8f6f6; + width: 70px; + + display: flex; + justify-content: center; + align-items: center; +} +.form-widget-number-unit input:disabled + .unit-label { + opacity: 0.4; + cursor: not-allowed; +} diff --git a/assets/css/_layout/form-widgets.css b/assets/css/_layout/form-widgets.css new file mode 100644 index 00000000..a207bcda --- /dev/null +++ b/assets/css/_layout/form-widgets.css @@ -0,0 +1,140 @@ +input, textarea, select, button, a.button { + width: 100%; + padding: 6px 11px; + background: #f9f9f9; + border: 1px solid #cacaca; + border-radius: 7px; + + outline: 0 solid transparent; + transition: linear 0.1s background-color, linear 0.1s outline; + + font: inherit; + color: inherit; + box-sizing: border-box; +} +input:focus, textarea:focus, select:focus, button:focus, a.button:focus { + outline: 2px solid rgb(240,127,31, 0.6); + border-color: #F07F1F; + background-color: #fff; + + /* needed for WebKit/Blink */ + outline-offset: 0px; +} +input:disabled, textarea:disabled, select:disabled { + opacity: 0.4; + cursor: not-allowed; +} +input::placeholder, textarea::placeholder { + color: inherit; + opacity: 0.3; +} +textarea { + resize: vertical; + min-height: 130px; +} +input[type="number"] { + text-align: center; +} +@supports (-webkit-app-region: initial) +{ + input[type="search"]::-webkit-search-cancel-button { + /* remove "X" from search input in WebKit */ + -webkit-appearance: none; + } + input::-webkit-calendar-picker-indicator { + /* remove ugly arrow in inputs with datalist attribute in WebKit */ + display: none; + } +} +@supports (-webkit-nbsp-mode: initial) +{ + input[type="search"] { + /* make search input consistent in WebKit, not Blink */ + -webkit-appearance: none; + } +} + +select { + /* hide default select arrow */ + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + + background-image: url("data:image/svg+xml,%3Csvg height='16px' width='45px' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolygon fill='%23999' points='23.303,-0.002 12.467,10.834 1.63,-0.002 -0.454,2.082 12.467,15.002 14.551,12.918 25.387,2.082'/%3E%3C/svg%3E"); + background-position: center right; + background-repeat: no-repeat; + background-size: 30px; +} + +button, a.button { + display: inline-block; + width: auto; + padding-left: 20px; + padding-right: 20px; + + background: #f8f8f8; + color: #ba5400; + font-weight: bold; + border-color: #ddd; + box-shadow: 0 0 2px rgba(0,0,0, 0.17); +} +button:hover:not(:disabled), a.button:hover { + background-color: #e9e9e9; +} +button:focus, a.button:focus { + background-color: #f8f8f8; +} +button:active, a.button:active { + opacity: 0.7; +} +button:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +a.button { + text-align: center; + width: auto; + text-decoration: none; + + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; + border: none; + padding: 0; +} + +input[type="checkbox"], input[type="radio"] { + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + + display: inline-block; + width: 19px; + height: 19px; + padding: 0; + margin: 0; + border-radius: 4px; + + background-position: center; + background-repeat: no-repeat; + background-size: 9px 9px; +} +input[type="radio"] { + border-radius: 10px; +} +input[type="checkbox"]:checked { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 1 1'%3E%3Crect fill='%23FF7500' rx='0.25' x='0' y='0' width='1' height='1'/%3E%3C/svg%3E"); +} +input[type="radio"]:checked { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 1 1'%3E%3Crect fill='%23FF7500' rx='1' x='0' y='0' width='1' height='1'/%3E%3C/svg%3E"); +} + +input[type="checkbox"]:focus-visible + label, input[type="radio"]:focus-visible + label { + /* keep in sync with *:focus-visible */ + outline-offset: 3px; + outline: 2px solid rgb(240,127,31, 0.6); +} diff --git a/assets/css/_layout/global.css b/assets/css/_layout/global.css new file mode 100644 index 00000000..a517f821 --- /dev/null +++ b/assets/css/_layout/global.css @@ -0,0 +1,116 @@ +@font-face { + font-family: "LatoLatinWeb"; + src: url('../../fonts/LatoLatin-Regular.woff2'), url('../../fonts/LatoLatin-Regular.woff'); +} +@font-face { + font-family: "LatoLatinWeb"; + src: url('../../fonts/LatoLatin-Heavy.woff2'); + font-weight: bold; +} +@font-face { + font-family: "LatoLatinWeb"; + src: url('../../fonts/LatoLatin-Italic.woff2'); + font-style: italic; +} +@font-face { + font-family: "LatoLatinWeb"; + src: url('../../fonts/LatoLatin-HeavyItalic.woff2'); + font-weight: bold; + font-style: italic; +} +@font-face { + font-family: "Inconsolata"; + src: url('../../fonts/Inconsolata.woff2'); +} + +body { + font: 17px "LatoLatinWeb", "Liberation Sans", "Arial", sans-serif; + color: #000; + text-rendering: optimizeLegibility; + background: #f7f6f5; + + cursor: default; + overflow-y: scroll; + margin: 0; + padding: 0; +} + +html { + /* make sure sticky site header and sticky form action button don't cover site content */ + /* in case of client-side validation error or keyboard based navigation */ + scroll-padding-top: 100px; + scroll-padding-bottom: 125px; +} + +a { + color: #b85200; + transition: linear 0.1s color; + + text-decoration: none; + border: none; + outline: none; +} +a:hover { + text-decoration: underline; + color: #0084FF; + outline: none; +} +a:active { + color: #64BE00; + outline: none; +} + +*:focus-visible { + outline-offset: 3px; + outline: 2px solid rgb(240,127,31, 0.6); +} + +.sr-only { + position: absolute !important; + height: 1px !important; + width: 1px !important; + overflow: hidden !important; + clip: rect(1px, 1px, 1px, 1px) !important; + padding: 0 !important; + border: 0 !important; +} + +.skip-link { + position: absolute; + left: 40px; + top: 40px; + transform: translateX(-200%); + + background: #fff; + padding: 15px; + line-height: 1; + border: 3px solid currentColor; + font-size: 1.3em; + font-weight: bold; + + /* above sticky site header */ + z-index: 1001; +} +.skip-link:focus { + transform: translateX(0); +} + +.site-width { + max-width: 1100px; + margin-left: auto; + margin-right: auto; + padding-left: 34px; + padding-right: 34px; +} + +@media (max-width: 767px) +{ + .site-width { + padding-left: 5vw; + padding-right: 5vw; + + /* override style from .custom-width and .full-width */ + min-width: 320px !important; + box-sizing: border-box; + } +} diff --git a/assets/css/_layout/information.css b/assets/css/_layout/information.css new file mode 100644 index 00000000..5ab06e9e --- /dev/null +++ b/assets/css/_layout/information.css @@ -0,0 +1,24 @@ +.information { + border-radius: 7px; + box-shadow: 0 4px 22px rgba(0,0,0, 0.07); + padding: 34px 100px 34px 34px; + background-color: rgba(180,240,255, 0.2); + + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='51.264' height='116.008' viewBox='0 0 6.408 14.501'%3E%3Cpath fill='rgba(0,0,0, 0.12)' d='M4.603 0c-.496 0-.924.165-1.28.497-.353.331-.533.731-.533 1.196 0 .466.179.865.533 1.193.356.328.784.492 1.28.492.497 0 .922-.164 1.275-.492a1.57 1.57 0 0 0 .53-1.193c0-.465-.177-.865-.53-1.196A1.792 1.792 0 0 0 4.603 0zM2.92 4.906c-.404 0-.834.072-1.29.218-.454.144-.931.318-1.43.521l-.2.828c.146-.056.324-.113.53-.176.206-.06.408-.09.604-.09.401 0 .67.066.813.201.141.135.212.375.212.717 0 .192-.022.403-.068.633-.046.228-.104.473-.171.731l-.761 2.688a8.23 8.23 0 0 0-.147.759c-.03.224-.045.444-.045.659 0 .553.203 1.008.61 1.367.409.36.982.539 1.718.539.479 0 .9-.063 1.262-.189.363-.126.847-.308 1.455-.548l.203-.827a3.564 3.564 0 0 1-.506.17 2.4 2.4 0 0 1-.626.097c-.392 0-.67-.065-.827-.194-.16-.129-.239-.372-.239-.728 0-.141.024-.351.073-.626.05-.275.105-.521.167-.736l.757-2.68c.074-.247.125-.516.152-.811.027-.294.041-.5.041-.617 0-.564-.199-1.023-.594-1.376-.397-.352-.961-.53-1.692-.53z'/%3E%3C/svg%3E"); + background-position: right 34px center; + background-repeat: no-repeat; + background-size: 27px auto; + /* https://icons8.com/icon/76/info */ +} +.information a { + color: #0076AB; +} + +@media (max-width: 767px) +{ + .information { + padding: 26px 20px; + font-size: 0.9em; + background-image: none; + } +} diff --git a/assets/css/_layout/main-content.css b/assets/css/_layout/main-content.css new file mode 100644 index 00000000..a9ff8b4c --- /dev/null +++ b/assets/css/_layout/main-content.css @@ -0,0 +1,48 @@ +.main-content { + padding-top: 10px; + padding-bottom: 35px; + margin: auto; + + /* make sure footer is always at bottom */ + min-height: calc(100vh - 225px - 57px - 10px - 35px); +} + +.main-content > header h1 { + color: #E76A00; + margin: 18px 0 30px; + font-size: 34px; + text-shadow: 0 0 2px #eee; + + font-weight: normal; +} +.main-content > header h1.separated-title { + display: flex; + flex-direction: column-reverse; +} +.main-content > header h1 .additional { + font-size: 15px; + color: #000; + opacity: 0.55; + padding-left: 2px; + padding-top: 5px; +} + +.main-content h2, .main-content .h2 { + font-size: 27px; + margin: 17px 0; + text-shadow: 0 0 2px #eee; + + font-weight: normal; +} + +@media (max-width: 767px) +{ + .main-content { + /* make sure footer is always at bottom, outside of the initial viewport */ + min-height: calc(100vh - 250px); + } + + .main-content > header h1 { + font-size: 29px; + } +} diff --git a/assets/css/_layout/notification.css b/assets/css/_layout/notification.css new file mode 100644 index 00000000..01e65da9 --- /dev/null +++ b/assets/css/_layout/notification.css @@ -0,0 +1,84 @@ +@keyframes notification_show +{ + from { + opacity: 0; + transform: translateX(-50%) translateY(-100%); + } + to { + transform: translateX(-50%) translateY(0); + } +} +@keyframes notification_hide +{ + from { + transform: translateX(-50%) translateY(0); + } + to { + opacity: 0; + transform: translateX(-50%) translateY(-40%); + + /* hide for screen readers */ + visibility: hidden; + } +} + +.notification { + position: fixed; + top: 65px; + left: 50%; + + /* unsuppored by Chrome, included in animations instead */ + /* translate: -50%; */ + + /* above site header */ + z-index: 1001; + + min-width: 320px; + max-width: 800px; + + text-align: center; + font-weight: bold; + + background: rgba(255, 252, 228, 0.9); + border: 1px solid #e6e6e6; + color: #000; + + font-weight: bold; + padding: 14px 26px; + border-radius: 7px; + box-shadow: 0 4px 20px rgba(0,0,0, 0.07); + + animation: notification_show 0.5s ease, notification_hide 0.8s ease; + animation-delay: 0s, 6s; + animation-fill-mode: forwards, forwards; +} +.notification.error { + background: rgba(255,0,0, 0.8); + color: #FFF; + border-color: transparent; + box-shadow: 0 4px 20px rgba(0,0,0, 0.15); + + animation-delay: 0s, 11s; +} + +@media (max-width: 767px) +{ + .notification { + max-width: auto; + min-width: auto; + width: 80%; + box-sizing: border-box; + + top: unset; + + /* above sticky save changes button */ + bottom: 80px; + } +} + +@media (prefers-reduced-motion) +{ + .notification { + animation-duration: 0s, 0s; + } +} diff --git a/assets/css/_layout/radio-tables-list.css b/assets/css/_layout/radio-tables-list.css new file mode 100644 index 00000000..bfef4773 --- /dev/null +++ b/assets/css/_layout/radio-tables-list.css @@ -0,0 +1,64 @@ +.radio-tables-list-item { + background: #fff; + border: 1px solid #dfdfdf; + border-radius: 7px; + box-shadow: 0 0 6px rgba(0,0,0, 0.09); + padding: 17px 24px; + margin: 20px 0; + + outline: 0 solid transparent; + transition: linear 0.1s outline; +} +.radio-tables-list-item:hover { + background: #fcfcfc; + border-color: #e6d1be; +} +.radio-tables-list-item:focus-within { + outline: 2px solid rgb(240,127,31, 0.6); + border-color: #F07F1F; +} +.radio-tables-list-item dd { + margin-left: 0; +} +.radio-tables-list-item a { + /* make outline visible with overflow */ + outline-offset: -1px; +} + +.radio-tables-list-title a, .radio-tables-list-details dl { + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.radio-tables-list-title a { + display: block; + font-size: 1.6em; + color: #D66200; +} + +@media (max-width: 767px) +{ + .radio-tables-list-item { + padding-left: 18px; + padding-right: 18px; + } + .radio-tables-list-title a { + font-size: 1.3em; + line-height: 1.4; + } +} + +.radio-tables-list-details dl { + margin: 7px 0 2px; + font-size: 0.8em; +} +.radio-tables-list-details dt, .radio-tables-list-details dd { + display: inline; +} +.radio-tables-list-details dt::after { + content: ':'; +} +.radio-tables-list-details dt:not(:first-child) { + margin-left: 20px; +} diff --git a/assets/css/_layout/remove-dialog.css b/assets/css/_layout/remove-dialog.css new file mode 100644 index 00000000..dd338129 --- /dev/null +++ b/assets/css/_layout/remove-dialog.css @@ -0,0 +1,100 @@ +.remove-dialog { + background: #fff; + border: 1px solid #858585; + border-radius: 7px; + box-shadow: 0 4px 22px rgba(0,0,0, 0.4); + padding: 40px 35px 35px; + + max-width: 500px; + + text-align: center; + animation: dialog_show 0.5s ease; +} +.remove-dialog.no-JS-fallback { + position: fixed; + top: 80px; + z-index: 99999; +} +.remove-dialog::backdrop { + background: rgba(0,0,0, 0.4); +} + +@media (max-width: 600px) +{ + .remove-dialog { + margin-left: 30px; + margin-right: 30px; + } +} + +.remove-dialog-question { + font-weight: 700; +} + +.remove-dialog .form-item-checkbox { + display: inline-block; + margin-top: 15px; +} + +.remove-dialog button { + display: inline-block; + margin: 30px 7px 0 7px; +} +.remove-dialog button.remove { + color: #e10000; +} + +@keyframes dialog_show +{ + from { + opacity: 0; + transform: scale(0.9) translateY(-70px); + } + to { + transform: scale(1) translateY(0); + } +} + +@media (prefers-reduced-motion) +{ + .remove-dialog { + animation: none; + } +} + + +/* remove button */ + +.button.remove-button { + padding-left: 10px !important; + padding-right: 10px !important; + background: none; + border: none; + box-shadow: none; + + color: #e10000; + opacity: 0.95; + + /* https://www.iconfinder.com/icons/1041650/garbage_rubbish_trash_icon */ + background-image: url("data:image/svg+xml,%3Csvg height='32' viewBox='-0.5 -0.7 31 32' width='31' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath clip-rule='evenodd' d='M29.8 8.3zm-2 2h-1v18c0 1.1-.9 2-2 2h-19c-1.1 0-2-.9-2-2v-18H2c-1.1 0-2-.9-2-2s.9-2 2-2h6.9C9.2 2.8 12 0 15.4 0s6.3 2.8 6.5 6.3h5.8c1.1 0 2 .9 2 2 .1 1.1-.8 2-1.9 2zM15.4 3.2c-1.8 0-3.2 1.3-3.4 3.1h6.9c-.3-1.7-1.7-3.1-3.5-3.1zm8.4 8.3c0-.5-.2-.9-.5-1.1h-16c-.3.2-.5.6-.5 1.1v14.7c0 .9.7 1.6 1.6 1.6h13.8c.9 0 1.6-.7 1.6-1.6V11.5zm-5 .8h3v14h-3v-14zm-5 0h3v14h-3v-14zm-5 0h3v14h-3v-14zM0 8.3z' fill='%23e10000' fill-rule='evenodd'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-size: 15px; + background-position: left 10px center; + + text-indent: 20px; +} +.form-action-button .button.remove-button { + position: absolute; + left: -10px; +} + +@media (max-width: 767px) +{ + .button.remove-button { + text-indent: -9999px; + overflow: hidden; + background-position: center; + background-size: 15px; + width: 40px; + } +} diff --git a/assets/css/_layout/search-form.css b/assets/css/_layout/search-form.css new file mode 100644 index 00000000..6e516c42 --- /dev/null +++ b/assets/css/_layout/search-form.css @@ -0,0 +1,20 @@ +.search-form { + max-width: 500px; + margin: 40px auto; + + padding-bottom: 10px; +} + +.search-form input { + background: #fff; + box-shadow: 0 2px 14px rgba(0,0,0, 0.09); + font-size: 1.3em; + padding: 10px 12px 10px 45px; + + /* https://www.iconfinder.com/icons/298865/search_icon */ + background-image: url("data:image/svg+xml,%3Csvg fill='%23777' height='1024' width='973.125' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M960 832L710.875 582.875C746.438 524.812 768 457.156 768 384 768 171.969 596 0 384 0 171.969 0 0 171.969 0 384c0 212 171.969 384 384 384 73.156 0 140.812-21.562 198.875-57L832 960c17.5 17.5 46.5 17.375 64 0l64-64c17.5-17.5 17.5-46.5 0-64zM384 640c-141.375 0-256-114.625-256-256s114.625-256 256-256 256 114.625 256 256-114.625 256-256 256z'/%3E%3C/svg%3E"); + + background-size: 25px auto; + background-position: 12px 11px; + background-repeat: no-repeat; +} diff --git a/assets/css/_layout/site-footer.css b/assets/css/_layout/site-footer.css new file mode 100644 index 00000000..70bbf85a --- /dev/null +++ b/assets/css/_layout/site-footer.css @@ -0,0 +1,77 @@ +.site-footer { + background: #bb5400; + color: #fff; + + padding-top: 30px; + padding-bottom: 30px; + line-height: 2em; + + text-align: center; +} +.site-footer > div { + display: grid; + grid-gap: 15px; +} + +@media (min-width: 999px) +{ + .site-footer { + padding-top: 50px; + padding-bottom: 50px; + + text-align: left; + } + + .site-footer > div::before { + content: ''; + display: inline-block; + width: 145px; + height: 125px; + background: url('../../images/icon.svg'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + filter: brightness(10); + opacity: 0.5; + } + .site-footer > div { + grid-template-columns: min-content 1fr min-content; + align-items: center; + grid-gap: 80px; + } +} + +.site-footer a { + color: inherit; + padding: 6px 0; +} +.site-footer a:focus-visible { + outline-color: currentColor; +} +.site-footer ul { + display: inline; + margin: 0; + padding-left: 0; + list-style-type: none; +} +.site-footer li { + display: inline-block; + + margin: 0 10px; +} +.site-footer .secondary-line { + opacity: 0.6; +} + +@media (min-width: 999px) +{ + .site-footer ul { + white-space: nowrap; + } + .site-footer li { + margin: 0; + } + .site-footer li + li { + margin-left: 20px; + } +} diff --git a/assets/css/_layout/site-header.css b/assets/css/_layout/site-header.css new file mode 100644 index 00000000..dc025b58 --- /dev/null +++ b/assets/css/_layout/site-header.css @@ -0,0 +1,48 @@ +.site-header { + background-color: #fff; + border-bottom: 1px solid #dfdfdf; + padding-top: 9px; + padding-bottom: 9px; + + position: sticky; + top: 0; + z-index: 1000; +} +.site-header.develop { + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cpattern id='a' viewBox='0 0 8 8' width='24' height='24' patternUnits='userSpaceOnUse'%3E%3Cpath fill='rgba%28255%2C0%2C0%2C0.06%29' d='M0 0h4L0 4zm0 8l8-8v4L4 8z'/%3E%3C/pattern%3E%3C/defs%3E%3Cpath fill='url%28%23a%29' d='M0 0h24v24H0z'/%3E%3C/svg%3E"); + background-size: 30px; +} +.site-header > div { + display: flex; + justify-content: space-between; + align-items: center; +} + +.site-header .site-logo { + background: url('../../images/logo.svg') no-repeat; + background-size: contain; + background-position: center; + height: 38px; + width: 172px; + + display: block; + text-indent: -9999px; + overflow: hidden; +} + +@media (max-width: 767px) +{ + .site-header { + position: static; + padding-top: 14px; + padding-bottom: 9px; + } + .site-header > div { + flex-direction: column; + align-items: unset; + } + + .site-header .site-logo { + margin-bottom: 10px; + } +} diff --git a/assets/css/_layout/site-navigation.css b/assets/css/_layout/site-navigation.css new file mode 100644 index 00000000..7bf4f0c0 --- /dev/null +++ b/assets/css/_layout/site-navigation.css @@ -0,0 +1,117 @@ +.site-navigation { + text-align: right; +} +.site-navigation ul { + margin: 0; + padding-left: 0; + list-style-type: none; +} +.site-navigation li { + display: inline-block; + margin-right: 20px; +} +.site-navigation li:last-child { + margin-right: 0; +} +.site-navigation a { + color: #477E2D; + + display: inline-block; + padding: 6px 0; +} +.site-navigation a:hover { + color: #EE6D00; +} +.site-navigation li.user-item { + color: #111; +} +.site-navigation li.user-item::before { + content: ''; + display: inline-block; + vertical-align: -3px; + width: 1px; + height: 18px; + margin-left: -3px; + margin-right: 17px; + background: rgba(0,0,0, 0.16); +} +.site-navigation li.user-item + li.user-item::before, +.site-navigation li.user-item:first-child::before { + display: none; +} +.site-navigation li.user-item a { + color: inherit; +} + +@media (min-width: 768px) +{ + .site-navigation li.user-submenu { + position: relative; + } + .site-navigation li.user-submenu > span { + padding-top: 6px; + padding-bottom: 6px; + padding-right: 23px; + + background-image: url("data:image/svg+xml,%3Csvg height='16px' width='25px' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolygon fill='%23555' points='23.303,-0.002 12.467,10.834 1.63,-0.002 -0.454,2.082 12.467,15.002 14.551,12.918 25.387,2.082'/%3E%3C/svg%3E"); + background-position: top 52.5% right; + background-repeat: no-repeat; + background-size: 15px; + } + .site-navigation li.user-submenu ul { + position: absolute; + right: 0; + + /* hide visually by default, keep for screen readers */ + top: -9999px; + + padding: 12px 20px; + text-align: left; + background: #fff; + border: 1px solid #cacaca; + border-radius: 7px; + box-shadow: 0 0 10px rgba(0,0,0, 0.14); + + /* keep in sync with ::before height */ + margin-top: 10px; + } + .site-navigation li.user-submenu ul::before { + content: ''; + position: absolute; + left: 0; + right: 0; + top: -10px; + height: 10px; + } + .site-navigation li.user-submenu:hover ul, .site-navigation li.user-submenu:focus-within ul { + top: auto; + } + .site-navigation li.user-submenu li { + white-space: nowrap; + display: block; + margin: 0; + } + .site-navigation li.user-submenu a { + display: block; + } +} + +@media (max-width: 767px) +{ + .site-navigation { + line-height: 1.45em; + text-align: left; + } + .site-navigation li.user-item::before { + display: none; + } + .site-navigation li.user-submenu > span { + display: none; + } + .site-navigation li.user-submenu { + display: block; + } + .site-navigation li.user-submenu ul { + font-size: 0.8em; + } +} diff --git a/assets/css/part/sortable-table.css b/assets/css/_layout/sortable-table.css similarity index 64% rename from assets/css/part/sortable-table.css rename to assets/css/_layout/sortable-table.css index 019700b1..9207e018 100644 --- a/assets/css/part/sortable-table.css +++ b/assets/css/_layout/sortable-table.css @@ -1,13 +1,8 @@ table.sortable th[data-sort] { cursor: pointer; } -table.sortable th[data-sort]:focus { - text-decoration: underline; - outline: 1px dashed #F07F1F; - outline-offset: -2px; -} -table.sortable th[data-sort]:focus:hover { - outline: none; +table.sortable th[data-sort]:focus-visible { + outline-offset: -3px; } table.sortable th[data-sort]::after { diff --git a/assets/css/_layout/ui-block.css b/assets/css/_layout/ui-block.css new file mode 100644 index 00000000..873103f1 --- /dev/null +++ b/assets/css/_layout/ui-block.css @@ -0,0 +1,41 @@ +.ui-block + .ui-block { + margin-top: 40px; +} + +.ui-block-content { + background: #fff; + border: 1px solid #dfdfdf; + border-radius: 7px; + box-shadow: 0 4px 22px rgba(0,0,0, 0.09); + padding: 26px 22px; +} +.ui-block-heading { + width: 200px; + margin-right: 25px; +} + +@media (min-width: 999px) +{ + .ui-block { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + } + .ui-block-content { + flex-grow: 1; + } + .ui-block-heading { + flex-shrink: 0; + } +} + +.ui-block-content > .form-item:first-child, .ui-block-content > .form-columns:first-child { + margin-top: -2px; +} + +.ui-block-content .information { + box-shadow: none; +} +.ui-block-content > .information:first-child { + margin-top: 0; +} diff --git a/assets/css/admin.css b/assets/css/admin.css index 630f8154..deecba2a 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -33,7 +33,7 @@ table tbody tr:hover { background: #ddd; } table td, table th { - padding: 0.26em 0.6em; + padding: 4px 10px; } table th { text-align: left; @@ -50,26 +50,3 @@ table + p { font-style: italic; text-align: center; } - - -/* log */ - -.logs { - height: 500px; - font: 0.8em /1.5em "Source Code Pro", "Consolas", "Inconsolata", monospace; - overflow-y: scroll; - overflow-x: auto; - padding: 1em; - - background: #fff; - box-shadow: 0 0 2px #cdcdcd; -} -.logs pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} -.logs pre b { - display: inline-block; - padding-bottom: 4px; -} diff --git a/assets/css/common.css b/assets/css/common.css deleted file mode 100644 index 4c411807..00000000 --- a/assets/css/common.css +++ /dev/null @@ -1,709 +0,0 @@ -/* general */ - -@font-face { - font-family: "LatoLatinWeb"; - src: url('../fonts/LatoLatin-Regular.woff2'), url('../fonts/LatoLatin-Regular.woff'); -} -@font-face { - font-family: "LatoLatinWeb"; - src: url('../fonts/LatoLatin-Heavy.woff2'); - font-weight: bold; -} -@font-face { - font-family: "LatoLatinWeb"; - src: url('../fonts/LatoLatin-Italic.woff2'); - font-style: italic; -} -@font-face { - font-family: "LatoLatinWeb"; - src: url('../fonts/LatoLatin-HeavyItalic.woff2'); - font-weight: bold; - font-style: italic; -} -@font-face { - font-family: "Inconsolata"; - src: url('../fonts/Inconsolata.woff2'); -} - -body { - font: 17px "LatoLatinWeb", "Liberation Sans", "Arial", sans-serif; - color: #000; - text-rendering: optimizeLegibility; - background: #F7FFF3; - - cursor: default; - overflow-y: scroll; - margin: 0; - padding: 0; -} - -a { - color: #D66200; - transition: linear 0.1s color; - - text-decoration: none; - border: none; - outline: none; -} -a:focus { - text-decoration: underline; - outline: 1px dashed #F07F1F; - outline-offset: 2px; -} -a:hover { - text-decoration: underline; - color: #0084FF; - outline: none; -} -a:active { - color: #64BE00; - outline: none; -} - -.sr-only { - position: absolute !important; - height: 1px; width: 1px; - overflow: hidden; - clip: rect(1px, 1px, 1px, 1px); -} - - -/* site width */ - -.site-width { - max-width: 1000px; - margin-left: auto; - margin-right: auto; - padding-left: 2em; - padding-right: 2em; -} - -@media (max-width: 800px) -{ - .site-width { - padding-left: 5vw; - padding-right: 5vw; - - /* override style from .custom-width and .full-width */ - min-width: 320px !important; - box-sizing: border-box; - } -} - -@media (min-width: 1400px) -{ - .site-width { - max-width: 1100px; - } -} - - -/* site header */ - -.site-header { - background-color: rgba(255,255,255, 0.6); - box-shadow: 0 0 5px rgba(0,0,0, 0.2); - padding-top: 0.6em; - padding-bottom: 0.6em; -} -.site-header.develop { - background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cpattern id='a' viewBox='0 0 8 8' width='24' height='24' patternUnits='userSpaceOnUse'%3E%3Cpath fill='rgba%28255%2C0%2C0%2C0.06%29' d='M0 0h4L0 4zm0 8l8-8v4L4 8z'/%3E%3C/pattern%3E%3C/defs%3E%3Cpath fill='url%28%23a%29' d='M0 0h24v24H0z'/%3E%3C/svg%3E"); - background-size: 30px; -} -.site-header > div { - display: flex; - justify-content: space-between; -} - -.site-header .site-logo { - background: url('../images/logo.svg') no-repeat; - background-size: 238px auto; - background-position: 4px 4px; - width: 245px; - height: 62px; - - display: block; - text-indent: -9999px; - overflow: hidden; -} - -@media (max-width: 800px) -{ - .site-header .site-logo { - width: 72px; - flex-grow: 0; - flex-shrink: 0; - } -} - - -/* site navigation */ - -.site-navigation { - text-align: right; -} -.site-navigation ul { - margin: 0; - padding-left: 0; - list-style-type: none; -} -.site-navigation li { - display: inline-block; -} -.site-navigation li::before { - margin: 0 0.4em; - content: " | "; - color: rgba(0,0,0, 0.1); - font-size: 0.9em; - vertical-align: 1px; -} -.site-navigation li:first-child::before { - display: none; -} -.site-navigation a { - text-shadow: 0 0 2px #eee; - color: #5D9A3F; - - display: inline-block; -} -.site-navigation a:hover { - color: #EE6D00; -} -.site-navigation .user-menu { - margin: 0.55em 0 0.6em; - font-size: 0.8em; - color: #8E8E8E; -} -.site-navigation .user-menu a { - color: inherit; -} -.site-navigation .user-menu .username { - font-weight: bold; -} - -@media (max-width: 800px) -{ - .site-navigation { - line-height: 1.75em; - } - .site-navigation li::before { - display: none; - } - .site-navigation li { - margin-left: 0.7em; - } - - .site-navigation .user-menu { - margin: 0.1em 0 0.2em; - } -} - - -/* site footer */ - -.site-footer { - font-size: 0.8em; - text-align: center; - - border-top: 1px solid rgba(0,0,0, 0.11); - padding: 1em 0 2.6em 0; - margin-top: 2em; -} -.site-footer ul { - display: inline; - margin: 0; - padding-left: 0; - list-style-type: none; -} -.site-footer p, .site-footer li { - display: inline-block; - margin: 0 0.73em; -} - -@media (max-width: 800px) -{ - .site-footer { - line-height: 2em; - } -} - - -/* notification */ - -.notification-wrapper { - display: block; - - text-align: center; - font-weight: bold; -} -.notification { - padding: 0.2em 2em; - - background: rgba(255,247,189, 0.6); - color: #000; -} -.notification.error { - background: rgba(255,0,0, 0.7); - color: #FFF; -} - -@keyframes notification_show -{ - from { - transform: translateY(-450%); - } - to { - transform: translateY(0); - } -} -@keyframes notification_hide -{ - from { - transform: translateY(0); - } - to { - transform: translateY(-450%); - opacity: 0; - } -} - -.JS .notification-wrapper { - position: absolute; - z-index: 100; - top: 76px; - left: 0; - right: 0; - - animation: notification_show 0.55s ease; -} -.JS .notification-wrapper .notification { - display: inline-block; - min-width: 400px; - max-width: 800px; - - font-weight: bold; - padding: 0.8em 1.5em; - box-shadow: 0 0 3px rgba(0,0,0, 0.2); - border-radius: 2px; -} -.JS .notification-wrapper.hidden { - animation: notification_hide 1.25s ease; - animation-fill-mode: forwards; -} - -@media (max-width: 800px) -{ - @keyframes notification_show - { - from { - transform: translateY(200%); - } - to { - transform: translateY(0); - } - } - @keyframes notification_hide - { - from { - transform: translateY(0); - } - to { - transform: translateY(200%); - opacity: 0; - } - } - - .JS .notification-wrapper { - position: fixed; - bottom: 30px; - top: auto; - } - .JS .notification-wrapper .notification { - min-width: auto; - max-width: auto; - border-radius: 0; - - width: 80%; - } -} - - -/* main content */ - -.main-content { - padding-top: 0.5em; - padding-bottom: 0.5em; - margin: auto; - min-height: 70vh; -} - -.main-content > header h1 { - color: #EE6D00; - margin: 1.05rem 0 1.5rem; - font-size: 2em; - text-shadow: 0 0 2px #eee; - - font-weight: normal; -} -.main-content > header h1.separated-title { - display: flex; - flex-direction: column-reverse; -} -.main-content > header h1 .additional { - font-size: 0.4em; - color: #000; - opacity: 0.4; - padding-left: 2px; - padding-top: 0.3rem; -} - -.main-content h2, .main-content .h2 { - font-size: 1.6em; - margin: 1rem 0; - text-shadow: 0 0 2px #eee; - - font-weight: normal; -} - -@media (max-width: 800px) -{ - .main-content { - min-height: auto; - } - - .main-content > header h1 { - font-size: 1.7em; - } -} - - -/* form widgets */ - -input, textarea, select, button, a.button { - font-size: 0.88em; - width: 100%; - padding: 5px 11px; - background: #F3F3F3; - box-shadow: inset 2px 2px 2px #DEDEDE; - border: 1px solid #C2C2C2; - - outline: none; - font: inherit; - color: inherit; - box-sizing: border-box; -} -input:focus, textarea:focus, select:focus, button:focus, a.button:focus { - outline: 1px solid #F07F1F; - - /* needed for WebKit/Blink */ - outline-offset: 0px; -} -input:disabled, textarea:disabled, select:disabled { - opacity: 0.4; - cursor: not-allowed; -} -textarea { - resize: vertical; - min-height: 130px; -} -input[type="number"] { - width: 110px; - text-align: center; - margin-right: 0.2em; -} -@supports (-webkit-app-region: initial) -{ - input[type="search"]::-webkit-search-cancel-button { - /* remove "X" from search input in WebKit */ - -webkit-appearance: none; - } - input::-webkit-calendar-picker-indicator { - /* remove ugly arrow in inputs with datalist attribute in WebKit */ - display: none; - } -} -@supports (-webkit-nbsp-mode: initial) -{ - input[type="search"] { - /* make search input consistent in WebKit, not Blink */ - -webkit-appearance: none; - } -} - -select { - /* hide default select arrow */ - -moz-appearance: none; - -webkit-appearance: none; - appearance: none; - - background-image: url("data:image/svg+xml,%3Csvg height='16px' width='35px' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolygon fill='%23999' points='23.303,-0.002 12.467,10.834 1.63,-0.002 -0.454,2.082 12.467,15.002 14.551,12.918 25.387,2.082'/%3E%3C/svg%3E"); - background-position: center right; - background-repeat: no-repeat; - - /* needed for WebKit */ - border-radius: 0; -} -@supports (-moz-user-select: none) -{ - /* smaller padding for Firefox */ - select { - padding: 4px 7px; - } -} - -button, a.button { - max-width: 40%; - margin: 1em auto 0; - background: #F8FFDF; - color: #F07F1F; - font-weight: bold; - transition: linear 0.1s background-color; - border-color: #DDDDDD; - box-shadow: 0 0 2px rgba(0,0,0, 0.08); - display: block; -} -button:hover:not(:disabled), button:focus, a.button:hover, a.button:focus { - background: #F07F1F; - color: #F2FFC6; -} -button:disabled { - opacity: 0.4; - cursor: not-allowed; -} - -a.button { - text-align: center; - width: auto; - text-decoration: none; - - display: inline-block; - margin: 0; -} - -fieldset { - padding: 0; - border: none; - padding: 0.3em 0; - - /* undo "magic" top margin from fieldset */ - margin: -0.9em 0 0; -} - -@media (max-width: 800px) -{ - button { - max-width: 70%; - } -} - - -/* form widgets — checkboxes + labels */ - -input[type="checkbox"], input[type="radio"] { - width: auto; - padding: 0; - margin: 0; - - /* hide real checkbox and keep it above fake checkbox */ - position: absolute; - z-index: 2; - opacity: 0; -} - -input[type="checkbox"] + label::before, input[type="radio"] + label::before { - content: " "; - - display: inline-block; - width: 17px; - height: 17px; - padding: 0; - - position: absolute; - z-index: 1; - margin-top: 1px; - - /* negative label's padding-left */ - margin-left: calc(-1 * (17px + 0.7em)); - - /* copied from form widgets styles */ - background: #F3F3F3; - box-shadow: inset 2px 2px 2px #DEDEDE; - border: 1px solid #C2C2C2; -} -input[type="radio"] + label::before { - border-radius: 10px; - -moz-outline-radius: 10px; -} -input[type="checkbox"]:focus + label::before, input[type="radio"]:focus + label::before { - /* copied from form widgets styles */ - outline: 1px solid #F07F1F; -} -input[type="checkbox"]:checked + label::before, input[type="radio"]:checked + label::before { - background-position: center; - background-repeat: no-repeat; - background-size: 9px 9px; -} -input[type="checkbox"]:checked + label::before { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 1.852 1.852'%3E%3Cpath fill='%23FF7500' d='M0 0h1.852v1.852H0z'/%3E%3C/svg%3E"); -} -input[type="radio"]:checked + label::before { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 1.852 1.852'%3E%3Ccircle fill='%23FF7500' cx='19.798' cy='24.486' r='.926' transform='translate%28-18.872 -23.56%29'/%3E%3C/svg%3E"); -} - -input[type="checkbox"] + label, input[type="radio"] + label { - /* keep in sync with ::before's margin-left */ - padding-left: calc(17px + 0.7em); -} -input[type="checkbox"]:focus:not(:hover) + label, input[type="radio"]:focus:not(:hover) + label { - outline: 1px dotted #F6BD8D; - outline-offset: 3px; -} - - -/* form widgets — special */ - -input.color { - max-width: 200px; -} -input.color[style] { - box-shadow: none; -} - - -/* form items lists */ - -.form-item { - width: 100%; -} - -.form-item.form-regular { - display: flex; - margin: 0.7em 0; -} -.form-item.form-regular > *:first-child { - width: 40%; -} -.form-item.form-regular > *:last-child { - flex: 1; -} - -.form-item.form-checkbox { - margin: 0.9em 0; - - /* needed for inputs and fake checkboxes with position absolute */ - position: relative; -} - -.form-item label { - display: block; -} -.form-item.form-regular label { - margin-top: 5px; -} - -@media (max-width: 800px) -{ - .form-item.form-regular { - flex-direction: column; - } - .form-item.form-regular > *:first-child { - width: auto; - } - .form-item.form-regular label { - margin-bottom: 0.5em; - } - - .form-item.form-checkbox { - margin: 1.3em 0; - } -} - - -/* radio tables list */ - -.radio-tables-list-item { - background: #FAFFF8; - border: 1px solid #D2D2D2; - padding: 1em 1.4em; - margin: 1.2em 0; -} -.radio-tables-list-item dd { - margin-left: 0; -} - -.radio-tables-list-item:focus-within:not(:hover) { - background: rgba(255,255,255, 1); - border-color: #BABABA; -} - -.radio-tables-list-title a, .radio-tables-list-details dl { - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.radio-tables-list-title a { - display: block; - font-size: 1.6em; -} -.radio-tables-list-title a:focus { - /* make outline visible with overflow */ - outline-offset: -1px; -} - -.radio-tables-list-details dl { - margin: 0.4em 0 0.1em; - font-size: 0.8em; -} -.radio-tables-list-details dt, .radio-tables-list-details dd { - display: inline; -} -.radio-tables-list-details dt::after { - content: ':'; -} -.radio-tables-list-details dt:not(:first-child) { - margin-left: 1em; -} - - -/* information paragraph */ - -.information { - box-shadow: 0 0 2px #eee; - padding: 2em 100px 2em 2em; - background-color: rgba(180,240,255, 0.35); - - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='51.264' height='116.008' viewBox='0 0 6.408 14.501'%3E%3Cpath fill='rgba(0,0,0, 0.12)' d='M4.603 0c-.496 0-.924.165-1.28.497-.353.331-.533.731-.533 1.196 0 .466.179.865.533 1.193.356.328.784.492 1.28.492.497 0 .922-.164 1.275-.492a1.57 1.57 0 0 0 .53-1.193c0-.465-.177-.865-.53-1.196A1.792 1.792 0 0 0 4.603 0zM2.92 4.906c-.404 0-.834.072-1.29.218-.454.144-.931.318-1.43.521l-.2.828c.146-.056.324-.113.53-.176.206-.06.408-.09.604-.09.401 0 .67.066.813.201.141.135.212.375.212.717 0 .192-.022.403-.068.633-.046.228-.104.473-.171.731l-.761 2.688a8.23 8.23 0 0 0-.147.759c-.03.224-.045.444-.045.659 0 .553.203 1.008.61 1.367.409.36.982.539 1.718.539.479 0 .9-.063 1.262-.189.363-.126.847-.308 1.455-.548l.203-.827a3.564 3.564 0 0 1-.506.17 2.4 2.4 0 0 1-.626.097c-.392 0-.67-.065-.827-.194-.16-.129-.239-.372-.239-.728 0-.141.024-.351.073-.626.05-.275.105-.521.167-.736l.757-2.68c.074-.247.125-.516.152-.811.027-.294.041-.5.041-.617 0-.564-.199-1.023-.594-1.376-.397-.352-.961-.53-1.692-.53z'/%3E%3C/svg%3E"); - background-position: right 2em center; - background-repeat: no-repeat; - background-size: 27px auto; - /* https://icons8.com/icon/76/info */ -} -.information a { - color: #0088C6; -} - -@media (max-width: 800px) -{ - .information { - padding: 1.5em 1.2em; - font-size: 0.9em; - background-image: none; - } -} - - -/* search form */ - -.search-form { - display: flex; - margin: 1em 0 2em; - - /* needed for WebKit, not Blink */ - align-items: center; -} -.search-form button { - margin: 0; - margin-left: 0.6em; - width: 80px; -} diff --git a/assets/css/dark-error.css b/assets/css/dark-error.css index ad3eae45..4b3e7783 100644 --- a/assets/css/dark-error.css +++ b/assets/css/dark-error.css @@ -15,7 +15,7 @@ body { @media (max-height: 600px) and (max-width: 400px) { body { - font-size: 0.9em; + font-size: 15em; } } @@ -41,9 +41,10 @@ body { text-indent: -9999px; overflow: hidden; } -.logo a:focus { - outline: none; - background-color: rgba(255,255,255, 0.05); +.logo a:focus-visible { + color: inherit; + outline: 2px solid currentColor; + outline-offset: 3px; } @media (max-height: 600px) @@ -77,5 +78,5 @@ body { .content h1 { font-weight: normal; font-size: 1.5em; - margin: 0.8em 0; + margin: 14px 0; } diff --git a/assets/css/all-radio-tables.css b/assets/css/general/all-radio-tables.css similarity index 78% rename from assets/css/all-radio-tables.css rename to assets/css/general/all-radio-tables.css index b3654a1e..a70ff7dd 100644 --- a/assets/css/all-radio-tables.css +++ b/assets/css/general/all-radio-tables.css @@ -1,11 +1,11 @@ .all-radio-tables-navigation { padding-left: 0; - margin: 1.5em 0; + margin: 25px 0; text-align: center; } .all-radio-tables-navigation li { display: inline-block; - margin: 0 1em; + margin: 0 10px; } .all-radio-tables-navigation li a { color: #5D9A3F; @@ -14,10 +14,10 @@ font-weight: bold; } -@media (max-width: 800px) +@media (max-width: 767px) { .all-radio-tables-navigation li { display: block; - margin: 0.4em 0; + margin: 15px 0; } } diff --git a/assets/css/general/homepage.css b/assets/css/general/homepage.css new file mode 100644 index 00000000..b78a7e7a --- /dev/null +++ b/assets/css/general/homepage.css @@ -0,0 +1,157 @@ +.homepage-logo { + display: none; +} + +.homepage-section { + text-align: center; +} +.homepage-section + .homepage-section { + margin-top: 50px; +} +.homepage-section h2 { + font-size: 1.6em; + color: #4A673C; + margin-bottom: 30px; +} + +.homepage-section-about { + max-width: 600px; + margin-left: auto; + margin-right: auto; +} +.homepage-section-about p { + line-height: 1.6em; + margin-top: 40px; + margin-bottom: 25px; +} + +.homepage-radio-tables-list { + list-style: none; + padding-left: 0; + + display: grid; + grid-gap: 20px; + + text-align: left; + + margin-top: 10px; + margin-bottom: 35px; +} +.homepage-radio-tables-list li { + display: flex; + flex-direction: column; + justify-content: space-between; + + height: 100%; + box-sizing: border-box; + overflow-x: hidden; + + background: #fff; + border: 1px solid #dfdfdf; + border-radius: 7px; + box-shadow: 0 0 6px rgba(0,0,0, 0.09); + padding: 15px 15px; + + outline: 0 solid transparent; + transition: linear 0.1s outline; +} +.homepage-radio-tables-list li:hover { + background: #fcfcfc; + border-color: #e6d1be; +} +.homepage-radio-tables-list li:focus-within { + outline: 2px solid rgb(240,127,31, 0.6); + border-color: #F07F1F; +} +.homepage-radio-tables-list a { + display: block; + text-overflow: ellipsis; + white-space: nowrap; + overflow-x: hidden; +} +.homepage-radio-tables-list .date { + display: block; + color: #000; + opacity: 0.9; + margin-top: 8px; + font-size: 0.8em; +} + +.search-form { + margin-top: 20px; +} + +@media (min-width: 768px) +{ + .site-header .site-logo { + visibility: hidden; + } + + .homepage-radio-tables-list { + grid-template-columns: 1fr 1fr 1fr; + } + + .homepage-logo { + display: block; + height: 80px; + margin: 40px auto 0; + } + .homepage-logo .line-1, .homepage-logo .line-2, .homepage-logo .line-3 { + animation: logo-line-fade 6s; + animation-iteration-count: infinite; + animation-fill-mode: both; + } + .homepage-logo .line-1 { + animation-delay: 1s; + } + .homepage-logo .line-2 { + animation-delay: 2s; + } + .homepage-logo .line-3 { + animation-delay: 3s; + } + .homepage-logo .dot { + animation: logo-dot-fade 2s; + animation-iteration-count: infinite; + animation-delay: 0.45s; + animation-fill-mode: both; + } + + @keyframes logo-line-fade + { + 0% { + filter: opacity(0.1); + } + 5% { + filter: opacity(1); + } + 60% { + filter: opacity(1); + } + 100% { + filter: opacity(0.1); + } + } + @keyframes logo-dot-fade + { + 0% { + filter: opacity(0.2); + } + 10% { + filter: opacity(1); + } + 90% { + filter: opacity(1); + } + 100% { + filter: opacity(0.2); + } + } + + @media (prefers-reduced-motion) + { + .homepage-logo .line-1, .homepage-logo .line-2, .homepage-logo .line-3, .homepage-logo .dot { + animation: none; + } + } +} diff --git a/assets/css/static-page.css b/assets/css/general/static-page.css similarity index 76% rename from assets/css/static-page.css rename to assets/css/general/static-page.css index edbf1ef3..08af6be2 100644 --- a/assets/css/static-page.css +++ b/assets/css/general/static-page.css @@ -10,15 +10,15 @@ } .terms-of-service-page > *:last-child { flex-basis: 310px; - padding-left: 3em; + padding-left: 50px; } .terms-of-service-page ol { - padding-left: 1.5em; + padding-left: 27px; } .terms-of-service-page ol li { - padding-left: 0.3em; - margin: 0.3em 0; + padding-left: 5px; + margin: 5px 0; } .terms-of-service-page h2 { margin-top: 0; @@ -37,22 +37,20 @@ } .plain-text-page p, .plain-text-page ol { line-height: 1.6em; - margin: 0.6em 0; + margin: 10px 0; } .plain-text-page ol { - padding-left: 1.5em; + padding-left: 25px; } .plain-text-page li { - margin: 0.2em 0; + margin: 3px 0; } .plain-text-page p.p-for-button { line-height: inherit; - margin: 1.3em 0; + margin: 25px 0; text-align: center; } .plain-text-page a.button { max-width: none; - padding-left: 1.4em; - padding-right: 1.4em; margin-bottom: -1px; } diff --git a/assets/css/homepage.css b/assets/css/homepage.css deleted file mode 100644 index cb3549b9..00000000 --- a/assets/css/homepage.css +++ /dev/null @@ -1,83 +0,0 @@ -.homepage { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - align-items: start; - margin: 1em 0; -} -.homepage > * { - width: 48.3%; - box-sizing: border-box; - margin: 1em 0; -} - -.homepage section { - background: rgba(255,255,255, 0.4); - box-shadow: 0 0 2px rgba(0,0,0, 0.15); - padding: 0.2em 1.3em 0.4em; -} -.homepage section h2 { - font-size: 1.4em; - padding-bottom: 4px; - border-bottom: 1px solid rgba(200,200,200, 0.3); - color: #4A673C; -} -.homepage section p { - line-height: 1.35em; -} - -.homepage-radio-tables-list { - list-style: none; - padding-left: 0; -} -.homepage-radio-tables-list li { - margin: 0.3em 0; - - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; -} -.homepage-radio-tables-list a { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - padding: 0.05em 0; - margin-right: 1em; - display: block; - width: 100%; -} -.homepage-radio-tables-list span { - white-space: nowrap; - text-align: right; - color: rgba(0,0,0, 0.4); - font-size: 0.7em; -} -.homepage-radio-tables-list a:hover + span, .homepage-radio-tables-list a:focus + span { - color: rgba(0,0,0, 0.8); -} - -.homepage .search-form { - margin: 1.3em 0; -} - -@media (max-width: 800px) -{ - .homepage { - flex-direction: column; - } - .homepage > * { - /* keep this to properly cut off radio tables names */ - width: 100%; - } -} - -@media (max-width: 450px) -{ - .homepage-radio-tables-list li { - flex-direction: column; - align-items: start; - margin: 0.5em 0; - } -} diff --git a/assets/css/layout.css b/assets/css/layout.css new file mode 100644 index 00000000..b4a08a09 --- /dev/null +++ b/assets/css/layout.css @@ -0,0 +1,21 @@ +@import './_layout/global.css'; +@import './_layout/site-header.css'; +@import './_layout/site-navigation.css'; +@import './_layout/site-footer.css'; +@import './_layout/main-content.css'; + +@import './_layout/form-widgets.css'; +@import './_layout/form-widgets-extra.css'; +@import './_layout/form-layout.css'; +@import './_layout/form-action-button.css'; + +@import './_layout/ui-block.css'; +@import './_layout/notification.css'; +@import './_layout/information.css'; + +@import './_layout/radio-tables-list.css'; +@import './_layout/search-form.css'; +@import './_layout/remove-dialog.css'; +@import './_layout/sortable-table.css'; + +@import './_layout/ckeditor-overrides.css'; diff --git a/assets/css/my-account-settings.css b/assets/css/my-account-settings.css deleted file mode 100644 index a4f2c505..00000000 --- a/assets/css/my-account-settings.css +++ /dev/null @@ -1,13 +0,0 @@ -@import './part/ckeditor.css'; - - -.my-account-details > * { - display: flex; -} -.my-account-details > * > * { - flex: 1; - margin: 0.3em 0; -} -.my-account-details dt::after { - content: ":"; -} diff --git a/assets/css/part/radio-table-status.css b/assets/css/part/radio-table-status.css deleted file mode 100644 index d14ad57b..00000000 --- a/assets/css/part/radio-table-status.css +++ /dev/null @@ -1,26 +0,0 @@ -/* radio table status chooser */ - -.radio-table-status .form-item { - padding: 1.2em 0 1.1em 78px; - box-sizing: border-box; - background-repeat: no-repeat; - background-position: left 2px center; - background-size: 50px; - /* https://icomoon.io/ */ -} -.radio-table-status .form-item:nth-of-type(1) { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='512' height='512'%3E%3Cpath d='M256.005.004C114.623.004.009 114.618.009 256c0 141.384 114.614 255.996 255.996 255.996 141.384 0 255.996-114.613 255.996-255.996C512.001 114.618 397.388.004 256.005.004zm144.846 341.328c4.576-21.575 7.464-44.475 8.404-68.265h67.97a219.61 219.61 0 0 1-16.363 68.265H400.85zM111.16 170.668c-4.576 21.575-7.465 44.475-8.404 68.266H34.787a219.656 219.656 0 0 1 16.361-68.266h60.012zm254.729 0c5.127 21.846 8.212 44.721 9.216 68.266H273.071v-68.266h92.817zm-92.817-34.133V36.623c7.781 2.264 15.49 6.064 23.055 11.397 14.177 9.995 27.74 25.39 39.221 44.525 7.937 13.227 14.801 27.954 20.531 43.99h-82.806zm-96.408-43.991c11.482-19.135 25.043-34.532 39.221-44.525 7.564-5.333 15.274-9.133 23.054-11.397v99.913h-82.806c5.731-16.035 12.595-30.762 20.531-43.99zm62.275 78.124v68.266H136.907c1.002-23.544 4.089-46.419 9.215-68.266h92.817zM51.149 341.332a219.643 219.643 0 0 1-16.361-68.265h67.969c.939 23.791 3.828 46.69 8.404 68.265H51.149zm85.758-68.265h102.032v68.265h-92.817c-5.126-21.845-8.212-44.721-9.215-68.265zm102.032 102.397v99.913c-7.78-2.264-15.49-6.065-23.054-11.397-14.178-9.995-27.74-25.392-39.221-44.525-7.937-13.228-14.8-27.956-20.532-43.99h82.807zm96.408 43.991c-11.481 19.134-25.044 34.531-39.221 44.525-7.565 5.332-15.274 9.132-23.055 11.397v-99.913h82.807c-5.731 16.035-12.595 30.762-20.532 43.99zm-62.275-78.123v-68.265h102.033c-1.002 23.544-4.089 46.421-9.216 68.265h-92.817zm136.183-102.398c-.94-23.791-3.828-46.691-8.404-68.266h60.012a219.68 219.68 0 0 1 16.363 68.266h-67.97zm33.765-102.399h-51.146c-9.94-31.339-23.667-58.822-40.168-80.76 22.685 10.848 43.23 25.395 61.18 43.344 11.456 11.454 21.526 23.966 30.134 37.416zM99.125 99.12c17.951-17.951 38.495-32.496 61.18-43.344-16.502 21.938-30.226 49.421-40.168 80.761H68.992c8.607-13.45 18.678-25.961 30.133-37.416zM68.992 375.464h51.145c9.941 31.339 23.667 58.822 40.169 80.762-22.685-10.848-43.23-25.395-61.18-43.346-11.456-11.454-21.527-23.966-30.134-37.416zm343.894 37.416c-17.95 17.95-38.495 32.498-61.18 43.346 16.502-21.938 30.228-49.422 40.168-80.762h51.146c-8.608 13.45-18.678 25.961-30.134 37.416z'/%3E%3C/svg%3E"); -} -.radio-table-status .form-item:nth-of-type(2) { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='512' height='512'%3E%3Cpath d='M341.334 370.935v-23.457c31.331-17.656 56.887-61.675 56.887-105.7 0-70.69 0-127.996-85.33-127.996s-85.33 57.305-85.33 127.996c0 44.026 25.555 88.044 56.887 105.7v23.457c-96.487 7.887-170.661 55.296-170.661 112.614h398.209c0-57.317-74.175-104.726-170.661-112.614z'/%3E%3Cpath d='M145.428 381.92c24.582-16.067 55.144-28.282 88.793-35.738-6.69-7.904-12.759-16.721-17.996-26.231-13.517-24.548-20.662-51.58-20.662-78.173 0-38.24 0-74.358 13.6-103.891 13.203-28.668 36.945-46.441 70.763-53.105-7.517-33.99-27.528-56.331-80.807-56.331-85.33 0-85.33 57.305-85.33 127.997 0 44.025 25.556 88.044 56.887 105.7v23.457C74.189 293.492.015 340.901.015 398.219h123.999c6.452-5.738 13.598-11.187 21.417-16.298z'/%3E%3C/svg%3E"); -} -.radio-table-status .form-item:nth-of-type(3) { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='512' height='512'%3E%3Cpath d='M472.971 23.025c-9.373-9.372-24.568-9.372-33.941 0L337.948 124.107c-25.969-7.877-53.474-12.111-81.948-12.111-111.659 0-208.441 65.021-256 160 20.561 41.062 50.324 76.52 86.511 103.548L7.03 455.025c-9.373 9.373-9.373 24.568 0 33.941 4.686 4.687 10.828 7.03 16.97 7.03s12.284-2.343 16.971-7.029l432-432c9.372-9.373 9.372-24.569 0-33.942zM208 175.996c21.12 0 39.041 13.647 45.46 32.598l-60.862 60.862c-18.951-6.419-32.598-24.34-32.598-45.46 0-26.51 21.49-48 48-48zm-152.942 96c19.146-30.262 44.637-55.962 74.717-75.148a231.722 231.722 0 0 1 5.931-3.65c-4.981 13.664-7.706 28.411-7.706 43.798 0 27.445 8.643 52.869 23.35 73.709l-30.462 30.462c-26.223-18.421-48.601-41.941-65.83-69.171zM384 236.996c0-13.583-2.128-26.667-6.051-38.949L217.045 358.951A127.894 127.894 0 0 0 256 364.996c70.691 0 128-57.309 128-128z'/%3E%3Cpath d='M415.013 160.983l-34.681 34.681c.632.393 1.265.784 1.893 1.184 30.081 19.187 55.571 44.887 74.717 75.148-19.146 30.261-44.637 55.961-74.718 75.148-37.797 24.109-81.445 36.852-126.224 36.852-19.332 0-38.451-2.38-56.981-7.02l-38.447 38.447c29.859 10.731 61.975 16.573 95.428 16.573 111.655 0 208.438-65.021 256-160-22.511-44.958-56.059-83.198-96.987-111.013z'/%3E%3C/svg%3E"); -} - -@media (max-width: 800px) -{ - .radio-table-status input[type="radio"] + label::before { - top: 40%; - } -} diff --git a/assets/css/part/tabbed-ui.css b/assets/css/part/tabbed-ui.css deleted file mode 100644 index 7bbc3175..00000000 --- a/assets/css/part/tabbed-ui.css +++ /dev/null @@ -1,107 +0,0 @@ -.JS .tabbed-ui > div { - background: #FAFFF8; - padding: 0.5em 1.2em 0.8em; - border: 1px solid #D2D2D2; -} -.JS .tabbed-ui > div:not(.tabbed-ui-current) { - display: none; -} - -.tabbed-ui-navigator { - padding-left: 0; - list-style-type: none; - margin: 0 0 -1px; - - vertical-align: bottom; -} -@supports (-webkit-nbsp-mode: initial) -{ - .tabbed-ui-navigator { - /* needed for WebKit, not Blink */ - margin-bottom: 0; - } -} -.tabbed-ui-navigator li { - display: inline; -} -.tabbed-ui-navigator button { - margin-right: 0.4em; - padding: 0.2em 0.9em; - background: #FAFFF8; - border: 1px solid #D2D2D2; - opacity: 0.63; - - white-space: nowrap; - display: inline-block; - outline-style: none; - box-shadow: none; - color: inherit; - transition-property: opacity; - width: auto; - font-weight: normal; - margin-top: 0; -} -.tabbed-ui-navigator button:hover { - background: #fff; - color: inherit; -} -.tabbed-ui-navigator button:focus:not(:hover):not(:active) { - opacity: 1; - outline-style: dashed; -} -.tabbed-ui-navigator button.tabbed-ui-current { - opacity: 1; - border-bottom-color: #FAFFF8; - padding-top: 0.6em; - background: #FAFFF8; -} - -@media (max-width: 800px) -{ - .tabbed-ui-navigator { - margin-bottom: 0.4em; - } - - .tabbed-ui-navigator button { - margin-bottom: 0.5em; - } - .tabbed-ui-navigator button, .tabbed-ui-navigator button.tabbed-ui-current { - padding: 0.2em 0.9em; - border: 1px solid #D2D2D2; - } - .tabbed-ui-navigator li:last-child button { - margin-right: 0; - } - .tabbed-ui-navigator button.tabbed-ui-current { - font-weight: bold; - border-color: #aaa; - } -} - - -/* information paragraph in tabbed UI */ - -.JS .tabbed-ui .information { - box-shadow: none; - padding-top: 1.5em; - padding-bottom: 1.5em; -} - -@media (max-width: 800px) -{ - .JS .tabbed-ui .information { - padding-top: 0.9em; - padding-bottom: 0.9em; - } -} - - -/* button in tabbed UI */ - -.JS .tabbed-ui > div > form > button { - margin-top: 1.5em; - margin-bottom: 0.7em; -} -.JS .tabbed-ui + button { - margin-top: 1.3em; -} diff --git a/assets/css/radio-station-edit-add.css b/assets/css/radio-station/edit-add.css similarity index 76% rename from assets/css/radio-station-edit-add.css rename to assets/css/radio-station/edit-add.css index 77d65284..f1ff8346 100644 --- a/assets/css/radio-station-edit-add.css +++ b/assets/css/radio-station/edit-add.css @@ -1,10 +1,17 @@ +.form-columns-with-number-first { + grid-template-columns: 1fr 1fr 1fr; +} +.form-columns-with-number-first *:last-child { + grid-column: 2 / -1; +} + .radio-station-disabled-column input:not(:focus), .radio-station-disabled-column select:not(:focus), .radio-station-disabled-column textarea:not(:focus) { opacity: 0.5; } .radio-station-disabled-column label, .radio-station-disabled-column .unit-label { - opacity: 0.6; + opacity: 0.65; text-decoration: line-through; text-decoration-color: rgba(0,0,0, 0.5); } diff --git a/assets/css/radio-table-create.css b/assets/css/radio-table-create.css deleted file mode 100644 index 2a115aa2..00000000 --- a/assets/css/radio-table-create.css +++ /dev/null @@ -1,2 +0,0 @@ -@import './part/radio-table-status.css'; -@import './part/ckeditor.css'; diff --git a/assets/css/radio-table-remove.css b/assets/css/radio-table-remove.css deleted file mode 100644 index 9fb7aa8a..00000000 --- a/assets/css/radio-table-remove.css +++ /dev/null @@ -1,21 +0,0 @@ -/* radio station list */ - -.radio-stations-remove { - max-height: 430px; - overflow-y: auto; - box-sizing: border-box; - padding: 0.05em 1em; - background: #F9FCF9; - border: 1px solid #D6EAD0; -} -.radio-stations-remove fieldset { - margin: 0; -} - - -/* radio table warning */ - -.radio-table-remove-confirm { - border: 3px dashed red; - padding: 0 1em; -} diff --git a/assets/css/radio-table-settings.css b/assets/css/radio-table-settings.css deleted file mode 100644 index 746b7796..00000000 --- a/assets/css/radio-table-settings.css +++ /dev/null @@ -1,108 +0,0 @@ -@import '~huebee/huebee.css'; - -@import './part/radio-table-status.css'; -@import './part/ckeditor.css'; - - -/* columns UI */ - -.JS .radio-table-columns { - width: 80%; - margin: 1.2em auto; -} -.JS .radio-table-columns .form-item { - background: #F3F3F3; - box-shadow: inset 2px 2px 2px #DEDEDE; - border: 1px solid #C2C2C2; - padding: 0.5em 1em; - box-sizing: border-box; - align-items: center; -} -.JS .radio-table-columns .form-item label { - margin-top: 0; -} -.JS .radio-table-columns .form-item > :last-child { - text-align: right; -} -.JS .radio-table-columns .form-item.hidden-column { - background: #FBFBFB; - box-shadow: inset 2px 2px 2px #efefef; - border-color: #dfdfdf; -} -.JS .radio-table-columns .form-item.hidden-column label { - text-decoration: line-through; - opacity: 0.5; -} -.JS .radio-table-columns .form-item button { - display: inline; - width: auto; - height: 2em; - min-width: 85px; - margin: 0 0 0 0.6em; - white-space: nowrap; -} -.JS .radio-table-columns .form-item button:first-of-type { - margin-left: 0; -} - -@media (max-width: 800px) -{ - .JS .radio-table-columns { - width: auto; - } - .JS .radio-table-columns .form-item.form-regular { - flex-direction: row; - } - .JS .radio-table-columns .form-item.form-regular label { - margin-bottom: 0; - } -} - -@media (max-width: 700px) -{ - .JS .radio-table-columns .form-item.form-regular { - flex-direction: column; - } - .JS .radio-table-columns .form-item.form-regular label { - margin-bottom: 0.7em; - } - .JS .radio-table-columns .form-item > :last-child { - text-align: center; - white-space: nowrap; - } -} -@media (max-width: 440px) -{ - .JS .radio-table-columns .form-item button { - min-width: auto; - height: auto; - font-size: 0.8em; - padding-top: 0.6em; - padding-bottom: 0.6em; - } -} - - -/* export list */ - -.radio-table-export-list { - list-style: none; - padding-left: 0; -} -.radio-table-export-list li { - margin-top: 0.8em; -} -.radio-table-export-list a { - margin-right: 0.7em; - width: 250px; - vertical-align: baseline; -} - -@media (max-width: 800px) -{ - .radio-table-export-list a.button { - margin: 0.23em 0 0.7em 0; - width: 100%; - max-width: 100%; - } -} diff --git a/assets/css/radio-table-show.css b/assets/css/radio-table-show.css deleted file mode 100644 index 95c635ac..00000000 --- a/assets/css/radio-table-show.css +++ /dev/null @@ -1,421 +0,0 @@ -/* container */ - -.radio-table-container { - /* reset visual vertical padding of table cells */ - margin: 0 -0.4em; - - overflow-x: auto; - - position: relative; - background-size: 40px 100%; - background-repeat: no-repeat; -} - -.radio-table-container.overflow-right { - background-image: linear-gradient(90deg, rgba(0,0,0, 0), rgba(250,230,205, 0.4)); - background-position: right; -} -.radio-table-container.overflow-left { - background-image: linear-gradient(90deg, rgba(250,230,205, 0.4), rgba(0,0,0, 0)); - background-position: left; -} -.radio-table-container.overflow-left.overflow-right { - background-image: linear-gradient(90deg, rgba(0,0,0, 0), rgba(250,230,205, 0.4)), - linear-gradient(90deg, rgba(250,230,205, 0.4), rgba(0,0,0, 0)); - background-position: right, left; -} - -@media (max-width: 800px) -{ - .radio-table-container { - margin-left: -5vw; - margin-right: -5vw; - } -} - - -/* the table */ - -.radio-table { - width: 100%; - border-collapse: collapse; - - font-size: 0.96em; -} - -.radio-table td, .radio-table th { - padding: 0.31em 0.5em; - text-align: left; -} -.radio-table th { - padding-bottom: 0.5em; - color: #477E2D; - font-weight: normal; - white-space: nowrap; -} -.radio-table td { - border-top: 1px solid rgba(0,0,0, 0.05); -} -@supports (-webkit-nbsp-mode: initial) -{ - /* color for WebKit, not Blink */ - .radio-table td { - border-top: 1px solid rgba(0,0,0, 0.03); - } -} - -.radio-table tbody tr { - transition: background 0.1s; -} -.radio-table tbody tr:hover, .radio-table tbody tr:focus-within { - background: rgba(250,230,205, 0.8); -} - -.radio-table a { - display: block; -} - - -/* special cells */ - -.radio-table tr.text-bold { - font-weight: bold; -} -.radio-table tr.text-italic { - font-style: italic; -} -.radio-table tr.text-strikethrough { - text-decoration: line-through; -} -.radio-table tr.background-1 { - background: rgba(253,198,198, 0.65) !important; -} -.radio-table tr.background-2 { - background: rgba(175,255,150, 0.65) !important; -} -.radio-table tr.background-3 { - background: rgba(156,230,255, 0.65) !important; -} -.radio-table tr.background-4 { - background: rgba(255,255,120, 0.65) !important; -} -.radio-table tr.background-5 { - background: rgba(255,177,255, 0.65) !important; -} - -.radio-table .quality { - color: rgba(0,0,0, 0); - display: block; -} -.radio-table .quality::selection { - opacity: 0; -} -.radio-table .quality::before { - text-shadow: 0 0 2px #CCC; - margin-right: -10px; - letter-spacing: 2px; -} -.radio-table .quality.quality-5::before { - color: #0BB300; - content: "●●●●●"; -} -.radio-table .quality.quality-4::before { - color: #A8D300; - content: "●●●●"; -} -.radio-table .quality.quality-3::before { - color: #BDAC00; - content: "●●●"; -} -.radio-table .quality.quality-2::before { - color: #FF5E00; - content: "●●"; -} -.radio-table .quality.quality-1::before { - color: #FF0017; - content: "●"; -} - -.radio-table .rds { - white-space: pre; - background: rgba(160,160,160, 0.2); - font-family: "Inconsolata", "Consolas", "Source Code Pro", "Lucida Console", monospace; - color: black; - padding: 1px 3px; - transition: background 0.2s, color 0.2s; - - /* reset styles from radio station appearance settings */ - font-weight: normal; - font-style: normal; - text-decoration: none; -} -.radio-table .rds[tabindex]:hover, .radio-table .rds:focus { - background: rgba(0,0,0, 0.7); - color: #fff; - outline: none; -} - -.radio-table small { - font-size: 0.7em; - vertical-align: 2px; -} -.radio-table abbr { - text-decoration: none; - border-bottom: 1px dotted #ccc; -} -.radio-table time { - white-space: nowrap; -} - -.radio-table a.external { - display: inline-block; - text-decoration: none; -} -.radio-table a.external svg { - height: 0.9em; - vertical-align: -1px; - padding: 0 2px; - fill: currentColor; -} - -.radio-table .comment-collapsed svg { - height: 1em; - vertical-align: -2px; - padding: 0 2px; - fill: currentColor; -} - - -/* details section */ - -.radio-table-details { - margin-top: 1.8em; - line-height: 1.5em; - - width: 100%; -} -.radio-table-details p { - margin: 0.4em 0; -} - -.radio-table-details dl { - display: flex; - align-items: center; - width: 100%; - - border-top: 1px solid rgba(0,0,0, 0.05); - font-size: 0.9em; - margin-top: 1.5em; - padding: 0.8em 2em 0 2em; - box-sizing: border-box; -} -.radio-table-details dl > * { - display: flex; - align-items: center; - flex: 1; -} -.radio-table-details dl > * > * { - flex: 1; -} -.radio-table-details dt, .radio-table-details dd { - padding: 0 0.5em; - margin: 0; -} -.radio-table-details dt { - text-align: right; -} -.radio-table-details dt::after { - content: ":"; -} -.radio-table-details dd { - text-align: left; - border-right: 1px solid rgba(0,0,0, 0.05); -} -.radio-table-details dd:last-child { - border-right: none; -} - -@media (max-width: 800px) -{ - .radio-table-details dl { - flex-direction: column; - align-items: stretch; - padding-left: 0; - padding-right: 0; - } - - .radio-table-details dt, .radio-table-details dd { - padding-top: 0.15em; - padding-bottom: 0.15em; - } - .radio-table-details dd { - border-right: none; - } -} - - -/* RDS popup */ - -@keyframes rds_popup_show -{ - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -.rds-popup { - background: #fff; - color: #000; - border: 1px solid #eee; - box-shadow: 0 0 3px rgba(0,0,0, 0.4); - width: 430px; - - animation-name: rds_popup_show; - animation-duration: 0.2s; - animation-timing-function: ease-out; - animation-delay: 0.15s; - animation-fill-mode: both; -} - -.rds-popup h2 { - background: #eee; - color: #555; - font-size: 0.9em; - text-transform: uppercase; - text-align: center; - padding: 0.1em 0; - margin: 0; -} - -.rds-popup dl { - font-size: 0.8em; - margin: 0.9em 1.2em; -} -.rds-popup dl > div { - display: flex; - flex: 1; - width: 100%; -} -.rds-popup dl > div[hidden] { - /* weird fix of weird bug */ - display: none; -} -.rds-popup dt { - min-width: 12%; - font-weight: bold; - - /* keep in sync with div's margin */ - padding: 0.3em 0; -} -.rds-popup dt::after { - content: ":"; -} -.rds-popup dd { - margin-left: 0; -} - -.rds-popup dd span { - display: inline; - margin-right: 8px; - - white-space: pre; - background: rgba(160,160,160, 0.2); - font-family: "Inconsolata", "Consolas", "Source Code Pro", "Lucida Console", monospace; -} -.rds-popup dd.rds-wrap span { - white-space: pre-wrap; -} -.rds-popup dd div { - margin: 0.3em 0; -} - - -/* number indentation in tables */ - -.number-indent-1 { - padding-left: 1ch; -} -.number-indent-2 { - padding-left: 2ch; -} -.number-indent-3 { - padding-left: 3ch; -} -.number-indent-4 { - padding-left: 4ch; -} -.number-indent-5 { - padding-left: 5ch; -} - - -/* customized radio table page */ - -.full-width .site-width { - max-width: 100%; -} -@media (min-width: 900px) -{ - .full-width .site-width, .custom-width .site-width { - padding-left: 4vw; - padding-right: 4vw; - } -} - -.customized-radio-table .site-header { - background-color: rgba(255,255,255, 0.9); -} - -.customized-radio-table .radio-table tbody tr:hover, .customized-radio-table .radio-table tbody tr:focus-within { - background: rgba(200,200,200, 0.4); -} -.customized-radio-table .radio-table .quality::before { - text-shadow: 0 0 1px #000; -} -.customized-radio-table .radio-table .rds { - background: rgba(200,200,200, 0.8); -} -.customized-radio-table .radio-table .rds[tabindex]:hover, .customized-radio-table .radio-table .rds:focus { - background: rgba(0,0,0, 0.55); -} -.customized-radio-table table.sortable th[data-sort]:focus { - outline-color: currentColor; -} - -.customized-radio-table .customizable-color h1 { - color: inherit; - text-shadow: none; -} -.customized-radio-table .customizable-color .radio-table th { - color: inherit; - opacity: 0.8; - text-shadow: 0 0 0 rgba(0,0,0, 0.7); -} -.customized-radio-table .customizable-color .radio-table td { - border-top: 0; - box-shadow: inset 0 2px 2px -3px currentColor; -} -@supports (-moz-user-select: none) -{ - .customized-radio-table .customizable-color .radio-table td { - box-shadow: inset 0 3px 3px -5px currentColor; - } -} -.customized-radio-table .customizable-color a { - color: inherit; - text-decoration: underline; - text-decoration-skip-ink: none; -} -.customized-radio-table .customizable-color a:focus { - outline-color: currentColor; -} - -@media (max-width: 800px) -{ - .customized-radio-table .customizable-background { - background-image: none !important; - } -} diff --git a/assets/css/radio-table/_show/appearance-customization.css b/assets/css/radio-table/_show/appearance-customization.css new file mode 100644 index 00000000..d03045a5 --- /dev/null +++ b/assets/css/radio-table/_show/appearance-customization.css @@ -0,0 +1,65 @@ +.full-width .site-width { + max-width: 100%; +} +@media (min-width: 900px) +{ + .full-width .site-width, .custom-width .site-width { + padding-left: 4vw; + padding-right: 4vw; + } +} + +.customized-radio-table .site-header { + background-color: rgba(255,255,255, 0.9); +} +.customized-radio-table .site-footer { + background: rgba(30,30,30, 0.7); +} +.customized-radio-table .site-footer > div::before { + opacity: 0.3; +} + +.customized-radio-table .radio-table tbody tr:hover, .customized-radio-table .radio-table tbody tr:focus-within { + background: rgba(200,200,200, 0.4); +} +.customized-radio-table .radio-table .quality::before { + text-shadow: 0 0 1px #000; +} +.customized-radio-table .radio-table .rds { + background: rgba(200,200,200, 0.8); +} +.customized-radio-table .radio-table .rds[tabindex]:hover, .customized-radio-table .radio-table .rds:focus { + background: rgba(0,0,0, 0.55); +} +.customized-radio-table table.sortable th[data-sort]:focus { + outline-color: currentColor; +} + +.customized-radio-table .customizable-color h1 { + color: inherit; + text-shadow: none; +} +.customized-radio-table .customizable-color .radio-table th { + color: inherit; + opacity: 0.8; + text-shadow: 0 0 0 rgba(0,0,0, 0.7); +} +.customized-radio-table .customizable-color .radio-table td { + border-top: 0; + box-shadow: inset 0 2px 2px -3px currentColor; +} +.customized-radio-table .customizable-color a { + color: inherit; + text-decoration: underline; + text-decoration-skip-ink: none; +} +.customized-radio-table .customizable-color a:focus { + outline-color: currentColor; +} + +@media (max-width: 767px) +{ + .customized-radio-table .customizable-background { + background-image: none !important; + } +} diff --git a/assets/css/radio-table/_show/number-indent.css b/assets/css/radio-table/_show/number-indent.css new file mode 100644 index 00000000..8cc8a217 --- /dev/null +++ b/assets/css/radio-table/_show/number-indent.css @@ -0,0 +1,15 @@ +.number-indent-1 { + padding-left: 1ch; +} +.number-indent-2 { + padding-left: 2ch; +} +.number-indent-3 { + padding-left: 3ch; +} +.number-indent-4 { + padding-left: 4ch; +} +.number-indent-5 { + padding-left: 5ch; +} diff --git a/assets/css/radio-table/_show/radio-table-container.css b/assets/css/radio-table/_show/radio-table-container.css new file mode 100644 index 00000000..50b0b2bb --- /dev/null +++ b/assets/css/radio-table/_show/radio-table-container.css @@ -0,0 +1,35 @@ +.radio-table-container { + /* reset visual vertical padding of table cells */ + margin-left: -6px; + margin-right: -6px; + + margin-top: -10px; + + overflow-x: auto; + + position: relative; + background-size: 40px 100%; + background-repeat: no-repeat; +} + +.radio-table-container.overflow-right { + background-image: linear-gradient(90deg, rgba(0,0,0, 0), rgba(250,230,205, 0.4)); + background-position: right; +} +.radio-table-container.overflow-left { + background-image: linear-gradient(90deg, rgba(250,230,205, 0.4), rgba(0,0,0, 0)); + background-position: left; +} +.radio-table-container.overflow-left.overflow-right { + background-image: linear-gradient(90deg, rgba(0,0,0, 0), rgba(250,230,205, 0.4)), + linear-gradient(90deg, rgba(250,230,205, 0.4), rgba(0,0,0, 0)); + background-position: right, left; +} + +@media (max-width: 767px) +{ + .radio-table-container { + margin-left: -5vw; + margin-right: -5vw; + } +} diff --git a/assets/css/radio-table/_show/radio-table-details.css b/assets/css/radio-table/_show/radio-table-details.css new file mode 100644 index 00000000..b33150f4 --- /dev/null +++ b/assets/css/radio-table/_show/radio-table-details.css @@ -0,0 +1,64 @@ +.radio-table-details { + margin-top: 30px; + line-height: 1.5em; + + width: 100%; +} +.radio-table-details p { + margin: 7px 0; +} + +.radio-table-details dl { + display: flex; + align-items: center; + width: 100%; + + border-top: 1px solid rgba(0,0,0, 0.05); + font-size: 0.9em; + margin-top: 25px; + padding: 14px 34px 0 34px; + box-sizing: border-box; +} +.radio-table-details dl > * { + display: flex; + align-items: center; + flex: 1; +} +.radio-table-details dl > * > * { + flex: 1; +} +.radio-table-details dt, .radio-table-details dd { + padding: 0 10px; + margin: 0; +} +.radio-table-details dt { + text-align: right; +} +.radio-table-details dt::after { + content: ":"; +} +.radio-table-details dd { + text-align: left; + border-right: 1px solid rgba(0,0,0, 0.05); +} +.radio-table-details dd:last-child { + border-right: none; +} + +@media (max-width: 767px) +{ + .radio-table-details dl { + flex-direction: column; + align-items: stretch; + padding-left: 0; + padding-right: 0; + } + + .radio-table-details dt, .radio-table-details dd { + padding-top: 3px; + padding-bottom: 3px; + } + .radio-table-details dd { + border-right: none; + } +} diff --git a/assets/css/radio-table/_show/radio-table.css b/assets/css/radio-table/_show/radio-table.css new file mode 100644 index 00000000..c0f839a4 --- /dev/null +++ b/assets/css/radio-table/_show/radio-table.css @@ -0,0 +1,144 @@ +.radio-table { + width: 100%; + border-collapse: collapse; + + font-size: 0.95em; +} + +.radio-table td, .radio-table th { + padding: 5px 8px; + text-align: left; +} +.radio-table th { + padding-bottom: 10px; + color: #477E2D; + font-weight: normal; + white-space: nowrap; +} +.radio-table td { + border-top: 1px solid rgba(0,0,0, 0.05); +} + +.radio-table tbody tr { + transition: background 0.1s; +} +.radio-table tbody tr:hover, .radio-table tbody tr:focus-within { + background: rgba(250,230,205, 0.8); +} + +.radio-table tr.text-bold { + font-weight: bold; +} +.radio-table tr.text-italic { + font-style: italic; +} +.radio-table tr.text-strikethrough { + text-decoration: line-through; +} +.radio-table tr.background-1 { + background: rgba(253,198,198, 0.45) !important; +} +.radio-table tr.background-2 { + background: rgba(175,255,150, 0.4) !important; +} +.radio-table tr.background-3 { + background: rgba(156,230,255, 0.35) !important; +} +.radio-table tr.background-4 { + background: rgba(255,255,120, 0.3) !important; +} +.radio-table tr.background-5 { + background: rgba(255,177,255, 0.3) !important; +} + +.radio-table .quality { + display: block; +} +.radio-table .quality .raw { + color: rgba(0,0,0, 0); + position: absolute; +} +.radio-table .quality .raw::selection { + opacity: 0; +} +.radio-table .quality .dots::before { + text-shadow: 0 0 2px #CCC; + margin-right: -10px; + letter-spacing: 2px; +} +.radio-table .quality.quality-5 .dots::before { + color: #0BB300; + content: "●●●●●"; +} +.radio-table .quality.quality-4 .dots::before { + color: #A8D300; + content: "●●●●"; +} +.radio-table .quality.quality-3 .dots::before { + color: #BDAC00; + content: "●●●"; +} +.radio-table .quality.quality-2 .dots::before { + color: #FF5E00; + content: "●●"; +} +.radio-table .quality.quality-1 .dots::before { + color: #FF0017; + content: "●"; +} + +.radio-table .rds { + white-space: pre; + background: rgba(160,160,160, 0.2); + font-family: "Inconsolata", "Consolas", "Source Code Pro", "Lucida Console", monospace; + color: black; + padding: 1px 3px; + transition: background 0.2s, color 0.2s; + + /* reset styles from radio station appearance settings */ + font-weight: normal; + font-style: normal; + text-decoration: none; +} +.radio-table .rds[tabindex]:hover, .radio-table .rds:focus { + background: rgba(0,0,0, 0.7); + color: #fff; + outline: none; +} + +.radio-table small { + font-size: 0.7em; + vertical-align: 2px; +} +.radio-table abbr { + text-decoration: none; + border-bottom: 1px dotted #ccc; +} +.radio-table time { + white-space: nowrap; +} + +.radio-table a:not(:hover) { + color: #a84c00; + text-decoration: underline; + text-decoration-style: dotted; + text-decoration-color: rgba(0,0,0, 0.2); +} + +.radio-table a.external { + display: inline-block; + text-decoration: none; +} +.radio-table a.external svg { + height: 0.9em; + vertical-align: -1px; + padding: 0 2px; + fill: currentColor; +} + +.radio-table .comment-collapsed svg { + height: 1em; + vertical-align: -2px; + padding: 0 2px; + fill: currentColor; +} diff --git a/assets/css/radio-table/_show/rds-popup.css b/assets/css/radio-table/_show/rds-popup.css new file mode 100644 index 00000000..501e86e3 --- /dev/null +++ b/assets/css/radio-table/_show/rds-popup.css @@ -0,0 +1,77 @@ +@keyframes rds_popup_show +{ + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.rds-popup { + background: #fff; + color: #000; + border: 1px solid #cacaca; + border-radius: 7px; + overflow: hidden; + box-shadow: 0 0 10px rgba(0,0,0, 0.14); + width: 430px; + + animation-name: rds_popup_show; + animation-duration: 0.2s; + animation-timing-function: ease-out; + animation-delay: 0.15s; + animation-fill-mode: both; +} + +.rds-popup h2 { + background: #eee; + color: #555; + font-size: 0.9em; + text-transform: uppercase; + text-align: center; + padding: 5px 0; + margin: 0; +} + +.rds-popup dl { + font-size: 0.8em; + margin: 15px 20px; +} +.rds-popup dl > div { + display: flex; + flex: 1; + width: 100%; +} +.rds-popup dl > div[hidden] { + /* weird fix of weird bug */ + display: none; +} +.rds-popup dt { + min-width: 12%; + font-weight: bold; + + /* keep in sync with div's margin */ + padding: 5px 0; +} +.rds-popup dt::after { + content: ":"; +} +.rds-popup dd { + margin-left: 0; +} + +.rds-popup dd span { + display: inline; + margin-right: 8px; + + white-space: pre; + background: rgba(160,160,160, 0.2); + font-family: "Inconsolata", "Consolas", "Source Code Pro", "Lucida Console", monospace; +} +.rds-popup dd.rds-wrap span { + white-space: pre-wrap; +} +.rds-popup dd div { + margin: 5px 0; +} diff --git a/assets/css/radio-table/columns.css b/assets/css/radio-table/columns.css new file mode 100644 index 00000000..e7989c48 --- /dev/null +++ b/assets/css/radio-table/columns.css @@ -0,0 +1,75 @@ +.radio-table-columns-ui { + margin: 20px auto; + max-width: 800px; +} +.radio-table-columns-ui .form-item { + border-radius: 7px; + background: #fff; + border: 1px solid #dfdfdf; + box-shadow: 0 0 6px rgba(0,0,0, 0.09); + + padding: 10px 20px; + box-sizing: border-box; + display: flex; + align-items: center; +} +.radio-table-columns-ui .form-item label { + margin: 0; +} +.radio-table-columns-ui .form-item > :last-child { + text-align: right; + flex: 1; +} +.radio-table-columns-ui .form-item.hidden-column { + background: #fafafa; + box-shadow: inset 1px 1px 2px #efefef; + border-color: #e9e9e9; +} +.radio-table-columns-ui .form-item.hidden-column label { + text-decoration: line-through; + opacity: 0.6; +} +.radio-table-columns-ui .form-item button { + display: inline; + width: auto; + min-width: 100px; + margin: 0 0 0 10px; + white-space: nowrap; +} +.radio-table-columns-ui .form-item button:first-of-type { + margin-left: 0; +} + +@media (max-width: 767px) +{ + .radio-table-columns-ui .form-item { + flex-direction: row; + } + .radio-table-columns-ui .form-item label { + margin-bottom: 0; + } +} + +@media (max-width: 700px) +{ + .radio-table-columns-ui .form-item { + flex-direction: column; + } + .radio-table-columns-ui .form-item label { + margin-bottom: 15px; + } + .radio-table-columns-ui .form-item > :last-child { + text-align: center; + white-space: nowrap; + } +} +@media (max-width: 440px) +{ + .radio-table-columns-ui .form-item button { + min-width: auto; + height: auto; + font-size: 0.8em; + padding-top: 10px; + padding-bottom: 10px; + } +} diff --git a/assets/css/radio-table/create.css b/assets/css/radio-table/create.css new file mode 100644 index 00000000..651e55ed --- /dev/null +++ b/assets/css/radio-table/create.css @@ -0,0 +1,9 @@ +.ui-block { + max-width: 750px; + margin-left: auto; + margin-right: auto; +} + +.form-group { + padding-top: 5px; +} diff --git a/assets/css/radio-table/export.css b/assets/css/radio-table/export.css new file mode 100644 index 00000000..ca45a282 --- /dev/null +++ b/assets/css/radio-table/export.css @@ -0,0 +1,24 @@ +/* export list */ + +.radio-table-export-list { + list-style: none; + padding-left: 0; + margin: 0; +} +.radio-table-export-list li { + margin-top: 15px; +} +.radio-table-export-list a.button { + margin-right: 12px; + width: 250px; + vertical-align: baseline; +} + +@media (max-width: 1000px) +{ + .radio-table-export-list a.button { + margin: 15px 0 10px 0; + width: 100%; + max-width: 100%; + } +} diff --git a/assets/css/radio-table/settings.css b/assets/css/radio-table/settings.css new file mode 100644 index 00000000..63efee3a --- /dev/null +++ b/assets/css/radio-table/settings.css @@ -0,0 +1,8 @@ +@import '~huebee/huebee.css'; + + +/* color picker input */ + +input.color[style] { + box-shadow: none; +} diff --git a/assets/css/radio-table/show.css b/assets/css/radio-table/show.css new file mode 100644 index 00000000..bf0a498c --- /dev/null +++ b/assets/css/radio-table/show.css @@ -0,0 +1,6 @@ +@import './_show/radio-table-container.css'; +@import './_show/radio-table.css'; +@import './_show/radio-table-details.css'; +@import './_show/rds-popup.css'; +@import './_show/number-indent.css'; +@import './_show/appearance-customization.css'; diff --git a/assets/css/user-login-register.css b/assets/css/user-login-register.css deleted file mode 100644 index 4287ce21..00000000 --- a/assets/css/user-login-register.css +++ /dev/null @@ -1,18 +0,0 @@ -.user-login-screen { - max-width: 400px; - margin: 2.5em auto; -} - -/* keep these styles in sync with mobile form item list */ -.user-login-screen .form-item.form-regular { - flex-direction: column; -} -.user-login-screen .form-item.form-regular > *:first-child { - width: auto; -} -.user-login-screen .form-item.form-regular label { - margin-bottom: 0.5em; -} -.user-login-screen .form-item.form-checkbox { - margin: 1.3em 0; -} diff --git a/assets/css/user/login-register.css b/assets/css/user/login-register.css new file mode 100644 index 00000000..5c1b6b9f --- /dev/null +++ b/assets/css/user/login-register.css @@ -0,0 +1,4 @@ +.user-login-screen { + max-width: 400px; + margin: 45px auto; +} diff --git a/assets/css/user-public-profile.css b/assets/css/user/public-profile.css similarity index 73% rename from assets/css/user-public-profile.css rename to assets/css/user/public-profile.css index 7545635e..8b0a188e 100644 --- a/assets/css/user-public-profile.css +++ b/assets/css/user/public-profile.css @@ -10,28 +10,31 @@ .user-profile-details { display: flex; justify-content: center; - margin: 1em auto 1.5em; + margin: 18px auto 25px; font-style: italic; } .user-profile-details > * { display: flex; - margin: 0 1em; + margin: 0 20px; } .user-profile-details dt, .user-profile-details dd { - padding: 0 0.3em; + padding: 0 5px; margin: 0; } .user-profile-details dt::after { content: ":"; } -@media (max-width: 800px) +@media (max-width: 767px) { .user-profile-details { text-align: center; flex-direction: column; align-items: center; } + .user-profile-details > * + * { + margin-top: 8px; + } } @media (max-width: 470px) @@ -39,4 +42,7 @@ .user-profile-details > * { flex-direction: column; } + .user-profile-details > * + * { + margin-top: 15px; + } } diff --git a/assets/images/icon.svg b/assets/images/icon.svg new file mode 100644 index 00000000..a28a77eb --- /dev/null +++ b/assets/images/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/logo.svg b/assets/images/logo.svg index a0b486d7..0f7a82fb 100644 --- a/assets/images/logo.svg +++ b/assets/images/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/js/admin.js b/assets/js/admin.js index f0ba297c..8c11861b 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -1,6 +1,6 @@ import '../css/admin.css'; -import { SortableTable } from './src/SortableTable.js'; +import { SortableTable } from './common/SortableTable.js'; document.addEventListener('DOMContentLoaded', () => { let table = document.querySelector('table.sortable'); diff --git a/assets/js/common.js b/assets/js/common.js deleted file mode 100644 index 73d861d5..00000000 --- a/assets/js/common.js +++ /dev/null @@ -1,11 +0,0 @@ -import '../css/common.css'; - -document.documentElement.classList.add('JS'); - -document.addEventListener('DOMContentLoaded', () => { - let notification = document.querySelector('.notification-wrapper'); - - if (notification) { - setTimeout(() => { notification.classList.add('hidden'); }, 10000); - } -}); diff --git a/assets/js/src/NumberIndentManager.js b/assets/js/common/NumberIndentManager.js similarity index 100% rename from assets/js/src/NumberIndentManager.js rename to assets/js/common/NumberIndentManager.js diff --git a/assets/js/src/OverflowStylesManager.js b/assets/js/common/OverflowStylesManager.js similarity index 100% rename from assets/js/src/OverflowStylesManager.js rename to assets/js/common/OverflowStylesManager.js diff --git a/assets/js/src/RDSPopupManager.js b/assets/js/common/RDSPopupManager.js similarity index 100% rename from assets/js/src/RDSPopupManager.js rename to assets/js/common/RDSPopupManager.js diff --git a/assets/js/src/RadioTableColumnsUI.js b/assets/js/common/RadioTableColumnsUI.js similarity index 94% rename from assets/js/src/RadioTableColumnsUI.js rename to assets/js/common/RadioTableColumnsUI.js index bb17f235..9a9de125 100644 --- a/assets/js/src/RadioTableColumnsUI.js +++ b/assets/js/common/RadioTableColumnsUI.js @@ -10,8 +10,10 @@ export class RadioTableColumnsUI prepareUI() { + this.container.classList.add('radio-table-columns-ui'); + let buttonsTemplate = document.importNode( - this.container.querySelector('.radio-table-columns-buttons-template').content, true + this.container.querySelector('.radio-table-columns-ui-buttons-template').content, true ); let buttons = [ buttonsTemplate.querySelector('button.toggle'), @@ -45,7 +47,8 @@ export class RadioTableColumnsUI item.buttonToggle.disabled = true; } - let buttonsContainer = item.block.lastElementChild; + let buttonsContainer = document.createElement('div'); + item.block.appendChild(buttonsContainer) buttonsContainer.appendChild(item.buttonToggle); buttonsContainer.appendChild(item.buttonMoveUp); buttonsContainer.appendChild(item.buttonMoveDown); diff --git a/assets/js/common/RemoveDialogManager.js b/assets/js/common/RemoveDialogManager.js new file mode 100644 index 00000000..77a41236 --- /dev/null +++ b/assets/js/common/RemoveDialogManager.js @@ -0,0 +1,34 @@ +export class RemoveDialogManager +{ + constructor(container) + { + this.anchor = container.querySelector('.remove-button'); + this.dialog = container.querySelector('.remove-dialog'); + + this.setupButtonInPlaceOfAnchor(); + } + + setupButtonInPlaceOfAnchor() + { + // Use non-JS fallback if browser does not support dialog HTML tag. + if ('undefined' === typeof this.dialog.showModal) { + return; + } + + this.button = document.createElement('button'); + + this.button.innerHTML = this.anchor.innerHTML; + this.button.className = this.anchor.className; + this.button.type = 'button'; + + this.anchor.parentNode.replaceChild(this.button, this.anchor); + + this.button.addEventListener('click', this.onButtonClick.bind(this)) + } + + onButtonClick() + { + this.button.blur(); + this.dialog.showModal(); + } +} diff --git a/assets/js/src/SortableTable.js b/assets/js/common/SortableTable.js similarity index 98% rename from assets/js/src/SortableTable.js rename to assets/js/common/SortableTable.js index fbb08c27..9e88f152 100644 --- a/assets/js/src/SortableTable.js +++ b/assets/js/common/SortableTable.js @@ -1,5 +1,3 @@ -import '../../css/part/sortable-table.css'; - export class SortableTable { constructor(table) diff --git a/assets/js/my-account-settings.js b/assets/js/my-account-settings.js deleted file mode 100644 index cc7d7f3b..00000000 --- a/assets/js/my-account-settings.js +++ /dev/null @@ -1,7 +0,0 @@ -import '../css/my-account-settings.css'; - -import { TabbedUI } from './src/TabbedUI.js'; - -document.addEventListener('DOMContentLoaded', () => { - new TabbedUI(document.querySelector('.tabbed-ui')); -}); diff --git a/assets/js/radio-station-edit-add.js b/assets/js/radio-station/edit-add.js similarity index 81% rename from assets/js/radio-station-edit-add.js rename to assets/js/radio-station/edit-add.js index bc8a709c..bad50793 100644 --- a/assets/js/radio-station-edit-add.js +++ b/assets/js/radio-station/edit-add.js @@ -1,6 +1,6 @@ -import '../css/radio-station-edit-add.css'; +import '../../css/radio-station/edit-add.css'; -import { TabbedUI } from './src/TabbedUI.js'; +import { RemoveDialogManager } from '../common/RemoveDialogManager.js'; function setupFrequencyWithDabChannelSync() { @@ -19,7 +19,7 @@ function setupFrequencyWithDabChannelSync() } document.addEventListener('DOMContentLoaded', () => { - new TabbedUI(document.querySelector('.tabbed-ui')); - setupFrequencyWithDabChannelSync(); + + new RemoveDialogManager(document); }); diff --git a/assets/js/radio-table-create.js b/assets/js/radio-table-create.js deleted file mode 100644 index a542dde7..00000000 --- a/assets/js/radio-table-create.js +++ /dev/null @@ -1,7 +0,0 @@ -import '../css/radio-table-create.css'; - -import { TabbedUI } from './src/TabbedUI.js'; - -document.addEventListener('DOMContentLoaded', () => { - new TabbedUI(document.querySelector('.tabbed-ui')); -}); diff --git a/assets/js/radio-table-remove.js b/assets/js/radio-table-remove.js deleted file mode 100644 index 65827a00..00000000 --- a/assets/js/radio-table-remove.js +++ /dev/null @@ -1,25 +0,0 @@ -import '../css/radio-table-remove.css'; - -import { TabbedUI } from './src/TabbedUI.js'; - -function setupRemoveConfirmDialog() -{ - let input = document.querySelector('.radio-table-remove-confirm'); - - let confirmDialog = () => { - if (input.checked && false === window.confirm(input.dataset.confirmMessage)) { - input.checked = false; - input.blur(); - } - - input.removeEventListener('change', confirmDialog); - }; - - input.addEventListener('change', confirmDialog); -} - -document.addEventListener('DOMContentLoaded', () => { - new TabbedUI(document.querySelector('.tabbed-ui')); - - setupRemoveConfirmDialog(); -}); diff --git a/assets/js/radio-table/columns.js b/assets/js/radio-table/columns.js new file mode 100644 index 00000000..4f124386 --- /dev/null +++ b/assets/js/radio-table/columns.js @@ -0,0 +1,7 @@ +import '../../css/radio-table/columns.css'; + +import { RadioTableColumnsUI } from '../common/RadioTableColumnsUI.js'; + +document.addEventListener('DOMContentLoaded', () => { + new RadioTableColumnsUI(document.querySelector('.radio-table-columns')); +}); diff --git a/assets/js/radio-table-settings.js b/assets/js/radio-table/settings.js similarity index 72% rename from assets/js/radio-table-settings.js rename to assets/js/radio-table/settings.js index d4f7a7fc..eecf66b4 100644 --- a/assets/js/radio-table-settings.js +++ b/assets/js/radio-table/settings.js @@ -1,7 +1,7 @@ -import '../css/radio-table-settings.css'; +import '../../css/radio-table/settings.css'; + +import { RemoveDialogManager } from '../common/RemoveDialogManager.js'; -import { TabbedUI } from './src/TabbedUI.js'; -import { RadioTableColumnsUI } from './src/RadioTableColumnsUI.js'; import Huebee from 'huebee'; function setupColorInputs() @@ -36,12 +36,15 @@ function setupColorInputs() function setupCustomWidthInput() { let widthTypeInput = document.querySelector('.radio-table-width-type'); - let customWidthFieldWrapper = document.querySelector('.radio-table-custom-width-wrapper'); + let customWidthField = document.querySelector('.radio-table-custom-width'); const WIDTH_CUSTOM = widthTypeInput.dataset.customWidthValue; + // This is not added server-side for better UX of non-JS users. + customWidthField.required = true; + let updateFieldVisibility = () => { - customWidthFieldWrapper.hidden = (widthTypeInput.value != WIDTH_CUSTOM); + customWidthField.disabled = (widthTypeInput.value != WIDTH_CUSTOM); }; updateFieldVisibility(); @@ -50,9 +53,8 @@ function setupCustomWidthInput() } document.addEventListener('DOMContentLoaded', () => { - new TabbedUI(document.querySelector('.tabbed-ui')); - new RadioTableColumnsUI(document.querySelector('.radio-table-columns')); - setupColorInputs(); setupCustomWidthInput(); + + new RemoveDialogManager(document); }); diff --git a/assets/js/radio-table-show.js b/assets/js/radio-table/show.js similarity index 54% rename from assets/js/radio-table-show.js rename to assets/js/radio-table/show.js index 41c7df37..4a310f25 100644 --- a/assets/js/radio-table-show.js +++ b/assets/js/radio-table/show.js @@ -1,9 +1,9 @@ -import '../css/radio-table-show.css'; +import '../../css/radio-table/show.css'; -import { NumberIndentManager } from './src/NumberIndentManager.js'; -import { OverflowStylesManager } from './src/OverflowStylesManager.js'; -import { RDSPopupManager } from './src/RDSPopupManager.js'; -import { SortableTable } from './src/SortableTable.js'; +import { NumberIndentManager } from '../common/NumberIndentManager.js'; +import { OverflowStylesManager } from '../common/OverflowStylesManager.js'; +import { RDSPopupManager } from '../common/RDSPopupManager.js'; +import { SortableTable } from '../common/SortableTable.js'; document.addEventListener('DOMContentLoaded', () => { let container = document.querySelector('.radio-table-container'); diff --git a/assets/js/src/TabbedUI.js b/assets/js/src/TabbedUI.js deleted file mode 100644 index 459370d1..00000000 --- a/assets/js/src/TabbedUI.js +++ /dev/null @@ -1,102 +0,0 @@ -import '../../css/part/tabbed-ui.css'; - -export class TabbedUI -{ - constructor(container) - { - this.container = container; - this.panels = [...container.children]; - this.navigator = null; - this.buttons = []; - - this.setupNavigator(); - this.setupPanels(); - } - - get LAST_PANEL_SESSION_KEY() - { - return 'tabbed-ui__' + location.pathname; - } - - setupNavigator() - { - this.navigator = document.createElement('ul'); - this.navigator.classList.add('tabbed-ui-navigator'); - this.navigator.setAttribute('role', 'tablist'); - - this.panels.forEach((panel, i) => { - let title = panel.querySelector('h2').innerHTML; - let item = document.createElement('li'); - let button = document.createElement('button'); - - button.innerHTML = title; - button.dataset.panelNumber = i; - button.type = 'button'; - button.addEventListener('click', this.onNavigatorButtonClick.bind(this)); - button.setAttribute('role', 'tab'); - - this.buttons[i] = button; - - item.appendChild(button); - this.navigator.appendChild(item); - }); - - this.container.insertBefore(this.navigator, this.panels[0]); - } - - setupPanels() - { - let defaultPanelNumber = 0; - - let currentHash = location.hash.replace('#', ''); - let savedPanelNumber = sessionStorage.getItem(this.LAST_PANEL_SESSION_KEY); - - if (null != savedPanelNumber) { - defaultPanelNumber = savedPanelNumber; - } - - this.panels.forEach((panel, i) => { - panel.setAttribute('role', 'tabpanel'); - - if (currentHash && panel.id == currentHash) { - defaultPanelNumber = i; - - // If default panel was determined by URL hash, scroll to - // the top of the page to prevent default browser behavior. - let scrollToTop = () => { - window.scroll(0, 0); - window.removeEventListener('scroll', scrollToTop); - }; - window.addEventListener('scroll', scrollToTop); - } - }); - - this.changePanel(defaultPanelNumber); - } - - changePanel(panelNumber) - { - this.panels.forEach((panel, i) => { - panel.classList.remove('tabbed-ui-current'); - this.buttons[i].classList.remove('tabbed-ui-current'); - - if (i == panelNumber) { - panel.classList.add('tabbed-ui-current'); - this.buttons[i].classList.add('tabbed-ui-current'); - } - }); - } - - onNavigatorButtonClick(event) - { - event.preventDefault(); - - let button = event.target; - let panelNumber = button.dataset.panelNumber; - - button.blur(); - - this.changePanel(panelNumber); - sessionStorage.setItem(this.LAST_PANEL_SESSION_KEY, panelNumber); - } -} diff --git a/assets/public/icon.png b/assets/public/icon.png index d0b866e0..205184dd 100644 Binary files a/assets/public/icon.png and b/assets/public/icon.png differ diff --git a/config/services.yaml b/config/services.yaml index 5c3c4fe6..03304f6a 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -60,7 +60,7 @@ services: arguments: $exporters: !tagged_iterator app.radio_table_exporter - App\Form\Extension\ValidationFlashErrorExtension: + App\Form\Extension\ValidationErrorExtension: tags: [{ name: form.type_extension, priority: -100 }] App\Twig\TrackerExtension: diff --git a/src/Controller/GeneralController.php b/src/Controller/GeneralController.php index a138fafb..98ea5fe8 100644 --- a/src/Controller/GeneralController.php +++ b/src/Controller/GeneralController.php @@ -21,8 +21,7 @@ public function homepage(RadioTableRepository $radioTableRepository): Response ]); return $this->render('general/homepage.html.twig', [ - 'last_updated_radio_tables' => $radioTableRepository->findPublicOrderedByLastUpdateTime(10), - 'last_created_radio_tables' => $radioTableRepository->findPublicOrderedByCreationTime(10), + 'last_updated_radio_tables' => $radioTableRepository->findPublicOrderedByLastUpdateTime(12), 'search_form' => $form->createView(), ]); } @@ -36,7 +35,7 @@ public function aboutService(): Response } /** - * @Route({"pl": "/regulamin", "en": "/terms-of-service"}, name="terms_of_service") + * @Route("/regulamin", name="terms_of_service") */ public function termsOfService(): Response { diff --git a/src/Controller/RadioStationController.php b/src/Controller/RadioStationController.php index bbbb9f90..09c6dbcc 100644 --- a/src/Controller/RadioStationController.php +++ b/src/Controller/RadioStationController.php @@ -5,6 +5,7 @@ use App\Entity\RadioStation; use App\Entity\RadioTable; use App\Form\RadioStationEditType; +use App\Form\RadioStationBulkRemoveType; use Doctrine\ORM\EntityManagerInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; @@ -100,18 +101,55 @@ public function copy(RadioStation $radioStation): Response /** * @Route( * {"pl": "/wykaz/{radioTableId}/usun-stacje/{id}", "en": "/list/{radioTableId}/delete-station/{id}"}, - * name="radio_station.remove" + * name="radio_station.remove", + * methods={"POST"} * ) * @IsGranted("IS_AUTHENTICATED_REMEMBERED") * @IsGranted("RADIO_TABLE_MODIFY", subject="radioStation", statusCode=404) */ - public function remove(RadioStation $radioStation): Response + public function remove(RadioStation $radioStation, EntityManagerInterface $entityManager): Response { - $this->addFlash('notice', 'radio_station.remove.notification.chosen_to_be_removed'); + $entityManager->remove($radioStation); + $entityManager->flush(); - return $this->forward(RadioTableController::class . '::remove', [ + $this->addFlash('notice', 'radio_station.remove.notification.removed'); + + return $this->redirectToRoute('radio_table.show', [ 'id' => $radioStation->getRadioTable()->getId(), - 'radioStationToRemove' => $radioStation, + ]); + } + + /** + * @Route({"pl": "/wykaz/{id}/usun-stacje", "en": "/list/{id}/delete-stations"}, name="radio_station.bulk_remove") + * @IsGranted("IS_AUTHENTICATED_REMEMBERED") + * @IsGranted("RADIO_TABLE_MODIFY", subject="radioTable", statusCode=404) + */ + public function bulkRemove(RadioTable $radioTable, Request $request, EntityManagerInterface $entityManager): Response + { + $form = $this->createForm(RadioStationBulkRemoveType::class, null, ['radio_table' => $radioTable]); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $chosenToRemove = $form->getData()['chosenToRemove']; + + if (count($chosenToRemove) > 0) { + foreach ($chosenToRemove as $radioStation) { + $entityManager->remove($radioStation); + } + $entityManager->flush(); + + $this->addFlash('notice', 'radio_station.bulk_remove.notification.bulk_removed'); + + // Form needs to be reloaded to not display removed radio stations. + return $this->redirectToRoute('radio_station.bulk_remove', [ + 'id' => $radioTable->getId(), + ]); + } + } + + return $this->render('radio_station/bulk_remove.html.twig', [ + 'form' => $form->createView(), + 'radio_table' => $radioTable, ]); } } diff --git a/src/Controller/RadioTableController.php b/src/Controller/RadioTableController.php index 0add499a..19fdad51 100644 --- a/src/Controller/RadioTableController.php +++ b/src/Controller/RadioTableController.php @@ -2,17 +2,15 @@ namespace App\Controller; -use App\Entity\RadioStation; use App\Entity\RadioTable; use App\Export\RadioTableExporterProvider; -use App\Form\RadioStationRemoveType; +use App\Form\RadioTableColumnsType; use App\Form\RadioTableCreateType; use App\Form\RadioTableRemoveType; use App\Form\RadioTableSettingsType; use App\Repository\RadioStationRepository; use Doctrine\ORM\EntityManagerInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -53,7 +51,7 @@ public function create(Request $request, EntityManagerInterface $entityManager): $entityManager->flush(); $this->addFlash('notice', 'radio_table.create.notification.created'); - return $this->redirectToRoute('user.my_radio_tables'); + return $this->redirectToRoute('radio_table.show', ['id' => $radioTable->getId()]); } return $this->render('radio_table/create.html.twig', [ @@ -84,6 +82,29 @@ public function settings(RadioTable $radioTable, Request $request, ]); } + /** + * @Route({"pl": "/wykaz/{id}/kolumny", "en": "/list/{id}/columns"}, name="radio_table.columns") + * @IsGranted("IS_AUTHENTICATED_REMEMBERED") + * @IsGranted("RADIO_TABLE_MODIFY", subject="radioTable", statusCode=404) + */ + public function columns(RadioTable $radioTable, Request $request, + EntityManagerInterface $entityManager): Response + { + $form = $this->createForm(RadioTableColumnsType::class, $radioTable); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager->flush(); + + $this->addFlash('notice', 'common.notification.saved_changes'); + } + + return $this->render('radio_table/columns.html.twig', [ + 'form' => $form->createView(), + 'radio_table' => $radioTable, + ]); + } + /** * @Route( * {"pl": "/wykaz/{id}/eksport/{_format}", "en": "/list/{id}/export/{_format}"}, @@ -118,70 +139,23 @@ public function download(RadioTable $radioTable, string $_format, */ public function export(RadioTable $radioTable): Response { - return $this->redirectToRoute('radio_table.settings', [ - 'id' => $radioTable->getId(), - '_fragment' => 'export', + return $this->render('radio_table/export.html.twig', [ + 'radio_table' => $radioTable, ]); } /** - * This action handles both radio table removing and radio station removing. - * - * @Route({"pl": "/wykaz/{id}/usun", "en": "/list/{id}/delete"}, name="radio_table.remove") - * @ParamConverter("radioStationToRemove", class="stdClass") + * @Route({"pl": "/wykaz/{id}/usun", "en": "/list/{id}/delete"}, methods={"POST"}, name="radio_table.remove") * @IsGranted("IS_AUTHENTICATED_REMEMBERED") * @IsGranted("RADIO_TABLE_MODIFY", subject="radioTable", statusCode=404) */ - public function remove(RadioTable $radioTable, Request $request, EntityManagerInterface $entityManager, - RadioStation $radioStationToRemove = null): Response + public function remove(RadioTable $radioTable, EntityManagerInterface $entityManager): Response { - $form_RadioTable = $this->createForm(RadioTableRemoveType::class); - $form_RadioTable->handleRequest($request); - - $form_RadioStation = $this->createForm(RadioStationRemoveType::class, - $radioStationToRemove ? ['chosenToRemove' => [$radioStationToRemove]] : null, - ['radio_table' => $radioTable] - ); - $form_RadioStation->handleRequest($request); + $entityManager->remove($radioTable); + $entityManager->flush(); - if ($form_RadioTable->isSubmitted() && $form_RadioTable->isValid()) { - $confirmed = (true === $form_RadioTable->getData()['confirm']); + $this->addFlash('notice', 'radio_table.remove.notification.removed'); - if ($confirmed) { - $entityManager->remove($radioTable); - $entityManager->flush(); - - $this->addFlash('notice', 'radio_table.remove.notification.removed'); - return $this->redirectToRoute('user.my_radio_tables'); - } - else { - $this->addFlash('error', 'radio_table.remove.notification.not_yet'); - } - } - elseif ($form_RadioStation->isSubmitted() && $form_RadioStation->isValid()) { - $chosenToRemove = $form_RadioStation->getData()['chosenToRemove']; - - if (count($chosenToRemove) > 0) { - foreach ($chosenToRemove as $radioStation) { - $entityManager->remove($radioStation); - } - $entityManager->flush(); - - $this->addFlash('notice', 'radio_station.remove.notification.bulk_removed'); - - // Redirect to after successful radio stations removing. - // * Form needs to be reloaded to not display removed radio stations. - // * URL needs to be changed to avoid 404 error if page was forwarded from RadioStationController. - return $this->redirectToRoute('radio_table.remove', [ - 'id' => $radioTable->getId(), - ]); - } - } - - return $this->render('radio_table/remove.html.twig', [ - 'form_radio_table' => $form_RadioTable->createView(), - 'form_radio_station' => $form_RadioStation->createView(), - 'radio_table' => $radioTable, - ]); + return $this->redirectToRoute('user.my_radio_tables'); } } diff --git a/src/Entity/Embeddable/RadioStation/Rds.php b/src/Entity/Embeddable/RadioStation/Rds.php index 38521dec..fbf18821 100644 --- a/src/Entity/Embeddable/RadioStation/Rds.php +++ b/src/Entity/Embeddable/RadioStation/Rds.php @@ -33,7 +33,7 @@ class Rds * Programme Type * @ORM\Column(type="string", length=25, nullable=true) - * @Assert\Length(max=25, maxMessage="radio_station.rds_pty.max_length") + * @Assert\Length(max=25) */ private $pty; @@ -41,7 +41,7 @@ class Rds * Programme Identification * * @ORM\Column(type="string", length=4, nullable=true) - * @Assert\Length(max=4, maxMessage="radio_station.rds_pi.max_length") + * @Assert\Length(max=4) */ private $pi; diff --git a/src/Entity/Embeddable/RadioTable/Appearance.php b/src/Entity/Embeddable/RadioTable/Appearance.php index dfb4df85..5587c4d5 100644 --- a/src/Entity/Embeddable/RadioTable/Appearance.php +++ b/src/Entity/Embeddable/RadioTable/Appearance.php @@ -18,13 +18,13 @@ class Appearance /** * @ORM\Column(type="string", length=10, nullable=true) - * @HexColor(message="radio_table.appearance.fg_invalid") + * @HexColor() */ private $textColor; /** * @ORM\Column(type="string", length=10, nullable=true) - * @HexColor(message="radio_table.appearance.bg_invalid") + * @HexColor() */ private $backgroundColor; @@ -36,11 +36,11 @@ class Appearance /** * @ORM\Column(type="integer", nullable=true) - * @Assert\GreaterThanOrEqual(900, message="radio_table.appearance.width_min_value") + * @Assert\GreaterThanOrEqual(900, message="radio_table.appearance_width_min_value") * @Assert\Expression( * "value || this.getWidthType() !== width_custom", * values={"width_custom": Appearance::WIDTH_CUSTOM}, - * message="radio_table.appearance.width_required" + * message="This value should not be blank." * ) */ private $customWidth; diff --git a/src/Entity/RadioStation.php b/src/Entity/RadioStation.php index 216941b3..6246cdf8 100644 --- a/src/Entity/RadioStation.php +++ b/src/Entity/RadioStation.php @@ -58,47 +58,47 @@ class RadioStation /** * @ORM\Column(type="string", length=100) - * @Assert\NotBlank(message="radio_station.name.not_blank") - * @Assert\Length(max=100, maxMessage="radio_station.name.max_length") + * @Assert\NotBlank() + * @Assert\Length(max=100) */ private $name; /** * @ORM\Column(type="string", length=50, nullable=true) - * @Assert\Length(max=50, maxMessage="radio_station.radio_group.max_length") + * @Assert\Length(max=50) */ private $radioGroup; /** * @ORM\Column(type="string", length=50, nullable=true) - * @Assert\Length(max=50, maxMessage="radio_station.country.max_length") + * @Assert\Length(max=50) */ private $country; /** * @ORM\Column(type="string", length=100, nullable=true) - * @Assert\Length(max=50, maxMessage="radio_station.region.max_length") + * @Assert\Length(max=50) */ private $region; /** * @ORM\Column(type="decimal", precision=8, scale=3) - * @Assert\NotBlank(message="radio_station.frequency.not_blank") + * @Assert\NotBlank() * @Assert\Type("numeric") - * @Assert\GreaterThan(0, message="radio_station.frequency.greater_than_zero") + * @Assert\GreaterThan(0) */ private $frequency; /** * @ORM\Column(type="string", length=100, nullable=true) - * @Assert\Length(max=100, maxMessage="radio_station.location.max_length") + * @Assert\Length(max=100) */ private $location; /** * @ORM\Column(type="decimal", precision=8, scale=3, nullable=true) * @Assert\Type("numeric") - * @Assert\GreaterThan(0, message="radio_station.power.greater_than_zero") + * @Assert\GreaterThan(0) */ private $power; @@ -110,19 +110,19 @@ class RadioStation /** * @ORM\Column(type="string", length=50, nullable=true) - * @Assert\Length(max=100, maxMessage="radio_station.multiplex.max_length") + * @Assert\Length(max=100) */ private $multiplex; /** * @ORM\Column(type="string", length=5, nullable=true) - * @DabChannel(message="radio_station.dab_channel.invalid_for_frequency") + * @DabChannel() */ private $dabChannel; /** * @ORM\Column(type="integer", nullable=true) - * @Assert\GreaterThan(0, message="radio_station.distance.greater_than_zero") + * @Assert\GreaterThan(0) */ private $distance; @@ -140,13 +140,13 @@ class RadioStation /** * @ORM\Column(type="integer", nullable=true) * @Assert\Type("int") - * @Assert\GreaterThan(0, message="radio_station.private_number.greater_than_zero") + * @Assert\GreaterThan(0) */ private $privateNumber; /** * @ORM\Column(type="string", length=10, nullable=true) - * @YearMonthDate(message="radio_station.first_log_date.invalid_format") + * @YearMonthDate() */ private $firstLogDate; @@ -170,14 +170,14 @@ class RadioStation /** * @ORM\Column(type="string", length=500, nullable=true) - * @Assert\Length(max=500, maxMessage="radio_station.comment.max_length") + * @Assert\Length(max=500) */ private $comment; /** * @ORM\Column(type="string", length=300, nullable=true) - * @Assert\Length(max=500, maxMessage="radio_station.external_anchor.max_length") - * @Assert\Url(message="radio_station.external_anchor.invalid_format") + * @Assert\Length(max=500) + * @Assert\Url() */ private $externalAnchor; diff --git a/src/Entity/RadioTable.php b/src/Entity/RadioTable.php index 68fd6efd..f22fe12c 100644 --- a/src/Entity/RadioTable.php +++ b/src/Entity/RadioTable.php @@ -73,8 +73,8 @@ class RadioTable /** * @ORM\Column(type="string", length=100) - * @Assert\NotBlank(message="radio_table.name.not_blank") - * @Assert\Length(max=100, maxMessage="radio_table.name.max_length") + * @Assert\NotBlank() + * @Assert\Length(max=100) */ private $name; @@ -89,8 +89,7 @@ class RadioTable * @ClassConstantsChoice(class=RadioTable::class, prefix="COLUMN_", multiple=true) * @Assert\Expression( * "frequency in value && name in value", - * values={"frequency"=RadioTable::COLUMN_FREQUENCY, "name"=RadioTable::COLUMN_NAME}, - * message="radio_table.columns.frequency_and_name_required" + * values={"frequency"=RadioTable::COLUMN_FREQUENCY, "name"=RadioTable::COLUMN_NAME} * ) */ private $columns = [ @@ -112,7 +111,7 @@ class RadioTable /** * @ORM\Column(type="string", length=2000, nullable=true) - * @Assert\Length(max=2000, maxMessage="radio_table.description.max_length") + * @Assert\Length(max=2000) */ private $description; diff --git a/src/Entity/User.php b/src/Entity/User.php index dba2673e..bf8c3f86 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -12,7 +12,7 @@ /** * @ORM\Entity(repositoryClass="App\Repository\UserRepository") * @ORM\EntityListeners({"App\Doctrine\EntityListener\UserListener"}) - * @UniqueEntity("name", groups={"Default", "RedefinePassword"}, message="user.name.not_unique") + * @UniqueEntity("name", groups={"Default", "RedefinePassword"}, message="user.name_not_unique") * @ORM\Cache("NONSTRICT_READ_WRITE") */ class User implements UserInterface, LegacyPasswordAuthenticatedUserInterface, PasswordAuthenticatedUserInterface @@ -26,11 +26,11 @@ class User implements UserInterface, LegacyPasswordAuthenticatedUserInterface, P /** * @ORM\Column(type="string", length=50, unique=true) - * @Assert\NotBlank(groups={"Default", "RedefinePassword"}, message="user.name.not_blank") - * @Assert\Length(max=50, groups={"Default", "RedefinePassword"}, maxMessage="user.name.max_length") + * @Assert\NotBlank(groups={"Default", "RedefinePassword"}) + * @Assert\Length(max=50, groups={"Default", "RedefinePassword"}) * @Assert\Regex( * "/^[a-zA-Z0-9_\.\-]*$/", groups={"Default", "RedefinePassword"}, - * message="user.name.invalid_chars" + * message="user.name_invalid_chars" * ) */ private $name; @@ -42,11 +42,8 @@ class User implements UserInterface, LegacyPasswordAuthenticatedUserInterface, P private $password; /** - * @Assert\NotBlank(groups={"RedefinePassword"}, message="user.password.not_blank") - * @Assert\Length( - * max=100, groups={"RedefinePassword"}, - * maxMessage="user.password.max_length" - * ) + * @Assert\NotBlank(groups={"RedefinePassword"}) + * @Assert\Length(max=100, groups={"RedefinePassword"}) */ private $plainPassword; @@ -62,7 +59,7 @@ class User implements UserInterface, LegacyPasswordAuthenticatedUserInterface, P /** * @ORM\Column(type="string", length=2000, nullable=true) - * @Assert\Length(max=2000, groups={"Default", "RedefinePassword"}, maxMessage="user.about_me.max_length") + * @Assert\Length(max=2000, groups={"Default", "RedefinePassword"}) */ private $aboutMe; diff --git a/src/EventSubscriber/AdminFlashMessageSubscriber.php b/src/EventSubscriber/AdminFlashMessageSubscriber.php index 4dcb2280..c58b06d7 100644 --- a/src/EventSubscriber/AdminFlashMessageSubscriber.php +++ b/src/EventSubscriber/AdminFlashMessageSubscriber.php @@ -22,8 +22,8 @@ public function onRestrictedAdminAccess(): void /** @var Session */ $session = $request->getSession(); - // TODO: get rid of this ugly workaround! - $session->getFlashBag()->add('validation_error', 'Przeglądasz prywatny zasób jako administrator.'); + // This triggers warning about untranslated string. + $session->getFlashBag()->add('notice', 'Przeglądasz prywatny zasób jako administrator.'); } /** diff --git a/src/Form/Extension/RadioStationEditExtension.php b/src/Form/Extension/RadioStationEditExtension.php index 3b7d8f31..4d38c03e 100644 --- a/src/Form/Extension/RadioStationEditExtension.php +++ b/src/Form/Extension/RadioStationEditExtension.php @@ -49,8 +49,8 @@ public function finishView(FormView $view, FormInterface $form, array $options): $formChildrenToHide = []; /** @var FormInterface[] $form */ - foreach ($form as $childrenName => $children) { - $propertyPath = (string) $children->getPropertyPath(); + foreach ($form as $childName => $child) { + $propertyPath = (string) $child->getPropertyPath(); $columnName = self::PROPERTY_PATH_TO_COLUMN[$propertyPath] ?? $propertyPath; if (in_array($columnName, self::PROPERTY_PATH_NON_COLUMNS)) { @@ -58,13 +58,13 @@ public function finishView(FormView $view, FormInterface $form, array $options): } if (false === in_array($columnName, $visibleColumns)) { - $formChildrenToHide[] = $childrenName; + $formChildrenToHide[] = $childName; } } - foreach ($view as $childrenName => $children) { - $children->vars['disabled_radio_table_column'] = - in_array($childrenName, $formChildrenToHide); + foreach ($view as $childName => $child) { + $child->vars['disabled_radio_table_column'] = + in_array($childName, $formChildrenToHide); } } diff --git a/src/Form/Extension/ValidationFlashErrorExtension.php b/src/Form/Extension/ValidationErrorExtension.php similarity index 50% rename from src/Form/Extension/ValidationFlashErrorExtension.php rename to src/Form/Extension/ValidationErrorExtension.php index 96d3d8cc..d773795c 100644 --- a/src/Form/Extension/ValidationFlashErrorExtension.php +++ b/src/Form/Extension/ValidationErrorExtension.php @@ -7,9 +7,22 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\HttpFoundation\RequestStack; -class ValidationFlashErrorExtension extends AbstractTypeExtension +/** + * This extension applies user experience tweaks when validation error occurs. + * + * * `autofocus` HTML attribute is added to all invalid form fields. Only one + * form field can be autofocused by browser but adding the attribute to all + * of them autofocuses first one from top of the page (order of form children + * in PHP does not have to match order of fields in HTML markup). + * + * * Generic notification about "invalid form below" is shown to the user. + * Here it's needed to make sure it's only one to show it only once. + */ +class ValidationErrorExtension extends AbstractTypeExtension { private $requestStack; @@ -27,23 +40,18 @@ public function buildForm(FormBuilderInterface $builder, array $options): void /** @var Session */ $session = $this->requestStack->getCurrentRequest()->getSession(); - $error = $form->getErrors(true)[0]; - $template = $error->getMessageTemplate(); - - // Set generic error message if current text isn't specified by this application. - if ( - false !== stripos($template, 'this') || - (false !== stripos($template, 'value') && false === stripos($template, 'value }}')) - ) { - $session->getFlashBag()->add('error', 'common.notification.invalid_form'); - } - else { - $session->getFlashBag()->add('validation_error', $error->getMessage()); - } + $session->getFlashBag()->add('error', 'common.notification.invalid_form'); } }); } + public function finishView(FormView $view, FormInterface $form, array $options): void + { + if ($form->isSubmitted() && false === $form->isValid()) { + $view->vars['attr']['autofocus'] = true; + } + } + static public function getExtendedTypes(): array { return [FormType::class]; diff --git a/src/Form/RadioStationRemoveType.php b/src/Form/RadioStationBulkRemoveType.php similarity index 94% rename from src/Form/RadioStationRemoveType.php rename to src/Form/RadioStationBulkRemoveType.php index dd8f1ac4..cb4df921 100644 --- a/src/Form/RadioStationRemoveType.php +++ b/src/Form/RadioStationBulkRemoveType.php @@ -12,7 +12,7 @@ use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; -class RadioStationRemoveType extends AbstractType +class RadioStationBulkRemoveType extends AbstractType { private $radioStationRepository; @@ -53,7 +53,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setRequired(['radio_table']); $resolver->setAllowedTypes('radio_table', RadioTable::class); $resolver->setDefaults([ - 'label_format' => 'radio_station.remove.form.%name%', + 'label_format' => 'radio_station.bulk_remove.form.%name%', ]); } } diff --git a/src/Form/RadioStationEditType.php b/src/Form/RadioStationEditType.php index 13412baa..1a02ebc3 100644 --- a/src/Form/RadioStationEditType.php +++ b/src/Form/RadioStationEditType.php @@ -155,7 +155,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]) ->add('externalAnchor', UrlType::class, [ 'required' => false, + 'label' => $this->translator->trans('column.externalAnchor', [], 'radio_table'), 'default_protocol' => null, + 'attr' => [ + 'placeholder' => $this->translator->trans('radio_station.edit.form.externalAnchor.help'), + ], + 'translation_domain' => false, ]) ->add('comment', TextareaType::class, [ 'required' => false, diff --git a/src/Form/RadioTableRemoveType.php b/src/Form/RadioTableColumnsType.php similarity index 55% rename from src/Form/RadioTableRemoveType.php rename to src/Form/RadioTableColumnsType.php index 33002029..44c309eb 100644 --- a/src/Form/RadioTableRemoveType.php +++ b/src/Form/RadioTableColumnsType.php @@ -2,18 +2,20 @@ namespace App\Form; +use App\Entity\RadioTable; +use App\Form\Type\RadioTableColumnsType as RadioTableColumnsUIType; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -class RadioTableRemoveType extends AbstractType +class RadioTableColumnsType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('confirm', CheckboxType::class, [ - 'required' => false, + ->add('columns', RadioTableColumnsUIType::class, [ + 'label_format' => 'column.%name%', + 'translation_domain' => 'radio_table', ]) ; } @@ -21,7 +23,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - 'label_format' => 'radio_table.remove.form.%name%', + 'data_class' => RadioTable::class, ]); } } diff --git a/src/Form/RadioTableCreateType.php b/src/Form/RadioTableCreateType.php index 84b16292..2ac95111 100644 --- a/src/Form/RadioTableCreateType.php +++ b/src/Form/RadioTableCreateType.php @@ -3,10 +3,11 @@ namespace App\Form; use App\Entity\RadioTable; -use FOS\CKEditorBundle\Form\Type\CKEditorType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; class RadioTableCreateType extends AbstractType @@ -14,17 +15,8 @@ class RadioTableCreateType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('name') - ->add('frequencyUnit', ChoiceType::class, [ - 'choices' => [ - 'MHz' => RadioTable::FREQUENCY_MHZ, - 'kHz' => RadioTable::FREQUENCY_KHZ, - ], - 'choice_translation_domain' => false, - ]) - ->add('description', CKEditorType::class, [ - 'required' => false, - 'sanitize_html' => true, + ->add('name', null, [ + 'help' => 'radio_table.settings.form.name.help', ]) ->add('status', ChoiceType::class, [ 'expanded' => true, @@ -40,6 +32,13 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ; } + public function finishView(FormView $view, FormInterface $form, array $options): void + { + foreach ($view['status'] as $children) { + $children->vars['help'] = 'radio_table.settings.form.status.choice.'.$children->vars['value'].'.help'; + } + } + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ diff --git a/src/Form/RadioTableSearchType.php b/src/Form/RadioTableSearchType.php index c9de4d37..49ef96e5 100644 --- a/src/Form/RadioTableSearchType.php +++ b/src/Form/RadioTableSearchType.php @@ -12,7 +12,11 @@ class RadioTableSearchType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('s', SearchType::class) + ->add('s', SearchType::class, [ + 'attr' => [ + 'placeholder' => 'common.search_form.form.s.help', + ], + ]) ->setMethod('GET') ; } diff --git a/src/Form/RadioTableSettingsType.php b/src/Form/RadioTableSettingsType.php index a8a7d074..1018a233 100644 --- a/src/Form/RadioTableSettingsType.php +++ b/src/Form/RadioTableSettingsType.php @@ -5,7 +5,7 @@ use App\Entity\Embeddable\RadioTable\Appearance; use App\Entity\RadioTable; use App\Form\Type\IntegerUnitType; -use App\Form\Type\RadioTableColumnsType; +use FOS\CKEditorBundle\Form\Type\CKEditorType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -19,6 +19,17 @@ public function buildForm(FormBuilderInterface $builder, array $options): void parent::buildForm($builder, $options); $builder + ->add('description', CKEditorType::class, [ + 'required' => false, + 'sanitize_html' => true, + ]) + ->add('frequencyUnit', ChoiceType::class, [ + 'choices' => [ + 'MHz' => RadioTable::FREQUENCY_MHZ, + 'kHz' => RadioTable::FREQUENCY_KHZ, + ], + 'choice_translation_domain' => false, + ]) ->add('maxSignalLevelUnit', ChoiceType::class, [ 'choices' => [ 'dB' => RadioTable::MAX_SIGNAL_LEVEL_DB, @@ -39,10 +50,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void }, 'choice_translation_domain' => 'radio_table', ]) - ->add('columns', RadioTableColumnsType::class, [ - 'label_format' => 'column.%name%', - 'translation_domain' => 'radio_table', - ]) ->add('appearanceBackgroundColor', TextType::class, [ 'property_path' => 'appearance.backgroundColor', @@ -68,8 +75,8 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ->add('appearanceCustomWidth', IntegerUnitType::class, [ 'property_path' => 'appearance.customWidth', - 'required' => false, 'attr' => ['min' => '900'], + 'required' => false, ]) ->add('appearanceCollapsedComments', CheckboxType::class, [ 'property_path' => 'appearance.collapsedComments', diff --git a/src/Form/UserRegisterType.php b/src/Form/UserRegisterType.php index 0e8b27fb..d4fc2edf 100644 --- a/src/Form/UserRegisterType.php +++ b/src/Form/UserRegisterType.php @@ -17,9 +17,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('name', null, [ - 'attr' => [ - 'title' => 'user.register.form.name.help', - ], + 'help' => 'user.register.form.name.help', ]) ->add('plainPassword', RepeatedType::class, [ 'type' => PasswordType::class, diff --git a/src/Form/UserSettingsType.php b/src/Form/UserSettingsType.php index 02116f54..c0793a17 100644 --- a/src/Form/UserSettingsType.php +++ b/src/Form/UserSettingsType.php @@ -21,10 +21,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'required' => false, 'sanitize_html' => true, ]) - ->add('publicProfile') + ->add('publicProfile', null, [ + 'required' => false, + 'help' => 'user.settings.form.publicProfile.help', + ]) ->add('currentPassword', PasswordType::class, [ 'mapped' => false, 'required' => false, + 'help' => 'user.settings.form.currentPassword.help', 'constraints' => [ new SecurityAssert\UserPassword([ 'groups' => 'ChangingPasswordTab', diff --git a/src/Repository/RadioTableRepository.php b/src/Repository/RadioTableRepository.php index 092db045..62ea4c4c 100644 --- a/src/Repository/RadioTableRepository.php +++ b/src/Repository/RadioTableRepository.php @@ -36,11 +36,6 @@ public function findPublicOrderedByFrequencyUnit(): array return $this->findAllPublic('frequencyUnit', null); } - public function findPublicOrderedByCreationTime(int $limit = null): array - { - return $this->findAllPublic('creationTime', $limit); - } - private function findAllPublic(string $orderBy, ?int $limit): array { $query = $this->createQueryBuilder('radioTable') diff --git a/src/Validator/DabChannel.php b/src/Validator/DabChannel.php index 96517c80..73a65f6a 100644 --- a/src/Validator/DabChannel.php +++ b/src/Validator/DabChannel.php @@ -9,5 +9,5 @@ */ class DabChannel extends Constraint { - public $message = 'The value "{{ value }}" is not valid.'; + public $message = 'radio_station.dab_channel_invalid_for_frequency'; } diff --git a/src/Validator/HexColor.php b/src/Validator/HexColor.php index f469ebd4..3a7dc4c0 100644 --- a/src/Validator/HexColor.php +++ b/src/Validator/HexColor.php @@ -9,5 +9,5 @@ */ class HexColor extends Constraint { - public $message = 'The value "{{ value }}" is not valid.'; + public $message = 'radio_table.color_invalid_format'; } diff --git a/src/Validator/YearMonthDate.php b/src/Validator/YearMonthDate.php index d704f299..441887e4 100644 --- a/src/Validator/YearMonthDate.php +++ b/src/Validator/YearMonthDate.php @@ -9,4 +9,5 @@ */ class YearMonthDate extends Date { + public $message = 'radio_station.first_log_date_invalid_format'; } diff --git a/templates/admin/_admin_layout.html.twig b/templates/admin/_admin_layout.html.twig index b4231f7b..e18292f3 100644 --- a/templates/admin/_admin_layout.html.twig +++ b/templates/admin/_admin_layout.html.twig @@ -8,8 +8,14 @@ {% block page_title %}Panel administracyjny{% endblock %} {% block context_menu %} - +
  • + + Lista wykazów + +
  • +
  • + + Lista użytkowników + +
  • {% endblock %} diff --git a/templates/common/form/ckeditor.html.twig b/templates/common/form/ckeditor.html.twig index 47542486..e4a092da 100644 --- a/templates/common/form/ckeditor.html.twig +++ b/templates/common/form/ckeditor.html.twig @@ -1,6 +1,7 @@ {% use '@FOSCKEditor/Form/ckeditor_widget.html.twig' %} {% block ckeditor_widget %} + {% set attr = attr|merge({ class: 'ckeditor' }) %} {{ parent() }}