mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Collapse inactive view sections on mobile screens.
Summary: Implement an approach to makind multi-section screens usable on mobile by collapsing inactive sections to a small area. When clicked, they become active and expand, while the rest of the sections are collapsed. Test Plan: Added a basic test case of collapsing inactive sections. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2725
This commit is contained in:
parent
890a8709f3
commit
6c10a43c5d
@ -34,7 +34,7 @@ import {App} from 'app/client/ui/App';
|
||||
import {DocHistory} from 'app/client/ui/DocHistory';
|
||||
import {IPageWidget, toPageWidget} from 'app/client/ui/PageWidgetPicker';
|
||||
import {IPageWidgetLink, linkFromId, selectBy} from 'app/client/ui/selectBy';
|
||||
import {testId} from 'app/client/ui2018/cssVars';
|
||||
import {mediaSmall, testId} from 'app/client/ui2018/cssVars';
|
||||
import {IconName} from 'app/client/ui2018/IconList';
|
||||
import {ActionGroup} from 'app/common/ActionGroup';
|
||||
import {delay} from 'app/common/delay';
|
||||
@ -687,6 +687,11 @@ const cssViewContentPane = styled('div', `
|
||||
position: relative;
|
||||
min-width: 240px;
|
||||
margin: 12px;
|
||||
@media ${mediaSmall} {
|
||||
& {
|
||||
margin: 4px;
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
& {
|
||||
margin: 0px;
|
||||
|
@ -17,6 +17,7 @@
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
min-width: 0px;
|
||||
flex-grow: var(--flex-grow, 1) !important;
|
||||
}
|
||||
|
||||
.layout_hbox.layout_fill_window {
|
||||
|
@ -140,7 +140,7 @@ LayoutBox.prototype.buildDom = function() {
|
||||
kd.cssClass(wrap(function() { return (self.layout.fillWindow ? 'layout_fill_window' :
|
||||
(self.isLastChild() ? 'layout_last_child' : null));
|
||||
})),
|
||||
kd.style('flexGrow', wrap(function() {
|
||||
kd.style('--flex-grow', wrap(function() {
|
||||
return (self.isVBox() || (self.isHBox() && self.layout.fillWindow)) ? self.flexSize() : '';
|
||||
})),
|
||||
kd.domData('layoutBox', this),
|
||||
|
@ -4,34 +4,15 @@
|
||||
}
|
||||
|
||||
.viewsection_title {
|
||||
background-color: #e5e5e5;
|
||||
color: black;
|
||||
flex-shrink: 0;
|
||||
align-items: baseline;
|
||||
line-height: 2;
|
||||
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.active_section > .viewsection_title {
|
||||
background-color: #3e568e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.viewsection_content.newui > .viewsection_title {
|
||||
height: 24px;
|
||||
margin-left: -16px; /* to include drag handle that shows up on hover */
|
||||
color: var(--grist-color-slate);
|
||||
background-color: unset;
|
||||
font-size: var(--grist-small-font-size);
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.active_section.newui > .viewsection_title {
|
||||
background-color: unset;
|
||||
color: var(--grist-color-slate);
|
||||
}
|
||||
|
||||
.viewsection_titletext {
|
||||
@ -41,14 +22,8 @@
|
||||
|
||||
.viewsection_content {
|
||||
background-color: #ffffff;
|
||||
margin: 4px;
|
||||
overflow: visible;
|
||||
box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.viewsection_content.newui {
|
||||
margin: 12px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.viewsection_title_colorbox {
|
||||
@ -59,15 +34,10 @@
|
||||
box-shadow: inset 0px 0px 5px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
/* TODO should be switched to use new icon */
|
||||
.viewsection_drag_indicator {
|
||||
visibility: hidden;
|
||||
margin: auto;
|
||||
padding: 0px 2px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
/* TODO should be switched to use new icon */
|
||||
.viewsection_drag_indicator.newui {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
@ -131,19 +101,16 @@
|
||||
.view_data_pane_container {
|
||||
position: relative;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.viewsection_content.newui > .view_data_pane_container {
|
||||
border: 1px solid var(--grist-color-dark-grey);
|
||||
}
|
||||
|
||||
@media not print {
|
||||
.active_section.newui > .view_data_pane_container {
|
||||
.active_section > .view_data_pane_container {
|
||||
box-shadow: -2px 0 0 0px var(--grist-color-light-green);
|
||||
border-left: 1px solid var(--grist-color-light-green);
|
||||
}
|
||||
|
||||
.active_section.newui > .view_data_pane_container.viewsection_type_detail {
|
||||
.active_section > .view_data_pane_container.viewsection_type_detail {
|
||||
/* this color is a translucent version of grist-color-light-green */
|
||||
box-shadow: -2px 0 0 0px var(--grist-color-inactive-cursor);
|
||||
border-left: 1px solid var(--grist-color-inactive-cursor);
|
||||
|
@ -13,11 +13,11 @@ import {createObsArray} from 'app/client/lib/koArrayWrap';
|
||||
import {ViewRec, ViewSectionRec} from 'app/client/models/DocModel';
|
||||
import {reportError} from 'app/client/models/errors';
|
||||
import {viewSectionMenu} from 'app/client/ui/ViewSectionMenu';
|
||||
import {testId} from 'app/client/ui2018/cssVars';
|
||||
import {colors, mediaSmall, testId} from 'app/client/ui2018/cssVars';
|
||||
import {editableLabel} from 'app/client/ui2018/editableLabel';
|
||||
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||
import {mod} from 'app/common/gutil';
|
||||
import {computedArray, Disposable, dom, fromKo, Holder, IDomComponent, subscribe} from 'grainjs';
|
||||
import {computedArray, Disposable, dom, fromKo, Holder, IDomComponent, styled, subscribe} from 'grainjs';
|
||||
import * as ko from 'knockout';
|
||||
import * as _ from 'underscore';
|
||||
|
||||
@ -121,6 +121,21 @@ export class ViewLayout extends DisposableWithEvents implements IDomComponent {
|
||||
// (See https://stackoverflow.com/questions/2381336/detect-click-into-iframe-using-javascript).
|
||||
this.listenTo(this.gristDoc.app, 'clipboard_blur', this._maybeFocusInSection);
|
||||
|
||||
const classActive = cssLayoutBox.className + '-active';
|
||||
const classInactive = cssLayoutBox.className + '-inactive';
|
||||
this.autoDispose(subscribe(fromKo(this.viewModel.activeSectionId), (use, id) => {
|
||||
this._layout.forEachBox((box: {dom: Element}) => {
|
||||
box.dom.classList.add(classInactive);
|
||||
box.dom.classList.remove(classActive);
|
||||
});
|
||||
let elem: Element|null = this._layout.getLeafBox(id)?.dom;
|
||||
while (elem?.matches('.layout_box')) {
|
||||
elem.classList.remove(classInactive);
|
||||
elem.classList.add(classActive);
|
||||
elem = elem.parentElement;
|
||||
}
|
||||
}));
|
||||
|
||||
const commandGroup = {
|
||||
deleteSection: () => { this._removeViewSection(this.viewModel.activeSectionId()); },
|
||||
nextSection: () => { this._otherSection(+1); },
|
||||
@ -158,11 +173,13 @@ export class ViewLayout extends DisposableWithEvents implements IDomComponent {
|
||||
const vs: ViewSectionRec = this.docModel.viewSections.getRowModel(sectionRowId);
|
||||
return dom('div.view_leaf.viewsection_content.flexvbox.flexauto',
|
||||
testId(`viewlayout-section-${sectionRowId}`),
|
||||
this.gristDoc.app.addNewUIClass(),
|
||||
|
||||
cssViewLeaf.cls(''),
|
||||
cssViewLeafInactive.cls('', (use) => !vs.isDisposed() && !use(vs.hasFocus)),
|
||||
dom.cls('active_section', vs.hasFocus),
|
||||
|
||||
dom.maybe((use) => use(vs.viewInstance) !== null, () => dom('div.viewsection_title.flexhbox',
|
||||
dom('span.viewsection_drag_indicator.glyphicon.glyphicon-option-vertical',
|
||||
this.gristDoc.app.addNewUIClass(),
|
||||
// Makes element grabbable only if grist is not readonly.
|
||||
dom.cls('layout_grabbable', (use) => !use(this.gristDoc.isReadonlyKo))),
|
||||
dom('div.flexitem.flexhbox',
|
||||
@ -270,3 +287,79 @@ export class ViewLayout extends DisposableWithEvents implements IDomComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cssViewLeaf = styled('div', `
|
||||
@media ${mediaSmall} {
|
||||
& {
|
||||
margin: 4px;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const cssViewLeafInactive = styled('div', `
|
||||
@media ${mediaSmall} {
|
||||
& {
|
||||
overflow: hidden;
|
||||
background: repeating-linear-gradient(
|
||||
-45deg,
|
||||
${colors.mediumGreyOpaque},
|
||||
${colors.mediumGreyOpaque} 10px,
|
||||
${colors.lightGrey} 10px,
|
||||
${colors.lightGrey} 20px
|
||||
);
|
||||
border: 1px solid ${colors.darkGrey};
|
||||
border-radius: 4px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
&.layout_vbox {
|
||||
max-width: 32px;
|
||||
}
|
||||
&.layout_hbox {
|
||||
max-height: 32px;
|
||||
}
|
||||
& > .viewsection_title.flexhbox {
|
||||
position: absolute;
|
||||
}
|
||||
& > .view_data_pane_container,
|
||||
& .viewsection_buttons,
|
||||
& .grist-single-record__menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const cssLayoutBox = styled('div', `
|
||||
@media ${mediaSmall} {
|
||||
&-active, &-inactive {
|
||||
transition: flex-grow 0.4s;
|
||||
}
|
||||
&-active > &-inactive,
|
||||
&-active > &-inactive.layout_hbox .layout_hbox,
|
||||
&-active > &-inactive.layout_vbox .layout_vbox {
|
||||
flex: none !important;
|
||||
}
|
||||
|
||||
&-active > &-inactive.layout_hbox.layout_leaf,
|
||||
&-active > &-inactive.layout_hbox .layout_hbox.layout_leaf {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
&-active > &-inactive.layout_vbox.layout_leaf,
|
||||
&-active > &-inactive.layout_vbox .layout_vbox.layout_leaf {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&-inactive.layout_leaf {
|
||||
min-height: 40px;
|
||||
min-width: 40px;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
Loading…
Reference in New Issue
Block a user