add dragging to catalog contents

This commit is contained in:
Seth Ladygo
2019-05-11 01:37:27 -07:00
parent a544b7eb6a
commit 26a03fc63f
3 changed files with 490 additions and 15 deletions

View File

@ -1,41 +1,308 @@
<template> <template>
<v-container fluid grid-list-lg ma-0 pa-1> <v-container fluid grid-list-lg ma-0 pa-1>
<v-layout row fill-height> <v-layout row fill-height>
<v-btn @click="step = 1">Add products</v-btn>
<v-btn @click="step = 3">Auto arrange</v-btn>
</v-layout>
<v-flex xs4> <v-layout row fill-height>
<SectionList/> <v-flex xs3>
<v-card>
<v-toolbar dense>
<v-toolbar-title>Sections</v-toolbar-title>
</v-toolbar>
<div id="sections" class="list-group">
<v-list-tile
v-for="item in sections"
:key="item.id"
class="v-list__tile list-group-item list-item"
v-bind:class="{'selected': selected === item}"
>
<v-list-tile-content class="section-parent">
<v-list-tile-title v-html="item.name"></v-list-tile-title>
<v-list-tile-sub-title class="section-drop" :section-id="item.id"></v-list-tile-sub-title>
</v-list-tile-content>
</v-list-tile>
</div>
</v-card>
</v-flex> </v-flex>
<v-flex xs4> <v-flex xs3>
<ProductFamilyList/> <v-card>
<v-toolbar dense>
<v-toolbar-title>Models</v-toolbar-title>
</v-toolbar>
<div id="models" class="list-group">
<div v-for="item in selectedModels" :key="item.model" class="v-list__tile list-item">
<v-list-tile-title v-html="item.name"></v-list-tile-title>
</div>
</div>
</v-card>
</v-flex> </v-flex>
<v-flex xs4> <v-flex xs3>
<MaterialList/> <v-card>
<v-toolbar dense>
<v-toolbar-title>Materials</v-toolbar-title>
</v-toolbar>
<div id="materials" class="list-group">
<div
v-for="item in selectedMaterials"
:key="item.id"
class="v-list__tile list-item"
>
<v-list-tile-title v-html="item.id"></v-list-tile-title>
</div>
</div>
</v-card>
</v-flex>
<v-flex xs3>
<RawDisplayer :value="selectedMaterials" title="mats" />
</v-flex> </v-flex>
</v-layout> </v-layout>
</v-container> </v-container>
</template> </template>
<script> <script>
import SectionList from './SectionList' import Sortable from 'sortablejs'
import ProductFamilyList from './ProductFamilyList' import { mapState, mapGetters, mapActions } from 'vuex'
// import ModelList from './ModelList' import arrayMove from 'array-move'
import MaterialList from './MaterialList' import RawDisplayer from './RawDisplayer'
import { paginateModels } from '@/pagination'
export default { export default {
components: { components: {
SectionList, RawDisplayer,
ProductFamilyList,
// ModelList,
MaterialList,
}, },
data: () => ({ data: () => ({
// NOTE do we need?
'placeholderText': 'placeholder',
// NOTE do we need?
'selected': null,
}),
computed: {
...mapState([
'catalog',
'materials',
]),
...mapGetters([
'section',
'sections',
'selectedSection',
'selectedModel',
'sectionModels',
'modelMaterials',
'material',
]),
selectedModels() {
let section = this.selectedSection
return this.sectionModels(section)
},
selectedMaterials() {
let model = this.selectedModel
return this.modelMaterials(model)
},
},
/*
watch: {
catalog: function(cat) {
console.log('catalog changed')
addSectionDrops(this)
},
},
*/
methods: {
reorderModel: function(fromIndex, toIndex) {
let models = this.selectedModels
arrayMove.mutate(models, fromIndex, toIndex)
let pages = paginateModels(models)
this.setSectionPages({ section: this.selectedSection, pages: pages })
},
reorderMaterial: function(fromIndex, toIndex) {
let mats = this.selectedMaterials
arrayMove.mutate(mats, fromIndex, toIndex)
this.setModelMaterials({ model: this.selectedModel, materials: mats })
},
/*
groupByModel: function(materials) {
let models = []
for (let mat of materials) {
let model = models.length > 0 ? models[models.length - 1] : null
if (model === null || model.id !== mat.model) {
// new model
model = { id: mat.model,
name: mat.name,
materials: [mat.id] }
models.push(model)
} else {
// add to existing
model.materials.push(mat.id)
}
}
return models
},
*/
...mapActions([
'reorderSection',
'setSectionPages',
'setModelMaterials',
'moveModelToSection',
'moveMaterialToSection',
]),
},
mounted: function() {
let me = this
// reordering of sections
Sortable.create(document.getElementById('sections'), {
group: {
name: 'sections',
},
animation: 150,
onEnd: function(evt) {
me.reorderSection({ from: evt.oldIndex, to: evt.newIndex })
},
}) })
// reordering of models
Sortable.create(document.getElementById('models'), {
group: {
name: 'models',
},
animation: 150,
onStart: function(evt) {
addSectionDrops(me)
},
onEnd: function(evt) {
if (evt.from === evt.to) {
me.reorderModel(evt.oldIndex, evt.newIndex)
} else {
// dropped on a section.
let models = me.selectedModels
let model = models[evt.oldIndex]
let sectionID = Number(evt.to.getAttribute('section-id'))
let newSection = me.section(sectionID)
if (me.selectedSection.id !== newSection.id) {
me.moveModelToSection({ model: model,
oldSection: me.selectedSection,
newSection: newSection })
} else {
console.log('not moving to same section')
}
}
},
})
// reordering of materials
Sortable.create(document.getElementById('materials'), {
group: {
name: 'materials',
},
animation: 150,
onStart: function(evt) {
addSectionDrops(me)
},
onEnd: function(evt) {
if (evt.from === evt.to) {
me.reorderMaterial(evt.oldIndex, evt.newIndex)
} else {
// dropped on a section.
let mats = me.selectedMaterials
let mat = mats[evt.oldIndex]
let sectionID = Number(evt.to.getAttribute('section-id'))
let newSection = me.section(sectionID)
if (me.selectedSection.id !== newSection.id) {
me.moveMaterialToSection({ material: mat,
oldModel: me.selectedModel,
newSection: newSection })
} else {
console.log('not moving to same section')
}
}
},
})
},
}
function addSectionDrops(myvue) {
// make sure our section drop targets are set up
// after catalog load or section add
// drop model or material into a section
var drops = document.getElementsByClassName('section-drop')
for (var i = 0; i < drops.length; ++i) {
if (!drops[i].getAttribute('drop-ready')) {
drops[i].setAttribute('drop-ready', true)
Sortable.create(drops[i], {
group: {
name: 'section-drops',
pull: false,
put: function(to, from) {
// don't allow drop on section where it already lives
let oldSectionID = myvue.selectedSection.id
let newSectionID = to.el.getAttribute('section-id')
if (Number(oldSectionID) === Number(newSectionID)) {
return false
}
// allow drop from anything but another section
return from.options.group.name !== 'sections'
},
},
animation: 150,
// (actual drop is handled in the originating list)
})
}
}
} }
</script> </script>
<style> <style>
.selected {
background-color: purple;
}
.sortable-chosen {
color: black;
background-color: #fddd04;
}
.sortable-ghost {
background-color: #bababa;
}
.section-parent {
position: relative;
}
.section-drop {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 100%;
}
.list-item {
border-bottom: 1px solid #ddd;
}
</style> </style>

View File

@ -1,6 +1,8 @@
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import arrayMove from 'array-move'
import axios from 'axios' import axios from 'axios'
import { paginateModels } from '@/pagination'
Vue.use(Vuex) Vue.use(Vuex)
@ -82,6 +84,77 @@ export const store = new Vuex.Store({
savingCatalog: false, savingCatalog: false,
}, },
getters: { getters: {
// NOTE don't use if you're not doing anything interesting, just use state.
material: (state) => (id) => {
return state.materials[id]
},
sections: state => {
if (state.catalog != null) {
return state.catalog.sections
} else {
return null
}
},
section: (state) => (id) => {
if (state.catalog != null) {
return state.catalog.sections.find(s => s.id === id)
} else {
return null
}
},
selectedSection: state => {
if (state.catalog != null) {
return state.catalog.sections[0]
} else {
return null
}
},
selectedModel: state => {
if (state.catalog != null) {
return state.catalog.sections[0].pages[0][0]
} else {
return null
}
},
sectionModels: (state, getters) => (section) => {
// remove pages, return models (blocks) in order
let models = []
if (section && section.pages) {
for (let page of section.pages) {
for (let model of page) {
if (model.ids && model.ids.length > 0) {
// add extra info from a material
let material = getters.material(model.ids[0])
if (material) {
model['name'] = material.name
model['model'] = material.model
model['family'] = material.family
} else {
console.log('no material found for id', model.ids[0])
}
models.push(model)
}
}
}
}
return models
},
modelMaterials: (state, getters) => (model) => {
if (model && model.ids && model.ids.length > 0) {
return model.ids.map(id => getters.material(id))
}
return []
},
/// OLD STUFF
// //
// catalog info // catalog info
// //
@ -95,6 +168,14 @@ export const store = new Vuex.Store({
// return state.catalog.sections.find(s => s.id === id) // return state.catalog.sections.find(s => s.id === id)
// }, // },
catalogSections: state => {
if (state.catalog != null) {
return state.catalog.sections
} else {
return null
}
},
catalogProperty: (state) => (key) => { catalogProperty: (state) => (key) => {
if (state.catalog != null) { if (state.catalog != null) {
return state.catalog[key] return state.catalog[key]
@ -137,6 +218,23 @@ export const store = new Vuex.Store({
}, },
}, },
mutations: { mutations: {
reorderSection(state, { from, to }) {
if (from === to) return
state.catalog.sections = arrayMove(state.catalog.sections, from, to)
},
setSectionPages(state, { section, pages }) {
if (section) {
section.pages = pages
}
},
setModelMaterials(state, { model, materials }) {
if (model) {
model.ids = materials.map(m => (typeof m === 'object') ? m.id : m)
}
},
// SET_CAT_SECTIONS: (state, payload) => { // SET_CAT_SECTIONS: (state, payload) => {
// state.catalog.sections = payload // state.catalog.sections = payload
// }, // },
@ -185,6 +283,71 @@ export const store = new Vuex.Store({
}, },
actions: { actions: {
reorderSection({ commit }, payload) {
commit('reorderSection', payload)
},
setSectionPages({ commit }, payload) {
commit('setSectionPages', payload)
},
setModelMaterials({ commit }, payload) {
commit('setModelMaterials', payload)
},
moveModelToSection({ commit, getters }, { model, oldSection, newSection }) {
// remove model from old section
let oldModels = getters.sectionModels(oldSection)
oldModels = oldModels.filter(m => m.model !== model.model)
let oldPages = paginateModels(oldModels)
commit('setSectionPages', { section: oldSection, pages: oldPages })
// add model to new section
let newModels = getters.sectionModels(newSection)
let existingModel = newModels.find(m => m.model === model.model)
if (existingModel) {
// merge
var ids = existingModel.ids
ids.push(...model.ids)
// uniquify
ids = ids.filter((v, i, a) => a.indexOf(v) === i)
existingModel.ids = ids
} else {
// add at end
newModels.push(model)
}
let newPages = paginateModels(newModels)
commit('setSectionPages', { section: newSection, pages: newPages })
},
moveMaterialToSection({ commit, getters }, { material, oldModel, newSection }) {
// ensure material, not just id
if (typeof material === 'string' || typeof material === 'number') {
material = getters.material(material)
}
// remove material from old model
let mats = oldModel.ids.filter(id => id !== material.id)
commit('setModelMaterials', { model: oldModel, materials: mats })
// add material to new section
let newModels = getters.sectionModels(newSection)
let existingModel = newModels.find(m => m.model === material.model)
if (existingModel) {
// merge
var ids = existingModel.ids
ids.push(material.id)
// uniquify
ids = ids.filter((v, i, a) => a.indexOf(v) === i)
existingModel.ids = ids
} else {
// add at end
newModels.push({ ids: [material.id] })
}
let newPages = paginateModels(newModels)
commit('setSectionPages', { section: newSection, pages: newPages })
},
// SET_CAT_SECTIONS: (context, payload) => { // SET_CAT_SECTIONS: (context, payload) => {
// context.commit('SET_CAT_SECTIONS', payload) // context.commit('SET_CAT_SECTIONS', payload)
// }, // },

View File

@ -0,0 +1,45 @@
export function paginateModels(models) {
for (let model of models) {
// ensure sizes
model.size = modelSize(model)
}
let pages = []
let currentSize = 0
let currentPage = []
for (let model of models) {
currentSize += model.size
if (currentSize > 3) {
pages.push(currentPage)
currentPage = [model]
currentSize = model.size
} else {
currentPage.push(model)
}
}
if (currentPage.length > 0) {
pages.push(currentPage)
}
return pages
}
export function modelSize(model) {
if (!model || !model.ids) return 0
if (model.ids.length <= 4) return 1
if (model.ids.length <= 8) return 2
return 3
}
export function sectionModels(section) {
// remove pages, return models (blocks) in order
let models = []
if (section && section.pages) {
for (let page of section.pages) {
models.push(...page)
}
}
return models
}