mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 03:36:18 +08:00
225 lines
6.8 KiB
JavaScript
225 lines
6.8 KiB
JavaScript
import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
|
|
import { headerHeight } from 'discourse/views/header';
|
|
|
|
const PANEL_BODY_MARGIN = 30;
|
|
const mutationSupport = !Ember.testing && !!window['MutationObserver'];
|
|
|
|
export default Ember.Component.extend({
|
|
classNameBindings: [':menu-panel', 'visible::hidden', 'viewMode'],
|
|
_lastVisible: false,
|
|
|
|
showClose: Ember.computed.equal('viewMode', 'slide-in'),
|
|
|
|
_layoutComponent() {
|
|
if (!this.get('visible')) { return; }
|
|
|
|
const $window = $(window);
|
|
let width = this.get('maxWidth') || 300;
|
|
const windowWidth = parseInt($window.width());
|
|
|
|
if ((windowWidth - width) < 50) {
|
|
width = windowWidth - 50;
|
|
}
|
|
|
|
const viewMode = this.get('viewMode');
|
|
const $panelBody = this.$('.panel-body');
|
|
let contentHeight = parseInt(this.$('.panel-body-contents').height());
|
|
|
|
// We use a mutationObserver to check for style changes, so it's important
|
|
// we don't set it if it doesn't change. Same goes for the $panelBody!
|
|
const style = this.$().prop('style');
|
|
|
|
if (viewMode === 'drop-down') {
|
|
const $buttonPanel = $('header ul.icons');
|
|
if ($buttonPanel.length === 0) { return; }
|
|
|
|
// These values need to be set here, not in the css file - this is to deal with the
|
|
// possibility of the window being resized and the menu changing from .slide-in to .drop-down.
|
|
if (style.top !== '100%' || style.height !== 'auto') {
|
|
this.$().css({ top: '100%', height: 'auto' });
|
|
}
|
|
|
|
// adjust panel height
|
|
const fullHeight = parseInt($window.height());
|
|
const offsetTop = this.$().offset().top;
|
|
const scrollTop = $window.scrollTop();
|
|
|
|
if (contentHeight + (offsetTop - scrollTop) + PANEL_BODY_MARGIN > fullHeight) {
|
|
contentHeight = fullHeight - (offsetTop - scrollTop) - PANEL_BODY_MARGIN;
|
|
}
|
|
if ($panelBody.height() !== contentHeight) {
|
|
$panelBody.height(contentHeight);
|
|
}
|
|
$('body').addClass('drop-down-visible');
|
|
} else {
|
|
const menuTop = headerHeight();
|
|
|
|
let height;
|
|
const winHeight = $(window).height() - 16;
|
|
if ((menuTop + contentHeight) < winHeight) {
|
|
height = contentHeight + "px";
|
|
} else {
|
|
height = winHeight - menuTop;
|
|
}
|
|
|
|
if ($panelBody.prop('style').height !== '100%') {
|
|
$panelBody.height('100%');
|
|
}
|
|
if (style.top !== menuTop + "px" || style.height !== height) {
|
|
this.$().css({ top: menuTop + "px", height });
|
|
}
|
|
$('body').removeClass('drop-down-visible');
|
|
}
|
|
|
|
this.$().width(width);
|
|
},
|
|
|
|
@computed('force')
|
|
viewMode() {
|
|
const force = this.get('force');
|
|
if (force) { return force; }
|
|
|
|
const headerWidth = $('#main-outlet .container').width() || 1100;
|
|
const screenWidth = $(window).width();
|
|
const remaining = parseInt((screenWidth - headerWidth) / 2);
|
|
|
|
return (remaining < 50) ? 'slide-in' : 'drop-down';
|
|
},
|
|
|
|
@observes('viewMode', 'visible')
|
|
_visibleChanged() {
|
|
if (this.get('visible')) {
|
|
// Allow us to hook into things being shown
|
|
if (!this._lastVisible) {
|
|
Ember.run.scheduleOnce('afterRender', () => this.sendAction('onVisible'));
|
|
this._lastVisible = true;
|
|
}
|
|
|
|
$('html').on('click.close-menu-panel', (e) => {
|
|
const $target = $(e.target);
|
|
if ($target.closest('.header-dropdown-toggle').length > 0) { return; }
|
|
if ($target.closest('.menu-panel').length > 0) { return; }
|
|
this.hide();
|
|
});
|
|
this.performLayout();
|
|
this._watchSizeChanges();
|
|
|
|
// iOS does not handle scroll events well
|
|
if (!this.capabilities.isIOS) {
|
|
$(window).on('scroll.discourse-menu-panel', () => this.performLayout());
|
|
}
|
|
} else if (this._lastVisible) {
|
|
this._lastVisible = false;
|
|
Ember.run.scheduleOnce('afterRender', () => this.sendAction('onHidden'));
|
|
$('html').off('click.close-menu-panel');
|
|
$(window).off('scroll.discourse-menu-panel');
|
|
this._stopWatchingSize();
|
|
$('body').removeClass('drop-down-visible');
|
|
}
|
|
},
|
|
|
|
@computed()
|
|
showKeyboardShortcuts() {
|
|
return !this.site.mobileView && !this.capabilities.touch;
|
|
},
|
|
|
|
@computed()
|
|
showMobileToggle() {
|
|
return this.site.mobileView || (this.siteSettings.enable_mobile_theme && this.capabilities.touch);
|
|
},
|
|
|
|
@computed()
|
|
mobileViewLinkTextKey() {
|
|
return this.site.mobileView ? "desktop_view" : "mobile_view";
|
|
},
|
|
|
|
@computed()
|
|
faqUrl() {
|
|
return this.siteSettings.faq_url ? this.siteSettings.faq_url : Discourse.getURL('/faq');
|
|
},
|
|
|
|
performLayout() {
|
|
Ember.run.scheduleOnce('afterRender', this, this._layoutComponent);
|
|
},
|
|
|
|
_watchSizeChanges() {
|
|
if (mutationSupport) {
|
|
this._observer.disconnect();
|
|
this._observer.observe(this.element, { childList: true, subtree: true, characterData: true, attributes: true });
|
|
} else {
|
|
clearInterval(this._resizeInterval);
|
|
this._resizeInterval = setInterval(() => {
|
|
Ember.run(() => {
|
|
const $panelBodyContents = this.$('.panel-body-contents');
|
|
if ($panelBodyContents && $panelBodyContents.length) {
|
|
const contentHeight = parseInt($panelBodyContents.height());
|
|
if (contentHeight !== this._lastHeight) { this.performLayout(); }
|
|
this._lastHeight = contentHeight;
|
|
}
|
|
});
|
|
}, 500);
|
|
}
|
|
},
|
|
|
|
_stopWatchingSize() {
|
|
if (mutationSupport) {
|
|
this._observer.disconnect();
|
|
} else {
|
|
clearInterval(this._resizeInterval);
|
|
}
|
|
},
|
|
|
|
@on('didInsertElement')
|
|
_bindEvents() {
|
|
this.$().on('click.discourse-menu-panel', 'a', e => {
|
|
if (e.metaKey || e.ctrlKey || e.shiftKey) { return; }
|
|
const $target = $(e.target);
|
|
if ($target.data('ember-action') || $target.closest('.search-link').length > 0) { return; }
|
|
this.hide();
|
|
});
|
|
|
|
this.appEvents.on('dropdowns:closeAll', this, this.hide);
|
|
this.appEvents.on('dom:clean', this, this.hide);
|
|
|
|
$('body').on('keydown.discourse-menu-panel', e => {
|
|
if (e.which === 27) {
|
|
this.hide();
|
|
}
|
|
});
|
|
|
|
$(window).on('resize.discourse-menu-panel', () => {
|
|
this.propertyDidChange('viewMode');
|
|
this.performLayout();
|
|
});
|
|
|
|
if (mutationSupport) {
|
|
this._observer = new MutationObserver(() => {
|
|
Ember.run.debounce(this, this.performLayout, 50);
|
|
});
|
|
}
|
|
|
|
this.propertyDidChange('viewMode');
|
|
},
|
|
|
|
@on('willDestroyElement')
|
|
_removeEvents() {
|
|
this.appEvents.off('dom:clean', this, this.hide);
|
|
this.appEvents.off('dropdowns:closeAll', this, this.hide);
|
|
this.$().off('click.discourse-menu-panel');
|
|
$('body').off('keydown.discourse-menu-panel');
|
|
$('html').off('click.close-menu-panel');
|
|
$(window).off('resize.discourse-menu-panel');
|
|
$(window).off('scroll.discourse-menu-panel');
|
|
},
|
|
|
|
hide() {
|
|
this.set('visible', false);
|
|
},
|
|
|
|
actions: {
|
|
close() {
|
|
this.hide();
|
|
}
|
|
}
|
|
});
|