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 +1 -1
- package/src/card.js +17 -25
- package/src/element.js +7 -6
- package/src/menu.js +30 -32
- package/src/mixin/position.js +79 -0
- package/src/module/attributes.js +13 -3
- package/src/module/dataset.js +14 -7
- package/src/module/dot.js +27 -15
- package/src/skin/material/control/menu.sass +4 -13
package/package.json
CHANGED
package/src/card.js
CHANGED
|
@@ -1,36 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
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,
|
|
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
|
|
21
|
-
|
|
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 (
|
|
27
|
-
|
|
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, '
|
|
17
|
-
[Element, 'list', { tag: 'ul', class: 'items' }]
|
|
18
|
-
]
|
|
22
|
+
[Element, 'list', { tag: 'ul', class: 'items' }]
|
|
19
23
|
],
|
|
20
24
|
events: [
|
|
21
|
-
['element.click', '
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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.
|
|
90
|
-
|
|
91
|
-
return this
|
|
88
|
+
this.destroy()
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
close () {
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
}
|
package/src/module/attributes.js
CHANGED
|
@@ -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
|
|
14
|
+
if (!element || !options?.attributes) return
|
|
5
15
|
|
|
6
16
|
options.attributes.forEach(attribute => {
|
|
7
|
-
if (options[attribute]
|
|
8
|
-
const value = special.includes(attribute) ?
|
|
17
|
+
if (options[attribute] !== undefined) {
|
|
18
|
+
const value = special.includes(attribute) ? '' : options[attribute]
|
|
9
19
|
element.setAttribute(attribute, value)
|
|
10
20
|
}
|
|
11
21
|
})
|
package/src/module/dataset.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|