diff --git a/vj4/locale/zh_CN.yaml b/vj4/locale/zh_CN.yaml index 35e9e2648..cfb62c3e6 100644 --- a/vj4/locale/zh_CN.yaml +++ b/vj4/locale/zh_CN.yaml @@ -643,7 +643,6 @@ He: 他 She: 她 Bulletin: 公告 You cannot visit this domain.: 您不能访问此域。 -Menu: 菜单 'Copy failed :(': '复制失败 :(' Code copied to clipboard!: 代码已复制到剪贴板! No comments so far...: 目前还没有评论... diff --git a/vj4/ui/common/variables.inc.styl b/vj4/ui/common/variables.inc.styl index e87a91ce1..48fc6e02f 100644 --- a/vj4/ui/common/variables.inc.styl +++ b/vj4/ui/common/variables.inc.styl @@ -1,3 +1,6 @@ +// layout +$grid-padding = 15px // half gutter + // fonts $primary-font-family = "Open Sans", "Seravek", "Segoe UI", "Verdana", "PingFang SC", "Hiragino Sans GB", "Lantinghei SC", "Microsoft Yahei", "WenQuanYi Micro Hei", "sans" $header-font-family = "Open Sans", "Seravek", "Segoe UI", "Verdana", "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", "sans-serif" @@ -37,8 +40,8 @@ $font-size-icon = 16px $font-size-title = 20px // components -$section-gap-v = 30px -$section-gap-h = 20px +$section-gap-v = 25px +$section-gap-h = $grid-padding $section-margin = 30px $section-shadow = rem(0 6px 22px) rgba(#AFC2C9, 0.5) $section-bg-color = $content-bg-color diff --git a/vj4/ui/components/dropdown/Dropdown.js b/vj4/ui/components/dropdown/Dropdown.js index fe75c6377..0624e5d43 100644 --- a/vj4/ui/components/dropdown/Dropdown.js +++ b/vj4/ui/components/dropdown/Dropdown.js @@ -4,6 +4,7 @@ import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import responsiveCutoff from 'vj/breakpoints.json'; import zIndexManager from 'vj/utils/zIndexManager'; +import { isBelow } from 'vj/utils/mediaQuery'; export default class Dropdown extends DOMAttachedObject { static DOMAttachKey = 'vjDropdownInstance'; @@ -11,7 +12,7 @@ export default class Dropdown extends DOMAttachedObject { constructor($dom, options = {}) { if ($dom.attr('data-dropdown-trigger-desktop-only') !== undefined) { - if (window.innerWidth < responsiveCutoff.mobile) { + if (isBelow(responsiveCutoff.mobile)) { super(null); return; } diff --git a/vj4/ui/components/footer/footer.page.js b/vj4/ui/components/footer/footer.page.js new file mode 100644 index 000000000..0019581d8 --- /dev/null +++ b/vj4/ui/components/footer/footer.page.js @@ -0,0 +1,31 @@ +import { AutoloadPage } from 'vj/misc/PageLoader'; +import { isBelow } from 'vj/utils/mediaQuery'; +import { slideUp, slideDown } from 'vj/utils/slide'; +import responsiveCutoff from 'vj/breakpoints.json'; + +const footerPage = new AutoloadPage('footerPage', () => { + if ($('.footer').length === 0) { + return; + } + $('.footer__category.expandable > h1').click(async (ev) => { + if (!isBelow(responsiveCutoff.mobile)) { + return; + } + const $category = $(ev.currentTarget).closest('.footer__category'); + const $list = $category.find('.footer__category__expander'); + if ($category.hasClass('animating')) { + return; + } + $category.addClass('animating'); + if ($category.hasClass('expanded')) { + $category.removeClass('expanded'); + await slideUp($list, 300, { opacity: 1 }, { opacity: 0 }); + } else { + $category.addClass('expanded'); + await slideDown($list, 300, { opacity: 0 }, { opacity: 1 }); + } + $category.removeClass('animating'); + }); +}); + +export default footerPage; diff --git a/vj4/ui/components/footer/footer.page.styl b/vj4/ui/components/footer/footer.page.styl index 1da16be41..517bf4b9b 100644 --- a/vj4/ui/components/footer/footer.page.styl +++ b/vj4/ui/components/footer/footer.page.styl @@ -2,21 +2,55 @@ $footer-extra-link-gap = 20px .footer background: $footer-bg-color - padding: rem(20px 0) color: $text-1-color h1 font-size: rem($font-size-title) + +mobile() + padding: rem($grid-padding) + cursor: pointer + font-size: rem($font-size-secondary) + .footer__category - float: left - margin: rem(0 50px 50px 0) + padding-top: rem(30px) + padding-bottom: rem(50px) + + +mobile() + padding-top: rem($grid-padding) + padding-bottom: rem($grid-padding) + border-bottom: 1px solid darken($footer-bg-color, 5%) + + &.expandable + padding: 0 + + .expand-icon + display: none + + +mobile() + display: inline-block + + &.expanded .expand-icon + transform: rotate(180deg) .footer__category__list margin-top: rem(10px) + margin-bottom: rem(10px) font-size: rem($font-size-small) line-height: 2 + +mobile() + margin-top: 0 + padding-left: rem($grid-padding) + padding-right: rem($grid-padding) + +.footer__category__expander + +mobile() + display: none + + +above(rupture.mobile-cutoff) + display: block !important + .footer__extra-left float: left @@ -24,6 +58,7 @@ $footer-extra-link-gap = 20px float: right .footer__extra-link + padding: rem(20px 0) font-size: rem($font-size-small) .footer__extra-link-item @@ -46,3 +81,14 @@ $footer-extra-link-gap = 20px a:hover color: #FFF + ++mobile() + .footer__extra-left, .footer__extra-right + float: none + display: block + + .footer__extra-link-item + float: none + display: inline-block + margin-left: 0 + margin-right: $footer-extra-link-gap diff --git a/vj4/ui/components/header/header.page.styl b/vj4/ui/components/header/header.page.styl index d944abe6d..3f791c604 100644 --- a/vj4/ui/components/header/header.page.styl +++ b/vj4/ui/components/header/header.page.styl @@ -1,6 +1,13 @@ $header-gap-mini = ($header-bg-height-mini - $nav-item-height - $header-logo-height) / 2 $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-logo-domain-lh - $header-logo-system-lh) / 2 +.global-notification + padding: rem(20px 0) + background: #FFF + + &.warning + background: #ffe1a6 + .header position: relative height: rem($header-bg-height) @@ -11,23 +18,15 @@ $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-l +retina() background-image: url(./header-background.png) background-size: rem($header-bg-width $header-bg-height) - +mobile() - .header-content - text-align: center &.mini height: rem($header-bg-height-mini) -.header__layer - position: absolute - left: 0 - bottom: 0 - width: 100% - height: rem($header-layer-height) - background: rgba(#000, 0.5) - -.header.mini .header__layer - display: none + +mobile() + background: #FFF + height: auto !important + .header__content + display: none .header__logo display: block @@ -74,6 +73,43 @@ $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-l opacity: 1 text-decoration: underline +.header--collapsed + + #panel + padding-top: $header-bg-height + + .header + position: absolute + left: 0 + top: 0 + width: 100% + height: 0 + + .header__logo + transform: scale(0) + opacity: 0 + +.location + position: absolute + left: 0 + bottom: 0 + width: 100% + height: rem($header-layer-height) + background: rgba(#000, 0.5) + + +mobile() + position: relative + + .header__hamburger + display: none + + +mobile() + margin-top: rem(28px) + display: block + +.header.mini .location + display: none + .location-path color: rgba(#FFF, 0.3) line-height: rem(30px) @@ -83,6 +119,12 @@ $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-l a:hover color: rgba(#FFF, 0.9) + +mobile() + color: $text-3-color + + a, a:visited, a:hover + color: $text-1-color + .location-current color: #9ad4f1 line-height: rem(40px) @@ -90,25 +132,46 @@ $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-l overflow: hidden text-overflow: ellipsis -.global-notification - padding: rem(20px 0) - background: #FFF + +mobile() + color: $primary-color + font-size: 1.7rem - &.warning - background: #ffe1a6 +.header--mobile + display: none -.header--collapsed +.header--mobile, .location + +mobile() + display: block + background: #FFF + color: $text-1-color + line-height: rem($nav-item-height) + font-size: rem($font-size-title) + padding: rem(10px 0) + +.layout--immersive .header--mobile + background: transparent + a, a:visited, a:hover + color: #FFF + +.header--mobile__domain.is-system + display: block + width: $nav-logo-width // no rem + height: $nav-logo-height // no rem + margin-top: rem(10px) + background: url('../navigation/nav-logo-small_dark.png') no-repeat + +retina() + background: url('../navigation/nav-logo-small@2x_dark.png') no-repeat + background-size: $nav-logo-width $nav-logo-height // no rem - #panel - padding-top: $header-bg-height +.header--mobile__system + margin-left: rem(10px) + font-size: rem($font-size-secondary) - .header - position: absolute - left: 0 - top: 0 - width: 100% - height: 0 + &, &:hover, &:visited + color: $supplementary-color - .header__logo - transform: scale(0) - opacity: 0 +.header__hamburger + background: none + border: 0 + border-radius: 0 + outline: 0 diff --git a/vj4/ui/components/navigation/hamburgers.page.styl b/vj4/ui/components/navigation/hamburgers.page.styl index 98f8819a2..dd091aaf4 100644 --- a/vj4/ui/components/navigation/hamburgers.page.styl +++ b/vj4/ui/components/navigation/hamburgers.page.styl @@ -37,13 +37,19 @@ .hamburger-inner::after width: rem(18px) height: rem(2px) - background-color: #FFF + background-color: $primary-color border-radius: 1px position: absolute transition-property: transform transition-duration: 0.15s transition-timing-function: ease +.layout--immersive + .hamburger-inner, + .hamburger-inner::before, + .hamburger-inner::after + background-color: #FFF + .hamburger-inner::before, .hamburger-inner::after content: "" diff --git a/vj4/ui/components/navigation/index.js b/vj4/ui/components/navigation/index.js index f860413a7..fdd1c1e76 100644 --- a/vj4/ui/components/navigation/index.js +++ b/vj4/ui/components/navigation/index.js @@ -1,5 +1,6 @@ import _ from 'lodash'; import responsiveCutoff from 'vj/breakpoints.json'; +import { isBelow } from 'vj/utils/mediaQuery'; class MultipleStateContainer { constructor(onStateChange, initialState = false) { @@ -78,23 +79,16 @@ class Navigation { } getHeight() { - if (this.$nav.length === 0) { + if (isBelow(responsiveCutoff.mobile)) { return 0; } - if (window.innerWidth > responsiveCutoff.mobile) { - if (!this._navHeight) { - this._navHeight = this.$nav.height(); - } - return this._navHeight; + if (this.$nav.length === 0) { + return 0; } - const $slideoutNav = $('.nav--slideout-trigger'); - if ($slideoutNav.length > 0) { - if (!this._slideoutNavHeight) { - this._slideoutNavHeight = $slideoutNav.height(); - } - return this._slideoutNavHeight; + if (!this._navHeight) { + this._navHeight = this.$nav.height(); } - return 0; + return this._navHeight; } } diff --git a/vj4/ui/components/navigation/navigation.page.js b/vj4/ui/components/navigation/navigation.page.js index 65a5e5248..c01295737 100644 --- a/vj4/ui/components/navigation/navigation.page.js +++ b/vj4/ui/components/navigation/navigation.page.js @@ -6,6 +6,8 @@ import request from 'vj/utils/request'; import responsiveCutoff from 'vj/breakpoints.json'; import Navigation from '.'; +import { isAbove } from 'vj/utils/mediaQuery'; + const nav = Navigation.instance; const $nav = nav.$nav; @@ -31,7 +33,7 @@ const navigationPage = new AutoloadPage('navigationPage', () => { } if ($nav.length > 0 && document.documentElement.getAttribute('data-layout') === 'basic' - && window.innerWidth >= responsiveCutoff.mobile + && isAbove(responsiveCutoff.mobile) ) { $(window).on('scroll', _.throttle(handleScroll, 100)); $nav.hover( @@ -50,12 +52,18 @@ const navigationPage = new AutoloadPage('navigationPage', () => { menu: document.getElementById('menu'), padding: 256, tolerance: 70, + side: 'right', }); [['beforeopen', 'add'], ['beforeclose', 'remove']].forEach(([event, action]) => { - slideout.on(event, () => $('.nav__hamburger .hamburger')[`${action}Class`]('is-active')); + slideout.on(event, () => $('.header__hamburger .hamburger')[`${action}Class`]('is-active')); }); - $('.nav__hamburger').click(() => slideout.toggle()); + const $slideoutOverlay = $('.slideout-overlay'); + $slideoutOverlay.click(() => slideout.close()); + slideout.on('beforeopen', () => $slideoutOverlay.show()); + slideout.on('beforeclose', () => $slideoutOverlay.hide()); + + $('.header__hamburger').click(() => slideout.toggle()); }); export default navigationPage; diff --git a/vj4/ui/components/navigation/navigation.page.styl b/vj4/ui/components/navigation/navigation.page.styl index d0dfdba58..540b33860 100644 --- a/vj4/ui/components/navigation/navigation.page.styl +++ b/vj4/ui/components/navigation/navigation.page.styl @@ -1,7 +1,7 @@ .nav--placeholder height: $nav-item-height -.nav, .nav--slideout-trigger +.nav position: relative left: 0 top: 0 @@ -23,20 +23,6 @@ .nav > .row transition: max-width .5s ease-out-cubic -.nav--slideout-trigger - position: absolute - display: none - background: rgba(#000, 0.5) - color: #FFF - line-height: rem($nav-item-height) - - .nav__hamburger - background: none - border: 0 - border-radius: 0 - color: #FFF - outline: 0 - .nav--shadow position: fixed left: 0 @@ -137,6 +123,9 @@ &:hover text-decoration: none + +mobile() + display: none + .nav.showlogo, .layout--immersive .nav__logo width: rem($nav-logo-width + 10) @@ -180,9 +169,6 @@ .nav .columns padding: 0 - .nav--slideout-trigger - display: block - .nav--shadow display: none @@ -195,13 +181,6 @@ .nav__list-item float: none - .nav__logo - width: auto !important - opacity: 1 !important - background-position: center center - margin: 0 - height: rem($nav-logo-height + 40px) - .nav__item, .nav__item--round, .nav__list--secondary .nav__item--round, diff --git a/vj4/ui/components/signin/signInDialog.page.js b/vj4/ui/components/signin/signInDialog.page.js index d3ac701f6..120e79faa 100644 --- a/vj4/ui/components/signin/signInDialog.page.js +++ b/vj4/ui/components/signin/signInDialog.page.js @@ -1,6 +1,7 @@ import { AutoloadPage } from 'vj/misc/PageLoader'; import DomDialog from 'vj/components/dialog/DomDialog'; import responsiveCutoff from 'vj/breakpoints.json'; +import { isAbove } from 'vj/utils/mediaQuery'; const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => { const signInDialog = DomDialog.getOrConstruct($('.dialog--signin'), { @@ -9,7 +10,7 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => { }); // don't show quick login dialog if in mobile - if ($('[name="nav_login"]').length > 0 && window.innerWidth >= responsiveCutoff.mobile) { + if ($('[name="nav_login"]').length > 0 && isAbove(responsiveCutoff.mobile)) { // nav $('[name="nav_login"]').click((ev) => { if (ev.shiftKey || ev.metaKey || ev.ctrlKey) { diff --git a/vj4/ui/components/sticky/sticky.page.js b/vj4/ui/components/sticky/sticky.page.js index 6db8b1edc..c7a5c5cb5 100644 --- a/vj4/ui/components/sticky/sticky.page.js +++ b/vj4/ui/components/sticky/sticky.page.js @@ -2,15 +2,14 @@ import 'sticky-kit/dist/sticky-kit'; import _ from 'lodash'; import { AutoloadPage } from 'vj/misc/PageLoader'; - import Navigation from 'vj/components/navigation'; +import { isAbove } from 'vj/utils/mediaQuery'; import responsiveCutoff from 'vj/breakpoints.json'; function updateStickies($stickies) { - const ww = window.innerWidth; $stickies.get().forEach((element) => { const $sticky = $(element); - const shouldEnableSticky = (ww >= $sticky.data('sticky-cutoff-min')); + const shouldEnableSticky = (isAbove($sticky.data('sticky-cutoff-min'))); const stickyEnabled = $sticky.data('sticky-enabled'); if (shouldEnableSticky && !stickyEnabled) { const stickyOptions = {}; diff --git a/vj4/ui/misc/section.styl b/vj4/ui/misc/section.styl index e3718919c..47c984863 100644 --- a/vj4/ui/misc/section.styl +++ b/vj4/ui/misc/section.styl @@ -1,6 +1,9 @@ .immersive-section, .section margin-bottom: rem($section-margin) + +mobile() + margin-bottom: rem(10px) + &::before, &::after content: ' ' display: table @@ -14,13 +17,19 @@ transition: transform .5s, opacity .5s transition-timing-function: ease-out-cubic + +mobile() + box-shadow: none + margin-left: - rem($grid-padding) + margin-right: - rem($grid-padding) + .hasjs .section - transform: translateY(rem(30px)) - opacity: 0 + +above(rupture.mobile-cutoff) + transform: translateY(rem(30px)) + opacity: 0 - &.visible - transform: none - opacity: 1 + &.visible + transform: none + opacity: 1 .section__header padding: rem($section-gap-v) rem($section-gap-h) 0 diff --git a/vj4/ui/misc/slideout.styl b/vj4/ui/misc/slideout.styl index 697461fa1..060022810 100644 --- a/vj4/ui/misc/slideout.styl +++ b/vj4/ui/misc/slideout.styl @@ -16,19 +16,29 @@ body, #panel position: relative z-index: 1 +.slideout-overlay + display: none + position: absolute + left: 0 + top: 0 + width: 100% + height: 100% + z-index: 99999 + +mobile() .nav.slideout-menu position: fixed - left: 0 top: 0 bottom: 0 right: 0 + left: auto z-index: 0 width: 256px overflow-y: auto -webkit-overflow-scrolling: touch display: none height: auto + min-height: 100vh .slideout-open, .slideout-open body, diff --git a/vj4/ui/misc/structure.styl b/vj4/ui/misc/structure.styl index 4ed5b62c7..97d58b4f8 100644 --- a/vj4/ui/misc/structure.styl +++ b/vj4/ui/misc/structure.styl @@ -20,10 +20,12 @@ padding: rem(20px 0) flex: 1 -+above(rupture.desktop-cutoff) - .main + +above(rupture.desktop-cutoff) padding: rem(30px 0) + +mobile() + padding: rem(10px 0 0 0) + .v-center display: inline-block vertical-align: middle diff --git a/vj4/ui/pages/record_main.page.styl b/vj4/ui/pages/record_main.page.styl index 259b58c08..7b0f95a13 100644 --- a/vj4/ui/pages/record_main.page.styl +++ b/vj4/ui/pages/record_main.page.styl @@ -40,7 +40,16 @@ height: 100% background: #fff6d1 + +tablet() + .col--time, + .col--memory, + .col--lang + display: none + +mobile() .col--submit-by, - .col--submit-at + .col--submit-at, + .col--time, + .col--memory, + .col--lang display: none diff --git a/vj4/ui/templates/domain_main.html b/vj4/ui/templates/domain_main.html index 1a783134d..33e59a0d2 100644 --- a/vj4/ui/templates/domain_main.html +++ b/vj4/ui/templates/domain_main.html @@ -1,4 +1,4 @@ -{% set header_mini = true %} +{% set no_path_section = true %} {% extends "layout/basic.html" %} {% import "components/contest.html" as contest with context %} {% block content %} diff --git a/vj4/ui/templates/error.html b/vj4/ui/templates/error.html index be074e879..acd7e928e 100644 --- a/vj4/ui/templates/error.html +++ b/vj4/ui/templates/error.html @@ -1,4 +1,4 @@ -{% set header_mini = true %} +{% set no_path_section = true %} {% extends "layout/basic.html" %} {% block content %}