mirror of
https://github.com/Lissy93/dashy.git
synced 2025-07-24 22:25:16 +02:00
Replaced masonry plugin with CSS grid layout
This commit is contained in:
parent
479221c5d5
commit
cb631b9500
BIN
public/img/item-icons/tile-icons/networking/compliance.png
Normal file
BIN
public/img/item-icons/tile-icons/networking/compliance.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
@ -74,7 +74,6 @@ export default {
|
|||||||
@import '../../src/styles/constants.scss';
|
@import '../../src/styles/constants.scss';
|
||||||
|
|
||||||
.collapsable {
|
.collapsable {
|
||||||
// width: 310px;
|
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
border-radius: $curve-factor;
|
border-radius: $curve-factor;
|
||||||
@ -82,7 +81,9 @@ export default {
|
|||||||
// background: -webkit-linear-gradient(to left top, #9F86FF, #1CA8DD, #007AE1);
|
// background: -webkit-linear-gradient(to left top, #9F86FF, #1CA8DD, #007AE1);
|
||||||
// background: linear-gradient(to left top, #9F86FF, #1CA8DD, #007AE1);
|
// background: linear-gradient(to left top, #9F86FF, #1CA8DD, #007AE1);
|
||||||
box-shadow: 1px 1px 2px #130f23;
|
box-shadow: 1px 1px 2px #130f23;
|
||||||
width: auto;
|
height: fit-content;
|
||||||
|
width: 100%;
|
||||||
|
width: stretch;
|
||||||
// &.col-1 { width: 155px; }
|
// &.col-1 { width: 155px; }
|
||||||
// &.col-2 { width: 310px; }
|
// &.col-2 { width: 310px; }
|
||||||
// &.col-3 { width: 465px; }
|
// &.col-3 { width: 465px; }
|
||||||
|
@ -1,226 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// the component name `<masonry />`
|
|
||||||
// can be overridden with `Vue.use(Masonry, { name: 'the-masonry' });`
|
|
||||||
const componentName = 'masonry';
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
tag: {
|
|
||||||
type: [String],
|
|
||||||
default: 'div',
|
|
||||||
},
|
|
||||||
cols: {
|
|
||||||
type: [Object, Number, String],
|
|
||||||
default: 2,
|
|
||||||
},
|
|
||||||
gutter: {
|
|
||||||
type: [Object, Number, String],
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
css: {
|
|
||||||
type: [Boolean],
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
columnTag: {
|
|
||||||
type: [String],
|
|
||||||
default: 'div',
|
|
||||||
},
|
|
||||||
columnClass: {
|
|
||||||
type: [String, Array, Object],
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
columnAttr: {
|
|
||||||
type: [Object],
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the resulting value from `:col=` prop
|
|
||||||
// based on the window width
|
|
||||||
const breakpointValue = (mixed, windowWidth) => {
|
|
||||||
const valueAsNum = parseInt(mixed);
|
|
||||||
|
|
||||||
if (valueAsNum > -1) {
|
|
||||||
return mixed;
|
|
||||||
} if (typeof mixed !== 'object') {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let matchedBreakpoint = Infinity;
|
|
||||||
let matchedValue = mixed.default || 0;
|
|
||||||
|
|
||||||
for (const k in mixed) {
|
|
||||||
const breakpoint = parseInt(k);
|
|
||||||
const breakpointValRaw = mixed[breakpoint];
|
|
||||||
const breakpointVal = parseInt(breakpointValRaw);
|
|
||||||
|
|
||||||
if (isNaN(breakpoint) || isNaN(breakpointVal)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNewBreakpoint = windowWidth <= breakpoint && breakpoint < matchedBreakpoint;
|
|
||||||
|
|
||||||
if (isNewBreakpoint) {
|
|
||||||
matchedBreakpoint = breakpoint;
|
|
||||||
matchedValue = breakpointValRaw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchedValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
const component = {
|
|
||||||
props,
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
displayColumns: 2,
|
|
||||||
displayGutter: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.reCalculate();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bind resize handler to page
|
|
||||||
if (window) {
|
|
||||||
window.addEventListener('resize', this.reCalculate);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updated() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.reCalculate();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
if (window) {
|
|
||||||
window.removeEventListener('resize', this.reCalculate);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// Recalculate how many columns to display based on window width
|
|
||||||
// and the value of the passed `:cols=` prop
|
|
||||||
reCalculate() {
|
|
||||||
const previousWindowWidth = this.windowWidth;
|
|
||||||
|
|
||||||
this.windowWidth = (window ? window.innerWidth : null) || Infinity;
|
|
||||||
|
|
||||||
// Window resize events get triggered on page height
|
|
||||||
// change which when loading the page can result in multiple
|
|
||||||
// needless calculations. We prevent this here.
|
|
||||||
if (previousWindowWidth === this.windowWidth) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._reCalculateColumnCount(this.windowWidth);
|
|
||||||
|
|
||||||
this._reCalculateGutterSize(this.windowWidth);
|
|
||||||
},
|
|
||||||
|
|
||||||
_reCalculateGutterSize(windowWidth) {
|
|
||||||
this.displayGutter = breakpointValue(this.gutter, windowWidth);
|
|
||||||
},
|
|
||||||
|
|
||||||
_reCalculateColumnCount(windowWidth) {
|
|
||||||
let newColumns = breakpointValue(this.cols, windowWidth);
|
|
||||||
|
|
||||||
// Make sure we can return a valid value
|
|
||||||
newColumns = Math.max(1, Number(newColumns) || 0);
|
|
||||||
|
|
||||||
this.displayColumns = newColumns;
|
|
||||||
},
|
|
||||||
|
|
||||||
_getChildItemsInColumnsArray() {
|
|
||||||
const columns = [];
|
|
||||||
let childItems = this.$slots.default || [];
|
|
||||||
|
|
||||||
// This component does not work with a child <transition-group /> ..yet,
|
|
||||||
// so for now we think it may be helpful to ignore until we can find a way for support
|
|
||||||
if (childItems.length === 1 && childItems[0].componentOptions && childItems[0].componentOptions.tag == 'transition-group') {
|
|
||||||
childItems = childItems[0].componentOptions.children;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through child elements
|
|
||||||
for (let i = 0, visibleItemI = 0; i < childItems.length; i++, visibleItemI++) {
|
|
||||||
// skip Vue elements without tags, which includes
|
|
||||||
// whitespace elements and also plain text
|
|
||||||
if (!childItems[i].tag) {
|
|
||||||
visibleItemI--;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the column index the child item will end up in
|
|
||||||
const columnIndex = visibleItemI % this.displayColumns;
|
|
||||||
|
|
||||||
if (!columns[columnIndex]) {
|
|
||||||
columns[columnIndex] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
columns[columnIndex].push(childItems[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
render(createElement) {
|
|
||||||
const columnsContainingChildren = this._getChildItemsInColumnsArray();
|
|
||||||
const isGutterSizeUnitless = parseInt(this.displayGutter) === this.displayGutter * 1;
|
|
||||||
const gutterSizeWithUnit = isGutterSizeUnitless ? `${this.displayGutter}px` : this.displayGutter;
|
|
||||||
|
|
||||||
const columnStyle = {
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
backgroundClip: 'padding-box',
|
|
||||||
width: `${100 / this.displayColumns}%`,
|
|
||||||
border: '0 solid transparent',
|
|
||||||
borderLeftWidth: gutterSizeWithUnit,
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = columnsContainingChildren.map((children, index) =>
|
|
||||||
// / Create column element and inject the children
|
|
||||||
createElement(this.columnTag, {
|
|
||||||
key: `${index}-${columnsContainingChildren.length}`,
|
|
||||||
style: this.css ? columnStyle : null,
|
|
||||||
class: this.columnClass,
|
|
||||||
attrs: this.columnAttr,
|
|
||||||
}, children), // specify child items here
|
|
||||||
);
|
|
||||||
|
|
||||||
const containerStyle = {
|
|
||||||
display: ['-webkit-box', '-ms-flexbox', 'flex'],
|
|
||||||
marginLeft: `-${gutterSizeWithUnit}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return wrapper with columns
|
|
||||||
return createElement(
|
|
||||||
this.tag, // tag name
|
|
||||||
this.css ? { style: containerStyle } : null, // element options
|
|
||||||
columns, // column vue elements
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const Plugin = function () {};
|
|
||||||
|
|
||||||
Plugin.install = function (Vue, options) {
|
|
||||||
if (Plugin.installed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options && options.name) {
|
|
||||||
Vue.component(options.name, component);
|
|
||||||
} else {
|
|
||||||
Vue.component(componentName, component);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && window.Vue) {
|
|
||||||
window.Vue.use(Plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Plugin;
|
|
@ -4,10 +4,8 @@ import 'element-ui/lib/theme-chalk/index.css';
|
|||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import './registerServiceWorker';
|
import './registerServiceWorker';
|
||||||
import VueMasonry from './lib/vue-masonry-css'; // Thank you @PaulCollett 🙌 https://git.io/JeeYC
|
|
||||||
|
|
||||||
Vue.use(Element);
|
Vue.use(Element);
|
||||||
Vue.use(VueMasonry);
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
|
40
src/styles/media-queries.scss
Normal file
40
src/styles/media-queries.scss
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
/* Widths in px */
|
||||||
|
$tiny: 600px;
|
||||||
|
$small: 780px;
|
||||||
|
$medium: 1150px;
|
||||||
|
$large: 1780px;
|
||||||
|
$extra-large: 2800px;
|
||||||
|
|
||||||
|
/* Usage @include tablet { ... } */
|
||||||
|
|
||||||
|
@mixin phone {
|
||||||
|
@media (max-width: #{$tiny - 1px}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin tablet {
|
||||||
|
@media (min-width: #{$tiny}) and (max-width: #{$small - 1px}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin laptop {
|
||||||
|
@media (min-width: #{$small}) and (max-width: #{$medium - 1px}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin monitor {
|
||||||
|
@media (min-width: #{$medium}) and (max-width: #{$large - 1px}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin big-screen {
|
||||||
|
@media (min-width: #{$large}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,20 +3,15 @@
|
|||||||
<Header />
|
<Header />
|
||||||
<FilterTile @user-is-searchin="searching" class="filter-container" />
|
<FilterTile @user-is-searchin="searching" class="filter-container" />
|
||||||
<div class="item-group-container">
|
<div class="item-group-container">
|
||||||
<masonry
|
<ItemGroup
|
||||||
:cols="{600: 1, 780: 2, 1150: 2, 1780: 3, 9999: 4}"
|
v-for="(item, index) in items"
|
||||||
:gutter="30"
|
:key="index"
|
||||||
>
|
:groupId="item.id"
|
||||||
<ItemGroup
|
:title="item.name"
|
||||||
v-for="(item, index) in items"
|
:collapsed="item.collapsed"
|
||||||
:key="index"
|
:cols="item.cols"
|
||||||
:groupId="item.id"
|
:items="filterTiles(item.items)"
|
||||||
:title="item.name"
|
/>
|
||||||
:collapsed="item.collapsed"
|
|
||||||
:cols="item.cols"
|
|
||||||
:items="filterTiles(item.items)"
|
|
||||||
/>
|
|
||||||
</masonry>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -69,22 +64,34 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
@import '../../src/styles/color-pallet.scss';
|
@import '../../src/styles/color-pallet.scss';
|
||||||
|
@import '../../src/styles/media-queries.scss';
|
||||||
|
|
||||||
.home {
|
.home {
|
||||||
background: $background;
|
background: $background;
|
||||||
padding-bottom: 1px;
|
padding-bottom: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Outside container wrapping the item groups*/
|
||||||
.item-group-container {
|
.item-group-container {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-wrap: wrap;
|
gap: 10px;
|
||||||
margin: 2rem;
|
// grid-template-rows: masonry;
|
||||||
align-items:flex-start;
|
|
||||||
align-content:flex-start;
|
@include phone {
|
||||||
.item-group-outer {
|
grid-template-columns: repeat(1, 1fr);
|
||||||
margin: 10px;
|
}
|
||||||
|
@include tablet {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
@include laptop {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
@include monitor {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
@include big-screen {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user