material 0.9.20 → 0.9.22

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.
Files changed (51) hide show
  1. package/index.js +1 -0
  2. package/package.json +1 -1
  3. package/src/class/app.js +10 -0
  4. package/src/class/component.js +16 -13
  5. package/src/class/control.js +24 -16
  6. package/src/class/form.js +7 -22
  7. package/src/class/view.js +18 -19
  8. package/src/colorpicker.js +356 -0
  9. package/src/datehour.js +17 -12
  10. package/src/form/cancel.js +1 -2
  11. package/src/form/controller.js +10 -18
  12. package/src/form/data.js +175 -47
  13. package/src/form/fetch.js +18 -25
  14. package/src/form/render.js +2 -2
  15. package/src/form/submit.js +9 -3
  16. package/src/form/update.js +27 -30
  17. package/src/list/build.js +6 -1
  18. package/src/list/status.js +5 -0
  19. package/src/list/update.js +2 -0
  20. package/src/list/virtual.js +1 -1
  21. package/src/module/dom.js +0 -1
  22. package/src/module/emitter.js +45 -13
  23. package/src/module/extract.js +2 -2
  24. package/src/module/layout.js +1 -1
  25. package/src/module/request.js +49 -16
  26. package/src/selector.js +1 -1
  27. package/src/skin/material/control/colorpicker.sass +188 -0
  28. package/src/switch.js +6 -8
  29. package/src/switcher.js +21 -12
  30. package/src/text.js +1 -1
  31. package/src/textfield.js +6 -1
  32. package/src/list.new/build.js +0 -139
  33. package/src/list.new/click.js +0 -40
  34. package/src/list.new/collection.js +0 -30
  35. package/src/list.new/create.js +0 -45
  36. package/src/list.new/data.js +0 -16
  37. package/src/list.new/display.js +0 -78
  38. package/src/list.new/empty.js +0 -9
  39. package/src/list.new/filter.js +0 -114
  40. package/src/list.new/image.js +0 -19
  41. package/src/list.new/init.js +0 -6
  42. package/src/list.new/load.js +0 -9
  43. package/src/list.new/remove.js +0 -22
  44. package/src/list.new/render.js +0 -142
  45. package/src/list.new/request.js +0 -216
  46. package/src/list.new/search.js +0 -155
  47. package/src/list.new/select.js +0 -221
  48. package/src/list.new/status.js +0 -21
  49. package/src/list.new/update.js +0 -75
  50. package/src/list.new/virtual.js +0 -186
  51. package/src/list.new/virtual.new.js +0 -213
@@ -6,17 +6,8 @@ export default {
6
6
 
7
7
  this.disableControls()
8
8
 
9
- for (const field in this.field) {
10
- if (this.field.hasOwnProperty(field)) {
11
- this.bindControl(this.field[field])
12
- }
13
- }
14
-
15
- for (const file in this.file) {
16
- if (this.file.hasOwnProperty(file)) {
17
- this.bindControl(this.file[file])
18
- }
19
- }
9
+ Object.keys(this.field).forEach(field => this.bindControl(this.field[field]))
10
+ Object.keys(this.file).forEach(file => this.bindControl(this.file[file]))
20
11
  },
21
12
 
22
13
  setMode (mode) {
@@ -39,7 +30,7 @@ export default {
39
30
  },
40
31
 
41
32
  changeMode (mode) {
42
- // console.log('changeMode', mode)
33
+ // console.log('changeMode', mode, this.options.class)
43
34
 
44
35
  if (mode === 'update' && this.enableControls) {
45
36
  this.enableControls()
@@ -51,19 +42,18 @@ export default {
51
42
  },
52
43
 
53
44
  bindControl (control) {
54
- // console.log('bindControl')
45
+ // console.log('bindControl', control.class)
55
46
  if (control && control.on) {
56
47
  control.on('change', () => {
57
- // console.log('change', control, this.mode)
58
- if (this.mode === 'read') {
59
- this.setMode('update')
60
- }
48
+ if (this.isModified) this.isModified()
49
+ this.setMode('update')
50
+ this.emit('changed', this.options.class)
51
+ console.log('isModified', this.isModified)
61
52
  })
62
53
  }
63
54
  },
64
55
 
65
56
  enableControls () {
66
- // console.log('enableControls')
67
57
  const controls = this.options.controls || ['submit', 'cancel']
68
58
 
69
59
  if (controls === null) return
@@ -73,6 +63,8 @@ export default {
73
63
  this.ui[controls[i]].enable()
74
64
  }
75
65
  }
66
+
67
+ this.emit('enableControls')
76
68
  },
77
69
 
78
70
  disableControls () {
package/src/form/data.js CHANGED
@@ -1,82 +1,210 @@
1
1
  import { byString } from '../module/object'
2
2
 
3
3
  export default {
4
-
4
+ /**
5
+ * Initializes form data with fields and files
6
+ * @returns {FormData} Initialized form data
7
+ */
5
8
  initData () {
6
- // console.log('submit', this.mode)
9
+ try {
10
+ const data = new FormData()
7
11
 
8
- const data = new FormData()
12
+ if (this.mode !== 'create') {
13
+ this.appendSysinfo(data)
14
+ }
9
15
 
10
- if (this.mode !== 'create') {
11
- this.appendSysinfo(data)
16
+ return this.appendFiles(this.appendFields(data))
17
+ } catch (error) {
18
+ console.error('Error initializing form data:', error)
19
+ throw new Error('Failed to initialize form data')
12
20
  }
21
+ },
13
22
 
14
- this.appendFields(data)
15
- this.appendFiles(data)
16
-
17
- return data
23
+ /**
24
+ * Fetches and sets data for the form
25
+ * @param {Object} user - User data object
26
+ * @returns {Promise<void>}
27
+ */
28
+ async getData () {
29
+ // console.log('getData')
30
+ Object.entries(this.field).forEach(([fieldName, field]) => {
31
+ console.log('fieldName', fieldName)
32
+ })
18
33
  },
19
34
 
35
+ /**
36
+ * Appends system information to form data
37
+ * @param {FormData} data - FormData instance
38
+ * @returns {FormData} Updated FormData
39
+ */
20
40
  appendSysinfo (data) {
21
- const sys = this.options.sysinfo
22
- for (let i = 0; i < sys.length; i++) {
23
- // console.log('sys', sys[i], this.info[sys[i]])
24
- let name = sys[i]
25
- if (sys[i] === '_id') name = 'id'
26
-
27
- data.append(name, this.info[sys[i]])
41
+ // console.log('appendSysinfo')
42
+ if (!Array.isArray(this.options?.sysinfo)) {
43
+ return data
28
44
  }
29
45
 
46
+ this.options.sysinfo.forEach(field => {
47
+ const name = field === '_id' ? 'id' : field
48
+ const value = this.info?.[field]
49
+
50
+ if (value !== undefined) {
51
+ data.append(name, value)
52
+ }
53
+ })
54
+
30
55
  return data
31
56
  },
32
57
 
58
+ /**
59
+ * Appends form fields to FormData
60
+ * @param {FormData} data - FormData instance
61
+ * @returns {FormData} Updated FormData
62
+ */
33
63
  appendFields (data) {
34
- for (const field in this.field) {
35
- // console.log('field', field)
36
- if (this.field.hasOwnProperty(field)) {
37
- // console.log('check field', field)
38
- const value = this.field[field].get()
39
- if (value !== null) {
40
- if (this.mode === 'update' && this.options.update && this.options.update.modifiedOnly) {
41
- const modified = this.isModified(field, value)
42
- if (modified) {
43
- data.append(field, value)
44
- }
45
- } else {
46
- data.append(field, value)
47
- }
48
- }
49
- }
64
+ if (!this.field || !(data instanceof FormData)) {
65
+ return data
50
66
  }
51
67
 
68
+ Object.entries(this.field).forEach(([fieldName, field]) => {
69
+ const value = field?.get?.()
70
+
71
+ if (value === null || value === undefined) {
72
+ return
73
+ }
74
+
75
+ const shouldAppend = this.mode === 'update' &&
76
+ this.options?.update?.modifiedOnly
77
+ ? this.isModified(fieldName, value)
78
+ : true
79
+
80
+ if (shouldAppend) {
81
+ data.append(fieldName, value)
82
+ }
83
+ })
84
+
52
85
  return data
53
86
  },
54
87
 
88
+ /**
89
+ * Appends file inputs to FormData
90
+ * @param {FormData} data - FormData instance
91
+ * @returns {FormData} Updated FormData
92
+ */
55
93
  appendFiles (data) {
56
- // console.log('appendFiles', this.file)
57
- for (const file in this.file) {
58
- // console.log('this file', this.file, this.file[file].input)
59
- // console.log('file', file, this.file[file])
60
- if (this.file.hasOwnProperty(file) && this.file[file].input && this.file[file].input.value) {
61
- data.append(file, this.file[file].input.files[0])
62
- }
94
+ if (!this.file || !(data instanceof FormData)) {
95
+ return data
63
96
  }
64
97
 
98
+ Object.entries(this.file)
99
+ .filter(([, fileField]) => {
100
+ const input = fileField?.input
101
+ return input?.value && input?.files?.length > 0
102
+ })
103
+ .forEach(([fileName, fileField]) => {
104
+ data.append(fileName, fileField.input.files[0])
105
+ })
106
+
65
107
  return data
66
108
  },
67
109
 
68
- isModified (field, value) {
69
- const initial = byString(this.info, field)
110
+ /**
111
+ * Gets structured field data
112
+ * @returns {Object} Field data object
113
+ */
114
+ getFieldData () {
115
+ if (!this.field) {
116
+ return {}
117
+ }
118
+
119
+ return Object.entries(this.field).reduce((acc, [fieldName, field]) => {
120
+ const value = field?.get?.()
121
+ if (value !== null && value !== undefined) {
122
+ acc[fieldName] = value
123
+ }
124
+ return acc
125
+ }, {})
126
+ },
70
127
 
71
- if (typeof initial === 'number') {
72
- value = Number(value)
128
+ /**
129
+ * Gets structured file data
130
+ * @returns {Object} File data object
131
+ */
132
+ getFileData () {
133
+ if (!this.file) {
134
+ return {}
73
135
  }
74
136
 
75
- if (initial !== value) {
76
- // console.log('field', field, typeof value, value)
77
- return true
78
- } else {
137
+ return Object.entries(this.file).reduce((acc, [fileName, fileField]) => {
138
+ const input = fileField?.input
139
+ if (input?.value && input?.files?.length > 0) {
140
+ acc[fileName] = input.files[0]
141
+ }
142
+ return acc
143
+ }, {})
144
+ },
145
+
146
+ /**
147
+ * Checks if any fields have been modified from their initial values
148
+ * @returns {boolean} True if any field is modified
149
+ */
150
+ isModified () {
151
+ // console.log('checkModified', this.options.class)
152
+ // console.log('field', this.field)
153
+ // console.log('info', this.info)
154
+
155
+ if (!this.field || !this.info) {
79
156
  return false
80
157
  }
158
+
159
+ let modified = false
160
+ Object.entries(this.field).forEach(([fieldName]) => {
161
+ if (this.isFieldModified(fieldName)) {
162
+ modified = true
163
+ }
164
+ })
165
+
166
+ this.modified = modified
167
+
168
+ // console.log('modified', modified)
169
+
170
+ return modified
171
+ },
172
+
173
+ /**
174
+ * Checks if a field value has been modified
175
+ * @param {string} fieldName - Field name
176
+ * @returns {boolean} True if modified
177
+ */
178
+ /**
179
+ * Checks if a specific field value has been modified from its initial value
180
+ * @param {string} fieldName - Field name to check
181
+ * @returns {boolean} True if the field is modified
182
+ */
183
+ isFieldModified (fieldName) {
184
+ // console.log('isFieldModified', fieldName)
185
+ const currentValue = this.field[fieldName]?.get?.() ?? undefined
186
+ const initialValue = this.info[fieldName] ?? undefined
187
+
188
+ // Special case for text fields: treat undefined and empty string as equivalent
189
+ if (typeof currentValue === 'string' || currentValue === undefined) {
190
+ const normalizedCurrent = currentValue || ''
191
+ const normalizedInitial = initialValue || ''
192
+ return normalizedCurrent !== normalizedInitial
193
+ }
194
+
195
+ // Handle number comparisons
196
+ if (typeof initialValue === 'number') {
197
+ const parsedCurrent = Number(currentValue)
198
+ return isNaN(parsedCurrent) ? true : parsedCurrent !== initialValue
199
+ }
200
+
201
+ // Standard null/undefined checks
202
+ if (initialValue === undefined || initialValue === null) {
203
+ return currentValue !== null && currentValue !== undefined
204
+ }
205
+
206
+ log.error('modified', initialValue !== currentValue)
207
+
208
+ return initialValue !== currentValue
81
209
  }
82
210
  }
package/src/form/fetch.js CHANGED
@@ -1,30 +1,23 @@
1
+ import request from '../module/request'
2
+
1
3
  export default {
2
- fetch (option) {
4
+ async fetch (option) {
3
5
  // console.log('fetch', this.options.route, option.method)
4
- fetch(this.options.route, {
5
- method: option.method,
6
- body: option.formData
7
- }).then(r => r.json()).then(info => {
8
- if (info.error) {
9
- // console.log('Error: ' + info.error)
10
- if (this.error) {
11
- this.error(info)
12
- }
13
- } else {
14
- // console.log('updated', info, this.mode)
15
- if (this.mode === 'create') {
16
- this.emit('created', info)
17
- this.mode = 'read'
18
- } else {
19
- this.info = info
20
- this.emit('updated', info)
21
- this.mode = 'read'
22
- }
23
6
 
24
- if (this.success) {
25
- this.success(info)
26
- }
27
- }
28
- })
7
+ const info = await request(this.options.route, option.method, option.formData)
8
+
9
+ // console.log('updated', info, this.mode)
10
+ if (this.mode === 'create') {
11
+ this.emit('created', info)
12
+ this.mode = 'read'
13
+ } else {
14
+ this.info = info
15
+ this.emit('updated', info)
16
+ this.mode = 'read'
17
+ }
18
+
19
+ if (this.success) {
20
+ this.success(info)
21
+ }
29
22
  }
30
23
  }
@@ -12,10 +12,10 @@ export default {
12
12
  }
13
13
  }
14
14
 
15
- // console.log('dotified', this.field, info)
16
-
17
15
  if (!info) return
18
16
 
17
+ // console.log('dotified', this.field, info)
18
+
19
19
  for (const field in this.field) {
20
20
  if (this.field.hasOwnProperty(field)) {
21
21
  // console.log('field type', field, this.field[field])
@@ -1,15 +1,20 @@
1
1
  export default {
2
2
 
3
- submit (ev) {
4
- ev.preventDefault()
3
+ async submit (ev) {
4
+ if (ev) ev.preventDefault()
5
5
  // console.log('submit', this.mode)
6
6
 
7
7
  const data = this.initData()
8
8
 
9
+ // for (const pair of data.entries()) {
10
+ // console.log(`${pair[0]}: ${pair[1]}`)
11
+ // }
12
+
9
13
  // console.log('data', data)
10
14
 
11
15
  if (this.update) {
12
- this.update(data)
16
+ // console.log('this.update', this.update)
17
+ await this.update(data)
13
18
  } else {
14
19
  this.setMethod(data)
15
20
  }
@@ -18,6 +23,7 @@ export default {
18
23
  },
19
24
 
20
25
  setMethod (formData) {
26
+ // console.log('setMethod', this.mode)
21
27
  let method = 'PUT'
22
28
  if (this.mode === 'create') {
23
29
  method = 'POST'
@@ -1,46 +1,43 @@
1
+ import request from '../module/request'
2
+
1
3
  export default {
2
- update (data, options) {
3
- // console.log('update', data, options)
4
+ async update (body, options) {
5
+ // console.log('update', body, options)
4
6
  // console.log('mode', this.mode)
5
7
  options = options || {}
6
8
 
7
9
  // console.log('mode', this.mode)
8
10
  const method = options.method ||
9
- this.options[this.mode].method ||
10
- 'put'
11
+ this.options[this.mode].method ||
12
+ 'PUT'
11
13
 
12
14
  const action = options.action ||
13
- this.options[this.mode].action ||
14
- this.options.action
15
+ this.options[this.mode].action ||
16
+ this.options.action
15
17
 
16
18
  if (this.beforeCreate) {
17
- this.beforeCreate(data)
19
+ this.beforeCreate(body)
18
20
  }
19
21
 
20
- fetch(action, {
21
- method,
22
- body: data
23
- }).then((resp) => {
24
- // console.log('resp', resp)
25
- return resp.json()
26
- }).then(info => {
27
- // console.log('update', info)
28
- if (info.error) {
29
- // console.log('Error: ' + info.error)
30
- if (this.error) {
31
- this.error(info)
32
- }
22
+ // console.log('request', action, method, body)
23
+ const info = await request(action, method, body)
24
+
25
+ // console.log('update', info)
26
+ if (info.error) {
27
+ // console.log('Error: ' + info.error)
28
+ if (this.error) {
29
+ this.error(info)
30
+ }
31
+ } else {
32
+ // console.log('info', this.mode, info)
33
+ if (this.mode === 'create') {
34
+ this.emit('created', info)
35
+ this.setMode('read')
33
36
  } else {
34
- // console.log('info', this.mode, info)
35
- if (this.mode === 'create') {
36
- this.emit('created', info)
37
- this.setMode('read')
38
- } else {
39
- this.emit('updated', info)
40
- this.setMode('read')
41
- }
42
- this.info = info
37
+ this.emit('updated', info)
38
+ this.setMode('read')
43
39
  }
44
- })
40
+ this.info = info
41
+ }
45
42
  }
46
43
  }
package/src/list/build.js CHANGED
@@ -100,7 +100,12 @@ export default {
100
100
  // console.log('progress', progress, this.count)
101
101
 
102
102
  if (this.statusDisplay) {
103
- const percent = parseInt(progress / this.count * 100)
103
+ let percent = parseInt(progress / this.count * 100)
104
+ if (progress > this.count) {
105
+ percent = 100
106
+ progress = this.count
107
+ }
108
+
104
109
  this.statusDisplay('count', percent + '% | ' + progress + ' / ' + this.count)
105
110
  }
106
111
 
@@ -9,7 +9,12 @@ export default {
9
9
 
10
10
  statusCount (count) {
11
11
  // console.log('statusCount', count)
12
+ //
12
13
  let items = 'items'
14
+ if (count < 1) {
15
+ items = 'item'
16
+ }
17
+
13
18
  if (this.status && this.status.count && this.status.count.name) {
14
19
  items = this.status.count.name
15
20
  }
@@ -5,6 +5,8 @@ export default {
5
5
  update (info) {
6
6
  // console.log('update', info)
7
7
 
8
+ if (!info) return
9
+
8
10
  this.getUpdatedInfo(info[this.dataId])
9
11
  },
10
12
 
@@ -135,7 +135,7 @@ VirtualList.prototype.set = function (items) {
135
135
  }
136
136
 
137
137
  const progress = Math.ceil((scrollTop / self.itemHeight) + self.itemsByScreen) - 1
138
- // console.log('progress', progress)
138
+ // console.log('progress', progress, scrollTop, self.itemHeight)
139
139
  self.emit('progress', progress)
140
140
 
141
141
  // e.preventDefault && e.preventDefault()
package/src/module/dom.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // DOM manipulation function
2
2
 
3
3
  function append (container, element) {
4
- console.log(container, element)
5
4
  container.appendChild(element)
6
5
  return element
7
6
  }
@@ -1,21 +1,53 @@
1
+ // Event emitter mixin that can be extended into other objects
1
2
  export default {
2
- on (event, cb) {
3
- this.event = this.event || {}
4
- this.event[event] = this.event[event] || []
5
- this.event[event].push(cb)
3
+ // Initialize the events storage if needed
4
+ _initEvents () {
5
+ this.events = this.events || {}
6
+ },
7
+
8
+ // Add an event listener
9
+ on (event, listener) {
10
+ this._initEvents()
11
+ // Use nullish coalescing assignment for array initialization
12
+ this.events[event] ??= []
13
+ this.events[event].push(listener)
14
+ return this
15
+ },
16
+
17
+ // Emit an event with optional arguments
18
+ emit (event, ...args) {
19
+ this._initEvents()
20
+ // Use optional chaining to safely access and iterate events
21
+ this.events[event]?.forEach(listener => {
22
+ try {
23
+ listener.apply(this, args)
24
+ } catch (error) {
25
+ console.error(`Error in event listener for ${event}:`, error)
26
+ }
27
+ })
6
28
  return this
7
29
  },
8
- off (event, cb) {
9
- this.event = this.event || {}
10
- if (event in this.event === false) return
11
- this.event[event].splice(this.event[event].indexOf(cb), 1)
30
+
31
+ // Remove a specific listener for an event
32
+ off (event, listener) {
33
+ this._initEvents()
34
+ const listeners = this.events[event]
35
+ if (listeners?.length) {
36
+ const index = listeners.indexOf(listener)
37
+ if (index !== -1) {
38
+ listeners.splice(index, 1)
39
+ }
40
+ }
12
41
  return this
13
42
  },
14
- emit (event /* , args... */) {
15
- this.event = this.event || {}
16
- if (event in this.event === false) return
17
- for (let i = 0; i < this.event[event].length; i++) {
18
- this.event[event][i].apply(this, Array.prototype.slice.call(arguments, 1))
43
+
44
+ // Remove all listeners for an event or all events
45
+ removeAll (event) {
46
+ this._initEvents()
47
+ if (event) {
48
+ delete this.events[event]
49
+ } else {
50
+ this.events = {}
19
51
  }
20
52
  return this
21
53
  }
@@ -4,7 +4,7 @@
4
4
  * @param {[type]} str [description]
5
5
  * @return {[type]} [description]
6
6
  */
7
- function e (instance, ev) {
7
+ const e = (instance, ev) => {
8
8
  if (!ev) return instance
9
9
  else if (!ev.match(/\./)) {
10
10
  return {
@@ -43,7 +43,7 @@ function e (instance, ev) {
43
43
  * @param {string} func A string representing a function accessible in dot notation
44
44
  * @return {function} The function
45
45
  */
46
- function f (instance, func) {
46
+ const f = (instance, func) => {
47
47
  if (!func) return
48
48
 
49
49
  if (typeof func === 'function') {
@@ -16,7 +16,7 @@ function create (schema, container, structure = {}, level = 0, components = [])
16
16
  const fragment = document.createDocumentFragment()
17
17
 
18
18
  if (!Array.isArray(schema)) {
19
- console.trace('Schema is not an array!')
19
+ console.error('Schema is not an array!', container, level, schema)
20
20
  }
21
21
 
22
22
  for (let i = 0; i < schema.length; i++) {