Merge branch 'master' into dillo

This commit is contained in:
Francesco Siddi 2019-04-08 23:24:56 +02:00
commit 312b0a276a
38 changed files with 192 additions and 211 deletions

View File

@ -152,7 +152,7 @@ gulp.task('scripts_concat_tutti', function(done) {
let toUglify = [ let toUglify = [
source.jquery + 'dist/jquery.min.js', source.jquery + 'dist/jquery.min.js',
source.vue + 'dist/vue.min.js', source.vue + (enabled.uglify ? 'dist/vue.min.js' : 'dist/vue.js'),
source.popper + 'dist/umd/popper.min.js', source.popper + 'dist/umd/popper.min.js',
source.bootstrap + 'js/dist/index.js', source.bootstrap + 'js/dist/index.js',
source.bootstrap + 'js/dist/util.js', source.bootstrap + 'js/dist/util.js',

View File

@ -89,7 +89,7 @@ const TEMPLATE = `
<comment <comment
v-for="c in comment.replies" v-for="c in comment.replies"
@unit-of-work="childUnitOfWork" @unit-of-work="childUnitOfWork"
isReply=true :isReply="true"
:readOnly="readOnly" :readOnly="readOnly"
:comment="c" :comment="c"
:user="user" :user="user"
@ -158,7 +158,7 @@ Vue.component('comment', {
}, },
cancleEdit() { cancleEdit() {
this.doHideEditors(); this.doHideEditors();
EventBus.$emit(Events.EDIT_DONE, this.comment.id ); EventBus.$emit(Events.EDIT_DONE);
}, },
doHideEditors() { doHideEditors() {
this.isReplying = false; this.isReplying = false;

View File

@ -204,7 +204,7 @@ Vue.component('comment-editor', {
.fail((err) => {toastr.error(pillar.utils.messageFromError(err), 'Failed to submit comment')}) .fail((err) => {toastr.error(pillar.utils.messageFromError(err), 'Failed to submit comment')})
) )
.then(() => { .then(() => {
EventBus.$emit(Events.EDIT_DONE, this.comment._id); EventBus.$emit(Events.EDIT_DONE);
}); });
}, },
thenSubmit() { thenSubmit() {

View File

@ -63,7 +63,6 @@ Vue.component('comments-tree', {
comments: [], comments: [],
showLoadingPlaceholder: true, showLoadingPlaceholder: true,
user: pillar.utils.getCurrentUser(), user: pillar.utils.getCurrentUser(),
canPostComments: this.canPostCommentsStr == 'true'
} }
}, },
computed: { computed: {
@ -74,12 +73,6 @@ Vue.component('comments-tree', {
isLoggedIn() { isLoggedIn() {
return this.user.is_authenticated; return this.user.is_authenticated;
}, },
iSubscriber() {
return this.user.hasCap('subscriber');
},
canRenewSubscription() {
return this.user.hasCap('can-renew-subscription');
},
canReply() { canReply() {
return !this.readOnly && !this.replyHidden && this.isLoggedIn; return !this.readOnly && !this.replyHidden && this.isLoggedIn;
} }

View File

@ -45,7 +45,7 @@ Vue.component('comment-rating', {
.then((updatedComment) => { .then((updatedComment) => {
EventBus.$emit(Events.UPDATED_COMMENT, updatedComment); EventBus.$emit(Events.UPDATED_COMMENT, updatedComment);
}) })
.fail((err) => {toastr.error(pillar.utils.messageFromError(err), 'Faied to vote on comment')}) .fail((err) => {toastr.error(pillar.utils.messageFromError(err), 'Failed to vote on comment')})
); );
} }
} }

View File

@ -1,5 +1,14 @@
// Code from https://stackoverflow.com/a/42389266 /**
* Directive to detect clicks outside of component.
* Code from https://stackoverflow.com/a/42389266
*
* @example
* <div
* v-click-outside="()=>{console.log('User clicked outside component')}"
* >
* ...
* </div>
*/
Vue.directive('click-outside', { Vue.directive('click-outside', {
bind: function (el, binding, vnode) { bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) { el.clickOutsideEvent = function (event) {

View File

@ -3,7 +3,7 @@ import './comments/CommentTree'
import './customdirectives/click-outside' import './customdirectives/click-outside'
import { UnitOfWorkTracker } from './mixins/UnitOfWorkTracker' import { UnitOfWorkTracker } from './mixins/UnitOfWorkTracker'
import { BrowserHistoryState, StateSaveMode } from './mixins/BrowserHistoryState' import { BrowserHistoryState, StateSaveMode } from './mixins/BrowserHistoryState'
import { PillarTable, TableState } from './table/Table' import { PillarTable } from './table/Table'
import { CellPrettyDate } from './table/cells/renderer/CellPrettyDate' import { CellPrettyDate } from './table/cells/renderer/CellPrettyDate'
import { CellDefault } from './table/cells/renderer/CellDefault' import { CellDefault } from './table/cells/renderer/CellDefault'
import { ColumnBase } from './table/columns/ColumnBase' import { ColumnBase } from './table/columns/ColumnBase'
@ -28,7 +28,6 @@ let mixins = {
let table = { let table = {
PillarTable, PillarTable,
TableState,
columns: { columns: {
ColumnBase, ColumnBase,
Created, Created,

View File

@ -5,24 +5,6 @@ import './rows/filter/RowFilter'
import {UnitOfWorkTracker} from '../mixins/UnitOfWorkTracker' import {UnitOfWorkTracker} from '../mixins/UnitOfWorkTracker'
import {RowFilter} from './rows/filter/RowFilter' import {RowFilter} from './rows/filter/RowFilter'
/**
* Table State
*
* Used to restore a table to a given state.
*/
class TableState {
constructor(selectedIds) {
this.selectedIds = selectedIds || [];
}
/**
* Apply state to row
* @param {RowBase} rowObject
*/
applyRowState(rowObject) {
rowObject.isSelected = this.selectedIds.includes(rowObject.getId());
}
}
class ComponentState { class ComponentState {
/** /**
@ -46,8 +28,8 @@ const TEMPLATE =`
:rowObjects="sortedRowObjects" :rowObjects="sortedRowObjects"
:config="rowFilterConfig" :config="rowFilterConfig"
:componentState="(componentState || {}).rowFilter" :componentState="(componentState || {}).rowFilter"
@visibleRowObjectsChanged="onVisibleRowObjectsChanged" @visible-row-objects-changed="onVisibleRowObjectsChanged"
@componentStateChanged="onRowFilterStateChanged" @component-state-changed="onRowFilterStateChanged"
/> />
<pillar-table-actions <pillar-table-actions
@item-clicked="onItemClicked" @item-clicked="onItemClicked"
@ -55,8 +37,8 @@ const TEMPLATE =`
<pillar-table-column-filter <pillar-table-column-filter
:columns="columns" :columns="columns"
:componentState="(componentState || {}).columnFilter" :componentState="(componentState || {}).columnFilter"
@visibleColumnsChanged="onVisibleColumnsChanged" @visible-columns-changed="onVisibleColumnsChanged"
@componentStateChanged="onColumnFilterStateChanged" @component-state-changed="onColumnFilterStateChanged"
/> />
</div> </div>
<div class="pillar-table"> <div class="pillar-table">
@ -88,9 +70,9 @@ const TEMPLATE =`
* Extend ColumnFactoryBase to create the rows for your table * Extend ColumnFactoryBase to create the rows for your table
* Extend This Table with your ColumnFactory and RowSource * Extend This Table with your ColumnFactory and RowSource
* *
* @emits isInitialized When all rows has been fetched, and are initialized. * @emits is-initialized When all rows has been fetched, and are initialized.
* @emits selectItemsChanged(selectedItems) When selected rows has changed. * @emits selected-items-changed(selectedItems) When selected rows has changed.
* @emits componentStateChanged(newState) When table state changed. Filtered rows, visible columns... * @emits component-state-changed(newState) When table state changed. Filtered rows, visible columns...
*/ */
let PillarTable = Vue.component('pillar-table-base', { let PillarTable = Vue.component('pillar-table-base', {
template: TEMPLATE, template: TEMPLATE,
@ -98,7 +80,7 @@ let PillarTable = Vue.component('pillar-table-base', {
props: { props: {
selectedIds: { selectedIds: {
type: Array, type: Array,
default: [] default: () => {return []}
}, },
canChangeSelectionCB: { canChangeSelectionCB: {
type: Function, type: Function,
@ -109,13 +91,14 @@ let PillarTable = Vue.component('pillar-table-base', {
default: true default: true
}, },
componentState: { componentState: {
// Instance of ComponentState // Instance of ComponentState (but type Object since it has been deserialized)
type: Object, type: Object,
default: undefined default: undefined
} }
}, },
data: function() { data: function() {
return { return {
currentlySelectedIds: [],
columns: [], columns: [],
visibleColumns: [], visibleColumns: [],
visibleRowObjects: [], visibleRowObjects: [],
@ -161,6 +144,9 @@ let PillarTable = Vue.component('pillar-table-base', {
}, },
watch: { watch: {
selectedIds(newValue) { selectedIds(newValue) {
this.currentlySelectedIds = newValue;
},
currentlySelectedIds(newValue) {
this.rowAndChildObjects.forEach(item => { this.rowAndChildObjects.forEach(item => {
item.isSelected = newValue.includes(item.getId()); item.isSelected = newValue.includes(item.getId());
}); });
@ -169,12 +155,12 @@ let PillarTable = Vue.component('pillar-table-base', {
// Deep compare to avoid spamming un needed events // Deep compare to avoid spamming un needed events
let hasChanged = JSON.stringify(newValue ) !== JSON.stringify(oldValue); let hasChanged = JSON.stringify(newValue ) !== JSON.stringify(oldValue);
if (hasChanged) { if (hasChanged) {
this.$emit('selectItemsChanged', newValue); this.$emit('selected-items-changed', newValue);
} }
}, },
isInitialized(newValue) { isInitialized(newValue) {
if (newValue) { if (newValue) {
this.$emit('isInitialized'); this.$emit('is-initialized');
} }
}, },
currentComponentState(newValue, oldValue) { currentComponentState(newValue, oldValue) {
@ -182,14 +168,12 @@ let PillarTable = Vue.component('pillar-table-base', {
// Deep compare to avoid spamming un needed events // Deep compare to avoid spamming un needed events
let hasChanged = JSON.stringify(newValue ) !== JSON.stringify(oldValue); let hasChanged = JSON.stringify(newValue ) !== JSON.stringify(oldValue);
if (hasChanged) { if (hasChanged) {
this.$emit('componentStateChanged', newValue); this.$emit('component-state-changed', newValue);
} }
} }
} }
}, },
created() { created() {
let tableState = new TableState(this.selectedIds);
this.unitOfWork( this.unitOfWork(
Promise.all([ Promise.all([
this.columnFactory.thenGetColumns(), this.columnFactory.thenGetColumns(),
@ -200,12 +184,11 @@ let PillarTable = Vue.component('pillar-table-base', {
return this.rowsSource.thenInit(); return this.rowsSource.thenInit();
}) })
.then(() => { .then(() => {
let currentlySelectedIds = this.selectedItems.map(it => it._id); if (this.currentlySelectedIds.length === 0) {
if (currentlySelectedIds.length > 0) { this.currentlySelectedIds = this.selectedIds;
} else {
// User has clicked on a row while we inited the rows. Keep that selection! // User has clicked on a row while we inited the rows. Keep that selection!
tableState.selectedIds = currentlySelectedIds;
} }
this.rowAndChildObjects.forEach(tableState.applyRowState.bind(tableState));
this.isInitialized = true; this.isInitialized = true;
}) })
.catch((err) => {toastr.error(pillar.utils.messageFromError(err), 'Loading table failed')}) .catch((err) => {toastr.error(pillar.utils.messageFromError(err), 'Loading table failed')})
@ -234,24 +217,24 @@ let PillarTable = Vue.component('pillar-table-base', {
if(!this.canChangeSelectionCB()) return; if(!this.canChangeSelectionCB()) return;
if(this.isMultiToggleClick(clickEvent) && this.canMultiSelect) { if(this.isMultiToggleClick(clickEvent) && this.canMultiSelect) {
let slectedIdsWithoutClicked = this.selectedIds.filter(id => id !== itemId); let slectedIdsWithoutClicked = this.currentlySelectedIds.filter(id => id !== itemId);
if (slectedIdsWithoutClicked.length < this.selectedIds.length) { if (slectedIdsWithoutClicked.length < this.currentlySelectedIds.length) {
this.selectedIds = slectedIdsWithoutClicked; this.currentlySelectedIds = slectedIdsWithoutClicked;
} else { } else {
this.selectedIds = [itemId, ...this.selectedIds]; this.currentlySelectedIds = [itemId, ...this.currentlySelectedIds];
} }
} else if(this.isSelectBetweenClick(clickEvent) && this.canMultiSelect) { } else if(this.isSelectBetweenClick(clickEvent) && this.canMultiSelect) {
if (this.selectedIds.length > 0) { if (this.currentlySelectedIds.length > 0) {
let betweenA = this.selectedIds[this.selectedIds.length -1]; let betweenA = this.currentlySelectedIds[this.currentlySelectedIds.length -1];
let betweenB = itemId; let betweenB = itemId;
this.selectedIds = this.rowsBetween(betweenA, betweenB).map(it => it.getId()); this.currentlySelectedIds = this.rowsBetween(betweenA, betweenB).map(it => it.getId());
} else { } else {
this.selectedIds = [itemId]; this.currentlySelectedIds = [itemId];
} }
} }
else { else {
this.selectedIds = [itemId]; this.currentlySelectedIds = [itemId];
} }
}, },
isSelectBetweenClick(clickEvent) { isSelectBetweenClick(clickEvent) {
@ -282,8 +265,9 @@ let PillarTable = Vue.component('pillar-table-base', {
} }
}, },
components: { components: {
'pillar-table-row-filter': RowFilter 'pillar-table-row-filter': RowFilter,
'pillar-table-actions': {template:'<div/>'},
} }
}); });
export { PillarTable, TableState } export { PillarTable }

View File

@ -1,3 +1,6 @@
import {ColumnBase} from '../../columns/ColumnBase'
import {RowBase} from '../../rows/RowObjectBase'
const TEMPLATE =` const TEMPLATE =`
<div> <div>
{{ cellValue }} {{ cellValue }}
@ -11,9 +14,9 @@ const TEMPLATE =`
let CellDefault = Vue.component('pillar-cell-default', { let CellDefault = Vue.component('pillar-cell-default', {
template: TEMPLATE, template: TEMPLATE,
props: { props: {
column: Object, column: ColumnBase,
rowObject: Object, rowObject: RowBase,
rawCellValue: Object rawCellValue: [String,Number,Boolean,Array,Object,Date,Function,Symbol,],
}, },
computed: { computed: {
cellValue() { cellValue() {

View File

@ -1,3 +1,6 @@
import {RowBase} from '../../rows/RowObjectBase'
import {ColumnBase} from '../../columns/ColumnBase'
const TEMPLATE =` const TEMPLATE =`
<component class="pillar-cell" <component class="pillar-cell"
:class="cellClasses" :class="cellClasses"
@ -18,8 +21,8 @@ const TEMPLATE =`
let CellProxy = Vue.component('pillar-cell-proxy', { let CellProxy = Vue.component('pillar-cell-proxy', {
template: TEMPLATE, template: TEMPLATE,
props: { props: {
column: Object, // ColumnBase column: ColumnBase,
rowObject: Object // RowObject rowObject: RowBase,
}, },
computed: { computed: {
/** /**

View File

@ -1,3 +1,5 @@
import {ColumnBase} from '../../columns/ColumnBase'
const TEMPLATE =` const TEMPLATE =`
<div class="pillar-cell header-cell" <div class="pillar-cell header-cell"
:class="cellClasses" :class="cellClasses"
@ -34,7 +36,7 @@ const TEMPLATE =`
Vue.component('pillar-head-cell', { Vue.component('pillar-head-cell', {
template: TEMPLATE, template: TEMPLATE,
props: { props: {
column: Object column: ColumnBase,
}, },
computed: { computed: {
cellClasses() { cellClasses() {

View File

@ -56,14 +56,14 @@ class ComponentState {
/** /**
* Component to select what columns to render in the table. * Component to select what columns to render in the table.
* *
* @emits visibleColumnsChanged(columns) When visible columns has changed * @emits visible-columns-changed(columns) When visible columns has changed
* @emits componentStateChanged(newState) When column filter state changed. * @emits component-state-changed(newState) When column filter state changed.
*/ */
let Filter = Vue.component('pillar-table-column-filter', { let Filter = Vue.component('pillar-table-column-filter', {
template: TEMPLATE, template: TEMPLATE,
props: { props: {
columns: Array, // Instances of ColumnBase columns: Array, // Instances of ColumnBase
componentState: Object, // Instance of ComponentState componentState: Object, // Instance of ComponentState (but type Object since it has been deserialized)
}, },
data() { data() {
return { return {
@ -85,14 +85,14 @@ let Filter = Vue.component('pillar-table-column-filter', {
this.columnStates = this.createInitialColumnStates(); this.columnStates = this.createInitialColumnStates();
}, },
visibleColumns(visibleColumns) { visibleColumns(visibleColumns) {
this.$emit('visibleColumnsChanged', visibleColumns); this.$emit('visible-columns-changed', visibleColumns);
}, },
columnFilterState(newValue) { columnFilterState(newValue) {
this.$emit('componentStateChanged', newValue); this.$emit('component-state-changed', newValue);
} }
}, },
created() { created() {
this.$emit('visibleColumnsChanged', this.visibleColumns); this.$emit('visible-columns-changed', this.visibleColumns);
}, },
methods: { methods: {
createInitialColumnStates() { createInitialColumnStates() {

View File

@ -56,14 +56,14 @@ class ComponentState {
* Filter row objects based on enumeratable values. * Filter row objects based on enumeratable values.
* *
* @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed. * @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed.
* @emits componentStateChanged(newState) When row filter state changed. * @emits component-state-changed(newState) When row filter state changed.
*/ */
let EnumFilter = { let EnumFilter = {
template: TEMPLATE, template: TEMPLATE,
props: { props: {
label: String, label: String,
availableValues: Array, // Array with valid values [{value: abc, displayName: xyz},...] availableValues: Array, // Array with valid values [{value: abc, displayName: xyz},...]
componentState: Object, // Instance of ComponentState. componentState: Object, // Instance of ComponentState (but type Object since it has been deserialized)
valueExtractorCB: { valueExtractorCB: {
// Callback to extract enumvalue from a rowObject // Callback to extract enumvalue from a rowObject
type: Function, type: Function,
@ -107,14 +107,14 @@ let EnumFilter = {
}, },
watch: { watch: {
visibleRowObjects(visibleRowObjects) { visibleRowObjects(visibleRowObjects) {
this.$emit('visibleRowObjectsChanged', visibleRowObjects); this.$emit('visible-row-objects-changed', visibleRowObjects);
}, },
currentComponentState(newValue) { currentComponentState(newValue) {
this.$emit('componentStateChanged', newValue); this.$emit('component-state-changed', newValue);
} }
}, },
created() { created() {
this.$emit('visibleRowObjectsChanged', this.visibleRowObjects); this.$emit('visible-row-objects-changed', this.visibleRowObjects);
}, },
methods: { methods: {
shouldBeVisible(rowObject) { shouldBeVisible(rowObject) {

View File

@ -6,15 +6,15 @@ const TEMPLATE =`
:componentState="componentState" :componentState="componentState"
:rowObjects="rowObjects" :rowObjects="rowObjects"
:valueExtractorCB="extractName" :valueExtractorCB="extractName"
@visibleRowObjectsChanged="$emit('visibleRowObjectsChanged', ...arguments)" @visible-row-objects-changed="$emit('visible-row-objects-changed', ...arguments)"
@componentStateChanged="$emit('componentStateChanged', ...arguments)" @component-state-changed="$emit('component-state-changed', ...arguments)"
> />
`; `;
/** /**
* Filter row objects based on there name. * Filter row objects based on there name.
* *
* @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed. * @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed.
* @emits componentStateChanged(newState) When row filter state changed. * @emits component-state-changed(newState) When row filter state changed.
*/ */
let NameFilter = { let NameFilter = {
template: TEMPLATE, template: TEMPLATE,

View File

@ -5,8 +5,8 @@ const TEMPLATE =`
<name-filter <name-filter
:rowObjects="rowObjects" :rowObjects="rowObjects"
:componentState="componentState" :componentState="componentState"
@visibleRowObjectsChanged="$emit('visibleRowObjectsChanged', ...arguments)" @visible-row-objects-changed="$emit('visible-row-objects-changed', ...arguments)"
@componentStateChanged="$emit('componentStateChanged', ...arguments)" @component-state-changed="$emit('component-state-changed', ...arguments)"
/> />
</div> </div>
`; `;

View File

@ -7,15 +7,15 @@ const TEMPLATE =`
:componentState="componentState" :componentState="componentState"
:rowObjects="rowObjects" :rowObjects="rowObjects"
:valueExtractorCB="extractStatus" :valueExtractorCB="extractStatus"
@visibleRowObjectsChanged="$emit('visibleRowObjectsChanged', ...arguments)" @visible-row-objects-changed="$emit('visible-row-objects-changed', ...arguments)"
@componentStateChanged="$emit('componentStateChanged', ...arguments)" @component-state-changed="$emit('component-state-changed', ...arguments)"
> />
`; `;
/** /**
* Filter row objects based on there status. * Filter row objects based on there status.
* *
* @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed. * @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed.
* @emits componentStateChanged(newState) When row filter state changed. * @emits component-state-changed(newState) When row filter state changed.
*/ */
let StatusFilter = { let StatusFilter = {
template: TEMPLATE, template: TEMPLATE,

View File

@ -21,7 +21,7 @@ class ComponentState {
* Component to filter rowobjects by a text value * Component to filter rowobjects by a text value
* *
* @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed. * @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed.
* @emits componentStateChanged(newState) When row filter state changed. Filter query... * @emits component-state-changed(newState) When row filter state changed. Filter query...
*/ */
let TextFilter = { let TextFilter = {
template: TEMPLATE, template: TEMPLATE,
@ -67,14 +67,14 @@ let TextFilter = {
}, },
watch: { watch: {
visibleRowObjects(visibleRowObjects) { visibleRowObjects(visibleRowObjects) {
this.$emit('visibleRowObjectsChanged', visibleRowObjects); this.$emit('visible-row-objects-changed', visibleRowObjects);
}, },
currentComponentState(newValue) { currentComponentState(newValue) {
this.$emit('componentStateChanged', newValue); this.$emit('component-state-changed', newValue);
} }
}, },
created() { created() {
this.$emit('visibleRowObjectsChanged', this.visibleRowObjects); this.$emit('visible-row-objects-changed', this.visibleRowObjects);
}, },
methods: { methods: {
filterByText(rowObject) { filterByText(rowObject) {

View File

@ -4,7 +4,7 @@ const TEMPLATE =`
<pillar-head-cell <pillar-head-cell
v-for="c in columns" v-for="c in columns"
:column="c" :column="c"
key="c._id" :key="c._id"
@sort="(column, direction) => $emit('sort', column, direction)" @sort="(column, direction) => $emit('sort', column, direction)"
/> />
</div> </div>

View File

@ -1,4 +1,5 @@
import '../../cells/renderer/CellProxy' import '../../cells/renderer/CellProxy'
import {RowBase} from '../RowObjectBase'
const TEMPLATE =` const TEMPLATE =`
@ -21,7 +22,7 @@ const TEMPLATE =`
Vue.component('pillar-table-row', { Vue.component('pillar-table-row', {
template: TEMPLATE, template: TEMPLATE,
props: { props: {
rowObject: Object, rowObject: RowBase,
columns: Array columns: Array
}, },
computed: { computed: {

View File

@ -77,10 +77,6 @@ function containerResizeY(window_height){
'height': window_height_minus_nav + 'px'} 'height': window_height_minus_nav + 'px'}
); );
$('#project_context, #project_context-header').css(
{'top' : breadcrumbs_height}
);
$('#project_nav-container, #project_tree').css( $('#project_nav-container, #project_tree').css(
{'max-height': (window_height_minus_nav) + 'px', {'max-height': (window_height_minus_nav) + 'px',
'height': (window_height_minus_nav) + 'px'} 'height': (window_height_minus_nav) + 'px'}
@ -113,18 +109,12 @@ function showProjectSidebar(){
Cookies.remove('bcloud_ui', 'hide_project_sidebar'); Cookies.remove('bcloud_ui', 'hide_project_sidebar');
$('#project-container').addClass('is-sidebar-visible'); $('#project-container').addClass('is-sidebar-visible');
// Hide the toggle button.
$('.breadcrumbs-container .project-sidebar-toggle').hide();
} }
function hideProjectSidebar(){ function hideProjectSidebar(){
setJSONCookie('bcloud_ui', 'hide_project_sidebar', true); setJSONCookie('bcloud_ui', 'hide_project_sidebar', true);
$('#project-container').removeClass('is-sidebar-visible'); $('#project-container').removeClass('is-sidebar-visible');
// Show the toggle button.
$('.breadcrumbs-container .project-sidebar-toggle').show();
} }
function toggleProjectSidebar(){ function toggleProjectSidebar(){

View File

@ -1,32 +1,32 @@
$comments-width-max: 710px $comments-width-max: 710px
.comments-tree .comments-tree
@extend .position-relative
@extend .w-100
max-width: $comments-width-max max-width: $comments-width-max
position: relative
width: 100%
.comments-list-title .comments-list-title
padding: 15px 0 10px 0 @extend .py-2
font-size: 1.1em font-size: 1.1em
font-weight: 300 font-weight: 300
color: rgba($color-text, .5) color: $color-text-dark-hint
/* Each comment on the list*/ /* Each comment on the list*/
.comment-container, .comment-container,
.comment-reply-container .comment-reply-container
display: flex @extend .d-flex
position: relative @extend .py-2
padding: 15px 0 @extend .position-relative
transition: background-color 150ms ease-in-out, padding 150ms ease-in-out, margin 150ms ease-in-out transition: background-color 150ms ease-in-out, padding 150ms ease-in-out, margin 150ms ease-in-out
&.comment-linked &.comment-linked
@extend .px-3
background-color: $color-background-light !important background-color: $color-background-light !important
box-shadow: inset 3px 0 0 $color-info box-shadow: inset 5px 0 0 $color-primary
padding-right: 20px
&:before &:before
bottom: 15px bottom: 15px
color: $color-info color: $color-primary
content: 'Linked Comment' content: 'Linked Comment'
font-size: .8em font-size: .8em
position: absolute position: absolute
@ -108,18 +108,21 @@ $comments-width-max: 710px
padding-bottom: 0 padding-bottom: 0
width: 100% width: 100%
strong
color: $color-text-dark-primary
&.is-reply &.is-reply
padding: padding:
left: 20px left: 20px
top: 5px top: 5px
margin-left: 35px margin-left: 35px
box-shadow: inset 3px 0 0 $color-background box-shadow: inset 5px 0 0 $color-background
+media-xs +media-xs
padding-left: 15px padding-left: 15px
&.comment-linked &.comment-linked
box-shadow: inset 3px 0 0 $color-info box-shadow: inset 5px 0 0 $color-primary
&.is-first &.is-first
border-top: 1px solid $color-background border-top: 1px solid $color-background
@ -154,13 +157,12 @@ $comments-width-max: 710px
.comment-action-rating.up:before .comment-action-rating.up:before
content: '\e83f' // Heart filled content: '\e83f' // Heart filled
.comment-rating-value .comment-rating-value
padding-right: 15px
color: $color-text-dark-secondary color: $color-text-dark-secondary
cursor: default cursor: default
.comment-action-rating .comment-action-rating
@extend .px-2
cursor: pointer cursor: pointer
font-family: 'pillar-font' font-family: 'pillar-font'
height: 25px height: 25px
@ -169,22 +171,22 @@ $comments-width-max: 710px
.comment-action-rating.up .comment-action-rating.up
&:hover &:hover
color: $color-upvote color: $color-upvote
&:before &:before
content: '\e83e' // Heart outlined content: '\e83e' // Heart outlined
top: 2px
position: relative position: relative
top: 3px
.comment-action .comment-action
color: $color-primary
padding-left: 10px padding-left: 10px
margin-left: 10px
.action .action
color: $color-primary
cursor: pointer cursor: pointer
margin-left: 15px margin-left: 15px
&:hover &:hover
color: $color-primary color: lighten($color-primary, 10%)
text-decoration: underline
&:before &:before
color: $color-text-dark-secondary color: $color-text-dark-secondary
@ -386,21 +388,15 @@ $comments-width-max: 710px
content: '' content: ''
color: transparent color: transparent
.user-badges ul.blender-id-badges .user-badges ul
list-style: none @extend .list-unstyled
padding: 0 @extend .mt-1
margin: 4px 0 0 0
li
margin: 2px 0 !important
li, li a, li img
padding: 0 !important
li
display: inline
img img
width: 16px
height: 16px height: 16px
width: 16px
li
line-height: 1.2em
.comment-avatar .comment-avatar
@extend .d-flex @extend .d-flex
@ -409,10 +405,10 @@ $comments-width-max: 710px
.user-avatar .user-avatar
img img
border-radius: 50% @extend .rounded-circle
width: 2em
height: 2em
box-shadow: 0 0 0 0.2em $color-background-light box-shadow: 0 0 0 0.2em $color-background-light
height: 2em
width: 2em
.drag-hover .drag-hover
outline-style: solid outline-style: solid

View File

@ -1,4 +1,5 @@
$node-latest-thumbnail-size: 160px $node-latest-thumbnail-size: 160px
$breadcrumbs-container-height: 26px
/* Dark navbar when browsing a project. */ /* Dark navbar when browsing a project. */
body.project, body.project,
@ -32,8 +33,11 @@ body.svnman, body.edit_node_types, body.search-project
+media-xl +media-xl
left: $project_nav-width-xl left: $project_nav-width-xl
.project-sidebar-toggle
@extend .d-none
#project-side-container #project-side-container
display: none display: none // It's 'flex' when is-sidebar-visible is in #project-container
+media-xs +media-xs
position: fixed position: fixed
@ -126,6 +130,7 @@ body.svnman, body.edit_node_types, body.search-project
/* Header with name and node edit tools */ /* Header with name and node edit tools */
#project_context-header #project_context-header
top: $breadcrumbs-container-height
right: 0 right: 0
z-index: $z-index-base + 3 z-index: $z-index-base + 3
@ -258,9 +263,12 @@ ul.project-edit-tools
border: none border: none
.breadcrumbs-container .breadcrumbs-container
height: $breadcrumbs-container-height
top: $project_header-height + 1 top: $project_header-height + 1
#project_context #project_context
padding-top: $breadcrumbs-container-height + 1
.node-details-description .node-details-description
font: font:
size: 1.2em size: 1.2em

View File

@ -52,6 +52,7 @@
@import _notifications @import _notifications
@import _comments @import _comments
@import _error @import _error
@import font-pillar
@import components/base @import components/base
@import components/alerts @import components/alerts

View File

@ -1,9 +1,11 @@
.alert .alert
margin-bottom: 0 margin-bottom: 0
text-align: center
padding: 10px 20px padding: 10px 20px
z-index: 16 z-index: 16
p
margin: 0
// overriden by alert types // overriden by alert types
color: $color-text-dark color: $color-text-dark
background-color: $color-background background-color: $color-background

View File

@ -9,6 +9,7 @@
size: cover size: cover
margin-bottom: 0 margin-bottom: 0
position: relative position: relative
text-shadow: 1px 1px 0 rgba($black, .2)
word-break: break-word word-break: break-word
+media-sm +media-sm
@ -54,9 +55,3 @@
+media-sm +media-sm
font-size: $display4-size font-size: $display4-size
.display-4
font-size: $h2-font-size
+media-sm
font-size: $display4-size

View File

@ -57,3 +57,10 @@ body.is-mobile
blockquote blockquote
background-color: rgba($color-background, .1) background-color: rgba($color-background, .1)
box-shadow: inset 5px 0 0 rgba($color-background, .2) box-shadow: inset 5px 0 0 rgba($color-background, .2)
.btn-outline-primary
border-color: $color-text-light
color: $color-text-light
&:hover
border-color: transparent

View File

@ -46,9 +46,10 @@
@import "../../node_modules/bootstrap/scss/print" @import "../../node_modules/bootstrap/scss/print"
// Pillar components. // Pillar components.
@import "font-pillar"
@import "apps_base" @import "apps_base"
@import "components/base" @import "components/base"
@import "components/jumbotron" @import "components/jumbotron"
@import "components/alerts" @import "components/alerts"
@import "components/navbar" @import "components/navbar"

View File

@ -73,8 +73,14 @@ $tree-color-highlight-background-text: $primary
.jstree-icon:empty .jstree-icon:empty
left: 25px !important left: 25px !important
/* Closed Folder */ // Invisible element to expand/collapse folders.
// &.jstree-closed &.jstree-open .jstree-icon.jstree-ocl,
&.jstree-closed .jstree-icon.jstree-ocl
float: left
min-width: 30px
opacity: 0
position: absolute
z-index: 1
/* The text of the last level item */ /* The text of the last level item */
.jstree-anchor .jstree-anchor

View File

@ -26,8 +26,9 @@
// Pillar components. // Pillar components.
@import "apps_base" @import "font-pillar"
@import "apps_base"
@import "components/navbar" @import "components/navbar"
@import "components/dropdown" @import "components/dropdown"
@import "components/footer" @import "components/footer"

View File

@ -7,7 +7,6 @@ html(lang="en")
link(href="{{ url_for('static_pillar', filename='assets/ico/favicon.png') }}", rel="shortcut icon") link(href="{{ url_for('static_pillar', filename='assets/ico/favicon.png') }}", rel="shortcut icon")
link(href="{{ url_for('static_pillar', filename='assets/ico/apple-touch-icon-precomposed.png') }}", rel="icon apple-touch-icon-precomposed", sizes="192x192") link(href="{{ url_for('static_pillar', filename='assets/ico/apple-touch-icon-precomposed.png') }}", rel="icon apple-touch-icon-precomposed", sizes="192x192")
link(href="{{ url_for('static_pillar', filename='assets/css/font-pillar.css') }}", rel="stylesheet")
link(href="{{ url_for('static_pillar', filename='assets/css/base.css') }}", rel="stylesheet") link(href="{{ url_for('static_pillar', filename='assets/css/base.css') }}", rel="stylesheet")
link(href='//fonts.googleapis.com/css?family=Roboto:300,400', rel='stylesheet', type='text/css') link(href='//fonts.googleapis.com/css?family=Roboto:300,400', rel='stylesheet', type='text/css')
| {% block head %}{% endblock %} | {% block head %}{% endblock %}

View File

@ -44,7 +44,6 @@ html(lang="en")
| {% block head %}{% endblock %} | {% block head %}{% endblock %}
| {% block css %} | {% block css %}
link(href="{{ url_for('static_pillar', filename='assets/css/font-pillar.css') }}", rel="stylesheet")
| {% if title == 'blog' %} | {% if title == 'blog' %}
link(href="{{ url_for('static_pillar', filename='assets/css/blog.css') }}", rel="stylesheet") link(href="{{ url_for('static_pillar', filename='assets/css/blog.css') }}", rel="stylesheet")
| {% else %} | {% else %}

View File

@ -84,20 +84,3 @@ mixin timeline(projectid, sortdirection)
//- TODO: Make nicer reuseable placeholder //- TODO: Make nicer reuseable placeholder
.h3.text-muted.m-auto .h3.text-muted.m-auto
i.pi-spin.spin i.pi-spin.spin
//- Category listing (Learn, Libraries, etc).
mixin category_list_item(title, text, url, image)
.row.pb-2.my-2
.col-md-3
a(href=url, title=title)
img.img-fluid(alt=title, src=image)
.col-md-9
a(href=url, title=title)
h3.font-weight-bold
=title
.lead
=text
if block
block

View File

@ -42,6 +42,7 @@
a(href="{{ node.short_link }}") {{ node.short_link }} a(href="{{ node.short_link }}") {{ node.short_link }}
| {% endif %} | {% endif %}
.mx-3
comments-tree#comments-embed( comments-tree#comments-embed(
parent-id="{{ node._id }}" parent-id="{{ node._id }}"
) )

View File

@ -1,4 +1,4 @@
| {% extends 'projects/landing.html' %} | {% extends 'projects/view.html' %}
include ../../../mixins/components include ../../../mixins/components
| {% set title = node.properties.url %} | {% set title = node.properties.url %}
@ -12,7 +12,7 @@ include ../../../mixins/components
+nav-secondary-link( +nav-secondary-link(
href="{{ url_for('nodes.edit', node_id=node._id) }}") href="{{ url_for('nodes.edit', node_id=node._id) }}")
i.pi-edit.pr-2 i.pi-edit.pr-2
span Edit Post span Edit Page
| {% endif %} | {% endif %}
| {% if node.picture %} | {% if node.picture %}
@ -21,9 +21,8 @@ include ../../../mixins/components
"{{ node.name }}", "{{ node.name }}",
null, null,
"{{ node.picture.thumbnail('h', api=api) }}", "{{ node.picture.thumbnail('h', api=api) }}",
"{{ node.url }}") "{{ request.url }}")
| {% endif %} | {% else %}
.container.pt-5 .container.pt-5
.row .row
.col-12.text-center .col-12.text-center
@ -31,6 +30,7 @@ include ../../../mixins/components
| {{ node.name }} | {{ node.name }}
hr.pb-2 hr.pb-2
| {% endif %}
.container.pb-5 .container.pb-5
.row .row

View File

@ -1,7 +1,7 @@
| {% extends 'projects/view.html' %} | {% extends 'projects/view.html' %}
| {% block body %} | {% block body %}
#project-container.container #project-container.container.is-sidebar-visible
div.page-content div.page-content
| {% include 'nodes/edit_embed.html' %} | {% include 'nodes/edit_embed.html' %}
| {% endblock body %} | {% endblock body %}

View File

@ -10,8 +10,8 @@ include ../mixins/components
| {% endblock navigation_tabs %} | {% endblock navigation_tabs %}
| {% block body %} | {% block body %}
#project-container #project-container.is-sidebar-visible
#project-side-container #project-side-container.bg-light
#project_nav #project_nav
#project_nav-container #project_nav-container

View File

@ -29,14 +29,12 @@ li.nav-item
| {% endblock %} | {% endblock %}
| {% block css %} | {% block css %}
link(href="{{ url_for('static_pillar', filename='assets/css/font-pillar.css') }}", rel="stylesheet")
link(href="{{ url_for('static_pillar', filename='assets/css/theatre.css') }}", rel="stylesheet") link(href="{{ url_for('static_pillar', filename='assets/css/theatre.css') }}", rel="stylesheet")
| {% endblock %} | {% endblock %}
| {% block body %} | {% block body %}
#theatre-container.d-flex.position-relative.h-100.overflow-hidden( #theatre-container.d-flex.position-relative.h-100.overflow-hidden(
class="{% if current_user.is_authenticated %}with-info{% endif %}") class="{% if current_user.is_authenticated %}with-info{% endif %}")
| {% endblock %} | {% endblock %}
| {% block footer_scripts %} | {% block footer_scripts %}