material 0.8.5 → 0.8.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "material",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "A lightweight implementation of Material Design Components for the web - ES6",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/src/card.js CHANGED
@@ -1,36 +1,28 @@
1
- // import modules
2
- import create from './mixin/create'
3
- import insert from './mixin/insert'
4
- // import components
5
- import Layout from './layout'
6
-
7
- const defaults = {
8
- prefix: 'material',
9
- class: 'card',
10
- tag: 'div'
11
- }
1
+ import build from './module/build'
2
+ import display from './mixin/display'
3
+ import position from './mixin/position'
12
4
 
13
5
  class Card {
6
+ static defaults = {
7
+ class: 'card',
8
+ position: {
9
+ align: 'center',
10
+ vAlign: 'dynamic',
11
+ offsetX: 10,
12
+ offsetY: 10
13
+ },
14
+ close: true,
15
+ layout: []
16
+ }
17
+
14
18
  constructor (options) {
15
19
  this.init(options)
16
20
  this.build()
17
21
  }
18
22
 
19
23
  init (options) {
20
- this.options = Object.assign({}, defaults, options || {})
21
- Object.assign(this, insert)
22
- }
23
-
24
- /**
25
- * build the component using the super method
26
- * @return {Object} The class instance
27
- */
28
- build () {
29
- this.element = create(this.options)
30
-
31
- if (this.options.layout) {
32
- this.layout = new Layout(this.options.layout, this.element)
33
- }
24
+ this.options = Object.assign({}, Card.defaults, options || {})
25
+ Object.assign(this, build, display, position)
34
26
  }
35
27
  }
36
28
 
package/src/element.js CHANGED
@@ -7,6 +7,9 @@ class Element {
7
7
  this.options = Object.assign({}, options || {})
8
8
  // console.log('element options', options)
9
9
  const element = document.createElement(this.options.tag || 'div')
10
+
11
+ const container = this.options.container
12
+ delete this.options.container
10
13
  delete this.options.tag
11
14
 
12
15
  if (options.html) element.innerHTML = this.options.html
@@ -17,14 +20,12 @@ class Element {
17
20
 
18
21
  if (options.id) element.setAttribute('id', options.id)
19
22
 
20
- for (const property in this.options) {
21
- if (this.options.hasOwnProperty(property)) {
22
- element.setAttribute(property, this.options[property])
23
- }
23
+ for (const [property, value] of Object.entries(this.options)) {
24
+ element.setAttribute(property, value)
24
25
  }
25
26
 
26
- if (this.options.container) {
27
- this.options.container.appendChild(element)
27
+ if (container) {
28
+ container.appendChild(element)
28
29
  }
29
30
 
30
31
  return element
package/src/menu.js CHANGED
@@ -3,6 +3,7 @@ import EventEmitter from './mixin/emitter'
3
3
  import build from './module/build'
4
4
  import events from './module/events'
5
5
  import display from './mixin/display'
6
+ import position from './mixin/position'
6
7
 
7
8
  import Element from './element'
8
9
  import Button from './button'
@@ -10,15 +11,22 @@ import Button from './button'
10
11
  class Menu extends EventEmitter {
11
12
  static defaults = {
12
13
  class: 'menu',
13
- offset: 8,
14
14
  modal: false,
15
+ position: {
16
+ align: 'right',
17
+ vAlign: 'bottom',
18
+ offsetX: 10,
19
+ offsetY: 10
20
+ },
15
21
  layout: [
16
- [Element, 'surface', { class: 'surface' },
17
- [Element, 'list', { tag: 'ul', class: 'items' }]
18
- ]
22
+ [Element, 'list', { tag: 'ul', class: 'items' }]
19
23
  ],
20
24
  events: [
21
- ['element.click', 'close']
25
+ ['element.click', 'onClick'],
26
+ ['element.show', 'close'],
27
+ ['element.mouseenter', 'onMouseEnter'],
28
+ ['element.mouseleave', 'close'],
29
+ ['options.target.mouseleave', 'close']
22
30
  ]
23
31
  }
24
32
 
@@ -30,15 +38,18 @@ class Menu extends EventEmitter {
30
38
  this.setup()
31
39
 
32
40
  this.render(this.options.items)
41
+
42
+ return this
33
43
  }
34
44
 
35
45
  init (options) {
36
46
  this.options = Object.assign({}, Menu.defaults, options || {})
37
- Object.assign(this, build, display)
47
+ Object.assign(this, build, display, position)
38
48
  }
39
49
 
40
50
  setup () {
41
51
  events.attach(this.options.events, this)
52
+ this.closeTimeout = null
42
53
  }
43
54
 
44
55
  render (items) {
@@ -58,42 +69,29 @@ class Menu extends EventEmitter {
58
69
  item = new Element({ tag: 'li', class: 'divider' })
59
70
  } else {
60
71
  obj.container = this.ui.list
61
- obj.class = 'item'
62
- item = new Button(obj).on('click', (ev) => {
63
- // console.log('select', ev.target.name)
64
- this.emit('select', ev.target.name)
65
- })
72
+ item = new Element(obj).classList.add('item')
66
73
  }
67
74
 
68
75
  return this
69
76
  }
70
77
 
71
- position (target) {
72
- if (!target) return
73
- const caller = target.getBoundingClientRect()
74
- const screen = this.ui.surface.getBoundingClientRect()
75
- const menu = this.ui.list.getBoundingClientRect()
76
-
77
- // Calculate top position
78
- let top = caller.top + caller.height
79
- if (top + menu.height > window.innerHeight) {
80
- top = window.innerHeight - menu.height - this.options.offset
81
- }
82
- this.ui.surface.style.top = top + 'px'
78
+ onMouseEnter () {
79
+ clearTimeout(this.closeTimeout)
80
+ }
83
81
 
84
- // Calculate left position
85
- let left = caller.left - caller.width + menu.width - screen.width + caller.width
86
- if (left + menu.width > window.innerWidth) {
87
- left = window.innerWidth - menu.width - this.options.offset
82
+ onClick (ev) {
83
+ // console.log('onClick', ev.target, ev.target.getAttribute('name'))
84
+ const name = ev.target.getAttribute('name')
85
+ if (name) {
86
+ this.emit('select', name)
88
87
  }
89
- this.ui.surface.style.left = left + 'px'
90
-
91
- return this
88
+ this.destroy()
92
89
  }
93
90
 
94
91
  close () {
95
- // console.log('close')
96
- this.destroy()
92
+ this.closeTimeout = setTimeout(() => {
93
+ this.destroy()
94
+ }, 200)
97
95
  }
98
96
  }
99
97
 
@@ -0,0 +1,79 @@
1
+ export default {
2
+ position (target, position) {
3
+ target = target || this.options.target
4
+ position = position || this.options.position
5
+ const { offsetX = 0, offsetY = 0, align = 'center', vAlign = 'middle' } = position
6
+
7
+ const name = this.constructor.name
8
+
9
+ if (!target) return
10
+
11
+ const caller = target.getBoundingClientRect()
12
+ const screen = this.element.parentNode.getBoundingClientRect()
13
+ const element = this.element.getBoundingClientRect()
14
+
15
+ let left, top
16
+
17
+ switch (align) {
18
+ case 'left':
19
+ left = caller.left + caller.width
20
+ break
21
+ case 'right':
22
+ left = caller.left
23
+ if (left + element.width + offsetX > screen.width) {
24
+ console.log('out boundaries')
25
+ left = caller.left - element.width + caller.width
26
+ }
27
+ break
28
+ case 'center':
29
+ left = caller.left + (caller.width / 2) - (element.width / 2)
30
+
31
+ default:
32
+ left = caller.left + (caller.width / 2) - (element.width / 2)
33
+ }
34
+
35
+ // ensure that the element remains within the limits of its container
36
+
37
+ if (left < 0) left = offsetX
38
+
39
+ if (left + element.width + offsetX > screen.width) {
40
+ left = screen.width - element.width - offsetX
41
+ }
42
+
43
+ // console.log('left', left)
44
+
45
+ // left = Math.max(offsetX, Math.min(left, screen.width - element.width - offsetX))
46
+
47
+ const scrollY = window.scrollY
48
+
49
+ // console.log('class', name)
50
+ // console.log('scrollY', scrollY)
51
+ // console.log('vAlign', vAlign)
52
+ // console.log('target top', caller.top)
53
+
54
+ switch (vAlign) {
55
+ case 'top':
56
+ top = caller.top + scrollY - element.height
57
+ break
58
+ case 'bottom':
59
+ top = caller.top + caller.height + scrollY
60
+ break
61
+ case 'middle':
62
+ case 'dynamic':
63
+ if (caller.top + caller.height / 2 < screen.height / 2) {
64
+ top = caller.bottom + scrollY // Positionner en dessous si dans la moitié supérieure
65
+ } else {
66
+ top = caller.top + scrollY - element.height // Positionner au-dessus si dans la moitié inférieure
67
+ }
68
+ break
69
+
70
+ default:
71
+ top = caller.top + scrollY + (caller.height / 2) - (element.height / 2)
72
+ }
73
+
74
+ this.element.style.left = `${left}px`
75
+ this.element.style.top = `${top}px`
76
+
77
+ return this
78
+ }
79
+ }
@@ -1,11 +1,21 @@
1
+ /**
2
+ * Sets attributes on a DOM element based on provided options.
3
+ *
4
+ * This function iterates over a list of attributes and sets them on the specified element.
5
+ * Special attributes such as 'required', 'disabled', 'multiple', 'checked' are set as boolean attributes,
6
+ * while others are set with their corresponding values from the options.
7
+ *
8
+ * @param {Element} element - The DOM element to which attributes will be applied.
9
+ * @param {Object} options - An object containing the attributes and their values.
10
+ */
1
11
  const special = ['required', 'disabled', 'multiple', 'checked']
2
12
 
3
13
  const attributes = (element, options) => {
4
- if (!element || !options.attributes) return
14
+ if (!element || !options?.attributes) return
5
15
 
6
16
  options.attributes.forEach(attribute => {
7
- if (options[attribute] && String(options[attribute]) !== 'undefined') {
8
- const value = special.includes(attribute) ? attribute : options[attribute]
17
+ if (options[attribute] !== undefined) {
18
+ const value = special.includes(attribute) ? '' : options[attribute]
9
19
  element.setAttribute(attribute, value)
10
20
  }
11
21
  })
@@ -1,11 +1,18 @@
1
-
2
- function dataset (element, data) {
1
+ /**
2
+ * Applies data attributes to a DOM element based on an object's properties.
3
+ *
4
+ * This function takes an object and applies its key-value pairs as data attributes
5
+ * to a provided DOM element. Only the object's own properties (not inherited properties)
6
+ * are applied.
7
+ *
8
+ * @param {Element} element - The DOM element to which data attributes will be applied.
9
+ * @param {Object} data - An object containing key-value pairs to be applied as data attributes.
10
+ */
11
+ const dataset = (element, data) => {
3
12
  if (data) {
4
- for (const property in data) {
5
- if (data.hasOwnProperty(property)) {
6
- element.dataset[property] = data[property]
7
- }
8
- }
13
+ Object.entries(data).forEach(([key, value]) => {
14
+ element.dataset[key] = value
15
+ })
9
16
  }
10
17
  }
11
18
 
package/src/module/dot.js CHANGED
@@ -1,20 +1,32 @@
1
+ /**
2
+ * Converts a nested object into a flat object with dotted paths as keys.
3
+ *
4
+ * @param {Object} obj - The object to be flattened.
5
+ * @param {string} [base=''] - (Internal use) A prefix used to build the dotted paths. Users typically do not need to provide this.
6
+ * @returns {Object} A new object with the same values as `obj`, but flattened.
7
+ *
8
+ * @example
9
+ * const nested = { a: { b: { c: 1 }, d: 2 }, e: 3 };
10
+ * const flat = dot(nested);
11
+ * // flat is now { 'a.b.c': 1, 'a.d': 2, 'e': 3 }
12
+ *
13
+ * @example
14
+ * // It does not flatten arrays or date objects
15
+ * const complex = { a: [{ b: 1 }, { c: 2 }], d: new Date() };
16
+ * const flatComplex = dot(complex);
17
+ * // flatComplex is now { 'a': [{ b: 1 }, { c: 2 }], 'd': [Date Object] }
18
+ */
1
19
 
2
- function dot (obj) {
3
- const res = {}
4
- function recurse (obj, current) {
5
- for (const key in obj) {
6
- const value = obj[key]
7
- const newKey = (current ? current + '.' + key : key)
8
- if (value && typeof value === 'object') {
9
- recurse(value, newKey)
10
- } else {
11
- res[newKey] = value
12
- }
20
+ const dot = (obj, base = '') => {
21
+ return Object.entries(obj).reduce((acc, [key, value]) => {
22
+ const newKey = base ? `${base}.${key}` : key
23
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
24
+ Object.assign(acc, dot(value, newKey))
25
+ } else {
26
+ acc[newKey] = value
13
27
  }
14
- }
15
-
16
- recurse(obj)
17
- return res
28
+ return acc
29
+ }, {})
18
30
  }
19
31
 
20
32
  export default dot
@@ -2,26 +2,17 @@
2
2
  //display: none
3
3
  &.show
4
4
  //display: block
5
- .surface
6
- .list
7
- background: white
8
- .item
9
- color: rgba(0,0,0,.87)
5
+ .list
6
+ background: white
7
+ .item
8
+ color: rgba(0,0,0,.87)
10
9
 
11
10
  &.floating
12
- position: fixed
13
- top: 0
14
- left: 0
15
- bottom: 0
16
- right: 0
17
11
  display: flex
18
12
  align-items: center
19
13
  justify-content: center
20
14
  overflow: hidden
21
15
  opacity: 0
22
16
  transition: 0.2s
23
- .surface
24
- position: absolute
25
- background-color: white
26
17
 
27
18