material 0.9.12 → 0.9.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,221 @@
1
+ import clone from '../module/clone'
2
+
3
+ export default {
4
+ select (item, silent) {
5
+ // console.log('select', item.dataset.id)
6
+ if (!item || !item.dataset.id) return
7
+
8
+ const id = item.dataset.id
9
+
10
+ this.index = this.dataList.indexOf(id)
11
+ this.info = this.dataStore[id]
12
+
13
+ if (this.options.preventSelectAgain && this.id === id) {
14
+ return
15
+ }
16
+
17
+ const selected = this.ui.body.querySelector('.selected')
18
+ if (selected) selected.classList.remove('selected')
19
+
20
+ if (id) {
21
+ this.highlight(item)
22
+ this.item = item
23
+ this.id = id
24
+
25
+ if (silent !== true) {
26
+ this.emit('select', id)
27
+ this.emit('selectItem', item)
28
+ }
29
+ } else {
30
+ this.id = null
31
+ this.item = null
32
+ }
33
+ },
34
+
35
+ unselect () {
36
+ // console.log('unselect')
37
+ const item = this.ui.body.querySelector('[data-id="' + this.id + '"]') || this.item
38
+ this.id = null
39
+ if (item) {
40
+ item.classList.remove('selected')
41
+ }
42
+ this.index = null
43
+ this.item = null
44
+ },
45
+
46
+ getInfoById (id) {
47
+ return clone(this.dataStore[id])
48
+ },
49
+
50
+ getSelectedInfo () {
51
+ return clone(this.dataStore[this.id])
52
+ },
53
+
54
+ getItemById (id, silent) {
55
+ // console.log('selectItemById', id)
56
+ const item = this.ui.body.querySelector('[data-id="' + id + '"]')
57
+
58
+ if (!item) {
59
+ return null
60
+ } else {
61
+ return item
62
+ }
63
+ },
64
+
65
+ selectById (id, silent) {
66
+ // console.log('selectById', id)
67
+
68
+ if (!this.dataList) {
69
+ return false
70
+ }
71
+
72
+ const index = this.dataList.indexOf(id)
73
+ if (index < 0) {
74
+ // console.log('can\'t selectid, not in the list')
75
+ return false
76
+ }
77
+
78
+ this.index = index
79
+ this.id = id
80
+
81
+ this.selectItemById(id, 'down')
82
+
83
+ return id
84
+ },
85
+
86
+ selectItemById (id, direction) {
87
+ // console.log('selectItemById', id)
88
+ const item = this.ui.body.querySelector('[data-id="' + id + '"]')
89
+
90
+ this.id = id
91
+
92
+ if (!item) {
93
+ const index = this.dataList.indexOf(id)
94
+ // console.log('not found in DOM, check in dataList', index)
95
+ if (index > -1) {
96
+ const listSize = this.options.list.size
97
+ const slice = listSize * index / listSize
98
+ // console.log('getSlice', slice)
99
+ }
100
+
101
+ return
102
+ }
103
+
104
+ // console.log('item found', item)
105
+
106
+ // if (this.item) this.item.classList.remove('selected')
107
+
108
+ const selected = this.ui.body.querySelector('.selected')
109
+ if (selected) selected.classList.remove('selected')
110
+
111
+ this.item = item
112
+ item.classList.add('selected')
113
+
114
+ if (this.options.updatePosition === true) {
115
+ // console.log('selectPosition', item)
116
+ this.selectPosition(item, direction)
117
+ }
118
+
119
+ return item
120
+ },
121
+
122
+ selectPosition (item, direction) {
123
+ const offsetY = this.options.item.height
124
+ direction = direction || 'down'
125
+ // console.log('selectPosition', this.ui.body, direction)
126
+
127
+ this.coord = this.ui.body.getBoundingClientRect()
128
+
129
+ // console.log('coord', this.coord)
130
+
131
+ const itemTop = item.offsetTop + this.coord.top - this.ui.body.scrollTop
132
+
133
+ if (direction === 'down' && itemTop + this.options.item.height + offsetY - this.coord.y > this.coord.height) {
134
+ var top = this.ui.body.scrollTop + itemTop - this.coord.height - this.coord.y + this.options.item.height + offsetY
135
+ this.ui.body.scrollTo({
136
+ top,
137
+ left: 0
138
+ // behavior: 'smooth'
139
+ })
140
+ }
141
+
142
+ if (direction === 'up' && itemTop - offsetY < this.coord.top + this.coord.y) {
143
+ var top = this.ui.body.scrollTop + itemTop - this.coord.top - offsetY
144
+ // console.lof('scrollTo', top, item)
145
+ this.ui.body.scrollTo({
146
+ left: 0,
147
+ top
148
+ // behavior: 'smooth'
149
+ })
150
+ }
151
+ },
152
+
153
+ selectNext (silent) {
154
+ // console.log('selectNext', this.id)
155
+ const idx = this.dataList.indexOf(this.id)
156
+
157
+ const id = this.dataList[idx + 1]
158
+
159
+ // console.log('id', id)
160
+
161
+ if (id) {
162
+ this.id = id
163
+ // console.log('item', item)
164
+ this.selectItemById(id, 'down')
165
+
166
+ if (silent !== true) {
167
+ this.emit('select', id)
168
+ }
169
+ return id
170
+ } else {
171
+ return false
172
+ }
173
+ },
174
+
175
+ selectPrevious (silent) {
176
+ // console.log('selectPrevious', this.id)
177
+ const idx = this.dataList.indexOf(this.id)
178
+
179
+ const id = this.dataList[idx - 1]
180
+
181
+ if (id) {
182
+ this.id = id
183
+ // console.log('item', item)
184
+ this.selectItemById(id, 'up')
185
+
186
+ if (silent !== true) {
187
+ this.emit('select', id)
188
+ }
189
+ return id
190
+ } else {
191
+ return false
192
+ }
193
+ },
194
+
195
+ scrollIntoView (item) {
196
+ // console.log('selectPrevious', this.id)
197
+ item = item || this.item
198
+
199
+ if (item) {
200
+ item.scrollIntoView({ behavior: 'instant', block: 'end', inline: 'nearest' })
201
+ }
202
+ },
203
+
204
+ highlight (item) {
205
+ if (this.item) {
206
+ this.item.classList.remove('selected')
207
+ }
208
+ item.classList.add('selected')
209
+ },
210
+
211
+ selectCreated (info, item) {
212
+ // console.log('select create', info, this.item)
213
+ if (this.item) {
214
+ this.item.classList.remove('selected')
215
+ }
216
+
217
+ item.classList.add('selected')
218
+
219
+ this.emit('selectCreate', info)
220
+ }
221
+ }
@@ -0,0 +1,21 @@
1
+ export default {
2
+
3
+ statusDisplay (prop, info) {
4
+ // console.log('status', prop, info)
5
+ if (prop === 'count') {
6
+ this.statusCount(info)
7
+ }
8
+ },
9
+
10
+ statusCount (count) {
11
+ // console.log('statusCount', count)
12
+ let items = 'items'
13
+ if (this.status && this.status.count && this.status.count.name) {
14
+ items = this.status.count.name
15
+ }
16
+
17
+ if (this.ui.count) {
18
+ this.ui.count.innerHTML = '<span class="number">' + count + '</span> ' + items
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,75 @@
1
+ import { create } from '../module/layout'
2
+
3
+ export default {
4
+ update (info) {
5
+ // console.log('update', info)
6
+
7
+ this.getUpdatedInfo(info[this.dataId])
8
+ },
9
+
10
+ updateInfo (info) {
11
+ // console.log('update', info)
12
+ this.dataStore[info._id] = info
13
+
14
+ // update item in the list if visible
15
+ const item = this.ui.body.querySelector('[data-id="' + info[this.dataId] + '"]')
16
+ if (item) {
17
+ item.innerHTML = ''
18
+ const layout = create(this.options.layout.item, item)
19
+
20
+ this.renderInfo(layout, info)
21
+ }
22
+ },
23
+
24
+ getUpdatedIndex (id) {
25
+ // console.log('getUpdatedIndex', id)
26
+ let index = null
27
+ for (let i = 0; i < this.data.length; i++) {
28
+ // console.log('--', this.data[i][this.dataId], id)
29
+ if (this.data[i][this.dataId] === id) {
30
+ index = i
31
+ break
32
+ }
33
+ }
34
+
35
+ return index
36
+ },
37
+
38
+ getUpdatedInfo (id) {
39
+ // console.log('getUpdatedInfo', id)
40
+ const index = this.getUpdatedIndex(id)
41
+ // console.log('index', index)
42
+ const route = this.buildRoute(index + 1, 1)
43
+ // console.log('roote', route)
44
+ fetch(route).then((resp) => {
45
+ return resp.json()
46
+ }).then((data) => {
47
+ // console.log('info', data)
48
+ this.updateUpdatedInfo(data[0], index, id)
49
+ })
50
+ },
51
+
52
+ updateUpdatedInfo (info, index, id) {
53
+ // console.log('updateUpdatedInfo', info, index)
54
+
55
+ if (!info) return
56
+ if (info[this.dataId] !== id) return
57
+
58
+ this.info = info
59
+
60
+ // update datalist
61
+ this.data[index] = info
62
+
63
+ // update datastore
64
+ this.dataStore[info[this.dataId]] = info
65
+
66
+ // update item if on the screen
67
+ const item = this.ui.body.querySelector('[data-id="' + info[this.dataId] + '"]')
68
+ if (item) {
69
+ item.innerHTML = ''
70
+ const layout = create(this.options.layout.item, item)
71
+
72
+ this.renderInfo(layout, info)
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,186 @@
1
+ // based on https://sergimansilla.com/blog/virtual-scrolling/
2
+
3
+ // Import necessary modules
4
+ import passiveEvents from '../module/passive' // Import the passiveEvents utility.
5
+ import emitter from '../module/emitter' // Import the emitter module.
6
+
7
+ // Define the VirtualList class
8
+ class VirtualList {
9
+ constructor (options) {
10
+ // Initialize the class with options passed to the constructor.
11
+ this.options = { ...(options || {}) } // Spread operator to merge options with default values.
12
+ Object.assign(this, emitter) // Assign emitter methods to the instance.
13
+
14
+ // Destructure options for easier access to properties.
15
+ const { itemHeight = 0, size = 0, items = [], render, container } = options
16
+
17
+ // Initialize instance variables with default or provided values.
18
+ this.itemHeight = itemHeight
19
+ this.next = false
20
+ this.size = size
21
+ this.chunkSize = size
22
+ this.items = items
23
+ this.render = render
24
+ this.scroller = VirtualList.createScroller()
25
+ this.container = container
26
+
27
+ // Add 'virtual' class to the container element.
28
+ this.container.classList.add('virtual')
29
+ }
30
+
31
+ // Method to render a chunk of items within the visible range.
32
+ renderChunk (node, from, number) {
33
+ console.error('renderChunck', from, number)
34
+ const fragment = document.createDocumentFragment()
35
+ fragment.appendChild(this.scroller)
36
+
37
+ let last = from + number
38
+ if (last > this.count) last = this.count
39
+
40
+ for (let i = from; i < last; i++) {
41
+ const item = this.render(i)
42
+
43
+ // Position the item absolutely based on its index and item height.
44
+ item.style.position = 'absolute'
45
+ item.style.top = i * this.itemHeight + 'px'
46
+ if (i === last) item.classList.add('last')
47
+ fragment.appendChild(item)
48
+ }
49
+
50
+ node.innerHTML = ''
51
+ node.appendChild(fragment)
52
+
53
+ // Emit 'next' event if applicable.
54
+ // console.log('last', last)
55
+ // console.log('count', this.count)
56
+ // console.log('next', this.next)
57
+ // console.log('size', this.size)
58
+
59
+ if (this.next && last && last > (this.count - this.size / 2)) {
60
+ this.emit('next', this.count)
61
+ }
62
+
63
+ this.next = true
64
+ }
65
+
66
+ // Method to set the items and render the initial chunk.
67
+ set (items, info) {
68
+ console.error('set', items, info.page, info.size)
69
+
70
+ this.next = false
71
+ this.items = items
72
+ this.count = items.length
73
+
74
+ if (this.count < 1 || this.count === undefined) return
75
+
76
+ // this.container.innerHTML = ''
77
+ // this.container.scrollTop = 0
78
+
79
+ this.itemsByScreen = info.size / 2
80
+
81
+ let size = this.itemsByScreen * 4
82
+ this.renderChunk(this.container, (info.page - 1) * size, size)
83
+
84
+ this.size = size
85
+
86
+ this.emit('size', size)
87
+
88
+ let lastRepaintY
89
+ const maxBuffer = this.itemsByScreen * this.itemHeight
90
+
91
+ // Add a scroll event listener to render items dynamically.
92
+ const onScroll = (e) => {
93
+ const scrollTop = e.target.scrollTop
94
+ console.log('scrollTop', scrollTop)
95
+ const first = parseInt(scrollTop / this.itemHeight) - this.itemsByScreen
96
+
97
+ size = this.itemsByScreen * 4
98
+
99
+ if (!lastRepaintY || Math.abs(scrollTop - lastRepaintY) > maxBuffer) {
100
+ this.renderChunk(this.container, first, size)
101
+ lastRepaintY = scrollTop
102
+ }
103
+
104
+ const progress = Math.ceil((scrollTop / this.itemHeight) + this.itemsByScreen) - 1
105
+ this.emit('progress', progress)
106
+ }
107
+
108
+ if (!this.scrollEvents) {
109
+ // Add scroll event listener with passive option if supported.
110
+ this.container.addEventListener(
111
+ 'scroll',
112
+ onScroll,
113
+ passiveEvents() ? { passive: true } : false
114
+ )
115
+ this.scrollEvents = true
116
+ }
117
+ }
118
+
119
+ // Method to add items and update the container height.
120
+ add (items, info) {
121
+ console.error('add', items.length, info)
122
+ this.items = items || this.items
123
+
124
+ if (!this.items) return
125
+
126
+ this.count = this.items.length
127
+ }
128
+
129
+ // Method to update the items and re-render the visible chunk.
130
+ update (items) {
131
+ console.error('update', items.length)
132
+ this.items = items || this.items
133
+
134
+ if (!this.items) return
135
+
136
+ this.count = this.items.length
137
+ const cachedItemsLen = this.itemsByScreen * 4
138
+ const scrollTop = this.container.scrollTop
139
+ const height = this.itemHeight * this.count
140
+
141
+ // this.scroller.style.height = height + 'px'
142
+
143
+ let first = parseInt(scrollTop / this.itemHeight) - this.itemsByScreen
144
+ first = first < 0 ? 0 : first
145
+
146
+ this.renderChunk(this.container, first, cachedItemsLen)
147
+ }
148
+
149
+ // Method to reset the items and container to an initial state.
150
+ reset () {
151
+ console.log('reset')
152
+ this.items = []
153
+ this.count = 0
154
+
155
+ this.container.scrollTop = 0
156
+
157
+ this.scroller.style.height = '0'
158
+ }
159
+
160
+ setCount (count) {
161
+ console.log('setCount', count, this.itemHeight)
162
+ this.totalCount = count
163
+ const height = this.itemHeight * count
164
+
165
+ this.container.height = height + 'px'
166
+
167
+ console.log('scroller height', height)
168
+ this.scroller.style.height = height + 'px'
169
+ }
170
+
171
+ // Method to get the total count of items.
172
+ getCount () {
173
+ console.log('getCount', this.totalCount)
174
+ return this.totalCount
175
+ }
176
+
177
+ // Static method to create a scroller element.
178
+ static createScroller () {
179
+ const scroller = document.createElement('div')
180
+ scroller.classList.add('scroller')
181
+ return scroller
182
+ }
183
+ }
184
+
185
+ // Export the VirtualList class as the default export.
186
+ export default VirtualList
@@ -0,0 +1,213 @@
1
+ // based on https://sergimansilla.com/blog/virtual-scrolling/
2
+
3
+ // Import necessary modules
4
+ import passiveEvents from '../module/passive' // Import the passiveEvents utility.
5
+ import emitter from '../module/emitter' // Import the emitter module.
6
+
7
+ // Define the VirtualList class
8
+ class VirtualList {
9
+ constructor (options) {
10
+ // Initialize the class with options passed to the constructor.
11
+ this.options = { ...(options || {}) } // Spread operator to merge options with default values.
12
+ Object.assign(this, emitter) // Assign emitter methods to the instance.
13
+
14
+ // Destructure options for easier access to properties.
15
+ const { itemHeight = 0, size = 0, items = [], render, container } = options
16
+
17
+ // Initialize instance variables with default or provided values.
18
+ this.itemHeight = itemHeight
19
+ this.next = false
20
+ this.size = size
21
+ this.chunkSize = size
22
+ this.items = items
23
+ this.render = render
24
+ this.scroller = VirtualList.createScroller()
25
+ this.container = container
26
+
27
+ // Get the offset height of the container.
28
+ this.setOffset()
29
+
30
+ // Add a window resize event listener to update the offset height.
31
+ window.addEventListener('resize', () => {
32
+ clearTimeout(this.resizeTimeout)
33
+
34
+ this.resizeTimeout = setTimeout(() => {
35
+ this.setOffset()
36
+ }, 50)
37
+ })
38
+
39
+ // Add 'virtual' class to the container element.
40
+ this.container.classList.add('virtual')
41
+ }
42
+
43
+ // Method to render a chunk of items within the visible range.
44
+ renderChunk (node, from, number) {
45
+ const fragment = document.createDocumentFragment()
46
+ fragment.appendChild(this.scroller)
47
+
48
+ let last = from + number
49
+ if (last > this.count) last = this.count
50
+
51
+ for (let i = from; i < last; i++) {
52
+ const item = this.render(i)
53
+
54
+ // Position the item absolutely based on its index and item height.
55
+ item.style.position = 'absolute'
56
+ item.style.top = i * this.itemHeight + 'px'
57
+ fragment.appendChild(item)
58
+ }
59
+
60
+ node.innerHTML = ''
61
+ node.appendChild(fragment)
62
+
63
+ // Emit 'next' event if applicable.
64
+ // console.log('last', last)
65
+ // console.log('count', this.count)
66
+ // console.log('next', this.next)
67
+ // console.log('size', this.size)
68
+
69
+ if (this.next && last && last > (this.count - this.size / 2)) {
70
+ this.emit('next', this.count)
71
+ }
72
+
73
+ this.next = true
74
+ }
75
+
76
+ // Method to set the items and render the initial chunk.
77
+ set (items) {
78
+ this.next = false
79
+ this.items = items
80
+ this.count = items.length
81
+
82
+ if (this.count < 1 || this.count === undefined) return
83
+
84
+ this.container.innerHTML = ''
85
+ this.container.scrollTop = 0
86
+
87
+ const height = this.itemHeight * this.count
88
+
89
+ this.scroller.style.height = height + 'px'
90
+
91
+ this.itemsByScreen = Math.ceil(this.offsetHeight / this.itemHeight)
92
+
93
+ let size = this.itemsByScreen * 4
94
+ this.renderChunk(this.container, 0, size)
95
+
96
+ this.size = size
97
+
98
+ this.emit('size', size)
99
+
100
+ let lastRepaintY
101
+ const maxBuffer = this.itemsByScreen * this.itemHeight
102
+
103
+ // Add a scroll event listener to render items dynamically.
104
+ const onScroll = (e) => {
105
+ const scrollTop = e.target.scrollTop
106
+ const first = parseInt(scrollTop / this.itemHeight) - this.itemsByScreen
107
+
108
+ size = this.itemsByScreen * 4
109
+
110
+ if (!lastRepaintY || Math.abs(scrollTop - lastRepaintY) > maxBuffer) {
111
+ this.renderChunk(this.container, first, size)
112
+ lastRepaintY = scrollTop
113
+ }
114
+
115
+ const progress = Math.ceil((scrollTop / this.itemHeight) + this.itemsByScreen) - 1
116
+ this.emit('progress', progress)
117
+ }
118
+
119
+ if (!this.scrollEvents) {
120
+ // Add scroll event listener with passive option if supported.
121
+ this.container.addEventListener(
122
+ 'scroll',
123
+ onScroll,
124
+ passiveEvents() ? { passive: true } : false
125
+ )
126
+ this.scrollEvents = true
127
+ }
128
+ }
129
+
130
+ // Method to update the items and re-render the visible chunk.
131
+ update (items) {
132
+ this.items = items || this.items
133
+
134
+ if (!this.items) return
135
+
136
+ this.count = this.items.length
137
+ const cachedItemsLen = this.itemsByScreen * 4
138
+ const scrollTop = this.container.scrollTop
139
+ const height = this.itemHeight * this.count
140
+
141
+ this.scroller.style.height = height + 'px'
142
+
143
+ let first = parseInt(scrollTop / this.itemHeight) - this.itemsByScreen
144
+ first = first < 0 ? 0 : first
145
+
146
+ this.renderChunk(this.container, first, cachedItemsLen)
147
+ }
148
+
149
+ // Method to add items and update the container height.
150
+ add (items) {
151
+ this.items = items || this.items
152
+
153
+ if (!this.items) return
154
+
155
+ this.count = this.items.length
156
+ const height = this.itemHeight * this.count
157
+
158
+ this.scroller.style.height = height + 'px'
159
+ }
160
+
161
+ // Method to reset the items and container to an initial state.
162
+ reset () {
163
+ this.items = []
164
+ this.count = 0
165
+
166
+ this.container.scrollTop = 0
167
+
168
+ this.scroller.style.height = '0'
169
+ }
170
+
171
+ // Method to set the offset height of the container.
172
+ setOffset (info) {
173
+ this.offsetHeight = this.container.offsetHeight
174
+ if (this.offsetHeight < 1) {
175
+ this.offsetHeight = window.innerHeight ||
176
+ document.documentElement.clientHeight ||
177
+ document.body.clientHeight
178
+ }
179
+
180
+ this.itemsByScreen = Math.ceil(this.offsetHeight / this.itemHeight)
181
+
182
+ const itemsByScreen = Math.ceil(this.offsetHeight / this.itemHeight)
183
+ const size = itemsByScreen * 4
184
+
185
+ return size
186
+ }
187
+
188
+ setCount = function (count) {
189
+ console.error('setCount', count)
190
+ this.count = count
191
+ const height = this.itemHeight * this.count
192
+
193
+ this.container.height = height + 'px'
194
+
195
+ // console.log('scroller height', height)
196
+ this.scroller.style.height = height + 'px'
197
+ }
198
+
199
+ // Method to get the total count of items.
200
+ getCount () {
201
+ return this.count
202
+ }
203
+
204
+ // Static method to create a scroller element.
205
+ static createScroller () {
206
+ const scroller = document.createElement('div')
207
+ scroller.classList.add('scroller')
208
+ return scroller
209
+ }
210
+ }
211
+
212
+ // Export the VirtualList class as the default export.
213
+ export default VirtualList