Merge pull request #5443 from quincylvania/resizable-sidebar

Resizable Sidebar
This commit is contained in:
Bryan Housel
2018-10-31 15:42:37 -04:00
committed by GitHub
11 changed files with 407 additions and 213 deletions
+52 -136
View File
@@ -2,7 +2,6 @@ import {
event as d3_event,
select as d3_select
} from 'd3-selection';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
@@ -11,10 +10,8 @@ import { tooltip } from '../util/tooltip';
import { behaviorHash } from '../behavior';
import { modeBrowse } from '../modes';
import { services } from '../services';
import { svgDefs, svgIcon } from '../svg';
import { utilGetDimensions } from '../util/dimensions';
import { utilRebind } from '../util';
import { uiAccount } from './account';
import { uiAttribution } from './attribution';
@@ -31,6 +28,7 @@ import { uiMapData } from './map_data';
import { uiMapInMap } from './map_in_map';
import { uiModes } from './modes';
import { uiNotice } from './notice';
import { uiPhotoviewer } from './photoviewer';
import { uiRestore } from './restore';
import { uiSave } from './save';
import { uiScale } from './scale';
@@ -47,8 +45,6 @@ import { uiCmd } from './cmd';
export function uiInit(context) {
var uiInitCounter = 0;
var dispatch = d3_dispatch('photoviewerResize');
function render(container) {
container
@@ -71,7 +67,6 @@ export function uiInit(context) {
container
.append('div')
.attr('id', 'sidebar')
.attr('class', 'col4')
.call(ui.sidebar);
var content = container
@@ -95,38 +90,55 @@ export function uiInit(context) {
.call(uiInfo(context))
.call(uiNotice(context));
var leadingArea = bar
.append('div')
.attr('class', 'leading-area');
var sidebarButton = leadingArea
.append('div')
.attr('class', 'button-wrap sidebar-collapse')
.append('button')
.attr('class', 'col12')
.attr('tabindex', -1)
.on('click', ui.sidebar.toggleCollapse)
.call(tooltip().title(t('sidebar_button.tooltip')).placement('bottom'));
var iconSuffix = textDirection === 'rtl' ? 'right' : 'left';
sidebarButton
.call(svgIcon('#iD-icon-sidebar-'+iconSuffix, 'pre-text'))
.append('span')
.attr('class', 'label')
.text(t('sidebar_button.title'));
bar
.append('div')
.attr('class', 'spacer col4');
var limiter = bar.append('div')
.attr('class', 'limiter');
limiter
.attr('class', 'center-area')
.append('div')
.attr('class', 'button-wrap joined col3')
.call(uiModes(context), limiter);
.attr('class', 'modes button-wrap joined')
.call(uiModes(context), bar);
limiter
var trailingArea = bar
.append('div')
.attr('class', 'button-wrap joined col1')
.call(uiUndoRedo(context));
.attr('class', 'trailing-area');
limiter
.append('div')
.attr('class', 'button-wrap col1')
.call(uiSave(context));
bar
trailingArea
.append('div')
.attr('class', 'full-screen')
.call(uiFullScreen(context));
bar
trailingArea
.append('div')
.attr('class', 'spinner')
.call(uiSpinner(context));
trailingArea
.append('div')
.attr('class', 'button-wrap joined')
.call(uiUndoRedo(context));
trailingArea
.append('div')
.attr('class', 'button-wrap save-wrap')
.call(uiSave(context));
var controls = bar
.append('div')
@@ -242,46 +254,12 @@ export function uiInit(context) {
.call(uiContributors(context));
var photoviewer = content
content
.append('div')
.attr('id', 'photoviewer')
.classed('al', true) // 'al'=left, 'ar'=right
.classed('hide', true);
photoviewer
.append('button')
.attr('class', 'thumb-hide')
.on('click', function () {
if (services.streetside) { services.streetside.hideViewer(); }
if (services.mapillary) { services.mapillary.hideViewer(); }
if (services.openstreetcam) { services.openstreetcam.hideViewer(); }
})
.append('div')
.call(svgIcon('#iD-icon-close'));
photoviewer
.append('button')
.attr('class', 'resize-handle-xy')
.on(
'mousedown',
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnX: true, resizeOnY: true })
);
photoviewer
.append('button')
.attr('class', 'resize-handle-x')
.on(
'mousedown',
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnX: true })
);
photoviewer
.append('button')
.attr('class', 'resize-handle-y')
.on(
'mousedown',
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnY: true })
);
.classed('hide', true)
.call(ui.photoviewer);
var mapDimensions = map.dimensions();
@@ -295,10 +273,9 @@ export function uiInit(context) {
};
d3_select(window)
.on('resize.editor', onResize);
onResize();
.on('resize.editor', ui.onResize);
ui.onResize();
var pa = 80; // pan amount
var keybinding = d3_keybinding('main')
@@ -350,83 +327,12 @@ export function uiInit(context) {
}
function onResize() {
mapDimensions = utilGetDimensions(content, true);
map.dimensions(mapDimensions);
// shrink photo viewer if it is too big
// (-90 preserves space at top and bottom of map used by menus)
var photoDimensions = utilGetDimensions(photoviewer, true);
if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
var setPhotoDimensions = [
Math.min(photoDimensions[0], mapDimensions[0]),
Math.min(photoDimensions[1], mapDimensions[1] - 90),
];
photoviewer
.style('width', setPhotoDimensions[0] + 'px')
.style('height', setPhotoDimensions[1] + 'px');
dispatch.call('photoviewerResize', photoviewer, setPhotoDimensions);
}
}
function pan(d) {
return function() {
d3_event.preventDefault();
context.pan(d, 100);
};
}
function buildResizeListener(target, eventName, dispatch, options) {
var resizeOnX = !!options.resizeOnX;
var resizeOnY = !!options.resizeOnY;
var minHeight = options.minHeight || 240;
var minWidth = options.minWidth || 320;
var startX;
var startY;
var startWidth;
var startHeight;
function startResize() {
var mapSize = context.map().dimensions();
if (resizeOnX) {
var maxWidth = mapSize[0];
var newWidth = clamp((startWidth + d3_event.clientX - startX), minWidth, maxWidth);
target.style('width', newWidth + 'px');
}
if (resizeOnY) {
var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
var newHeight = clamp((startHeight + startY - d3_event.clientY), minHeight, maxHeight);
target.style('height', newHeight + 'px');
}
dispatch.call(eventName, target, utilGetDimensions(target, true));
}
function clamp(num, min, max) {
return Math.max(min, Math.min(num, max));
}
function stopResize() {
d3_select(window)
.on('.' + eventName, null);
}
return function initResize() {
startX = d3_event.clientX;
startY = d3_event.clientY;
startWidth = target.node().getBoundingClientRect().width;
startHeight = target.node().getBoundingClientRect().height;
d3_select(window)
.on('mousemove.' + eventName, startResize, false)
.on('mouseup.' + eventName, stopResize, false);
};
}
}
@@ -461,5 +367,15 @@ export function uiInit(context) {
ui.sidebar = uiSidebar(context);
return utilRebind(ui, dispatch, 'on');
ui.photoviewer = uiPhotoviewer(context);
ui.onResize = function() {
var content = d3_select('#content');
var mapDimensions = utilGetDimensions(content, true);
context.map().dimensions(mapDimensions);
ui.photoviewer.onMapResize();
};
return ui;
}
-4
View File
@@ -91,10 +91,6 @@ export function uiModes(context) {
var showNotes = notesEnabled();
var data = showNotes ? modes : modes.slice(0, 3);
selection
.classed('col3', !showNotes) // 25%
.classed('col4', showNotes); // 33%
var buttons = selection.selectAll('button.add-button')
.data(data, function(d) { return d.id; });
+125
View File
@@ -0,0 +1,125 @@
import {
event as d3_event,
select as d3_select
} from 'd3-selection';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { svgIcon } from '../svg';
import { utilGetDimensions } from '../util/dimensions';
import { utilRebind } from '../util';
import { services } from '../services';
export function uiPhotoviewer(context) {
var dispatch = d3_dispatch('photoviewerResize');
function photoviewer(selection) {
selection
.append('button')
.attr('class', 'thumb-hide')
.on('click', function () {
if (services.streetside) { services.streetside.hideViewer(); }
if (services.mapillary) { services.mapillary.hideViewer(); }
if (services.openstreetcam) { services.openstreetcam.hideViewer(); }
})
.append('div')
.call(svgIcon('#iD-icon-close'));
selection
.append('button')
.attr('class', 'resize-handle-xy')
.on(
'mousedown',
buildResizeListener(selection, 'photoviewerResize', dispatch, { resizeOnX: true, resizeOnY: true })
);
selection
.append('button')
.attr('class', 'resize-handle-x')
.on(
'mousedown',
buildResizeListener(selection, 'photoviewerResize', dispatch, { resizeOnX: true })
);
selection
.append('button')
.attr('class', 'resize-handle-y')
.on(
'mousedown',
buildResizeListener(selection, 'photoviewerResize', dispatch, { resizeOnY: true })
);
function buildResizeListener(target, eventName, dispatch, options) {
var resizeOnX = !!options.resizeOnX;
var resizeOnY = !!options.resizeOnY;
var minHeight = options.minHeight || 240;
var minWidth = options.minWidth || 320;
var startX;
var startY;
var startWidth;
var startHeight;
function startResize() {
var mapSize = context.map().dimensions();
if (resizeOnX) {
var maxWidth = mapSize[0];
var newWidth = clamp((startWidth + d3_event.clientX - startX), minWidth, maxWidth);
target.style('width', newWidth + 'px');
}
if (resizeOnY) {
var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
var newHeight = clamp((startHeight + startY - d3_event.clientY), minHeight, maxHeight);
target.style('height', newHeight + 'px');
}
dispatch.call(eventName, target, utilGetDimensions(target, true));
}
function clamp(num, min, max) {
return Math.max(min, Math.min(num, max));
}
function stopResize() {
d3_select(window)
.on('.' + eventName, null);
}
return function initResize() {
startX = d3_event.clientX;
startY = d3_event.clientY;
startWidth = target.node().getBoundingClientRect().width;
startHeight = target.node().getBoundingClientRect().height;
d3_select(window)
.on('mousemove.' + eventName, startResize, false)
.on('mouseup.' + eventName, stopResize, false);
};
}
}
photoviewer.onMapResize = function() {
var photoviewer = d3_select('#photoviewer');
var content = d3_select('#content');
var mapDimensions = utilGetDimensions(content, true);
// shrink photo viewer if it is too big
// (-90 preserves space at top and bottom of map used by menus)
var photoDimensions = utilGetDimensions(photoviewer, true);
if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
var setPhotoDimensions = [
Math.min(photoDimensions[0], mapDimensions[0]),
Math.min(photoDimensions[1], mapDimensions[1] - 90),
];
photoviewer
.style('width', setPhotoDimensions[0] + 'px')
.style('height', setPhotoDimensions[1] + 'px');
dispatch.call('photoviewerResize', photoviewer, setPhotoDimensions);
}
};
return utilRebind(photoviewer, dispatch, 'on');
}
+3 -3
View File
@@ -69,9 +69,7 @@ export function uiSave(context) {
.style('background', background);
button.select('span.count')
.text(numChanges)
.style('background', background)
.style('border-color', background);
.text(numChanges);
}
@@ -88,6 +86,8 @@ export function uiSave(context) {
.call(tooltipBehavior);
button
.append('div')
.attr('class', 'save-inner-wrap')
.call(svgIcon('#iD-icon-save', 'pre-text'))
.append('span')
.attr('class', 'label')
+97 -1
View File
@@ -1,6 +1,11 @@
import _throttle from 'lodash-es/throttle';
import { selectAll as d3_selectAll } from 'd3-selection';
import { drag as d3_drag } from 'd3-drag';
import {
select as d3_select,
event as d3_event,
selectAll as d3_selectAll
} from 'd3-selection';
import {
osmEntity,
@@ -14,6 +19,8 @@ import {
uiNoteEditor
} from './index';
import { textDirection } from '../util/locale';
export function uiSidebar(context) {
var inspector = uiInspector(context);
@@ -25,6 +32,46 @@ export function uiSidebar(context) {
function sidebar(selection) {
var resizer = selection
.append('div')
.attr('id', 'sidebar-resizer');
// set the initial width constraints
selection.style('min-width', '280px');
selection.style('max-width', '400px');
selection.style('width', '33.3333%');
var container = d3_select('#id-container');
resizer.call(d3_drag()
.container(container.node())
.on('drag', function() {
var containerWidthPx = container.node().getBoundingClientRect().width;
var xMarginProperty = textDirection === 'rtl' ? 'margin-right' : 'margin-left';
// subtact 1px so the mouse stays in the div and maintains the col-resize cursor
var newWidthPx = textDirection === 'rtl' ? containerWidthPx - d3_event.x-1 : d3_event.x-1;
var shouldCollapse = newWidthPx < 280;
container.classed('sidebar-collapsed', shouldCollapse);
// allow large widths
selection.style('max-width', '85%');
if (shouldCollapse) {
selection.style(xMarginProperty,'-400px')
.style('width', '400px');
}
else {
var newWidthPercent = (newWidthPx / containerWidthPx) * 100;
selection.style(xMarginProperty, null)
.style('width', newWidthPercent+'%');
}
context.ui().onResize();
})
);
var featureListWrap = selection
.append('div')
.attr('class', 'feature-list-pane')
@@ -93,6 +140,9 @@ export function uiSidebar(context) {
sidebar.select = function(id, newFeature) {
if (!_current && id) {
// uncollapse the sidebar to show the editor
sidebar.toggleCollapse(false);
featureListWrap
.classed('inspector-hidden', true);
@@ -144,6 +194,51 @@ export function uiSidebar(context) {
if (_current) _current.remove();
_current = null;
};
sidebar.toggleCollapse = function(shouldCollapse) {
if (d3_event) {
d3_event.preventDefault();
}
var container = d3_select('#id-container');
var collapsing;
var isCollapsed = container.classed('sidebar-collapsed');
if (typeof shouldCollapse !== 'undefined') {
if (shouldCollapse === isCollapsed) {
return;
}
collapsing = shouldCollapse;
} else {
collapsing = !isCollapsed;
}
var sidebar = d3_select('#sidebar');
var xMarginProperty = textDirection === 'rtl' ? 'margin-right' : 'margin-left';
if (collapsing) {
var preSidebarWidthInPx = sidebar.node().getBoundingClientRect().width;
sidebar.style('width', preSidebarWidthInPx+'px');
sidebar.transition()
.style('width', '400px')
.style(xMarginProperty,'-400px')
.on('end',function(){
context.ui().onResize();
});
container.classed('sidebar-collapsed', true);
} else {
var containerWidthPx = container.node().getBoundingClientRect().width;
var postSidebarWidthInPx = Math.max(containerWidthPx*0.333333, 280);
sidebar.transition()
.style('width', postSidebarWidthInPx)
.style(xMarginProperty, '0px')
.on('end',function(){
sidebar.style('width', '33.3333%');
context.ui().onResize();
});
container.classed('sidebar-collapsed', false);
}
};
// toggle the sidebar collapse when double-clicking the resizer
resizer.on('dblclick', sidebar.toggleCollapse);
}
@@ -152,6 +247,7 @@ export function uiSidebar(context) {
sidebar.select = function() {};
sidebar.show = function() {};
sidebar.hide = function() {};
sidebar.toggleCollapse = function() {};
return sidebar;
}