@natachah/vanilla-frontend 0.1.9 → 0.1.11
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/docs/pages/components/drawer.html +43 -2
- package/docs/pages/javascript/sortable.html +37 -0
- package/docs/src/js/doc-layout.js +1 -1
- package/docs/src/scss/layout.scss +10 -7
- package/js/_drawer.js +30 -8
- package/js/_sortable.js +27 -2
- package/js/tests/sortable.test.js +25 -4
- package/natachah-vanilla-frontend-0.1.11.tgz +0 -0
- package/package.json +1 -1
- package/scss/base/_reset.scss +5 -0
- package/natachah-vanilla-frontend-0.1.9.tgz +0 -0
|
@@ -65,8 +65,49 @@
|
|
|
65
65
|
|
|
66
66
|
<h2>Javascript</h2>
|
|
67
67
|
<p>This component is mostly in javascript, to use it you must import the javascript file and create a new Drawer object.</p>
|
|
68
|
-
<p>You can
|
|
69
|
-
|
|
68
|
+
<p>You can have a <b>Backdrop</b> if you want to make it more like a drawer opening on the front of the content, you juste need a <code>#backdrop</code> somewhere on your page.</p>
|
|
69
|
+
|
|
70
|
+
<h3>Options</h3>
|
|
71
|
+
|
|
72
|
+
<table>
|
|
73
|
+
<thead>
|
|
74
|
+
<tr>
|
|
75
|
+
<th>Name</th>
|
|
76
|
+
<th>Description</th>
|
|
77
|
+
<th>Value</th>
|
|
78
|
+
</tr>
|
|
79
|
+
</thead>
|
|
80
|
+
<tbody>
|
|
81
|
+
<tr>
|
|
82
|
+
<td data-label="Name">
|
|
83
|
+
<p>breakpoint</p>
|
|
84
|
+
</td>
|
|
85
|
+
<td data-label="Description">
|
|
86
|
+
<p>The breakpoint to open/close the drawer</p>
|
|
87
|
+
</td>
|
|
88
|
+
<td data-label="Value">
|
|
89
|
+
<p><code>960</code> as an <code>int</code></p>
|
|
90
|
+
</td>
|
|
91
|
+
</tr>
|
|
92
|
+
<tr>
|
|
93
|
+
<td data-label="Name">
|
|
94
|
+
<p>cookie</p>
|
|
95
|
+
</td>
|
|
96
|
+
<td data-label="Description">
|
|
97
|
+
<p>The cookie name to save if you want to keep it open/close on refresh</p>
|
|
98
|
+
</td>
|
|
99
|
+
<td data-label="Value">
|
|
100
|
+
<p><code>false</code> by default, or a <code>string</code></p>
|
|
101
|
+
</td>
|
|
102
|
+
</tr>
|
|
103
|
+
</tbody>
|
|
104
|
+
</table>
|
|
105
|
+
|
|
106
|
+
<doc-code data-type="js">
|
|
107
|
+
import Drawer from '@natachah/vanilla-frontend/js/utilities/_drawer.js'
|
|
108
|
+
const drawer = document.getElementById('drawer')
|
|
109
|
+
if (drawer) new Drawer(drawer, { breakpoint : 960, cookkie: '_drawer-cookie' })
|
|
110
|
+
</doc-code>
|
|
70
111
|
|
|
71
112
|
<blockquote>
|
|
72
113
|
<p>Main use case are sidebar menu or main navigation menu.</p>
|
|
@@ -95,6 +95,43 @@
|
|
|
95
95
|
</ul>
|
|
96
96
|
</doc-code>
|
|
97
97
|
|
|
98
|
+
<h2>Manually move</h2>
|
|
99
|
+
<p>You can manually move the item by adding some button with <code>data-go="up"</code> or <code>data-go="down"</code> attribute</p>
|
|
100
|
+
<doc-demo>
|
|
101
|
+
<ul class="demo-sortable">
|
|
102
|
+
<li draggable="false">
|
|
103
|
+
<button data-go="up">UP</button><button data-go="down">Down</button>
|
|
104
|
+
Lorem, ipsum.
|
|
105
|
+
</li>
|
|
106
|
+
<li draggable="false">
|
|
107
|
+
<button data-go="up">UP</button><button data-go="down">Down</button>
|
|
108
|
+
Impedit, quod!
|
|
109
|
+
</li>
|
|
110
|
+
<li draggable="false">
|
|
111
|
+
<button data-go="up">UP</button><button data-go="down">Down</button>
|
|
112
|
+
Repudiandae, rerum.
|
|
113
|
+
</li>
|
|
114
|
+
</ul>
|
|
115
|
+
</doc-demo>
|
|
116
|
+
|
|
117
|
+
<doc-code>
|
|
118
|
+
<ul id="mySortableListWithBtn">
|
|
119
|
+
<li draggable="false">
|
|
120
|
+
<button data-go="up">UP</button><button data-go="down">Down</button>
|
|
121
|
+
Lorem, ipsum.
|
|
122
|
+
</li>
|
|
123
|
+
<li draggable="false">
|
|
124
|
+
<button data-go="up">UP</button><button data-go="down">Down</button>
|
|
125
|
+
Impedit, quod!
|
|
126
|
+
</li>
|
|
127
|
+
<li draggable="false">
|
|
128
|
+
<button data-go="up">UP</button><button data-go="down">Down</button>
|
|
129
|
+
Repudiandae, rerum.
|
|
130
|
+
</li>
|
|
131
|
+
</ul>
|
|
132
|
+
</doc-code>
|
|
133
|
+
|
|
134
|
+
|
|
98
135
|
<h2 id="javascript">Javascript</h2>
|
|
99
136
|
<p>To enable this component you need to import the javascript file and create a new Sortable object.</p>
|
|
100
137
|
<h3 id="events">Events</h3>
|
|
@@ -18,7 +18,7 @@ class DocLayout extends HTMLElement {
|
|
|
18
18
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pin-angle" viewBox="0 0 16 16">
|
|
19
19
|
<path d="M9.828.722a.5.5 0 0 1 .354.146l4.95 4.95a.5.5 0 0 1 0 .707c-.48.48-1.072.588-1.503.588-.177 0-.335-.018-.46-.039l-3.134 3.134a6 6 0 0 1 .16 1.013c.046.702-.032 1.687-.72 2.375a.5.5 0 0 1-.707 0l-2.829-2.828-3.182 3.182c-.195.195-1.219.902-1.414.707s.512-1.22.707-1.414l3.182-3.182-2.828-2.829a.5.5 0 0 1 0-.707c.688-.688 1.673-.767 2.375-.72a6 6 0 0 1 1.013.16l3.134-3.133a3 3 0 0 1-.04-.461c0-.43.108-1.022.589-1.503a.5.5 0 0 1 .353-.146m.122 2.112v-.002zm0-.002v.002a.5.5 0 0 1-.122.51L6.293 6.878a.5.5 0 0 1-.511.12H5.78l-.014-.004a5 5 0 0 0-.288-.076 5 5 0 0 0-.765-.116c-.422-.028-.836.008-1.175.15l5.51 5.509c.141-.34.177-.753.149-1.175a5 5 0 0 0-.192-1.054l-.004-.013v-.001a.5.5 0 0 1 .12-.512l3.536-3.535a.5.5 0 0 1 .532-.115l.096.022c.087.017.208.034.344.034q.172.002.343-.04L9.927 2.028q-.042.172-.04.343a1.8 1.8 0 0 0 .062.46z"/>
|
|
20
20
|
</svg>
|
|
21
|
-
0.1.
|
|
21
|
+
0.1.11
|
|
22
22
|
</span>
|
|
23
23
|
</li>
|
|
24
24
|
<li>
|
|
@@ -19,13 +19,11 @@ doc-layout {
|
|
|
19
19
|
overflow: hidden;
|
|
20
20
|
transition: all .5s ease-in-out;
|
|
21
21
|
|
|
22
|
-
> header
|
|
23
|
-
> aside > header {
|
|
22
|
+
> header {
|
|
24
23
|
grid-area: header;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
> footer
|
|
28
|
-
> aside > footer {
|
|
26
|
+
> footer {
|
|
29
27
|
grid-area: footer;
|
|
30
28
|
}
|
|
31
29
|
|
|
@@ -49,12 +47,17 @@ doc-layout {
|
|
|
49
47
|
--drawer-position: 0 auto 0 0;
|
|
50
48
|
|
|
51
49
|
// Set the grid for the sidebar based on the parent grid
|
|
52
|
-
grid-
|
|
53
|
-
grid-template-rows:
|
|
54
|
-
grid-template-areas: "
|
|
50
|
+
grid-template-columns: 1fr;
|
|
51
|
+
grid-template-rows: max-content 1fr;
|
|
52
|
+
grid-template-areas: "welcome" "menu";
|
|
53
|
+
|
|
54
|
+
> header {
|
|
55
|
+
grid-area: welcome;
|
|
56
|
+
}
|
|
55
57
|
|
|
56
58
|
// Design the nav
|
|
57
59
|
> nav {
|
|
60
|
+
grid-area: menu;
|
|
58
61
|
overflow-y: auto;
|
|
59
62
|
}
|
|
60
63
|
|
package/js/_drawer.js
CHANGED
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
import BaseComponent from './utilities/_base-component'
|
|
14
14
|
import ErrorMessage from "./utilities/_error"
|
|
15
|
+
import Cookie from "./utilities/_cookie"
|
|
15
16
|
|
|
16
17
|
export default class Drawer extends BaseComponent {
|
|
17
18
|
|
|
18
19
|
static OPTIONS = {
|
|
19
20
|
breakpoint: 960,
|
|
21
|
+
cookie: false
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -44,6 +46,8 @@ export default class Drawer extends BaseComponent {
|
|
|
44
46
|
|
|
45
47
|
this._focus = this._element.querySelector('a,button') ?? null
|
|
46
48
|
|
|
49
|
+
this._cookie = this._options.cookie ? new Cookie(this._options.cookie) : null
|
|
50
|
+
|
|
47
51
|
// Init the event listener
|
|
48
52
|
this.#init()
|
|
49
53
|
|
|
@@ -56,30 +60,50 @@ export default class Drawer extends BaseComponent {
|
|
|
56
60
|
*/
|
|
57
61
|
#init() {
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
const isOpenValue = window.innerWidth > this._options.breakpoint && !this._isOpen
|
|
64
|
+
|
|
65
|
+
// Init the default cookie
|
|
66
|
+
if (this._cookie && !this._cookie.has('open')) this._cookie.set({ 'open': isOpenValue })
|
|
67
|
+
|
|
68
|
+
// Init the default open or close
|
|
69
|
+
if (!this._cookie || (this._cookie.get('open') && !this._isOpen)) this.toggle()
|
|
61
70
|
|
|
62
71
|
// On resize check if need to open/close the drawer
|
|
63
72
|
window.onresize = () => {
|
|
64
73
|
clearTimeout(this._timeout)
|
|
65
74
|
this._timeout = setTimeout(() => {
|
|
66
|
-
if ((window.innerWidth
|
|
75
|
+
if ((window.innerWidth > this._options.breakpoint && !this._isOpen) || (window.innerWidth < this._options.breakpoint && this._isOpen)) {
|
|
76
|
+
this.toggle()
|
|
77
|
+
this.#resetCookie()
|
|
78
|
+
}
|
|
67
79
|
}, 250)
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
// On click on the button toggle the drawer
|
|
71
|
-
this._buttons.forEach((button) => button.addEventListener('click', () =>
|
|
83
|
+
this._buttons.forEach((button) => button.addEventListener('click', () => {
|
|
84
|
+
this.toggle()
|
|
85
|
+
if (window.innerWidth > this._options.breakpoint) this.#resetCookie()
|
|
86
|
+
if (this._focus && this._isOpen) this._focus.focus()
|
|
87
|
+
}))
|
|
72
88
|
|
|
73
89
|
// On click on the backdrop, close the drawer
|
|
74
90
|
if (this._backdrop) this._backdrop.addEventListener('click', () => this.toggle())
|
|
75
91
|
|
|
76
92
|
}
|
|
77
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Reset the cookie
|
|
96
|
+
*
|
|
97
|
+
*/
|
|
98
|
+
#resetCookie() {
|
|
99
|
+
if (this._cookie) this._cookie.set({ 'open': this._isOpen })
|
|
100
|
+
}
|
|
101
|
+
|
|
78
102
|
/**
|
|
79
103
|
* Toggle the drawer
|
|
80
104
|
*
|
|
81
105
|
*/
|
|
82
|
-
toggle(
|
|
106
|
+
toggle() {
|
|
83
107
|
|
|
84
108
|
// Change the state
|
|
85
109
|
this._isOpen = !this._isOpen
|
|
@@ -93,9 +117,7 @@ export default class Drawer extends BaseComponent {
|
|
|
93
117
|
// Change the [hidden] attribute on the drawer
|
|
94
118
|
this._element.hidden = !this._isOpen
|
|
95
119
|
|
|
96
|
-
// Toggle the focus
|
|
97
|
-
if (enableFocus && this._focus && this._isOpen) this._focus.focus()
|
|
98
|
-
|
|
99
120
|
}
|
|
100
121
|
|
|
122
|
+
|
|
101
123
|
}
|
package/js/_sortable.js
CHANGED
|
@@ -53,9 +53,12 @@ export default class Sortable extends BaseComponent {
|
|
|
53
53
|
|
|
54
54
|
// Drag and drop events
|
|
55
55
|
item.addEventListener('dragstart', () => this.#drag(item))
|
|
56
|
-
item.addEventListener('dragenter', () => this.#
|
|
56
|
+
item.addEventListener('dragenter', () => this.#dragging(item))
|
|
57
57
|
item.addEventListener('dragend', () => this.#drop(item))
|
|
58
58
|
|
|
59
|
+
const btns = item.querySelectorAll('[data-go]')
|
|
60
|
+
if (btns) btns.forEach(btn => btn.addEventListener('click', () => this.#move(item, btn.getAttribute('data-go') == 'up')))
|
|
61
|
+
|
|
59
62
|
})
|
|
60
63
|
|
|
61
64
|
}
|
|
@@ -100,7 +103,7 @@ export default class Sortable extends BaseComponent {
|
|
|
100
103
|
* @param {HTMLElement} item - The current item
|
|
101
104
|
* @private
|
|
102
105
|
*/
|
|
103
|
-
#
|
|
106
|
+
#dragging(item) {
|
|
104
107
|
|
|
105
108
|
// Check for errors
|
|
106
109
|
if (!(item instanceof HTMLElement)) throw new Error(ErrorMessage.instanceOf('item', 'HTMLElement'))
|
|
@@ -142,4 +145,26 @@ export default class Sortable extends BaseComponent {
|
|
|
142
145
|
|
|
143
146
|
}
|
|
144
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Manually move up and down
|
|
150
|
+
*
|
|
151
|
+
* @param {HTMLElement} item - The current item
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
#move(item, goUp = false) {
|
|
155
|
+
|
|
156
|
+
const sibling = goUp ? item.previousElementSibling : item.nextElementSibling
|
|
157
|
+
|
|
158
|
+
if (sibling) {
|
|
159
|
+
this._element.insertBefore(item, goUp ? sibling : sibling.nextElementSibling)
|
|
160
|
+
} else if (!goUp) {
|
|
161
|
+
this._element.appendChild(item)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Run custom event
|
|
165
|
+
this.emmitEvent('moved', { items: this.items, current: item })
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
145
170
|
}
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* - #Drag(): Emmit the sortable:drag event
|
|
13
13
|
* - #Drop(): Remove the [aria-grabbed] attribute
|
|
14
14
|
* - #Drop(): Emmit the sortable:drop event
|
|
15
|
+
* - #Move(): Go down on click and emmit the sortable:moved event
|
|
16
|
+
* - #Move(): Go up on click and emmit the sortable:moved event
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
import { describe, test, expect, beforeAll, vi } from "vitest"
|
|
@@ -29,9 +31,9 @@ beforeAll(() => {
|
|
|
29
31
|
|
|
30
32
|
document.body.innerHTML =
|
|
31
33
|
'<ul id="fakeList">' +
|
|
32
|
-
'<li draggable="false">One</li>' +
|
|
33
|
-
'<li draggable="false">Two</li>' +
|
|
34
|
-
'<li draggable="false">Three</li>' +
|
|
34
|
+
'<li id="first" draggable="false"><button data-go="up">UP</button><button data-go="down">Down</button>One</li>' +
|
|
35
|
+
'<li draggable="false"><button data-go="up">UP</button><button data-go="down">Down</button>Two</li>' +
|
|
36
|
+
'<li draggable="false"><button data-go="up">UP</button><button data-go="down">Down</button>Three</li>' +
|
|
35
37
|
'</ul>' +
|
|
36
38
|
'<table id="fakeTable">' +
|
|
37
39
|
'<tr draggable="false"><td data-handle="sortable">DRAG</td><td>One</td></tr>' +
|
|
@@ -41,7 +43,7 @@ beforeAll(() => {
|
|
|
41
43
|
|
|
42
44
|
fakeListSortable = new Sortable(document.getElementById('fakeList'))
|
|
43
45
|
fakeTableSortable = new Sortable(document.getElementById('fakeTable'))
|
|
44
|
-
fakeListItem = document.
|
|
46
|
+
fakeListItem = document.getElementById('first')
|
|
45
47
|
|
|
46
48
|
})
|
|
47
49
|
|
|
@@ -122,4 +124,23 @@ describe('#Drop()', () => {
|
|
|
122
124
|
expect(eventSpy).toHaveBeenCalledWith('drop', { current: fakeListItem, items: fakeListSortable.items })
|
|
123
125
|
})
|
|
124
126
|
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('#Move()', () => {
|
|
130
|
+
|
|
131
|
+
test('Go down on click and emmit the sortable:moved event', () => {
|
|
132
|
+
const eventSpy = vi.spyOn(fakeListSortable, 'emmitEvent')
|
|
133
|
+
fireEvent(fakeListItem.querySelector('[data-go=down]'), new MouseEvent('click'))
|
|
134
|
+
expect(fakeListItem).toBe(document.getElementById('fakeList').children[1])
|
|
135
|
+
expect(eventSpy).toHaveBeenCalledWith('moved', { current: fakeListItem, items: fakeListSortable.items })
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
test('Go up on click and emmit the sortable:moved event', () => {
|
|
139
|
+
const eventSpy = vi.spyOn(fakeListSortable, 'emmitEvent')
|
|
140
|
+
expect(fakeListItem).toBe(document.getElementById('fakeList').children[1])
|
|
141
|
+
fireEvent(fakeListItem.querySelector('[data-go=up]'), new MouseEvent('click'))
|
|
142
|
+
expect(fakeListItem).toBe(document.getElementById('fakeList').children[0])
|
|
143
|
+
expect(eventSpy).toHaveBeenCalledWith('moved', { current: fakeListItem, items: fakeListSortable.items })
|
|
144
|
+
})
|
|
145
|
+
|
|
125
146
|
})
|
|
Binary file
|
package/package.json
CHANGED
package/scss/base/_reset.scss
CHANGED
|
@@ -82,6 +82,11 @@ summary {
|
|
|
82
82
|
&:not(:has([data-handle=sortable])),
|
|
83
83
|
[data-handle=sortable] {
|
|
84
84
|
cursor: grab;
|
|
85
|
+
user-select: none;
|
|
86
|
+
pointer-events: auto;
|
|
87
|
+
-webkit-user-drag: element;
|
|
88
|
+
-webkit-touch-callout: none;
|
|
89
|
+
touch-action: none;
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
&[aria-grabbed=true],
|
|
Binary file
|