Version bump

This commit is contained in:
Neil Lalonde 2017-09-14 11:09:30 -04:00
commit b2660b7d12
241 changed files with 5939 additions and 3524 deletions

1
.gitignore vendored
View File

@ -54,6 +54,7 @@ bootsnap-compile-cache/
!/plugins/discourse-details/
!/plugins/discourse-nginx-performance-report
!/plugins/discourse-narrative-bot
!/plugins/discourse-presence
/plugins/*/auto_generated/
/spec/fixtures/plugins/my_plugin/auto_generated

View File

@ -43,6 +43,7 @@ before_install:
- git clone --depth=1 https://github.com/discourse/discourse-cakeday.git plugins/discourse-cakeday
- git clone --depth=1 https://github.com/discourse/discourse-canned-replies.git plugins/discourse-canned-replies
- git clone --depth=1 https://github.com/discourse/discourse-slack-official.git plugins/discourse-slack-official
- git clone --depth=1 https://github.com/discourse/discourse-chat-integration.git plugins/discourse-chat-integration
install:
- bash -c "if [ '$RAILS_MASTER' == '1' ]; then bundle update --retry=3 --jobs=3 arel rails seed-fu; fi"

View File

@ -38,6 +38,18 @@ source_file = plugins/discourse-narrative-bot/config/locales/server.en.yml
source_lang = en
type = YML
[discourse-org.presenceclientenyml]
file_filter = plugins/discourse-presence/config/locales/client.<lang>.yml
source_file = plugins/discourse-presence/config/locales/client.en.yml
source_lang = en
type = YML
[discourse-org.presenceserverenyml]
file_filter = plugins/discourse-presence/config/locales/server.<lang>.yml
source_file = plugins/discourse-presence/config/locales/server.en.yml
source_lang = en
type = YML
[discourse-org.403html]
file_filter = public/403.<lang>.html
source_file = public/403.html

View File

@ -122,7 +122,7 @@
<div>
<label>{{i18n 'groups.notification_level'}}</label>
{{notifications-button i18nPrefix='groups.notifications' notificationLevel=model.default_notification_level}}
{{notifications-button i18nPrefix='groups.notifications' value=model.default_notification_level}}
<div class='clearfix'></div>
</div>

View File

@ -174,6 +174,8 @@
</section>
{{/if}}
{{plugin-outlet name="admin-user-details" args=(hash model=model)}}
<section class='details'>
<h1>{{i18n 'admin.user.permissions'}}</h1>
@ -466,7 +468,7 @@
{{/if}}
<section>
<hr/>
<hr>
<div class="pull-right">
{{#unless model.anonymizeForbidden}}
{{d-button label="admin.user.anonymize"
@ -487,7 +489,7 @@
{{#if model.deleteExplanation}}
<div class="clearfix"></div>
<br/>
<br>
<div class="pull-right">
{{d-icon "exclamation-triangle"}} {{model.deleteExplanation}}
</div>

View File

@ -66,7 +66,6 @@
//= require ./discourse/controllers/navigation/default
//= require ./discourse/components/edit-category-panel
//= require ./discourse/components/dropdown-button
//= require ./discourse/components/notifications-button
//= require ./discourse/lib/link-mentions
//= require ./discourse/components/site-header
//= require ./discourse/components/d-editor

View File

@ -1,6 +1,16 @@
import { h } from 'virtual-dom';
let _renderers = [];
const REPLACEMENTS = {
'd-tracking': 'circle',
'd-muted': 'times-circle',
'd-regular': 'circle-o',
'd-watching': 'exclamation-circle',
'd-watching-first': 'dot-circle-o',
'd-drop-expanded': 'caret-down',
'd-drop-collapsed': 'caret-right',
};
export function renderIcon(renderType, id, params) {
for (let i=0; i<_renderers.length; i++) {
let renderer = _renderers[i];
@ -23,6 +33,11 @@ export function iconNode(id, params) {
return renderIcon('node', id, params);
}
// TODO: Improve how helpers are registered for vdom compliation
if (typeof Discourse !== "undefined") {
Discourse.__widget_helpers.iconNode = iconNode;
}
export function registerIconRenderer(renderer) {
_renderers.unshift(renderer);
}
@ -42,6 +57,8 @@ registerIconRenderer({
name: 'font-awesome',
string(id, params) {
id = REPLACEMENTS[id] || id;
let tagName = params.tagName || 'i';
let html = `<${tagName} class='${faClasses(id, params)}'`;
if (params.title) { html += ` title='${I18n.t(params.title)}'`; }
@ -54,6 +71,8 @@ registerIconRenderer({
},
node(id, params) {
id = REPLACEMENTS[id] || id;
let tagName = params.tagName || 'i';
const properties = {

View File

@ -7,6 +7,8 @@ const Discourse = Ember.Application.extend({
rootElement: '#main',
_docTitle: document.title,
RAW_TEMPLATES: {},
__widget_helpers: {},
showingSignup: false,
getURL(url) {
if (!url) return url;

View File

@ -1,39 +1,49 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import DropdownButton from 'discourse/components/dropdown-button';
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
import { iconHTML } from "discourse-common/lib/icon-library";
import computed from "ember-addons/ember-computed-decorators";
export default DropdownButton.extend({
buttonExtraClasses: 'no-text',
title: '',
text: iconHTML('bars') + ' ' + iconHTML('caret-down'),
classNames: ['category-notification-menu', 'category-admin-menu'],
export default DropdownSelectBoxComponent.extend({
classNames: ["categories-admin-dropdown"],
@computed()
dropDownContent() {
const includeReorder = this.get('siteSettings.fixed_category_positions');
icon: `${iconHTML('bars')}${iconHTML('caret-down')}`.htmlSafe(),
generatedHeadertext: null,
@computed
content() {
const items = [
{ id: 'create',
title: I18n.t('category.create'),
description: I18n.t('category.create_long'),
icon: 'plus' }
{
id: "create",
text: I18n.t("category.create"),
description: I18n.t("category.create_long"),
icon: "plus"
}
];
const includeReorder = this.get("siteSettings.fixed_category_positions");
if (includeReorder) {
items.push({
id: 'reorder',
title: I18n.t('categories.reorder.title'),
description: I18n.t('categories.reorder.title_long'),
icon: 'random'
id: "reorder",
text: I18n.t("categories.reorder.title"),
description: I18n.t("categories.reorder.title_long"),
icon: "random"
});
}
return items;
},
actionNames: {
create: 'createCategory',
reorder: 'reorderCategories'
create: "createCategory",
reorder: "reorderCategories"
},
clicked(id) {
this.sendAction('actionNames.' + id);
actions: {
onSelectRow(content) {
this._super(content);
this.sendAction(`actionNames.${this.get("value")}`);
this.set("value", null);
}
}
});

View File

@ -12,7 +12,7 @@ export default Ember.Component.extend({
@computed('expanded')
expandIcon(expanded) {
return expanded ? 'caret-down' : 'caret-right';
return expanded ? 'd-drop-expanded' : 'd-drop-collapsed';
},
allCategoriesUrl: function() {

View File

@ -1,13 +1,28 @@
import NotificationsButton from 'discourse/components/notifications-button';
import NotificationOptionsComponent from "discourse/components/notifications-button";
import computed from "ember-addons/ember-computed-decorators";
import { iconHTML } from "discourse-common/lib/icon-library";
export default NotificationsButton.extend({
classNames: ['notification-options', 'category-notification-menu'],
buttonIncludesText: false,
hidden: Em.computed.alias('category.deleted'),
notificationLevel: Em.computed.alias('category.notification_level'),
i18nPrefix: 'category.notifications',
export default NotificationOptionsComponent.extend({
classNames: ["category-notifications-button"],
clicked(id) {
this.get('category').setNotification(id);
hidden: Ember.computed.or("category.deleted", "site.isMobileDevice"),
i18nPrefix: "category.notifications",
value: Em.computed.alias("category.notification_level"),
@computed("value")
icon() {
return `${this._super()}${iconHTML("caret-down")}`.htmlSafe();
},
generatedHeadertext: null,
actions: {
onSelectRow(content) {
this._super(content);
this.get("category").setNotification(this.get("value"));
}
}
});

View File

@ -26,6 +26,12 @@ export default SelectBoxComponent.extend({
this.set("content", this.get("categories"));
this._scopeCategories();
}
if (Ember.isNone(this.get("value"))) {
if (this.siteSettings.allow_uncategorized_topics && this.get("allowUncategorized") !== false) {
this.set("value", Category.findUncategorized().id);
}
}
},
filterFunction: function(content) {

View File

@ -376,7 +376,7 @@ export default Ember.Component.extend({
return resolve([translations[full]]);
}
const match = term.match(/^:?(.*?):t(\d)?$/);
const match = term.match(/^:?(.*?):t([2-6])?$/);
if (match) {
let name = match[1];
let scale = match[2];

View File

@ -0,0 +1,33 @@
import computed from "ember-addons/ember-computed-decorators";
import SelectBoxComponent from "discourse/components/select-box";
export default SelectBoxComponent.extend({
classNames: ["dropdown-select-box"],
wrapper: false,
verticalOffset: 3,
collectionHeight: "auto",
fullWidthOnMobile: true,
selectBoxHeaderComponent: "dropdown-select-box/dropdown-header",
@computed
templateForRow: function() {
return (rowComponent) => {
let template = "";
const content = rowComponent.get("content");
const icon = rowComponent.icon();
if (icon) {
template += `<div class="icons">${icon}</div>`;
}
template += `
<div class="texts">
<span class="title">${Handlebars.escapeExpression(Ember.get(content, this.get("textKey")))}</span>
<span class="desc">${Handlebars.escapeExpression(content.description)}</span>
</div>
`;
return template;
};
}
});

View File

@ -0,0 +1,7 @@
import SelectBoxHeaderComponent from "discourse/components/select-box/select-box-header";
export default SelectBoxHeaderComponent.extend({
layoutName: "components/dropdown-select-box/dropdown-header",
classNames: ["dropdown-header"]
});

View File

@ -1,11 +1,17 @@
import NotificationsButton from 'discourse/components/notifications-button';
import NotificationOptionsComponent from "discourse/components/notifications-button";
export default NotificationsButton.extend({
classNames: ['notification-options', 'group-notification-menu'],
notificationLevel: Em.computed.alias('group.group_user.notification_level'),
i18nPrefix: 'groups.notifications',
export default NotificationOptionsComponent.extend({
classNames: ["group-notifications-button"],
clicked(id) {
this.get('group').setNotification(id, this.get('user.id'));
value: Em.computed.alias("group.group_user.notification_level"),
i18nPrefix: "groups.notifications",
actions: {
onSelectRow(content) {
this._super(content);
this.get("group").setNotification(this.get("value"), this.get("user.id"));
}
}
});

View File

@ -3,5 +3,5 @@ import { showEntrance } from "discourse/components/topic-list-item";
export default Ember.Component.extend({
click: showEntrance,
attributeBindings: ['topic.id:data-topic-id'],
classNameBindings: [':latest-topic-list-item', 'topic.archived']
classNameBindings: [':latest-topic-list-item', 'topic.archived', 'topic.visited']
});

View File

@ -2,6 +2,6 @@ import { showEntrance } from 'discourse/components/topic-list-item';
export default Ember.Component.extend({
tagName: 'tr',
classNameBindings: [':category-topic-link', 'topic.archived'],
classNameBindings: [':category-topic-link', 'topic.archived', 'topic.visited'],
click: showEntrance
});

View File

@ -1,50 +1,68 @@
import DropdownButton from 'discourse/components/dropdown-button';
import { allLevels, buttonDetails } from 'discourse/lib/notification-levels';
import { iconHTML } from 'discourse-common/lib/icon-library';
import computed from 'ember-addons/ember-computed-decorators';
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
import { iconHTML } from "discourse-common/lib/icon-library";
import computed from "ember-addons/ember-computed-decorators";
import { buttonDetails } from "discourse/lib/notification-levels";
import { allLevels } from "discourse/lib/notification-levels";
export default DropdownButton.extend({
classNames: ['notification-options'],
title: '',
buttonIncludesText: true,
activeItem: Em.computed.alias('notificationLevel'),
i18nPrefix: '',
i18nPostfix: '',
export default DropdownSelectBoxComponent.extend({
classNames: ["notifications-button"],
i18nPrefix: "",
i18nPostfix: "",
textKey: "key",
showFullTitle: true,
fullWidthOnMobile: true,
content: allLevels,
value: Em.computed.alias("notificationLevel"),
@computed("selectedDetails")
icon(details) {
return iconHTML(details.icon, {class: details.key}).htmlSafe();
},
@computed("selectedDetails.key", "i18nPrefix")
selectedTitle(key, prefix) {
return I18n.t(`${prefix}.${key}.title`);
},
@computed("value")
selectedDetails(value) {
return buttonDetails(value);
},
@computed("selectedTitle", "showFullTitle")
generatedHeadertext(selectedTitle, showFullTitle) {
return showFullTitle ? selectedTitle : null;
},
@computed
dropDownContent() {
const prefix = this.get('i18nPrefix');
const postfix = this.get('i18nPostfix');
return allLevels.map(l => {
const start = `${prefix}.${l.key}${postfix}`;
return {
id: l.id,
title: I18n.t(`${start}.title`),
description: I18n.t(`${start}.description`),
icon: l.icon,
iconClass: l.key.dasherize(),
};
});
titleForRow: function() {
return (rowComponent) => {
const notificationLevel = rowComponent.get(`content.${this.get("idKey")}`);
const details = buttonDetails(notificationLevel);
return I18n.t(`${this.get("i18nPrefix")}.${details.key}.title`);
};
},
@computed('notificationLevel')
text(notificationLevel) {
const details = buttonDetails(notificationLevel);
const { key } = details;
const icon = iconHTML(details.icon, { class: key.dasherize() });
@computed
templateForRow: function() {
return (rowComponent) => {
const content = rowComponent.get("content");
const start = `${this.get("i18nPrefix")}.${content.key}${this.get("i18nPostfix")}`;
const title = I18n.t(`${start}.title`);
const description = I18n.t(`${start}.description`);
if (this.get('buttonIncludesText')) {
const prefix = this.get('i18nPrefix');
const postfix = this.get('i18nPostfix');
const text = I18n.t(`${prefix}.${key}${postfix}.title`);
return `${icon}&nbsp;${text}<span class='caret'></span>`;
} else {
return `${icon}&nbsp;<span class='caret'></span>`;
}
},
clicked(id) {
this.set("notificationLevel", id);
return `
<div class="icons">
<span class="selection-indicator"></span>
${iconHTML(content.icon, { class: content.key.dasherize() })}
</div>
<div class="texts">
<span class="title">${Handlebars.escapeExpression(title)}</span>
<span class="desc">${Handlebars.escapeExpression(description)}</span>
</div>
`;
};
}
});

View File

@ -1,67 +1,22 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import computed from 'ember-addons/ember-computed-decorators';
import DropdownButton from 'discourse/components/dropdown-button';
import computed from "ember-addons/ember-computed-decorators";
export default DropdownButton.extend({
descriptionKey: 'help',
classNames: ['pinned-options'],
title: '',
buttonExtraClasses: 'btn-icon-text',
export default Ember.Component.extend({
descriptionKey: "help",
longDescription: function(){
const topic = this.get('topic');
const globally = topic.get('pinned_globally') ? '_globally' : '';
const key = 'topic_statuses.' + (topic.get('pinned') ? 'pinned' + globally : 'unpinned') + '.help';
classNames: ["pinned-button"],
classNameBindings: ["hidden:is-hidden"],
@computed("topic.pinned_globally", "topic.pinned")
reasonText(pinnedGlobally, pinned) {
const globally = pinnedGlobally ? "_globally" : "";
const pinnedKey = pinned ? `pinned${globally}` : "unpinned";
const key = `topic_statuses.${pinnedKey}.help`;
return I18n.t(key);
}.property('topic.pinned'),
target: Em.computed.alias('topic'),
hidden: function(){
const topic = this.get('topic');
return topic.get('deleted') || (!topic.get('pinned') && !topic.get('unpinned'));
}.property('topic.pinned', 'topic.deleted', 'topic.unpinned'),
activeItem: function(){
return this.get('topic.pinned') ? 'pinned' : 'unpinned';
}.property('topic.pinned'),
dropDownContent: function() {
const globally = this.get('topic.pinned_globally') ? '_globally' : '';
return [
{id: 'pinned',
title: I18n.t('topic_statuses.pinned' + globally + '.title'),
description: I18n.t('topic_statuses.pinned' + globally + '.help'),
icon: 'thumb-tack' },
{id: 'unpinned',
title: I18n.t('topic_statuses.unpinned.title'),
description: I18n.t('topic_statuses.unpinned.help'),
icon: 'thumb-tack',
iconClass: 'unpinned' }
];
}.property(),
@computed('topic.pinned', 'topic.pinned_globally')
text(pinned, pinnedGlobally) {
const globally = pinnedGlobally ? '_globally' : '';
const state = pinned ? 'pinned' + globally : 'unpinned';
const icon = iconHTML(
'thumb-tack',
{ tagName: 'span', class: (state === 'unpinned' ? 'unpinned' : null) }
);
return icon +
I18n.t('topic_statuses.' + state + '.title') + "<span class='caret'></span>";
},
clicked(id) {
const topic = this.get('topic');
if(id==='unpinned'){
topic.clearPin();
} else {
topic.rePin();
}
@computed("topic.pinned", "topic.deleted", "topic.unpinned")
hidden(pinned, deleted, unpinned) {
return deleted || (!pinned && !unpinned);
}
});

View File

@ -0,0 +1,73 @@
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
import computed from "ember-addons/ember-computed-decorators";
import { observes } from "ember-addons/ember-computed-decorators";
import { iconHTML } from "discourse-common/lib/icon-library";
export default DropdownSelectBoxComponent.extend({
classNames: ["pinned-options"],
@computed("topic.pinned")
value(pinned) {
return pinned ? "pinned" : "unpinned";
},
@observes("topic.pinned")
_pinnedChanged() {
this.set("value", this.get("topic.pinned") ? "pinned" : "unpinned");
},
@computed("topic.pinned_globally")
content(pinnedGlobally) {
const globally = pinnedGlobally ? "_globally" : "";
return [
{
id: "pinned",
text: I18n.t("topic_statuses.pinned" + globally + ".title"),
description: I18n.t('topic_statuses.pinned' + globally + '.help'),
icon: "thumb-tack"
},
{
id: "unpinned",
text: I18n.t("topic_statuses.unpinned.title"),
icon: "thumb-tack",
description: I18n.t('topic_statuses.unpinned.help'),
iconClass: "unpinned"
}
];
},
@computed("topic.pinned", "topic.pinned_globally")
icon(pinned, pinnedGlobally) {
const globally = pinnedGlobally ? "_globally" : "";
const state = pinned ? `pinned${globally}` : "unpinned";
return iconHTML(
"thumb-tack",
{ class: (state === "unpinned" ? "unpinned" : null) }
);
},
@computed("topic.pinned", "topic.pinned_globally")
generatedHeadertext(pinned, pinnedGlobally) {
const globally = pinnedGlobally ? "_globally" : "";
const state = pinned ? `pinned${globally}` : "unpinned";
const title = I18n.t(`topic_statuses.${state}.title`);
return `${title}${iconHTML("caret-down")}`.htmlSafe();
},
actions: {
onSelectRow(content) {
this._super(content);
const topic = this.get("topic");
if (this.get("value") === "unpinned") {
topic.clearPin();
} else {
topic.rePin();
}
}
}
});

View File

@ -1,18 +1,17 @@
import { on, observes } from "ember-addons/ember-computed-decorators";
import computed from "ember-addons/ember-computed-decorators";
import { iconHTML } from "discourse-common/lib/icon-library";
export default Ember.Component.extend({
layoutName: "components/select-box",
classNames: "select-box",
classNameBindings: ["expanded:is-expanded"],
classNameBindings: ["expanded:is-expanded", "hidden:is-hidden"],
expanded: false,
focused: false,
filterFocused: false,
renderBody: false,
wrapper: true,
hidden: false,
tabindex: 0,
scrollableParentSelector: ".modal-body",
@ -24,9 +23,9 @@ export default Ember.Component.extend({
clearable: false,
value: null,
highlightedValue: null,
selectedContent: null,
noContentLabel: I18n.t("select_box.no_content"),
lastHovered: null,
clearSelectionLabel: null,
idKey: "id",
@ -43,13 +42,17 @@ export default Ember.Component.extend({
selectBoxHeaderComponent: "select-box/select-box-header",
selectBoxCollectionComponent: "select-box/select-box-collection",
minWidth: 220,
maxCollectionHeight: 200,
collectionHeight: 200,
verticalOffset: 0,
horizontalOffset: 0,
fullWidthOnMobile: false,
castInteger: false,
click(event) {
event.stopPropagation();
},
filterFunction: function(content) {
return (selectBox) => {
const filter = selectBox.get("filter").toLowerCase();
@ -59,33 +62,37 @@ export default Ember.Component.extend({
};
},
@computed
titleForRow: function() {
return (rowComponent) => {
return rowComponent.get(`content.${this.get("textKey")}`);
};
}.property(),
},
@computed
shouldHighlightRow: function() {
return (rowComponent) => {
if (Ember.isNone(this.get("value")) && Ember.isNone(this.get("lastHovered"))) {
return false;
}
const id = this._castInteger(rowComponent.get(`content.${this.get("idKey")}`));
if (Ember.isNone(this.get("lastHovered"))) {
return id === this.get("value");
} else {
return id === this.get("lastHovered");
}
return id === this.get("highlightedValue");
};
}.property(),
},
@computed("value", "idKey")
shouldSelectRow(value, idKey) {
return (rowComponent) => {
const id = this._castInteger(rowComponent.get(`content.${idKey}`));
return id === value;
};
},
@computed
templateForRow: function() {
return (rowComponent) => {
let template = "";
if (rowComponent.get("content.icon")) {
template += iconHTML(Handlebars.escapeExpression(rowComponent.get("content.icon")));
const icon = rowComponent.icon();
if (icon) {
template += icon;
}
const text = rowComponent.get(`content.${this.get("textKey")}`);
@ -93,29 +100,50 @@ export default Ember.Component.extend({
return template;
};
}.property(),
},
applyDirection() {
const offsetTop = this.$()[0].getBoundingClientRect().top;
const windowHeight = $(window).height();
this.$().removeClass("is-above is-below is-left-aligned is-right-aligned");
let options = { left: "auto", bottom: "auto", left: "auto", top: "auto" };
const headerHeight = this.$(".select-box-header").outerHeight(false);
const filterHeight = this.$(".select-box-filter").outerHeight(false);
const bodyHeight = this.$(".select-box-body").outerHeight(false);
const windowWidth = $(window).width();
const windowHeight = $(window).height();
const boundingRect = this.$()[0].getBoundingClientRect();
const offsetTop = boundingRect.top;
if (windowHeight - (offsetTop + this.get("maxCollectionHeight") + filterHeight + headerHeight) < 0) {
this.$().addClass("is-reversed");
this.$(".select-box-body").css({
left: this.get("horizontalOffset"),
top: "auto",
bottom: headerHeight + this.get("verticalOffset")
});
if (this.get("fullWidthOnMobile") && this.site.isMobileDevice) {
const margin = 10;
const relativeLeft = this.$().offset().left - $(window).scrollLeft();
options.left = margin - relativeLeft;
options.width = windowWidth - margin * 2;
options.maxWidth = options.minWidth = "unset";
} else {
this.$().removeClass("is-reversed");
this.$(".select-box-body").css({
left: this.get("horizontalOffset"),
top: headerHeight + this.get("verticalOffset"),
bottom: "auto"
});
const offsetLeft = boundingRect.left;
const bodyWidth = this.$(".select-box-body").outerWidth(false);
const hasRightSpace = (windowWidth - (this.get("horizontalOffset") + offsetLeft + filterHeight + bodyWidth) > 0);
if (hasRightSpace) {
this.$().addClass("is-left-aligned");
options.left = this.get("horizontalOffset");
} else {
this.$().addClass("is-right-aligned");
options.right = this.get("horizontalOffset");
}
}
const componentHeight = this.get("verticalOffset") + bodyHeight + headerHeight;
const hasBelowSpace = windowHeight - offsetTop - componentHeight > 0;
if (hasBelowSpace) {
this.$().addClass("is-below");
options.top = headerHeight + this.get("verticalOffset");
} else {
this.$().addClass("is-above");
options.bottom = headerHeight + this.get("verticalOffset");
}
this.$(".select-box-body").css(options);
},
init() {
@ -136,7 +164,7 @@ export default Ember.Component.extend({
@on("willDestroyElement")
_removeDocumentListeners: function() {
$(document).off("click.select-box", "keydown.select-box");
$(document).off("click.select-box");
$(window).off("resize.select-box");
},
@ -156,12 +184,9 @@ export default Ember.Component.extend({
this._removeFixedPosition();
}
this.$().css("min-width", this.get("minWidth"));
const computedWidth = this.$().outerWidth(false);
const computedHeight = this.$().outerHeight(false);
this.$(".select-box-header").css("height", computedHeight);
this.$(".select-box-filter").css("height", computedHeight);
if (this.get("expanded")) {
@ -169,14 +194,14 @@ export default Ember.Component.extend({
this._applyFixedPosition(computedWidth, computedHeight);
}
this.$(".select-box-body").css("width", computedWidth);
this.$(".select-box-collection").css("max-height", this.get("maxCollectionHeight"));
this.$(".select-box-collection").css("max-height", this.get("collectionHeight"));
this.applyDirection();
if (this.get("wrapper")) {
this._positionSelectBoxWrapper();
}
Ember.run.schedule("afterRender", () => {
this.applyDirection();
if (this.get("wrapper")) {
this._positionSelectBoxWrapper();
}
});
} else {
if (this.get("wrapper")) {
this.$(".select-box-wrapper").hide();
@ -184,8 +209,42 @@ export default Ember.Component.extend({
}
},
keyDown(event) {
const keyCode = event.keyCode || event.which;
if (this.get("expanded")) {
if ((keyCode === 13 || keyCode === 9) && Ember.isPresent(this.get("highlightedValue"))) {
event.preventDefault();
this.send("onSelectRow", this.get("highlightedContent"));
}
if (keyCode === 9) {
this.set("expanded", false);
}
if (keyCode === 27) {
this.set("expanded", false);
event.stopPropagation();
}
if (keyCode === 38) {
event.preventDefault();
const self = this;
Ember.run.throttle(self, this._handleUpArrow, 50);
}
if (keyCode === 40) {
event.preventDefault();
const self = this;
Ember.run.throttle(self, this._handleDownArrow, 50);
}
}
},
@on("didRender")
_setupDocumentListeners: function() {
$(document).off("click.select-box");
$(document)
.on("click.select-box", (event) => {
if (this.isDestroying || this.isDestroyed) { return; }
@ -196,13 +255,6 @@ export default Ember.Component.extend({
if (!$target.closest($element).length) {
this.set("expanded", false);
}
})
.on("keydown.select-box", (event) => {
const keyCode = event.keyCode || event.which;
if (this.get("expanded") && keyCode === 9) {
this.set("expanded", false);
}
});
$(window).on("resize.select-box", () => this.set("expanded", false) );
@ -222,17 +274,12 @@ export default Ember.Component.extend({
const keyCode = event.keyCode || event.which;
if (keyCode === 13 || keyCode === 40) {
this.setProperties({expanded: true, focused: false});
return false;
}
if (keyCode === 27) {
this.$(".select-box-offscreen").blur();
return false;
this.setProperties({ expanded: true, focused: false });
event.stopPropagation();
}
if (keyCode >= 65 && keyCode <= 90) {
this.setProperties({expanded: true, focused: false});
this.setProperties({ expanded: true, focused: false });
Ember.run.schedule("afterRender", () => {
this.$(".filter-query").focus().val(String.fromCharCode(keyCode));
});
@ -243,7 +290,7 @@ export default Ember.Component.extend({
@observes("expanded")
_expandedChanged: function() {
if (this.get("expanded")) {
this.setProperties({ focused: false, renderBody: true });
this.setProperties({ highlightedValue: null, renderBody: true, focused: false });
if (this.get("filterable")) {
Ember.run.schedule("afterRender", () => this.$(".filter-query").focus());
@ -251,17 +298,37 @@ export default Ember.Component.extend({
};
},
@computed("value", "content.[]")
selectedContent(value, content) {
@computed("value", "content.[]", "idKey")
selectedContent(value, content, idKey) {
if (Ember.isNone(value)) {
return null;
}
return content.find((c) => {
return this._castInteger(c[this.get("idKey")]) === value;
return this._castInteger(Ember.get(c, idKey)) === value;
});
},
@computed("highlightedValue", "content.[]", "idKey")
highlightedContent(highlightedValue, content, idKey) {
if (Ember.isNone(highlightedValue)) {
return null;
}
return content.find((c) => {
return this._castInteger(Ember.get(c, idKey)) === highlightedValue;
});
},
@computed("headerText", "selectedContent", "textKey")
selectedTitle(headerText, selectedContent, textKey) {
if (Ember.isNone(selectedContent)) {
return headerText;
}
return selectedContent[textKey];
},
@computed("headerText", "dynamicHeaderText", "selectedContent", "textKey", "clearSelectionLabel")
generatedHeadertext(headerText, dynamic, selectedContent, textKey, clearSelectionLabel) {
if (dynamic && !Ember.isNone(selectedContent)) {
@ -275,14 +342,18 @@ export default Ember.Component.extend({
return headerText;
},
@computed("content.[]", "filter")
filteredContent(content, filter) {
@computed("content.[]", "filter", "idKey")
filteredContent(content, filter, idKey) {
let filteredContent;
if (Ember.isEmpty(filter)) {
filteredContent = content;
} else {
filteredContent = this.filterFunction(content)(this);
if (!Ember.isEmpty(filteredContent)) {
this.set("highlightedValue", filteredContent[0][idKey]);
}
}
return filteredContent;
@ -302,19 +373,20 @@ export default Ember.Component.extend({
this.set("filter", filter);
},
onHoverRow(content) {
const id = this._castInteger(Ember.get(content, this.get("idKey")));
this.set("highlightedValue", id);
},
onSelectRow(content) {
this.setProperties({
value: this._castInteger(content[this.get("idKey")]),
value: this._castInteger(Ember.get(content, this.get("idKey"))),
expanded: false
});
},
onClearSelection() {
this.setProperties({ value: null, expanded: false });
},
onHoverRow(content) {
this.set("lastHovered", this._castInteger(content[this.get("idKey")]));
}
},
@ -362,5 +434,45 @@ export default Ember.Component.extend({
});
this.get("scrollableParent").off("scroll.select-box");
},
_handleDownArrow() {
this._handleArrow("down");
},
_handleUpArrow() {
this._handleArrow("up");
},
_handleArrow(direction) {
const content = this.get("filteredContent");
const idKey = this.get("idKey");
const selectedContent = content.findBy(idKey, this.get("highlightedValue"));
const currentIndex = content.indexOf(selectedContent);
if (direction === "down") {
if (currentIndex < 0) {
this.set("highlightedValue", this._castInteger(Ember.get(content[0], idKey)));
} else if(currentIndex + 1 < content.length) {
this.set("highlightedValue", this._castInteger(Ember.get(content[currentIndex + 1], idKey)));
}
} else {
if (currentIndex <= 0) {
this.set("highlightedValue", this._castInteger(Ember.get(content[0], idKey)));
} else if(currentIndex - 1 < content.length) {
this.set("highlightedValue", this._castInteger(Ember.get(content[currentIndex - 1], idKey)));
}
}
Ember.run.schedule("afterRender", () => {
const $highlightedRow = this.$(".select-box-row.is-highlighted");
if ($highlightedRow.length === 0) { return; }
const $collection = this.$(".select-box-collection");
const rowOffset = $highlightedRow.offset();
const bodyOffset = $collection.offset();
$collection.scrollTop(rowOffset.top - bodyOffset.top);
});
}
});

View File

@ -9,8 +9,9 @@ export default Ember.Component.extend({
this._setCaretIcon();
},
click() {
click(event) {
this.sendAction("onToggle");
event.stopPropagation();
},
_setCaretIcon() {

View File

@ -1,4 +1,5 @@
import computed from 'ember-addons/ember-computed-decorators';
import { iconHTML } from "discourse-common/lib/icon-library";
export default Ember.Component.extend({
layoutName: "components/select-box/select-box-row",
@ -9,7 +10,7 @@ export default Ember.Component.extend({
attributeBindings: ["title"],
classNameBindings: ["isHighlighted:is-highlighted"],
classNameBindings: ["isHighlighted:is-highlighted", "isSelected:is-selected"],
@computed("titleForRow")
title(titleForRow) {
@ -21,11 +22,26 @@ export default Ember.Component.extend({
return templateForRow(this);
},
@computed("shouldHighlightRow", "lastHovered", "value")
@computed("shouldHighlightRow", "highlightedValue")
isHighlighted(shouldHighlightRow) {
return shouldHighlightRow(this);
},
@computed("shouldSelectRow", "value")
isSelected(shouldSelectRow) {
return shouldSelectRow(this);
},
icon() {
if (this.get("content.icon")) {
const iconName = this.get("content.icon");
const iconClass = this.get("content.iconClass");
return iconHTML(iconName, { class: iconClass });
}
return null;
},
mouseEnter() {
this.sendAction("onHover", this.get("content"));
},

View File

@ -62,6 +62,7 @@ const SiteHeaderComponent = MountWidget.extend(Docking, {
this.dispatch('notifications:changed', 'user-notifications');
this.dispatch('header:keyboard-trigger', 'header');
this.dispatch('search-autocomplete:after-complete', 'search-term');
this.appEvents.on('dom:clean', () => {
// For performance, only trigger a re-render if any menu panels are visible

View File

@ -22,7 +22,7 @@ export default Ember.Component.extend({
@computed('expanded')
expandedIcon(expanded) {
return expanded ? 'caret-down' : 'caret-right';
return expanded ? 'd-drop-expanded' : 'd-drop-collapsed';
},
@computed('tagId')

View File

@ -1,11 +1,24 @@
import NotificationsButton from 'discourse/components/notifications-button';
import NotificationOptionsComponent from "discourse/components/notifications-button";
import computed from "ember-addons/ember-computed-decorators";
import { iconHTML } from "discourse-common/lib/icon-library";
export default NotificationsButton.extend({
classNames: ['notification-options', 'tag-notification-menu'],
buttonIncludesText: false,
i18nPrefix: 'tagging.notifications',
export default NotificationOptionsComponent.extend({
classNames: ["tag-notifications-button"],
clicked(id) {
this.sendAction('action', id);
i18nPrefix: "tagging.notifications",
@computed("value")
icon() {
return `${this._super()}${iconHTML("caret-down")}`.htmlSafe();
},
generatedHeadertext: null,
actions: {
onSelectRow(content) {
this._super(content);
this.sendAction("action", this.get("value"));
}
}
});

View File

@ -15,7 +15,7 @@ export default Ember.Component.extend({
@computed
showAdminButton() {
return !this.site.mobileView && this.currentUser.get('canManageTopic');
return !this.site.mobileView && this.currentUser && this.currentUser.get('canManageTopic');
},
@computed('topic.message_archived')

View File

@ -8,7 +8,7 @@ export default SelectBoxComponent.extend({
dynamicHeaderText: false,
maxCollectionHeight: 300,
collectionHeight: 300,
init() {
this._super();

View File

@ -23,7 +23,7 @@ export function showEntrance(e) {
export default Ember.Component.extend(bufferedRender({
rerenderTriggers: ['bulkSelectEnabled', 'topic.pinned'],
tagName: 'tr',
classNameBindings: [':topic-list-item', 'unboundClassNames'],
classNameBindings: [':topic-list-item', 'unboundClassNames', 'topic.visited'],
attributeBindings: ['data-topic-id'],
'data-topic-id': Em.computed.alias('topic.id'),

View File

@ -1,21 +1,9 @@
import MountWidget from 'discourse/components/mount-widget';
import { observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
layoutName: "components/topic-notifications-button",
export default MountWidget.extend({
classNames: ['topic-notifications-container'],
widget: 'topic-notifications-button',
classNames: ["topic-notifications-button"],
buildArgs() {
return { topic: this.get('topic'), appendReason: true, showFullTitle: true };
},
showFullTitle: true,
@observes('topic.details.notification_level')
_queueRerender() {
this.queueRerender();
},
didInsertElement() {
this._super();
this.dispatch('topic-notifications-button:changed', 'topic-notifications-button');
}
appendReason: true,
});

View File

@ -0,0 +1,52 @@
import NotificationOptionsComponent from "discourse/components/notifications-button";
import { on } from "ember-addons/ember-computed-decorators";
import computed from "ember-addons/ember-computed-decorators";
import { topicLevels, buttonDetails } from "discourse/lib/notification-levels";
export default NotificationOptionsComponent.extend({
classNames: ["topic-notifications-options"],
content: topicLevels,
i18nPrefix: "topic.notifications",
value: Ember.computed.alias("topic.details.notification_level"),
@on("didInsertElement")
_bindGlobalLevelChanged() {
this.appEvents.on("topic-notifications-button:changed", (msg) => {
if (msg.type === "notification") {
if (this.get("value") !== msg.id) {
this.get("topic.details").updateNotifications(msg.id);
}
}
});
},
@on("willDestroyElement")
_unbindGlobalLevelChanged() {
this.appEvents.off("topic-notifications-button:changed");
},
@computed("value", "showFullTitle")
generatedHeadertext(value, showFullTitle) {
if (showFullTitle) {
const details = buttonDetails(value);
return I18n.t(`topic.notifications.${details.key}.title`);
} else {
return null;
}
},
actions: {
onSelectRow(content) {
const notificationLevelId = Ember.get(content, this.get("idKey"));
if (notificationLevelId !== this.get("value")) {
this.get("topic.details").updateNotifications(notificationLevelId);
}
this._super(content);
}
}
});

View File

@ -84,6 +84,5 @@ export default MountWidget.extend(Docking, {
}
this.dispatch('topic:current-post-scrolled', 'timeline-scrollarea');
this.dispatch('topic-notifications-button:changed', 'topic-notifications-button');
}
});

View File

@ -4,7 +4,7 @@ import Category from 'discourse/models/category';
export default Ember.Component.extend(bufferedRender({
classNames: ['topic-status-info'],
delayedRerender: null,
_delayedRerender: null,
rerenderTriggers: [
'statusType',
@ -58,12 +58,14 @@ export default Ember.Component.extend(bufferedRender({
buffer.push('</h3>');
// TODO Sam: concerned this can cause a heavy rerender loop
this.set('delayedRerender', Em.run.later(this, this.rerender, rerenderDelay));
if (!Ember.testing) {
this._delayedRerender = Ember.run.later(this, this.rerender, rerenderDelay);
}
},
willDestroyElement() {
if( this.delayedRerender ) {
Em.run.cancel(this.get('delayedRerender'));
if (this._delayedRerender) {
Em.run.cancel(this._delayedRerender);
}
},

View File

@ -25,7 +25,7 @@ function addBulkButton(action, key, opts) {
addBulkButton('showChangeCategory', 'change_category', {icon: 'pencil'});
addBulkButton('closeTopics', 'close_topics', {icon: 'lock'});
addBulkButton('archiveTopics', 'archive_topics', {icon: 'folder'});
addBulkButton('showNotificationLevel', 'notification_level', {icon: 'circle-o'});
addBulkButton('showNotificationLevel', 'notification_level', {icon: 'd-regular'});
addBulkButton('resetRead', 'reset_read', {icon: 'backward'});
addBulkButton('unlistTopics', 'unlist_topics', {
icon: 'eye-slash',

View File

@ -827,7 +827,7 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
this.messageBus.subscribe(`/topic/${this.get('model.id')}`, data => {
const topic = this.get('model');
if (data.notification_level_change) {
if (Ember.isPresent(data.notification_level_change)) {
topic.set('details.notification_level', data.notification_level_change);
topic.set('details.notifications_reason_id', data.notifications_reason_id);
return;

View File

@ -7,10 +7,6 @@ registerUnbound('topic-link', (topic, args) => {
topic.get('lastUnreadUrl');
const classes = ['title'];
if (topic.get('last_read_post_number') === topic.get('highest_post_number')) {
classes.push('visited');
}
if (args.class) {
args.class.split(" ").forEach(c => classes.push(c));
}

View File

@ -9,15 +9,15 @@ export const NotificationLevels = { WATCHING_FIRST_POST, WATCHING, TRACKING, REG
export function buttonDetails(level) {
switch(level) {
case WATCHING_FIRST_POST:
return { id: WATCHING_FIRST_POST, key: 'watching_first_post', icon: 'dot-circle-o' };
return { id: WATCHING_FIRST_POST, key: 'watching_first_post', icon: 'd-watching-first' };
case WATCHING:
return { id: WATCHING, key: 'watching', icon: 'exclamation-circle' };
return { id: WATCHING, key: 'watching', icon: 'd-watching' };
case TRACKING:
return { id: TRACKING, key: 'tracking', icon: 'circle' };
return { id: TRACKING, key: 'tracking', icon: 'd-tracking' };
case MUTED:
return { id: MUTED, key: 'muted', icon: 'times-circle' };
return { id: MUTED, key: 'muted', icon: 'd-muted' };
default:
return { id: REGULAR, key: 'regular', icon: 'circle-o' };
return { id: REGULAR, key: 'regular', icon: 'd-regular' };
}
}

View File

@ -35,6 +35,7 @@ export function transformBasicPost(post) {
primary_group_flair_bg_color: post.primary_group_flair_bg_color,
primary_group_flair_color: post.primary_group_flair_color,
wiki: post.wiki,
lastWikiEdit: post.last_wiki_edit,
firstPost: post.post_number === 1,
post_number: post.post_number,
cooked: post.cooked,

View File

@ -2,6 +2,9 @@ import { ajax } from 'discourse/lib/ajax';
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
import GroupHistory from 'discourse/models/group-history';
import RestModel from 'discourse/models/rest';
import Category from "discourse/models/category";
import User from "discourse/models/user";
import Topic from "discourse/models/topic";
const Group = RestModel.extend({
limit: 50,
@ -44,9 +47,9 @@ const Group = RestModel.extend({
if (ownerIds[member.id]) {
member.owner = true;
}
return Discourse.User.create(member);
return User.create(member);
}),
owners: result.owners.map(owner => Discourse.User.create(owner)),
owners: result.owners.map(owner => User.create(owner)),
});
});
},
@ -207,8 +210,9 @@ const Group = RestModel.extend({
return ajax(`/groups/${this.get('name')}/${type}.json`, { data: data }).then(posts => {
return posts.map(p => {
p.user = Discourse.User.create(p.user);
p.topic = Discourse.Topic.create(p.topic);
p.user = User.create(p.user);
p.topic = Topic.create(p.topic);
p.category = Category.findById(p.category_id);
return Em.Object.create(p);
});
});

View File

@ -33,6 +33,11 @@ const Topic = RestModel.extend({
message: null,
errorLoading: false,
@computed('last_read_post_number', 'highest_post_number')
visited(lastReadPostNumber, highestPostNumber) {
return lastReadPostNumber === highestPostNumber;
},
@computed('posters.firstObject')
creator(poster){
return poster && poster.user;
@ -407,6 +412,10 @@ const Topic = RestModel.extend({
});
},
@computed('excerpt')
escapedExcerpt(excerpt) {
return emojiUnescape(excerpt);
},
hasExcerpt: Em.computed.notEmpty('excerpt'),

View File

@ -34,7 +34,11 @@ export default Discourse.Route.extend({
});
} else {
$.cookie('destination_url', window.location.href);
this.replaceWith('login');
if (Discourse.showingSignup) {
Discourse.showingSignup = false;
} else {
self.replaceWith('login');
}
}
}

View File

@ -14,7 +14,12 @@ export default Discourse.Route.extend({
} else {
// User is not logged in
$.cookie('destination_url', window.location.href);
self.replaceWith('login');
if (Discourse.showingSignup) {
// We're showing the sign up modal
Discourse.showingSignup = false;
} else {
self.replaceWith('login');
}
}
}
});

View File

@ -4,7 +4,7 @@
{{#if category.read_restricted}}
{{d-icon "lock"}}
{{/if}}
{{category.name}}
<span class='d-label'>{{category.name}}</span>
</a>
{{else}}
{{#if noSubcategories}}

View File

@ -0,0 +1,12 @@
<button
class="btn {{if text 'btn-icon-text' 'no-text btn-icon'}}"
aria-label="{{selectedTitle}}"
type="button"
title="{{selectedTitle}}">
{{{icon}}}
{{#if text}}
<span class="d-button-label">{{{text}}}</span>
{{/if}}
</button>

View File

@ -0,0 +1,5 @@
{{pinned-options topic=topic}}
<p class="reason">
{{{reasonText}}}
</p>

View File

@ -9,6 +9,7 @@
{{component selectBoxHeaderComponent
text=generatedHeadertext
selectedTitle=selectedTitle
focused=focused
caretUpIcon=caretUpIcon
caretDownIcon=caretDownIcon
@ -26,6 +27,7 @@
icon=filterIcon
focused=filterFocused
placeholder=filterPlaceholder
tabindex=tabindex
}}
{{/if}}
@ -35,12 +37,13 @@
selectBoxRowComponent=selectBoxRowComponent
templateForRow=templateForRow
shouldHighlightRow=shouldHighlightRow
shouldSelectRow=shouldSelectRow
titleForRow=titleForRow
lastHovered=lastHovered
onSelectRow=(action "onSelectRow")
onHoverRow=(action "onHoverRow")
onClearSelection=(action "onClearSelection")
noContentLabel=noContentLabel
highlightedValue=highlightedValue
value=value
}}
{{/if}}

View File

@ -11,7 +11,8 @@
templateForRow=templateForRow
titleForRow=titleForRow
shouldHighlightRow=shouldHighlightRow
lastHovered=lastHovered
shouldSelectRow=shouldSelectRow
highlightedValue=highlightedValue
onSelect=onSelectRow
onHover=onHoverRow
value=value

View File

@ -1,5 +1,5 @@
{{input
tabindex="-1"
tabindex=tabindex
class="filter-query"
placeholder=placeholder
key-up=onFilterChange

View File

@ -2,7 +2,7 @@
{{d-icon icon class="icon"}}
{{/if}}
<span class="current-selection">
<span class="current-selection" title={{selectedTitle}}>
{{text}}
</span>

View File

@ -0,0 +1,7 @@
{{topic-notifications-options topic=topic showFullTitle=showFullTitle}}
{{#if appendReason}}
<p class="reason">
{{{topic.details.notificationReasonText}}}
</p>
{{/if}}

View File

@ -1,6 +1,6 @@
{{#if topic.hasExcerpt}}
<div class="topic-excerpt">
{{{topic.excerpt}}}
{{{topic.escapedExcerpt}}}
{{#if topic.excerptTruncated}}
<a href="{{topic.url}}">{{i18n 'read_more'}}</a>
{{/if}}

View File

@ -3,7 +3,7 @@
<table class="topic-list">
<tbody>
{{#each topics as |t|}}
<tr class="{{if t.archived 'archived'}}" data-topic-id={{t.id}}>
<tr class="{{if t.archived 'archived'}} {{if t.visited 'visited'}}" data-topic-id={{t.id}}>
<td>
<div class='main-link'>
{{topic-status topic=t}}

View File

@ -33,4 +33,3 @@
{{plugin-outlet name="category-navigation" args=(hash category=category)}}
</div>
{{/d-section}}

View File

@ -3,7 +3,7 @@
<label class="control-label">{{i18n 'user.categories_settings'}}</label>
<div class="controls category-controls">
<label>{{d-icon "exclamation-circle" class="icon watching"}} {{i18n 'user.watched_categories'}}</label>
<label>{{d-icon "d-watching" class="icon watching"}} {{i18n 'user.watched_categories'}}</label>
{{category-selector categories=model.watchedCategories blacklist=selectedCategories}}
</div>
<div class="instructions">{{i18n 'user.watched_categories_instructions'}}</div>
@ -12,7 +12,7 @@
</div>
<div class="controls category-controls">
<label>{{d-icon "circle" class="icon tracking"}} {{i18n 'user.tracked_categories'}}</label>
<label>{{d-icon "d-tracking" class="icon tracking"}} {{i18n 'user.tracked_categories'}}</label>
{{category-selector categories=model.trackedCategories blacklist=selectedCategories}}
</div>
<div class="instructions">{{i18n 'user.tracked_categories_instructions'}}</div>
@ -21,13 +21,13 @@
</div>
<div class="controls category-controls">
<label>{{d-icon "dot-circle-o" class="icon watching-first-post"}} {{i18n 'user.watched_first_post_categories'}}</label>
<label>{{d-icon "d-watching-first" class="icon watching-first-post"}} {{i18n 'user.watched_first_post_categories'}}</label>
{{category-selector categories=model.watchedFirstPostCategories}}
</div>
<div class="instructions">{{i18n 'user.watched_first_post_categories_instructions'}}</div>
<div class="controls category-controls">
<label>{{d-icon "times-circle" class="icon muted"}} {{i18n 'user.muted_categories'}}</label>
<label>{{d-icon "d-muted" class="icon muted"}} {{i18n 'user.muted_categories'}}</label>
{{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
</div>
<div class="instructions">{{i18n 'user.muted_categories_instructions'}}</div>

View File

@ -29,7 +29,7 @@
<div class="control-group muting">
<label class="control-label">{{i18n 'user.users'}}</label>
<div class="controls category-controls">
<label>{{d-icon "times-circle" class="muted icon"}} {{i18n 'user.muted_users'}}</label>
<label>{{d-icon "d-muted" class="muted icon"}} {{i18n 'user.muted_users'}}</label>
{{user-selector excludeCurrentUser=true usernames=model.muted_usernames class="user-selector"}}
</div>
<div class="instructions">{{i18n 'user.muted_users_instructions'}}</div>

View File

@ -4,25 +4,25 @@
<label class="control-label">{{i18n 'user.tag_settings'}}</label>
<div class="controls tag-controls">
<label>{{d-icon "exclamation-circle" class="icon watching"}} {{i18n 'user.watched_tags'}}</label>
<label>{{d-icon "d-watching" class="icon watching"}} {{i18n 'user.watched_tags'}}</label>
{{tag-chooser tags=model.watched_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
</div>
<div class="instructions">{{i18n 'user.watched_tags_instructions'}}</div>
<div class="controls tag-controls">
<label>{{d-icon "circle" class="icon tracking"}} {{i18n 'user.tracked_tags'}}</label>
<label>{{d-icon "d-regular" class="icon tracking"}} {{i18n 'user.tracked_tags'}}</label>
{{tag-chooser tags=model.tracked_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
</div>
<div class="instructions">{{i18n 'user.tracked_tags_instructions'}}</div>
<div class="controls tag-controls">
<label>{{d-icon "dot-circle-o" class="icon watching-first-post"}} {{i18n 'user.watched_first_post_tags'}}</label>
<label>{{d-icon "d-watching-first" class="icon watching-first-post"}} {{i18n 'user.watched_first_post_tags'}}</label>
{{tag-chooser tags=model.watching_first_post_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
</div>
<div class="instructions">{{i18n 'user.watched_first_post_tags_instructions'}}</div>
<div class="controls tag-controls">
<label>{{d-icon "times-circle" class="icon muted"}} {{i18n 'user.muted_tags'}}</label>
<label>{{d-icon "d-muted" class="icon muted"}} {{i18n 'user.muted_tags'}}</label>
{{tag-chooser tags=model.muted_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
</div>
<div class="instructions">{{i18n 'user.muted_tags_instructions'}}</div>

View File

@ -3,8 +3,11 @@
<p>
{{{stopNotificiationsText}}}
</p>
<p>
{{i18n "topic.unsubscribe.change_notification_state"}} {{topic-notifications-button topic=model}}
{{i18n "topic.unsubscribe.change_notification_state"}}
</p>
{{topic-notifications-button topic=model}}
</div>
</div>

View File

@ -116,7 +116,6 @@
<div style='clear: both'></div>
</div>
{{#unless collapsedInfo}}
<div class='secondary'>
<dl>

View File

@ -0,0 +1,35 @@
export default class ComponentConnector {
constructor(widget, componentName, opts) {
this.widget = widget;
this.opts = opts;
this.componentName = componentName;
}
init() {
const $elem = $('<div style="display: inline-block;" class="widget-component-connector"></div>');
const elem = $elem[0];
const { opts, widget, componentName } = this;
Ember.run.next(() => {
const mounted = widget._findView();
const view = widget
.register
.lookupFactory(`component:${componentName}`)
.create(opts);
if (Ember.setOwner) {
Ember.setOwner(view, Ember.getOwner(mounted));
}
mounted._connected.push(view);
view.renderer.appendTo(view, $elem[0]);
});
return elem;
}
update() { }
}
ComponentConnector.prototype.type = 'Widget';

View File

@ -2,21 +2,21 @@ import PostCooked from 'discourse/widgets/post-cooked';
import DecoratorHelper from 'discourse/widgets/decorator-helper';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import { iconNode } from 'discourse-common/lib/icon-library';
import DiscourseURL from 'discourse/lib/url';
import hbs from 'discourse/widgets/hbs-compiler';
createWidget('post-link-arrow', {
html(attrs) {
if (attrs.above) {
return h('a.post-info.arrow', {
attributes: { title: I18n.t('topic.jump_reply_up') }
}, iconNode('arrow-up'));
} else {
return h('a.post-info.arrow', {
attributes: { title: I18n.t('topic.jump_reply_down') }
}, iconNode('arrow-down'));
}
},
template: hbs`
{{#if attrs.above}}
<a class="post-info arrow" title={{i18n "topic.jump_reply_up"}}>
{{fa-icon "arrow-up"}}
</a>
{{else}}
<a class="post-info arrow" title={{i18n "topic.jump_reply_down"}}>
{{fa-icon "arrow-down"}}
</a>
{{/if}}
`,
click() {
DiscourseURL.routeTo(this.attrs.shareUrl);

View File

@ -0,0 +1,3 @@
export default function hbs() {
console.log('Templates should be precompiled server side');
}

View File

@ -28,23 +28,26 @@ createWidget('header-notifications', {
},
html(attrs) {
const { currentUser } = this;
const { user } = attrs;
const contents = [ avatarImg(this.settings.avatarSize, {
template: currentUser.get('avatar_template'),
username: currentUser.get('username')
template: user.get('avatar_template'),
username: user.get('username')
}) ];
const unreadNotifications = currentUser.get('unread_notifications');
const unreadNotifications = user.get('unread_notifications');
if (!!unreadNotifications) {
contents.push(this.attach('link', { action: attrs.action,
className: 'badge-notification unread-notifications',
rawLabel: unreadNotifications }));
contents.push(this.attach('link', {
action: attrs.action,
className: 'badge-notification unread-notifications',
rawLabel: unreadNotifications,
omitSpan: true
}));
}
const unreadPMs = currentUser.get('unread_private_messages');
const unreadPMs = user.get('unread_private_messages');
if (!!unreadPMs) {
if (!currentUser.get('read_first_notification')) {
if (!user.get('read_first_notification')) {
contents.push(h('span.ring'));
if (!attrs.active && attrs.ringBackdrop) {
contents.push(h('span.ring-backdrop-spotlight'));
@ -55,9 +58,12 @@ createWidget('header-notifications', {
}
};
contents.push(this.attach('link', { action: attrs.action,
className: 'badge-notification unread-private-messages',
rawLabel: unreadPMs }));
contents.push(this.attach('link', {
action: attrs.action,
className: 'badge-notification unread-private-messages',
rawLabel: unreadPMs,
omitSpan: true
}));
}
return contents;
@ -72,9 +78,7 @@ createWidget('user-dropdown', jQuery.extend({
},
html(attrs) {
const { currentUser } = this;
return h('a.icon', { attributes: { href: currentUser.get('path'), 'data-auto-route': true } },
return h('a.icon', { attributes: { href: attrs.user.get('path'), 'data-auto-route': true } },
this.attach('header-notifications', attrs));
}
}, dropdown));
@ -106,7 +110,7 @@ createWidget('header-dropdown', jQuery.extend({
}, dropdown));
createWidget('header-icons', {
tagName: 'ul.icons.clearfix',
tagName: 'ul.icons.d-header-icons.clearfix',
buildAttributes() {
return { role: 'navigation' };
@ -139,10 +143,13 @@ createWidget('header-icons', {
});
const icons = [search, hamburger];
if (this.currentUser) {
icons.push(this.attach('user-dropdown', { active: attrs.userVisible,
action: 'toggleUserMenu',
ringBackdrop: attrs.ringBackdrop }));
if (attrs.user) {
icons.push(this.attach('user-dropdown', {
active: attrs.userVisible,
action: 'toggleUserMenu',
ringBackdrop: attrs.ringBackdrop,
user: attrs.user
}));
}
return icons;
@ -204,7 +211,8 @@ export default createWidget('header', {
userVisible: state.userVisible,
searchVisible: state.searchVisible,
ringBackdrop: state.ringBackdrop,
flagCount: attrs.flagCount })];
flagCount: attrs.flagCount,
user: this.currentUser })];
if (state.searchVisible) {
const contextType = this.searchContextType();

View File

@ -54,7 +54,13 @@ export default createWidget('link', {
}
if (!attrs.hideLabel) {
result.push(this.label(attrs));
let label = this.label(attrs);
if (attrs.omitSpan) {
result.push(label);
} else {
result.push(h('span.d-label', label));
}
}
const currentUser = this.currentUser;

View File

@ -1,3 +1,4 @@
import hbs from 'discourse/widgets/hbs-compiler';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
@ -23,14 +24,17 @@ createWidget('menu-links', {
createWidget('menu-panel', {
tagName: 'div.menu-panel',
template: hbs`
<div class='panel-body'>
<div class='panel-body-contents clearfix'>
{{yield}}
</div>
</div>
`,
buildAttributes(attrs) {
if (attrs.maxWidth) {
return { 'data-max-width': attrs.maxWidth };
}
},
html(attrs) {
return h('div.panel-body', h('div.panel-body-contents.clearfix', attrs.contents()));
}
});

View File

@ -173,20 +173,18 @@ registerButton('reply', attrs => {
registerButton('bookmark', attrs => {
if (!attrs.canBookmark) { return; }
let iconClass = 'read-icon';
let buttonClass = 'bookmark';
let tooltip = 'bookmarks.not_bookmarked';
let className = 'bookmark';
if (attrs.bookmarked) {
iconClass += ' bookmarked';
buttonClass += ' bookmarked';
tooltip = 'bookmarks.created';
className += ' bookmarked';
}
return { action: 'toggleBookmark',
title: tooltip,
className: buttonClass,
contents: h('div', { className: iconClass }) };
return {
action: 'toggleBookmark',
title: attrs.bookmarked ? "bookmarks.created" : "bookmarks.not_bookmarked",
className,
icon: 'bookmark'
};
});
registerButton('admin', attrs => {

View File

@ -1,17 +1,18 @@
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import hbs from 'discourse/widgets/hbs-compiler';
export default createWidget('post-placeholder', {
tagName: 'article.placeholder',
html() {
return h('div.row', [
h('div.topic-avatar', h('div.placeholder-avatar')),
h('div.topic-body', [
h('div.placeholder-text'),
h('div.placeholder-text'),
h('div.placeholder-text')
])
]);
}
template: hbs`
<div class='row'>
<div class='topic-avatar'>
<div class='placeholder-avatar'></div>
</div>
<div class='topic-body'>
<div class='placeholder-text'></div>
<div class='placeholder-text'></div>
<div class='placeholder-text'></div>
</div>
</div>
`
});

View File

@ -171,19 +171,22 @@ createWidget('post-meta-data', {
}, iconNode('eye-slash')));
}
const lastWikiEdit = attrs.wiki && attrs.lastWikiEdit && new Date(attrs.lastWikiEdit);
const createdAt = new Date(attrs.created_at);
if (createdAt) {
result.push(h('div.post-info',
h('a.post-date', {
attributes: {
href: attrs.shareUrl,
'data-share-url': attrs.shareUrl,
'data-post-number': attrs.post_number,
}
}, dateNode(createdAt))
));
const date = lastWikiEdit ? dateNode(lastWikiEdit) : dateNode(createdAt);
const attributes = {
class: "post-date",
href: attrs.shareUrl,
'data-share-url': attrs.shareUrl,
'data-post-number': attrs.post_number
};
if (lastWikiEdit) {
attributes["class"] += " last-wiki-edit";
}
result.push(h('div.post-info', h('a', { attributes }, date)));
if (attrs.via_email) {
result.push(this.attach('post-email-indicator', attrs));
}

View File

@ -2,13 +2,11 @@ import { iconNode } from 'discourse-common/lib/icon-library';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import { avatarFor } from 'discourse/widgets/post';
import hbs from 'discourse/widgets/hbs-compiler';
createWidget('pm-remove-group-link', {
tagName: 'a.remove-invited',
html() {
return iconNode('times');
},
template: hbs`{{fa-icon "times"}}`,
click() {
bootbox.confirm(I18n.t("private_message_info.remove_allowed_group", {name: this.attrs.name}), (confirmed) => {
@ -35,10 +33,7 @@ createWidget('pm-map-user-group', {
createWidget('pm-remove-link', {
tagName: 'a.remove-invited',
html() {
return iconNode('times');
},
template: hbs`{{fa-icon "times"}}`,
click() {
bootbox.confirm(I18n.t("private_message_info.remove_allowed_user", {name: this.attrs.username}), (confirmed) => {

View File

@ -5,16 +5,16 @@ import { createWidget } from 'discourse/widgets/widget';
createWidget('search-term', {
tagName: 'input',
buildId: () => 'search-term',
buildKey: (attrs) => `search-term-${attrs.id}`,
buildKey: () => `search-term`,
defaultState() {
this.appEvents.on("search-autocomplete:after-complete", () => {
this.state.afterAutocomplete = true;
});
return { afterAutocomplete: false };
},
searchAutocompleteAfterComplete() {
this.state.afterAutocomplete = true;
},
buildAttributes(attrs) {
return { type: 'text',
value: attrs.value || '',

View File

@ -130,15 +130,19 @@ createWidget('search-menu-results', {
className: "filter filter-type"})));
}
return [
let resultNode = [
h('ul', this.attach(rt.componentName, {
searchContextEnabled: attrs.searchContextEnabled,
searchLogId: attrs.results.grouped_search_result.search_log_id,
results: rt.results,
term: attrs.term
})),
h('div.no-results', more)
];
if (more.length) {
resultNode.push(h('div.no-results', more));
}
return resultNode;
});
}
});

View File

@ -115,26 +115,29 @@ export default createWidget('search-menu', {
panelContents() {
const contextEnabled = searchData.contextEnabled;
const results = [
let searchInput = [
this.attach('search-term', { value: searchData.term, contextEnabled }),
];
if (searchData.term && searchData.loading) {
searchInput.push(h('div.searching', h('div.spinner')));
}
const results = [
h('div.search-input', searchInput),
this.attach('search-context', {
contextEnabled,
url: this.fullSearchUrl({ expanded: true })
})
];
if (searchData.term) {
if (searchData.loading) {
results.push(h('div.searching', h('div.spinner')));
} else {
results.push(this.attach('search-menu-results', {
term: searchData.term,
noResults: searchData.noResults,
results: searchData.results,
invalidTerm: searchData.invalidTerm,
searchContextEnabled: searchData.contextEnabled,
}));
}
if (searchData.term && !searchData.loading) {
results.push(this.attach('search-menu-results', {
term: searchData.term,
noResults: searchData.noResults,
results: searchData.results,
invalidTerm: searchData.invalidTerm,
searchContextEnabled: searchData.contextEnabled,
}));
}
return results;

View File

@ -136,7 +136,8 @@ createWidget('topic-map-link', {
target: "_blank",
'data-user-id': attrs.user_id,
'data-ignore-post-id': 'true',
title: attrs.url };
title: attrs.url,
rel: 'nofollow noopener' };
},
html(attrs) {

View File

@ -1,101 +0,0 @@
import { createWidget } from 'discourse/widgets/widget';
import { topicLevels, buttonDetails } from 'discourse/lib/notification-levels';
import { h } from 'virtual-dom';
import RawHTML from 'discourse/widgets/raw-html';
import { iconNode } from 'discourse-common/lib/icon-library';
createWidget('notification-option', {
buildKey: attrs => `topic-notifications-button-${attrs.id}`,
tagName: 'li',
html(attrs) {
return h('a', [
iconNode(attrs.icon, { class: `icon ${attrs.key}`, tagName: 'span' }),
h('div', [
h('span.title', I18n.t(`topic.notifications.${attrs.key}.title`)),
h('span.desc', I18n.t(`topic.notifications.${attrs.key}.description`)),
])
]);
},
click() {
this.sendWidgetAction('notificationLevelChanged', this.attrs.id);
}
});
export default createWidget('topic-notifications-button', {
tagName: 'span.btn-group.notification-options',
buildKey: () => `topic-notifications-button`,
defaultState() {
return { expanded: false };
},
buildClasses(attrs, state) {
if (state.expanded) { return "open"; }
},
buildAttributes() {
return { title: I18n.t('topic.notifications.title') };
},
buttonFor(level) {
const details = buttonDetails(level);
const button = {
className: `toggle-notification-options`,
label: null,
icon: details.icon,
action: 'toggleDropdown',
iconClass: details.key
};
if (this.attrs.showFullTitle) {
button.label = `topic.notifications.${details.key}.title`;
} else {
button.className = 'btn toggle-notifications-options notifications-dropdown';
}
return this.attach('button', button);
},
html(attrs, state) {
const details = attrs.topic.get('details');
const result = [ this.buttonFor(details.get('notification_level')) ];
if (state.expanded) {
result.push(h('ul.dropdown-menu', topicLevels.map(l => this.attach('notification-option', l))));
}
if (attrs.appendReason) {
result.push(new RawHTML({ html: `<p>${details.get('notificationReasonText')}</p>` }));
}
return result;
},
toggleDropdown() {
this.state.expanded = !this.state.expanded;
},
clickOutside() {
if (this.state.expanded) {
this.sendWidgetAction('toggleDropdown');
}
},
notificationLevelChanged(id) {
this.state.expanded = false;
return this.attrs.topic.get('details').updateNotifications(id);
},
topicNotificationsButtonChanged(msg) {
switch(msg.type) {
case 'notification':
if (this.attrs.topic.get('details.notification_level') !== msg.id) {
this.notificationLevelChanged(msg.id);
}
break;
}
}
});

View File

@ -1,4 +1,5 @@
import { createWidget } from 'discourse/widgets/widget';
import ComponentConnector from 'discourse/widgets/component_connector';
import { h } from 'virtual-dom';
import { relativeAge } from 'discourse/lib/formatter';
import { iconNode } from 'discourse-common/lib/icon-library';
@ -313,7 +314,14 @@ createWidget('timeline-footer-controls', {
}
if (currentUser) {
controls.push(this.attach('topic-notifications-button', { topic }));
controls.push(new ComponentConnector(this,
'topic-notifications-button',
{
topic,
appendReason: false,
showFullTitle: false
}
));
}
return controls;

View File

@ -112,6 +112,10 @@ export function createWidget(name, opts) {
opts.html = opts.html || emptyContent;
opts.draw = drawWidget;
if (opts.template) {
opts.html = opts.template;
}
Object.keys(opts).forEach(k => result.prototype[k] = opts[k]);
return result;
}

View File

@ -176,9 +176,10 @@ function findInlineCloseTag(state, openTag, start, max) {
closeTag = parseBBCodeTag(state.src, j, max);
if (!closeTag || closeTag.tag !== openTag.tag || !closeTag.closing) {
closeTag = null;
} else {
closeTag.start = j;
break;
}
closeTag.start = j;
break;
}
}
}

View File

@ -62,6 +62,7 @@ export function buildOptions(state) {
lookupImageUrls,
censoredWords,
allowedHrefSchemes: siteSettings.allowed_href_schemes ? siteSettings.allowed_href_schemes.split('|') : null,
allowedIframes: siteSettings.allowed_iframes ? siteSettings.allowed_iframes.split('|') : [],
markdownIt: true,
previewing
};

View File

@ -1,7 +1,5 @@
import xss from 'pretty-text/xss';
const _validIframes = [];
function attr(name, value) {
if (value) {
return `${name}="${xss.escapeAttrValue(value)}"`;
@ -69,7 +67,8 @@ export function sanitize(text, whiteLister) {
text = text.replace(/<([^A-Za-z\/\!]|$)/g, "&lt;$1");
const whiteList = whiteLister.getWhiteList(),
allowedHrefSchemes = whiteLister.getAllowedHrefSchemes();
allowedHrefSchemes = whiteLister.getAllowedHrefSchemes(),
allowedIframes = whiteLister.getAllowedIframes();
let extraHrefMatchers = null;
if (allowedHrefSchemes && allowedHrefSchemes.length > 0) {
@ -85,11 +84,13 @@ export function sanitize(text, whiteLister) {
const forTag = whiteList.attrList[tag];
if (forTag) {
const forAttr = forTag[name];
if ((forAttr && (forAttr.indexOf('*') !== -1 || forAttr.indexOf(value) !== -1)) ||
if (
(forAttr && (forAttr.indexOf('*') !== -1 || forAttr.indexOf(value) !== -1)) ||
(name.indexOf('data-') === 0 && forTag['data-*']) ||
((tag === 'a' && name === 'href') && hrefAllowed(value, extraHrefMatchers)) ||
(tag === 'img' && name === 'src' && (/^data:image.*$/i.test(value) || hrefAllowed(value, extraHrefMatchers))) ||
(tag === 'iframe' && name === 'src' && _validIframes.some(i => i.test(value)))) {
(tag === 'iframe' && name === 'src' && allowedIframes.some(i => { return value.toLowerCase().indexOf((i || '').toLowerCase()) === 0;}))
) {
return attr(name, value);
}
@ -114,10 +115,3 @@ export function sanitize(text, whiteLister) {
.replace(/&#39;/g, "'")
.replace(/ \/>/g, '>');
};
export function whiteListIframe(regexp) {
_validIframes.push(regexp);
}
whiteListIframe(/^(https?:)?\/\/www\.google\.com\/maps\/embed\?.+/i);
whiteListIframe(/^(https?:)?\/\/www\.openstreetmap\.org\/export\/embed.html\?.+/i);

View File

@ -9,6 +9,7 @@ export default class WhiteLister {
this._enabled = { "default": true };
this._allowedHrefSchemes = (options && options.allowedHrefSchemes) || [];
this._allowedIframes = (options && options.allowedIframes) || [];
this._rawFeatures = [["default", DEFAULT_LIST]];
this._cache = null;
@ -102,6 +103,10 @@ export default class WhiteLister {
getAllowedHrefSchemes() {
return this._allowedHrefSchemes;
}
getAllowedIframes() {
return this._allowedIframes;
}
}
// Only add to `default` when you always want your whitelist to occur. In other words,

View File

@ -14,10 +14,24 @@
font: 1.071em/0.9 "FontAwesome";
}
}
.select-box {
align-self: center;
&.categories-admin-dropdown, &.category-notifications-button, &.tag-notifications-button {
float: right;
margin-left: 8px;
}
}
}
html.anon .topic-list a.title:visited:not(.badge-notification) {color: $primary-medium; }
.topic-list a.title.visited:not(.badge-notification) {color: $primary-medium; }
html.anon .topic-list,
.topic-list-item.visited,
.latest-topic-list-item.visited,
.category-topic-link.visited,
#suggested-topics .topic-list tr.visited {
a.title:not(.badge-notification) { color: $primary-medium; }
}
.topic-list-main-link {
font-size: 1.143em;
@ -47,7 +61,7 @@ html.anon .topic-list a.title:visited:not(.badge-notification) {color: $primary-
vertical-align: top;
margin-top: 2px;
}
border-bottom: 1px solid $primary-low;
border-bottom: 1px solid $primary-low;
&.last-visit {
border-bottom: none;
@ -253,7 +267,7 @@ ol.category-breadcrumb {
overflow-x: hidden;
overflow-y: auto;
position: absolute;
border: 1px solid $primary-low;
border: 1px solid $primary-low;
background-color: $secondary;
z-index: 100;
@ -315,7 +329,7 @@ ol.category-breadcrumb {
@include unselectable;
font-size: 1.2em;
border: 1px solid $primary-low;
border: 1px solid $primary-low;
padding: 5px;
background: $secondary;
position: absolute;
@ -369,4 +383,3 @@ div.education {
@extend .list-cell;
border-bottom: 2px solid $primary-low;
}

View File

@ -1,45 +1,39 @@
img.avatar {
border-radius: 50%;
}
// Common
// global styles that apply to the Discourse application specifically
// BEWARE: changing these styles implies they take effect anywhere they are seen
// throughout the Discourse application
.container {
@extend .clearfix;
}
.wrap {
@extend .clearfix;
margin-right: auto;
margin-left: auto;
padding: 0 8px;
.contents {
position: relative;
// Animation Keyframes
@keyframes ping {
from {
transform: scale(0.25);
opacity: 1;
}
to {
transform: scale(2);
opacity: 0;
}
}
.full-width {
margin-left: 12px;
@keyframes rotate-forever {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
big {
font-size: 28px;
}
small {
font-size: 9px;
}
//setting a static limit on big and small prevents nesting abuse
blockquote {
@include post-aside;
clear: both;
}
a.no-href {
cursor: pointer;
@keyframes background-fade-highlight {
0% {
background-color: $tertiary-low;
}
100% {
background-color: transparent;
}
}
// Base Elements
html {
height: 100%;
}
@ -52,56 +46,170 @@ body {
@include clearfix;
}
h1, h2, h3, h4, h5, h6 {
big {
font-size: 28px;
}
small {
font-size: 9px;
}
//setting a static limit on big and small prevents nesting abuse
blockquote {
@include post-aside;
clear: both;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
margin-bottom: .5rem;
}
button.ok {
background: $success;
color: $secondary;
@include hover {
background: lighten($success, 10%);
button {
&.ok {
background: $success;
color: $secondary;
@include hover {
background: lighten($success, 10%);
color: $secondary;
}
}
&.cancel {
background: $danger;
color: $secondary;
@include hover {
background: lighten($danger, 10%);
color: $secondary;
}
}
}
button.cancel {
background: $danger;
color: $secondary;
@include hover {
background: lighten($danger, 10%);
color: $secondary;
ul.breadcrumb {
margin: 0 10px 0 10px;
}
a.no-href {
cursor: pointer;
}
img.avatar {
border-radius: 50%;
}
// don't wrap relative dates; we want Jul 26, '15, not: Jul
// 26,
// '15
span.relative-date {
white-space:nowrap;
}
label {
display: block;
margin-bottom: 5px;
}
input {
&[type="radio"],
&[type="checkbox"] {
margin: 3px 0;
line-height: normal;
cursor: pointer;
}
&[type="submit"],
&[type="reset"],
&[type="button"],
&[type="radio"],
&[type="checkbox"] {
width: auto;
}
&.invalid {
background-color: dark-light-choose(scale-color($danger, $lightness: 80%), scale-color($danger, $lightness: -60%));
}
.radio &[type="radio"],
.checkbox &[type="checkbox"] {
float: left;
margin-left: -18px;
}
}
// Common Classes
.radio,
.checkbox {
min-height: 18px;
padding-left: 18px;
.controls > &:first-child {
padding-top: 5px;
}
&.inline {
display: inline-block;
padding-top: 5px;
margin-bottom: 0;
vertical-align: middle;
}
}
.radio.inline .radio.inline,
.checkbox.inline .checkbox.inline {
margin-left: 10px;
}
.container {
@extend .clearfix;
}
.wrap {
@extend .clearfix;
margin-right: auto;
margin-left: auto;
padding: 0 8px;
.contents {
position: relative;
}
}
.boxed {
&.white {
background-color: $secondary;
}
}
.full-width {
margin-left: 12px;
}
// the default for table cells in topic list
// is scale-color($primary, $lightness: 50%)
// numbers get dimmer as they get colder
.coldmap-high {
color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)) !important;
}
.coldmap-med {
color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)) !important;
}
.coldmap-low {
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)) !important;
.coldmap {
&-high {
color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)) !important;
}
&-med {
color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)) !important;
}
&-low {
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)) !important;
}
}
#loading-message {
position: absolute;
font-size: 2.143em;
text-align: center;
top: 120px;
left: 500px;
color: $primary;
}
.top-space {
.top-space {
margin-top: 10px;
}
ul.breadcrumb {
margin: 0 10px 0 10px;
}
.message {
@include border-radius-all(8px);
@ -113,18 +221,6 @@ ul.breadcrumb {
}
}
#footer {
.container {
height: 50px;
.contents {
padding-top: 10px;
a[href] {
color: $secondary;
}
}
}
}
.clear-transitions {
transition:none !important;
}
@ -139,10 +235,6 @@ ul.breadcrumb {
}
}
input[type].invalid {
background-color: dark-light-choose(scale-color($danger, $lightness: 80%), scale-color($danger, $lightness: -60%));
}
.d-editor-input {
resize: none;
}
@ -157,43 +249,6 @@ input[type].invalid {
top: 60px !important;
}
label {
display: block;
margin-bottom: 5px;
}
input {
&[type="radio"], &[type="checkbox"] {
margin: 3px 0;
line-height: normal;
cursor: pointer;
}
&[type="submit"], &[type="reset"], &[type="button"], &[type="radio"], &[type="checkbox"] {
width: auto;
}
}
.radio, .checkbox {
min-height: 18px;
padding-left: 18px;
}
.radio input[type="radio"], .checkbox input[type="checkbox"] {
float: left;
margin-left: -18px;
}
.controls > {
.radio:first-child, .checkbox:first-child {
padding-top: 5px;
}
}
.radio.inline, .checkbox.inline {
display: inline-block;
padding-top: 5px;
margin-bottom: 0;
vertical-align: middle;
}
.radio.inline .radio.inline, .checkbox.inline .checkbox.inline {
margin-left: 10px;
}
.flex-center-align {
display: flex;
align-items: center;
@ -202,7 +257,10 @@ input {
.unread-private-messages {
color: $secondary;
background: $success;
&.badge-notification[href] {color: $secondary;}
&.badge-notification[href] {
color: $secondary;
}
}
.ring-backdrop-spotlight {
@ -256,50 +314,12 @@ input {
-webkit-animation-name: ping;
}
@-webkit-keyframes ping {
from {
$scale: 0.25;
transform: scale($scale);
-ms-transform: scale($scale);
-webkit-transform: scale($scale);
-o-transform: scale($scale);
-moz-transform: scale($scale);
opacity: 1;
}
to {
$scale: 2;
transform: scale($scale);
-ms-transform: scale($scale);
-webkit-transform: scale($scale);
-o-transform: scale($scale);
-moz-transform: scale($scale);
opacity: 0;
}
}
.fade {
opacity: 0;
transition: opacity 0.15s linear;
}
.fade.in {
opacity: 1;
}
@-webkit-keyframes rotate-forever {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes rotate-forever {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
&.in {
opacity: 1;
}
}
@ -328,7 +348,6 @@ input {
}
.content-list {
h3 {
color: $primary-medium;
font-size: 1.071em;
@ -340,50 +359,106 @@ input {
list-style: none;
margin: 0;
li:first-of-type {
border-top: 1px solid $primary-low;
}
li {
border-bottom: 1px solid $primary-low;
}
li a {
display: block;
padding: 10px;
color: $primary;
&:hover {
background-color: $primary-low;
color: $primary;
&:first-of-type {
border-top: 1px solid $primary-low;
}
&.active {
font-weight: bold;
a {
display: block;
padding: 10px;
color: $primary;
&:hover {
background-color: $primary-low;
color: $primary;
}
&.active {
font-weight: bold;
color: $primary;
}
}
}
}
}
// don't wrap relative dates, we want
//
// Jul 26, '15
//
// not
//
// Jul
// 26,
// '15
//
span.relative-date {
white-space:nowrap;
.form-vertical {
input,
textarea,
select,
.input-prepend,
.input-append {
display: inline-block;
margin-bottom: 0;
}
.control-group {
@include clearfix;
}
.control-label {
font-weight: bold;
font-size: 1.2em;
line-height: 2;
}
.controls {
margin-left: 0;
}
}
@keyframes background-fade-highlight {
0% {
background-color: $tertiary-low;
// Special elements
// Special elements
#main {
img.avatar {
&.header {
width: 45px;
height: 45px;
}
&.medium {
width: 32px;
height: 32px;
}
&.small {
width: 25px;
height: 25px;
}
&.tiny {
width: 20px;
height: 20px;
}
}
100% {
background-color: transparent;
.user-list {
.user {
padding-bottom: 5px;
}
}
}
#loading-message {
position: absolute;
font-size: 2.143em;
text-align: center;
top: 120px;
left: 500px;
color: $primary;
}
#footer {
.container {
height: 50px;
.contents {
padding-top: 10px;
a[href] {
color: $secondary;
}
}
}
}

View File

@ -1,151 +1,163 @@
.d-header {
width: 100%;
position: absolute;
top: 0;
z-index: 1001;
background-color: $header_background;
box-shadow: 0 2px 4px -1px rgba(0,0,0, .25);
width: 100%;
position: absolute;
top: 0;
z-index: 1001;
background-color: $header_background;
box-shadow: 0 2px 4px -1px rgba(0,0,0, .25);
.docked & {
position: fixed;
backface-visibility: hidden; /** do magic for scrolling performance **/
.docked & {
position: fixed;
backface-visibility: hidden; /** do magic for scrolling performance **/
}
.contents {
margin: 8px 0;
}
.title {
float: left;
a, a:visited {
color: $header_primary;
}
}
.contents {
margin: 8px 0;
}
#site-logo {
max-height: 40px;
}
.title {
float: left;
a, a:visited {
color: $header_primary;
}
}
.d-icon-home {
font-size: 1.643em;
}
#site-logo {
max-height: 40px;
}
.panel {
float: right;
position: relative;
}
.d-icon-home {
font-size: 1.643em;
}
.login-button, button.sign-up-button {
float: left;
margin-top: 7px;
padding: 6px 10px;
.fa { margin-right: 3px; }
}
.panel {
float: right;
position: relative;
}
button.login-button {
margin-left: 7px;
}
.login-button, button.sign-up-button {
float: left;
margin-top: 7px;
padding: 6px 10px;
.fa { margin-right: 3px; }
}
button.login-button {
margin-left: 7px;
}
.icons {
float: right;
text-align: center;
margin: 0 0 0 5px;
list-style: none;
> li {
float: left;
}
.icon {
display: block;
padding: 3px;
color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary);
text-decoration: none;
cursor: pointer;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
transition: all linear .15s;
&:hover {
color: $primary;
background-color: $primary-low;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
}
&:active {
color: $primary;
background-color: $primary-low;
}
}
.drop-down-visible & {
.active .icon {
position: relative;
color: #7b7b7b;
background-color: $secondary;
cursor: default;
border-top: 1px solid $primary-low;
border-left: 1px solid $primary-low;
border-right: 1px solid $primary-low;
.badge-notification {
top: -10px;
}
.flagged-posts {
right: 24px;
}
&:after {
display: block;
position: absolute;
top: 100%;
left: 0;
z-index: 1101;
width: 100%;
height: 0;
content: "";
border-top: 1px solid $secondary;
}
&:hover {
border-bottom: none;
}
}
}
.d-icon {
width: 32px;
height: 32px;
font-size: 1.714em;
line-height: 32px;
display: inline-block;
}
.notifications {
position: relative;
}
.badge-notification, .ring {
position: absolute;
top: -9px;
z-index: 1;
margin-left: 0;
}
.unread-notifications {
right: 0;
background-color: scale-color($tertiary, $lightness: 50%);
}
.unread-private-messages, .ring {
right: 25px;
}
.flagged-posts {
right: 65px;
}
}
.flagged-posts, .queued-posts {
background: $danger;
}
.d-header-icons {
float: right;
}
}
.header-dropdown-toggle, .drop-down {
.flagged-posts, .queued-posts {
background: $danger;
}
}
.d-header-icons {
text-align: center;
margin: 0 0 0 5px;
list-style: none;
> li {
float: left;
}
.icon {
position: relative;
display: block;
padding: 3px;
color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary);
text-decoration: none;
cursor: pointer;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
transition: all linear .15s;
&:hover {
color: $primary;
background-color: $primary-low;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
}
&:active {
color: $primary;
background-color: $primary-low;
}
}
.drop-down-visible & {
.active .icon {
position: relative;
color: #7b7b7b;
background-color: $secondary;
cursor: default;
border-top: 1px solid $primary-low;
border-left: 1px solid $primary-low;
border-right: 1px solid $primary-low;
.flagged-posts {
right: 24px;
}
&:after {
display: block;
position: absolute;
top: 100%;
left: 0;
z-index: 1101;
width: 100%;
height: 0;
content: "";
border-top: 1px solid $secondary;
}
&:hover {
border-bottom: none;
}
}
}
.d-icon {
width: 32px;
height: 32px;
font-size: 1.714em;
line-height: 32px;
display: inline-block;
}
.notifications {
position: relative;
}
.ring {
position: absolute;
top: -9px;
z-index: 1;
margin-left: 0;
}
.header-dropdown-toggle {
position: relative;
}
.badge-notification {
position: absolute;
z-index: 1;
left: 0;
top: -9px;
min-width: 6px;
}
.unread-notifications {
left: auto;
right: 0;
background-color: scale-color($tertiary, $lightness: 50%);
}
.unread-private-messages, .ring {
left: auto;
right: 25px;
}
}
.highlight-strong {
background-color: $highlight-medium;

View File

@ -129,13 +129,9 @@
.searching {
position: absolute;
top: 0.25em;
right: 1.5em;
top: -3px;
right: 0.75em;
.drop-down-visible & {
top: 0.2em;
right: 1.2em;
}
.spinner {
width: 10px;
height: 10px;

View File

@ -1,7 +0,0 @@
.fa.muted, .fa.watching-first-post {
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
}
.fa.tracking, .fa.watching {
color: $tertiary;
font-weight: normal;
}

View File

@ -0,0 +1,9 @@
.notifications-button.notifications-button.notifications-button {
.d-icon.regular, .d-icon.muted, .d-icon.watching-first-post {
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
}
.d-icon.tracking, .d-icon.watching {
color: $tertiary;
font-weight: normal;
}
}

View File

@ -49,6 +49,36 @@
padding-top: 5px;
}
.topic-category {
margin-top: 5px;
.topic-header-extra {
display: inline;
vertical-align: baseline;
margin: 0;
}
.badge-wrapper {
display: inline-block;
vertical-align: baseline;
margin-top: 0;
&.bullet .badge-category {
vertical-align: middle;
}
&.box, &.bullet {
vertical-align: middle;
}
&.box + .topic-header-extra,
&.bullet + .topic-header-extra,
&.bar + .topic-header-extra {
vertical-align: middle;
}
}
}
.add-tags .select2 {
margin: 0;
}
@ -187,18 +217,6 @@ header .discourse-tag {color: $tag-color }
margin-right: 8px;
}
.notification-options.tag-notification-menu {
float: right;
margin-bottom: 10px;
}
.tag-notification-menu .dropdown-menu {
right: 0;
top: 30px;
bottom: auto;
left: auto;
}
.tag-sort-options {
margin-bottom: 20px;
a {

View File

@ -247,7 +247,8 @@ aside.quote {
}
}
.wiki {
.wiki,
.last-wiki-edit {
color: green !important;
}
@ -420,3 +421,10 @@ a.mention, a.mention-group {
padding-bottom: 15px;
}
}
.broken-image, .large-image {
color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%));
border: 1px solid $primary-low;
font-size: 32px;
padding: 16px;
}

View File

@ -123,7 +123,7 @@
}
.topic-unsubscribe {
.notification-options {
.notifications-button {
display: inline-block;
float: none;
line-height: 2em;

View File

@ -58,19 +58,6 @@
}
}
.user-info.medium.badge-info {
min-height: 80px;
.granted-on {
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
}
.post-link {
display: block;
margin-top: 0.2em;
}
}
.show-badge .badge-user-info {
.earned {
font-size: 1.3em;

View File

@ -1,8 +1,266 @@
// Common styles for "/user" section
.user-right {
.list-actions {
margin-bottom: 10px;
.btn {
margin-right: 10px;
}
}
}
.user-main {
.d-icon-heart {
color: $love !important;
}
.about {
overflow: hidden;
width: 100%;
.secondary {
font-size: 0.929em;
.btn {
padding: 3px 12px;
}
dl {
margin: 0;
}
dd {
padding: 0;
overflow: hidden;
text-overflow: ellipsis;
color: $primary;
&.groups {
span:after {
content: ','
}
span:last-of-type:after {
content:''
}
}
}
dt {
color: $secondary-medium;
margin: 0;
display: inline-block;
}
}
.details {
background: rgba($secondary, .85);
blockquote {
background-color: $secondary-low;
border-left-color: $secondary-low;
}
h1 {
font-size: 2.143em;
font-weight: normal;
i {font-size: .8em;}
}
h2 {
font-size: 1.214em;
font-weight: normal;
margin-top: 10px;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
h3 {
font-weight: normal;
font-size: 1em;
margin: 5px 0;
.d-icon:not(:first-of-type) {
margin-left: 10px;
}
}
.groups {
margin-left: 10px;
display: inline;
}
img.avatar {
float: left;
}
.suspended {
color: $danger;
}
.primary {
width: 100%;
position: relative;
float: left;
h1 {
font-weight: bold;
}
.bio {
max-height: 300px;
overflow: auto;
a[href] {
text-decoration: underline;
}
img {
max-width: 100%;
}
}
}
}
.controls {
ul {
list-style-type: none;
}
a {
padding: 5px 10px;
margin-bottom: 10px;
}
}
&.collapsed-info {
.controls {
margin-top: 0;
}
.profile-image {
height: 0;
}
.details {
margin-top: 0;
background: rgba($secondary, .85);
.bio {
display: none;
}
.primary {
text-align: left;
margin-top: 0;
width: 100%;
.avatar {
float: left;
margin-right: 10px;
width: 45px;
height: 45px;
}
h1 {
font-size: 1.429em;
}
h2 {
font-size: 1.071em;
margin-top: 4px;
}
h3 {
display: none;
}
}
}
}
}
.staff-counters {
text-align: left;
background: $primary;
> div {
margin: 0 10px 0 0;
display: inline-block;
padding: 5px 0;
&:first-of-type {
padding-left: 10px;
}
span {
padding: 1px 5px;
border-radius: 10px;
}
}
a {
color: $secondary;
}
.active {
font-weight: bold;
}
}
.pill {
border-radius: 15px;
display: inline-block;
height: 30px;
width: 30px;
text-align: center;
vertical-align: middle;
line-height: 30px;
}
.helpful-flags {
background-color: green;
}
.flagged-posts {
background-color: #E49735;
}
.warnings-received {
background-color: #EC441B;
}
.deleted-posts {
background-color: #EC441B;
}
.suspensions {
background-color: #c22020;
}
.user-field {
clear: both;
margin-bottom: 10px;
&.text {
padding-top: 18px;
}
.controls {
label {
width: auto;
text-align: left;
font-weight: normal;
float: auto;
}
.instructions {
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
margin-top: 5px;
margin-bottom: 10px;
font-size: 80%;
line-height: 1.4em;
}
}
}
}
.user-field {
@ -17,24 +275,26 @@
.public-user-fields {
margin-top: 8px;
margin-bottom: 8px;
.user-field-name {
font-weight: bold;
}
}
.collapsed-info .public-user-fields {
display: none;
.collapsed-info & {
display: none;
}
}
.user-navigation {
.map {
height: 50px;
}
.avatar {
float: left;
width: 45px;
}
nav.buttons {
width: 180px;
padding: 0;
@ -44,6 +304,7 @@
box-sizing: border-box;
}
}
h2 {
a {
font-size: 1em;
@ -51,7 +312,6 @@
cursor: pointer;
}
}
}
.user-table {
@ -78,69 +338,6 @@
margin-bottom: 15px;
}
.user-info {
display: inline-block;
clear: both;
margin-bottom: 1em;
.user-image {
float: left;
padding-right: 4px;
}
.user-detail {
float: left;
width: 70%;
padding-left: 5px;
font-size: 13px;
.name-line {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.username a {
font-weight: bold;
color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%));
}
.name {
margin-left: 5px;
color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%));
}
.title {
margin-top: 3px;
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
}
}
}
.user-info.small {
width: 333px;
}
.user-info.medium {
width: 480px;
min-height: 60px;
.user-image {
width: 55px;
}
.user-detail {
width: 380px;
}
.username, .name {
display: block;
}
.name {
margin-left: 0;
}
}
.user-nav {
margin: 5px 0px;
padding-top: 10px;
@ -150,15 +347,6 @@
}
}
.user-right {
.list-actions {
margin-bottom: 10px;
.btn {
margin-right: 10px;
}
}
}
.top-section {
@include clearfix();
ul {
@ -229,6 +417,11 @@
.topic-info {
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%));
}
@media all and (max-width : 600px) {
float: none;
width: 100%;
}
}
.replies-section,
@ -252,19 +445,62 @@
}
}
@media all
and (max-width : 600px) {
.top-sub-section {
float: none;
width: 100%;
.groups {
.group-link {
color: $tertiary;
}
}
.user-preferences .tags .select2-container-multi {
border: 1px solid $primary-low;
width: 540px;
border-radius: 0;
.select2-choices {
border: none;
.user-preferences {
textarea {
height: 100px;
}
.static {
color: $primary;
display: inline-block;
}
.instructions {
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
margin-bottom: 10px;
font-size: 80%;
line-height: 1.4em;
a[href] {
color: $tertiary;
}
}
.avatar {
margin-left: 3px;
}
.warning {
background-color: scale-color($danger, $lightness: 30%);
padding: 5px 8px;
color: $secondary;
width: 520px;
}
.category-notifications .category-controls,
.tag-notifications .tag-controls {
margin-top: 24px;
}
.tags .select2-container-multi {
border: 1px solid $primary-low;
width: 540px;
border-radius: 0;
.select2-choices {
border: none;
}
}
}
.paginated-topics-list {
.user-content {
width: 100%;
margin-top: 0;
}
}

View File

@ -153,18 +153,19 @@
.list-controls {
.category-breadcrumb {
a.badge-category, .dropdown-header {
.category-breadcrumb {
a.badge-category, .dropdown-header {
display: inline-block;
padding: 5px 8px;
line-height: 20px;
&.category-dropdown-button {
margin-left: -4px;
padding: 5px;
width: 13px;
.d-icon-caret-right {
margin-left: 2px;
&.category-dropdown-button {
margin-left: -4px;
padding: 5px;
width: 13px;
.d-icon-caret-right {
margin-left: 2px;
}
}
}
@ -190,8 +191,6 @@
margin: 0 2px;
}
}
}
.category-dropdown-menu {

View File

@ -0,0 +1,6 @@
.categories-admin-dropdown.categories-admin-dropdown.categories-admin-dropdown {
.select-box-body {
min-width: auto;
width: 250px;
}
}

View File

@ -1,4 +1,4 @@
.select-box.category-select-box {
.category-select-box.category-select-box {
.select-box-row {
display: -webkit-box;
display: -ms-flexbox;
@ -23,6 +23,7 @@
}
.category-status {
color: $primary;
-webkit-box-flex: 0;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
@ -32,7 +33,7 @@
-webkit-box-flex: 0;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
color: $primary;
color: #919191;
font-size: 0.857em;
line-height: 16px;
}

View File

@ -0,0 +1,103 @@
.dropdown-select-box.dropdown-select-box {
display: inline-flex;
height: 30px;
min-width: auto;
&.is-expanded {
.collection,
.select-box-collection,
.select-box-body {
border-radius: 0;
}
}
.select-box-body {
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
background-clip: padding-box;
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
max-width: 300px;
width: 300px;
}
.select-box-row {
margin: 0;
padding: 10px 5px;
.icons {
display: flex;
align-items: flex-start;
justify-content: center;
align-self: flex-start;
margin-right: 10px;
margin-top: 2px;
width: 30px;
.d-icon {
font-size: 1.286em;
align-self: center;
margin-right: 0;
opacity: 1;
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
}
}
.texts {
line-height: 18px;
flex: 1;
align-items: flex-start;
display: flex;
flex-wrap: wrap;
flex-direction: column;
.title {
flex: 1;
font-weight: bold;
display: block;
font-size: 1em;
color: $primary;
padding: 0;
}
.desc {
flex: 1;
font-size: 0.857em;
font-weight: normal;
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%));;
white-space: normal;
}
}
&.is-highlighted {
background: $tertiary-low;
}
&:hover {
background: $highlight-medium;
}
}
.select-box-collection {
padding: 0;
}
.dropdown-header {
padding: 0;
border: 0;
outline: 0;
justify-content: flex-start;
background: none;
.d-icon + .d-icon {
margin-left: 5px;
}
.btn {
align-items: center;
justify-content: space-between;
flex-direction: row;
display: inline-flex;
height: 100%;
margin: 0;
}
}
}

View File

@ -0,0 +1,12 @@
.notifications-button.notifications-button.notifications-button {
.select-box-body {
min-width: 550px;
max-width: 550px;
}
.select-box-row {
.icons {
align-self: flex-start;
}
}
}

View File

@ -0,0 +1,39 @@
#topic-footer-buttons {
.pinned-button {
min-width: auto;
margin: 10px 0 5px 0;
&.is-hidden {
display: none;
}
.btn {
margin: 0;
}
.reason {
line-height: 16px;
margin: 0 0 0 10px;
}
}
}
.pinned-button {
display: flex;
justify-content: flex-start;
align-items: center;
margin: 0;
min-width: auto;
.pinned-options {
display: inline-flex;
}
.pinned-options.pinned-options.pinned-options {
.select-box-body {
min-width: unset;
max-width: unset;
width: 550px;
}
}
}

View File

@ -9,13 +9,18 @@
flex-direction: column;
position: relative;
height: 34px;
min-width: 220px;
&.is-hidden {
display: none;
}
&.small {
height: 28px;
}
&.is-expanded {
z-index: 9999;
z-index: 999;
.select-box-wrapper {
border: 1px solid $tertiary;
@ -39,14 +44,14 @@
}
.collection, {
border-radius: 0 0 3px 3px;
border-radius: inherit;
}
.select-box-header {
border-radius: 3px 3px 0 0;
}
&.is-reversed {
&.is-above {
.select-box-header {
border-radius: 0 0 3px 3px;
}
@ -61,7 +66,7 @@
}
}
&.is-reversed {
&.is-above {
.select-box-body {
bottom: 0;
top: auto;
@ -90,6 +95,16 @@
-ms-flex-align: center;
align-items: center;
justify-content: space-between;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
padding-left: 10px;
padding-right: 10px;
height: inherit;
&.is-focused {
border: 1px solid $tertiary;
@ -97,10 +112,43 @@
-webkit-box-shadow: $tertiary 0px 0px 6px 0px;
box-shadow: $tertiary 0px 0px 6px 0px;
}
.current-selection {
text-align: left;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
color: inherit;
}
.icon {
margin-right: 5px;
}
.caret-icon {
margin-left: 5px;
pointer-events: none;
}
.d-button-label {
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
.d-icon {
margin-left: 5px;
margin-right: 0;
}
}
}
.select-box-body {
display: none;
width: 100%;
background: $secondary;
-webkit-box-sizing: border-box;
box-sizing: border-box;
@ -119,6 +167,26 @@
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
.text {
margin: 0;
}
.d-icon {
margin-right: 5px;
}
&.is-highlighted {
background: $tertiary-low;
}
&.is-selected {
background: $highlight-medium;
}
&.is-selected.is-highlighted {
background: $tertiary-low;
}
}
.select-box-collection {
@ -127,9 +195,9 @@
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
-webkit-box-flex: 0;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
@ -137,15 +205,64 @@
background: $secondary;
overflow-x: hidden;
overflow-y: auto;
border-radius: 0 0 3px 3px;
border-radius: inherit;
margin: 0;
padding: 0;
-webkit-overflow-scrolling: touch;
.collection {
padding: 0;
margin: 0;
&:hover .select-box-row.is-highlighted:hover {
background: $tertiary-low;
}
}
&::-webkit-scrollbar {
-webkit-appearance: none;
width: 10px;
}
&::-webkit-scrollbar-thumb {
cursor: pointer;
border-radius: 5px;
background: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
}
&::-webkit-scrollbar-track {
background: transparent;
border-radius: 0;
}
}
.select-box-filter {
border-bottom: 1px solid $primary-low;
background: $secondary;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
padding: 0 10px;
.filter-query, .filter-query:focus, .filter-query:active {
background: none;
margin: 0;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
outline: none;
border: 0;
-webkit-box-shadow: none;
box-shadow: none;
width: 100%;
padding: 5px 0;
}
}
.select-box-wrapper {
@ -159,132 +276,18 @@
pointer-events: none;
border: 1px solid transparent;
}
}
.select-box .select-box-header {
height: inherit;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
padding-left: 10px;
padding-right: 10px;
.current-selection {
text-align: left;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
color: inherit;
}
.icon {
margin-right: 5px;
}
.caret-icon {
margin-left: 5px;
pointer-events: none;
}
}
.select-box .select-box-collection {
-webkit-box-flex: 0;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
.collection {
padding: 0;
margin: 0;
}
&::-webkit-scrollbar {
-webkit-appearance: none;
width: 10px;
}
&::-webkit-scrollbar-thumb {
cursor: pointer;
border-radius: 5px;
background: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
}
&::-webkit-scrollbar-track {
background: transparent;
border-radius: 0;
}
}
.select-box .select-box-row {
.text {
margin: 0;
}
.d-icon {
margin-right: 5px;
}
&.is-highlighted {
background: $highlight-medium;
}
&.is-selected {
a {
background: $highlight-medium;
}
}
}
.select-box .select-box-filter {
background: $secondary;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
padding: 0 10px;
.filter-query, .filter-query:focus, .filter-query:active {
background: none;
margin: 0;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
outline: none;
.select-box-offscreen, .select-box .select-box-offscreen:focus {
clip: rect(0 0 0 0);
width: 1px;
height: 1px;
border: 0;
-webkit-box-shadow: none;
box-shadow: none;
width: 100%;
padding: 5px 0;
margin: 0;
padding: 0;
overflow: hidden;
position: absolute;
outline: 0;
left: 0px;
top: 0px;
}
}
.select-box .select-box-offscreen, .select-box .select-box-offscreen:focus {
clip: rect(0 0 0 0);
width: 1px;
height: 1px;
border: 0;
margin: 0;
padding: 0;
overflow: hidden;
position: absolute;
outline: 0;
left: 0px;
top: 0px;
}

View File

@ -0,0 +1,30 @@
#topic-footer-buttons {
.topic-notifications-button {
min-width: auto;
margin: 10px 0 15px 0;
.btn {
margin: 0;
}
.reason {
line-height: 16px;
margin: 0 0 0 10px;
}
}
}
.topic-notifications-button .topic-notifications-options {
min-width: auto;
}
.topic-notifications-button {
display: inline-flex;
justify-content: flex-start;
align-items: center;
margin: 0;
.topic-notifications-options {
display: inline-flex;
}
}

View File

@ -0,0 +1,68 @@
// Common styles for "user-info" component
.user-info {
display: inline-block;
clear: both;
margin-bottom: 1em;
.user-image {
float: left;
padding-right: 4px;
}
.user-detail {
float: left;
width: 70%;
padding-left: 5px;
font-size: 13px;
.name-line {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.username a {
font-weight: bold;
color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%));
}
.name {
margin-left: 5px;
color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%));
}
.title {
margin-top: 3px;
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
}
}
&.small {
width: 333px;
}
&.medium {
min-height: 60px;
.username, .name {
display: block;
}
.name {
margin-left: 0;
}
&.badge-info {
min-height: 80px;
.granted-on {
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
}
.post-link {
display: block;
margin-top: 0.2em;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More