@natachah/vanilla-frontend 0.0.2
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/.gitlab-ci.yml +40 -0
- package/LICENSE.md +7 -0
- package/README.md +11 -0
- package/docs/index.html +36 -0
- package/docs/main.js +32 -0
- package/docs/pages/components/badge.html +154 -0
- package/docs/pages/components/button.html +186 -0
- package/docs/pages/components/card.html +184 -0
- package/docs/pages/components/dialog.html +334 -0
- package/docs/pages/components/disclosure.html +310 -0
- package/docs/pages/components/dropdown.html +255 -0
- package/docs/pages/components/form.html +331 -0
- package/docs/pages/components/list.html +140 -0
- package/docs/pages/components/loading.html +58 -0
- package/docs/pages/components/media.html +130 -0
- package/docs/pages/components/nav.html +119 -0
- package/docs/pages/components/progress.html +47 -0
- package/docs/pages/components/slider.html +311 -0
- package/docs/pages/components/table.html +168 -0
- package/docs/pages/javascript/autofill.html +170 -0
- package/docs/pages/javascript/checkall.html +59 -0
- package/docs/pages/javascript/comfort.html +134 -0
- package/docs/pages/javascript/consent.html +112 -0
- package/docs/pages/javascript/cookie.html +81 -0
- package/docs/pages/javascript/form.html +199 -0
- package/docs/pages/javascript/scroll.html +209 -0
- package/docs/pages/javascript/sidebar.html +53 -0
- package/docs/pages/javascript/sortable.html +148 -0
- package/docs/pages/javascript/toggle.html +191 -0
- package/docs/pages/javascript/tree.html +221 -0
- package/docs/pages/layout/grid.html +201 -0
- package/docs/pages/layout/reset.html +53 -0
- package/docs/pages/layout/typography.html +324 -0
- package/docs/pages/quick-start/conventions.html +112 -0
- package/docs/pages/quick-start/customization.html +187 -0
- package/docs/pages/quick-start/installation.html +95 -0
- package/docs/pages/quick-start/mixins.html +228 -0
- package/docs/pages/test.html +15 -0
- package/docs/src/js/demo.js +98 -0
- package/docs/src/js/doc-code.js +102 -0
- package/docs/src/js/doc-demo.js +14 -0
- package/docs/src/js/doc-layout.js +108 -0
- package/docs/src/scss/demo.scss +77 -0
- package/docs/src/scss/layout.scss +160 -0
- package/docs/src/scss/style.scss +278 -0
- package/docs/vite.config.mjs +23 -0
- package/esbuild.mjs +25 -0
- package/js/_autofill.js +131 -0
- package/js/_check-all.js +77 -0
- package/js/_comfort.js +174 -0
- package/js/_consent.js +84 -0
- package/js/_dialog.js +164 -0
- package/js/_dropdown.js +101 -0
- package/js/_scroll.js +184 -0
- package/js/_sidebar.js +97 -0
- package/js/_slider.js +249 -0
- package/js/_sortable.js +143 -0
- package/js/_tabpanel.js +88 -0
- package/js/_toggle.js +123 -0
- package/js/_tree.js +85 -0
- package/js/tests/autofill.test.js +157 -0
- package/js/tests/base-component.test.js +108 -0
- package/js/tests/check-all.test.js +88 -0
- package/js/tests/comfort.test.js +219 -0
- package/js/tests/consent.test.js +84 -0
- package/js/tests/cookie.test.js +102 -0
- package/js/tests/dialog.test.js +189 -0
- package/js/tests/dropdown.test.js +115 -0
- package/js/tests/form-helper.test.js +155 -0
- package/js/tests/scroll.test.js +203 -0
- package/js/tests/sidebar.test.js +99 -0
- package/js/tests/slider.test.js +307 -0
- package/js/tests/sortable.test.js +124 -0
- package/js/tests/tabpanel.test.js +114 -0
- package/js/tests/toggle.test.js +190 -0
- package/js/tests/tree.test.js +165 -0
- package/js/utilities/_base-component.js +101 -0
- package/js/utilities/_cookie.js +98 -0
- package/js/utilities/_error.js +80 -0
- package/js/utilities/_form-helper.js +101 -0
- package/package.json +42 -0
- package/scss/_badge.scss +37 -0
- package/scss/_button.scss +34 -0
- package/scss/_card.scss +122 -0
- package/scss/_dialog.scss +116 -0
- package/scss/_disclosure.scss +101 -0
- package/scss/_dropdown.scss +68 -0
- package/scss/_form.scss +197 -0
- package/scss/_grid.scss +40 -0
- package/scss/_group.scss +57 -0
- package/scss/_list.scss +18 -0
- package/scss/_loading.scss +49 -0
- package/scss/_media.scss +37 -0
- package/scss/_nav.scss +72 -0
- package/scss/_progress.scss +40 -0
- package/scss/_slider.scss +35 -0
- package/scss/_table.scss +36 -0
- package/scss/utilities/_mixin.scss +322 -0
- package/scss/utilities/_reset.scss +145 -0
- package/scss/utilities/_typography.scss +107 -0
- package/scss/vanilla-frontend.scss +23 -0
- package/scss/variables/_root.scss +70 -0
- package/scss/variables/_setting.scss +63 -0
- package/vitest.config.js +7 -0
package/js/_autofill.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Auto fill
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This class enable the functionality to automatic filling some fields from another
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import BaseComponent from './utilities/_base-component'
|
|
13
|
+
import ErrorMessage from "./utilities/_error"
|
|
14
|
+
|
|
15
|
+
export default class Autofill extends BaseComponent {
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates an instance
|
|
19
|
+
*
|
|
20
|
+
* @param {HTMLElement} el - The HTML element
|
|
21
|
+
* @param {object} options - The custom options
|
|
22
|
+
* @constructor
|
|
23
|
+
*/
|
|
24
|
+
constructor(el, options = {}) {
|
|
25
|
+
|
|
26
|
+
// Run the SUPER constructor from BaseComponent
|
|
27
|
+
super(el, options)
|
|
28
|
+
|
|
29
|
+
// Check for errors
|
|
30
|
+
if (el.tagName !== 'SELECT' && el.tagName !== 'INPUT') throw new Error(ErrorMessage.typeOf('el', 'input|select'))
|
|
31
|
+
if (!el.hasAttribute('aria-controls')) throw new Error(ErrorMessage.withAttribute('el', 'aria-controls'))
|
|
32
|
+
if (el.tagName === 'INPUT') {
|
|
33
|
+
if (el.type !== 'text' && el.type !== 'file') throw new Error(ErrorMessage.typeOf('type', 'text|file'))
|
|
34
|
+
if (el.type === 'text' && !el.hasAttribute('list')) throw new Error(ErrorMessage.withAttribute('input', 'list'))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Define the properties
|
|
38
|
+
this._type = el.tagName === 'SELECT' ? 'select' : el.type === 'file' ? 'file' : 'datalist'
|
|
39
|
+
|
|
40
|
+
this._fields = el.hasAttribute('aria-controls') ? el.getAttribute('aria-controls').split(' ').filter(id => document.getElementById(id)).map(id => document.getElementById(id)) : []
|
|
41
|
+
|
|
42
|
+
// Init the event listener
|
|
43
|
+
this.#init()
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Init the event listener
|
|
49
|
+
*
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
#init() {
|
|
53
|
+
|
|
54
|
+
// CHANGE the element
|
|
55
|
+
this._element.addEventListener('change', () => {
|
|
56
|
+
|
|
57
|
+
switch (this._type) {
|
|
58
|
+
case 'select':
|
|
59
|
+
this.#byOption(this._element.options[this._element.selectedIndex])
|
|
60
|
+
break
|
|
61
|
+
case 'datalist':
|
|
62
|
+
this.#byOption(document.querySelector(`#${this._element.getAttribute('list')} [value="${this._element.value}"]`))
|
|
63
|
+
break
|
|
64
|
+
case 'file':
|
|
65
|
+
this.#byFile(this._element.files[0])
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Set value from a <option>
|
|
75
|
+
*
|
|
76
|
+
* @param {string} option - Current option selected
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
#byOption(option) {
|
|
80
|
+
|
|
81
|
+
this._fields.forEach(field => {
|
|
82
|
+
const attribute = `data-${field.getAttribute('data-autofill')}`
|
|
83
|
+
field.value = option && option.hasAttribute(attribute) ? option.getAttribute(attribute) : ''
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// Emmit event
|
|
87
|
+
this.emmitEvent('changed', { current: option })
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Set value from an uploaded file
|
|
93
|
+
*
|
|
94
|
+
* @param {File} file - Current file selected
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
#byFile(file) {
|
|
98
|
+
|
|
99
|
+
// Clean data
|
|
100
|
+
const extension = file.name.split('.').pop()
|
|
101
|
+
const name = file.name.replace(`.${extension}`, '')
|
|
102
|
+
|
|
103
|
+
this._fields.forEach(field => {
|
|
104
|
+
|
|
105
|
+
// Define attribute
|
|
106
|
+
const attribute = field.getAttribute('data-autofill')
|
|
107
|
+
|
|
108
|
+
// Define the value
|
|
109
|
+
let value
|
|
110
|
+
switch (attribute) {
|
|
111
|
+
case 'filename':
|
|
112
|
+
value = name
|
|
113
|
+
break
|
|
114
|
+
case 'extension':
|
|
115
|
+
value = extension
|
|
116
|
+
break
|
|
117
|
+
default:
|
|
118
|
+
value = file[attribute] ?? ''
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Set the value
|
|
122
|
+
field.value = value
|
|
123
|
+
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// Emmit event
|
|
127
|
+
this.emmitEvent('changed', { current: file })
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
}
|
package/js/_check-all.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Check all
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This class enable the functionality to un/check a list of checkboxes
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import BaseComponent from './utilities/_base-component'
|
|
13
|
+
|
|
14
|
+
export default class CheckAll extends BaseComponent {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates an instance
|
|
18
|
+
*
|
|
19
|
+
* @param {HTMLElement} el - The HTML element
|
|
20
|
+
* @param {object} options - The custom options
|
|
21
|
+
* @constructor
|
|
22
|
+
*/
|
|
23
|
+
constructor(el, options = {}) {
|
|
24
|
+
|
|
25
|
+
// Run the SUPER constructor from BaseComponent
|
|
26
|
+
super(el, options)
|
|
27
|
+
|
|
28
|
+
// Define the properties
|
|
29
|
+
this._checkboxes = document.querySelectorAll(`input[type="checkbox"][name="${this._element.value}"]`)
|
|
30
|
+
|
|
31
|
+
// Init the event listener
|
|
32
|
+
this.#init()
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Init the event listener
|
|
38
|
+
*
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
#init() {
|
|
42
|
+
|
|
43
|
+
// Check the "check all" checkbox by default is needed
|
|
44
|
+
this._element.checked = this.number.total === this.number.checked
|
|
45
|
+
|
|
46
|
+
// CLICK on "check all"
|
|
47
|
+
this._element.addEventListener('change', () => {
|
|
48
|
+
this._checkboxes.forEach((checkbox) => checkbox.checked = this._element.checked)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// CLICK on "checkboxes"
|
|
52
|
+
this._checkboxes.forEach(checkbox => {
|
|
53
|
+
checkbox.addEventListener('change', () => this._element.checked = this.number.total === this.number.checked)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the number of checkboxes as total, checked and unchecked
|
|
60
|
+
*
|
|
61
|
+
* @returns {object}
|
|
62
|
+
*/
|
|
63
|
+
get number() {
|
|
64
|
+
|
|
65
|
+
let checkboxes = {
|
|
66
|
+
total: this._checkboxes.length,
|
|
67
|
+
checked: 0,
|
|
68
|
+
unchecked: 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this._checkboxes.forEach((checkbox) => checkbox.checked ? checkboxes.checked++ : checkboxes.unchecked++)
|
|
72
|
+
|
|
73
|
+
return checkboxes
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
package/js/_comfort.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Comfort
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This class create and manage the cookies for saving theme and style changes for visual comfort
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import Cookie from "./utilities/_cookie"
|
|
13
|
+
import ErrorMessage from "./utilities/_error"
|
|
14
|
+
|
|
15
|
+
export default class Comfort extends Cookie {
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates an instance
|
|
19
|
+
*
|
|
20
|
+
* @param {string} name - The name of the cookie
|
|
21
|
+
* @constructor
|
|
22
|
+
*/
|
|
23
|
+
constructor(name = '_comfort') {
|
|
24
|
+
|
|
25
|
+
// Run the SUPER constructor from Cookie
|
|
26
|
+
super(name)
|
|
27
|
+
|
|
28
|
+
// Define the properties
|
|
29
|
+
this._buttons = {
|
|
30
|
+
theme: document.querySelectorAll('body [data-theme]'),
|
|
31
|
+
style: document.querySelectorAll('body [data-style]'),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Init the cookie
|
|
35
|
+
this.#init()
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Init the event listener
|
|
41
|
+
*
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
#init() {
|
|
45
|
+
|
|
46
|
+
// If the cookie is NOT empty, toggle the attributes on <html> <body> and <button>
|
|
47
|
+
if (Object.keys(this.value).length !== 0) {
|
|
48
|
+
this.#toggleTheme()
|
|
49
|
+
this.#toggleStyle()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// CLICK on theme buttons
|
|
53
|
+
this._buttons.theme.forEach((button) => button.addEventListener('click', () => this.setTheme(button.value)))
|
|
54
|
+
|
|
55
|
+
// CLICK on style buttons
|
|
56
|
+
this._buttons.style.forEach((button) => button.addEventListener('click', () => this.setStyle(button.getAttribute('data-style'), button.value)))
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Toggle the [data-theme] attribute on the <html>
|
|
62
|
+
* and the [aria-pressed] attribute on the <button>
|
|
63
|
+
*
|
|
64
|
+
* @private
|
|
65
|
+
*/
|
|
66
|
+
#toggleTheme() {
|
|
67
|
+
|
|
68
|
+
// Do nothing if there is no theme in the cookie
|
|
69
|
+
if (!this.has('theme')) return
|
|
70
|
+
|
|
71
|
+
// Set the [data-theme] attribute on the <html>
|
|
72
|
+
document.documentElement.setAttribute('data-theme', this.get('theme'))
|
|
73
|
+
|
|
74
|
+
// Set the [aria-pressed] attribute on the <button>
|
|
75
|
+
this._buttons.theme.forEach(button => button.setAttribute('aria-pressed', button.value === this.get('theme')))
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Toggle the [style] attribute on the <body>
|
|
81
|
+
* and the [aria-pressed] attribute on the <button>
|
|
82
|
+
*
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
#toggleStyle() {
|
|
86
|
+
|
|
87
|
+
// Get the style object stored in cookie
|
|
88
|
+
const style = this.get('style')
|
|
89
|
+
|
|
90
|
+
// Do nothing if there is no style in the cookie
|
|
91
|
+
if (!style || style.length === 0) return
|
|
92
|
+
|
|
93
|
+
// Set the [style] attribute on the <body>
|
|
94
|
+
const styleAsString = Object.entries(style).map(([k, v]) => `${k}:${v}`).join(';')
|
|
95
|
+
document.body.setAttribute('style', styleAsString)
|
|
96
|
+
|
|
97
|
+
// Set the [aria-pressed] attribute on the <button>
|
|
98
|
+
this._buttons.style.forEach(button => {
|
|
99
|
+
const attribute = button.getAttribute('data-style')
|
|
100
|
+
button.setAttribute('aria-pressed', style.hasOwnProperty(attribute) && style[attribute] === button.value)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Save the theme inside the cookie
|
|
107
|
+
*
|
|
108
|
+
* @param {string} value - Name of the theme
|
|
109
|
+
*/
|
|
110
|
+
setTheme(value) {
|
|
111
|
+
|
|
112
|
+
// Check for errors
|
|
113
|
+
if (typeof value !== 'string') throw new Error(ErrorMessage.typeOf('value', 'string'))
|
|
114
|
+
|
|
115
|
+
// Do nothing if it's already the current theme
|
|
116
|
+
if (this.get('theme') === value) return
|
|
117
|
+
|
|
118
|
+
// Save the cookie
|
|
119
|
+
this.set({ ...this.value, theme: value })
|
|
120
|
+
|
|
121
|
+
// Then toggle theme
|
|
122
|
+
this.#toggleTheme()
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Save the style inside the cookie
|
|
128
|
+
*
|
|
129
|
+
* @param {string} name - Name of the CSS property
|
|
130
|
+
* @param {string} value - Value of the CSS property
|
|
131
|
+
*/
|
|
132
|
+
setStyle(name, value) {
|
|
133
|
+
|
|
134
|
+
// Check for errors
|
|
135
|
+
if (typeof name !== 'string') throw new Error(ErrorMessage.typeOf('name', 'string'))
|
|
136
|
+
if (typeof value !== 'string') throw new Error(ErrorMessage.typeOf('value', 'string'))
|
|
137
|
+
|
|
138
|
+
// Get the style inside the cookie
|
|
139
|
+
const style = this.get('style')
|
|
140
|
+
|
|
141
|
+
// Do nothing if it's already the current style
|
|
142
|
+
if (style && style.hasOwnProperty(name) && style[name] === value) return
|
|
143
|
+
|
|
144
|
+
// Save the cookie
|
|
145
|
+
this.set({
|
|
146
|
+
...this.value,
|
|
147
|
+
style: { ...this.value.style, ...{ [name]: value } }
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
// Then toggle style
|
|
151
|
+
this.#toggleStyle()
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Reset the comfort cookie
|
|
157
|
+
*
|
|
158
|
+
*/
|
|
159
|
+
reset() {
|
|
160
|
+
|
|
161
|
+
// Delete the cookie
|
|
162
|
+
this.delete()
|
|
163
|
+
|
|
164
|
+
// Remove the attributes on <html> <body> and <button>
|
|
165
|
+
document.documentElement.removeAttribute('data-theme')
|
|
166
|
+
document.body.removeAttribute('style');
|
|
167
|
+
|
|
168
|
+
[...this._buttons.theme, ...this._buttons.style].forEach(button => {
|
|
169
|
+
button.removeAttribute('aria-pressed')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
}
|
package/js/_consent.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Consent
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This class create and manage the cookies for saving the consent to comply the GDPR
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import Cookie from "./utilities/_cookie"
|
|
13
|
+
import Dialog from "./_dialog"
|
|
14
|
+
import ErrorMessage from "./utilities/_error"
|
|
15
|
+
|
|
16
|
+
export default class Consent extends Cookie {
|
|
17
|
+
|
|
18
|
+
static DEFAULT = { necessary: true }
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates an instance
|
|
22
|
+
*
|
|
23
|
+
* @param {string} name - The name of the cookie
|
|
24
|
+
* @param {string} dialogID - The id of the cookies dialog
|
|
25
|
+
* @constructor
|
|
26
|
+
*/
|
|
27
|
+
constructor(name = '_consent', dialogID = 'cookies') {
|
|
28
|
+
|
|
29
|
+
// Check for errors
|
|
30
|
+
if (typeof dialogID !== 'string') throw new Error(ErrorMessage.typeOf('dialogID', 'string'))
|
|
31
|
+
if (!document.getElementById(dialogID)) throw new Error(ErrorMessage.existById('dialog', dialogID))
|
|
32
|
+
|
|
33
|
+
// Run the SUPER constructor from Cookie
|
|
34
|
+
super(name)
|
|
35
|
+
|
|
36
|
+
// Define the properties
|
|
37
|
+
this._dialog = new Dialog(document.getElementById(dialogID))
|
|
38
|
+
|
|
39
|
+
// Init the cookie
|
|
40
|
+
this.#init()
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Init the event listener
|
|
46
|
+
*
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
#init() {
|
|
50
|
+
|
|
51
|
+
// If the cookie is empty, open the dialog
|
|
52
|
+
if (Object.keys(this.value).length === 0) this._dialog.open()
|
|
53
|
+
|
|
54
|
+
// Set the cookie via the dialog:closed event
|
|
55
|
+
this._dialog._element.addEventListener('dialog:closed', () => this.set(Consent.DEFAULT))
|
|
56
|
+
|
|
57
|
+
// Set the cookie via the dialog:submiting event
|
|
58
|
+
this._dialog._element.addEventListener('dialog:submiting', (e) => this.#setByForm(e.detail.form))
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Save the consent cookie via a form with input of type checkbox
|
|
64
|
+
*
|
|
65
|
+
* @param {HTMLElement} form
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
#setByForm(form) {
|
|
69
|
+
|
|
70
|
+
// Check for errors
|
|
71
|
+
if (!(form instanceof HTMLElement)) throw new Error('Whoops, the form parameter must be an HTML Element !')
|
|
72
|
+
|
|
73
|
+
// Get all the input of type checkbox with name cookies_consent[]
|
|
74
|
+
const fields = form.querySelectorAll('input[type="checkbox"][name="cookies_consent[]"]')
|
|
75
|
+
|
|
76
|
+
// If there is some fields, add each to the variable preferences and save it
|
|
77
|
+
if (fields.length) {
|
|
78
|
+
const preferences = [...fields].reduce((obj, field) => { return { ...obj, ...Consent.DEFAULT, [field.value]: field.checked } }, {})
|
|
79
|
+
this.set(preferences)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
}
|
package/js/_dialog.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Dialog
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This class enable the functionalities for open/close/submit an HTML dialog
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import BaseComponent from './utilities/_base-component'
|
|
13
|
+
import ErrorMessage from './utilities/_error'
|
|
14
|
+
|
|
15
|
+
export default class Dialog extends BaseComponent {
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates an instance
|
|
19
|
+
*
|
|
20
|
+
* @param {HTMLElement} el - The HTML element
|
|
21
|
+
* @param {object} options - The custom options
|
|
22
|
+
* @constructor
|
|
23
|
+
*/
|
|
24
|
+
constructor(el, options = {}) {
|
|
25
|
+
|
|
26
|
+
// Run the SUPER constructor from BaseComponent
|
|
27
|
+
super(el, options)
|
|
28
|
+
|
|
29
|
+
// Define the properties
|
|
30
|
+
this._isModal = Boolean(this._element.getAttribute('aria-modal'))
|
|
31
|
+
|
|
32
|
+
this._buttons = {
|
|
33
|
+
open: document.querySelectorAll(`[aria-controls=${this._element.id}]`) ?? [],
|
|
34
|
+
close: this._element.querySelectorAll('[data-dialog-close]') ?? [],
|
|
35
|
+
submit: this._element.querySelectorAll('[type=submit]') ?? [],
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this._form = this._element.querySelector('form') ?? {}
|
|
39
|
+
|
|
40
|
+
// Init the event listener
|
|
41
|
+
this.#init()
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Init the event listener
|
|
47
|
+
*
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
#init() {
|
|
51
|
+
|
|
52
|
+
// OPEN
|
|
53
|
+
this._buttons.open.forEach((button) => button.addEventListener('click', () => this.open()))
|
|
54
|
+
|
|
55
|
+
// CLOSE
|
|
56
|
+
this._buttons.close.forEach((button) => button.addEventListener('click', () => this.close()))
|
|
57
|
+
|
|
58
|
+
// SUBMIT
|
|
59
|
+
this._buttons.submit.forEach((button) => {
|
|
60
|
+
button.addEventListener('click', (e) => {
|
|
61
|
+
e.preventDefault()
|
|
62
|
+
this.submit()
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Remove the [inert] attribute from the body if the dialog a modal
|
|
70
|
+
*
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
#toggleBodyAttribute() {
|
|
74
|
+
if (!this._isModal) return
|
|
75
|
+
const numberOfDialogOpen = document.querySelectorAll('dialog[open][aria-modal=true]').length
|
|
76
|
+
numberOfDialogOpen ? document.body.setAttribute('inert', true) : document.body.removeAttribute('inert')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Open the dialog
|
|
81
|
+
*
|
|
82
|
+
*/
|
|
83
|
+
open() {
|
|
84
|
+
|
|
85
|
+
// Run event before opening, and stop if e.preventDefault()
|
|
86
|
+
const callback = this.emmitEvent('opening')
|
|
87
|
+
if (callback.defaultPrevented) return
|
|
88
|
+
|
|
89
|
+
// Open the dialog (the methode changed if it's a modal)
|
|
90
|
+
this._isModal ? this._element.showModal() : this._element.show()
|
|
91
|
+
|
|
92
|
+
// Toggle the [inert] attribute on the body
|
|
93
|
+
this.#toggleBodyAttribute()
|
|
94
|
+
|
|
95
|
+
// Run event after opened
|
|
96
|
+
this.emmitEvent('opened')
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Close the dialog
|
|
102
|
+
*
|
|
103
|
+
*/
|
|
104
|
+
close() {
|
|
105
|
+
|
|
106
|
+
// Run event before closing, and stop if e.preventDefault()
|
|
107
|
+
const callback = this.emmitEvent('closing')
|
|
108
|
+
if (callback.defaultPrevented) return
|
|
109
|
+
|
|
110
|
+
// Close the dialog
|
|
111
|
+
this._element.close()
|
|
112
|
+
|
|
113
|
+
// Toggle the [inert] attribute on the body
|
|
114
|
+
this.#toggleBodyAttribute()
|
|
115
|
+
|
|
116
|
+
// Run event after closed
|
|
117
|
+
this.emmitEvent('closed')
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Submit the form in the dialog as default
|
|
123
|
+
*
|
|
124
|
+
*/
|
|
125
|
+
submit() {
|
|
126
|
+
|
|
127
|
+
// Check for errors
|
|
128
|
+
if (!this._form.length) throw new Error(ErrorMessage.exist('form'))
|
|
129
|
+
|
|
130
|
+
// Stop if the form is invalid (Default required fields)
|
|
131
|
+
if (!this._form.reportValidity()) return
|
|
132
|
+
|
|
133
|
+
// Add [aria-busy] attribute on each submit button
|
|
134
|
+
this._buttons.submit.forEach((button) => button.setAttribute('aria-busy', true))
|
|
135
|
+
|
|
136
|
+
// Run event before submitting, and stop if e.preventDefault()
|
|
137
|
+
const callback = this.emmitEvent('submiting', { form: this._form })
|
|
138
|
+
if (callback.defaultPrevented) return
|
|
139
|
+
|
|
140
|
+
// Define if the form is submited by Javascript or default
|
|
141
|
+
if (this._form.getAttribute('method') == 'dialog') {
|
|
142
|
+
|
|
143
|
+
// Remove [aria-busy] attribute on each submit button
|
|
144
|
+
this._buttons.submit.forEach((button) => button.removeAttribute('aria-busy', true))
|
|
145
|
+
|
|
146
|
+
// Close the dialog element
|
|
147
|
+
this._element.close()
|
|
148
|
+
|
|
149
|
+
// Toggle the [inert] attribute on the body
|
|
150
|
+
this.#toggleBodyAttribute()
|
|
151
|
+
|
|
152
|
+
// Run event after submited
|
|
153
|
+
this.emmitEvent('submited', { form: this._form })
|
|
154
|
+
|
|
155
|
+
} else {
|
|
156
|
+
|
|
157
|
+
// Default <form> submit
|
|
158
|
+
this._form.submit()
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
}
|