@lemonadejs/tabs 5.0.0 → 5.8.0
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/README.md +131 -129
- package/dist/index.d.ts +15 -5
- package/dist/index.js +236 -49
- package/dist/style.css +63 -37
- package/package.json +20 -20
package/README.md
CHANGED
|
@@ -1,129 +1,131 @@
|
|
|
1
|
-
# LemonadeJS Tabs
|
|
2
|
-
|
|
3
|
-
[Official website and documentation is here](https://lemonadejs.net/components/tabs)
|
|
4
|
-
|
|
5
|
-
Compatible with Vanilla JavaScript, LemonadeJS, React, Vue or Angular.
|
|
6
|
-
|
|
7
|
-
The LemonadeJS JavaScript Tabs is a responsive and reactive component that creates selected tabs.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- Lightweight: The JavaScript Tabs is only about 2 KBytes;
|
|
12
|
-
- Integration: It can be used as a standalone library or integrated with any modern framework;
|
|
13
|
-
|
|
14
|
-
## Getting Started
|
|
15
|
-
|
|
16
|
-
You can install using NPM or using directly from a CDN.
|
|
17
|
-
|
|
18
|
-
### npm Installation
|
|
19
|
-
|
|
20
|
-
To install it in your project using npm, run the following command:
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
$ npm install @lemonadejs/tabs
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### CDN
|
|
27
|
-
|
|
28
|
-
To use tabs via a CDN, include the following script tags in your HTML file:
|
|
29
|
-
|
|
30
|
-
```html
|
|
31
|
-
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
|
|
32
|
-
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@lemonadejs/tabs/dist/index.min.js"></script>
|
|
33
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/tabs/dist/style.min.css" />
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Usage
|
|
37
|
-
|
|
38
|
-
Quick example with Lemonade
|
|
39
|
-
|
|
40
|
-
```javascript
|
|
41
|
-
import lemonade from 'lemonadejs'
|
|
42
|
-
import Tabs from '@lemonadejs/tabs';
|
|
43
|
-
import '@lemonadejs/tabs/dist/style.css';
|
|
44
|
-
|
|
45
|
-
export default function App() {
|
|
46
|
-
const self = this;
|
|
47
|
-
|
|
48
|
-
return `<div>
|
|
49
|
-
<Tabs :selected="0">
|
|
50
|
-
<div title="Tab 1">Content of the first tab</div>
|
|
51
|
-
<div title="Tab 2">Content of the second tab</div>
|
|
52
|
-
</Tabs>
|
|
53
|
-
</div
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Quick example with React
|
|
58
|
-
|
|
59
|
-
```jsx
|
|
60
|
-
import React, { useRef } from
|
|
61
|
-
import Tabs from
|
|
62
|
-
import '@lemonadejs/tabs/dist/style.css';
|
|
63
|
-
|
|
64
|
-
const data = [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
</
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
1
|
+
# LemonadeJS Tabs
|
|
2
|
+
|
|
3
|
+
[Official website and documentation is here](https://lemonadejs.net/components/tabs)
|
|
4
|
+
|
|
5
|
+
Compatible with Vanilla JavaScript, LemonadeJS, React, Vue or Angular.
|
|
6
|
+
|
|
7
|
+
The LemonadeJS JavaScript Tabs is a responsive and reactive component that creates selected tabs.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Lightweight: The JavaScript Tabs is only about 2 KBytes;
|
|
12
|
+
- Integration: It can be used as a standalone library or integrated with any modern framework;
|
|
13
|
+
|
|
14
|
+
## Getting Started
|
|
15
|
+
|
|
16
|
+
You can install using NPM or using directly from a CDN.
|
|
17
|
+
|
|
18
|
+
### npm Installation
|
|
19
|
+
|
|
20
|
+
To install it in your project using npm, run the following command:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
$ npm install @lemonadejs/tabs
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### CDN
|
|
27
|
+
|
|
28
|
+
To use tabs via a CDN, include the following script tags in your HTML file:
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
|
|
32
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@lemonadejs/tabs/dist/index.min.js"></script>
|
|
33
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/tabs/dist/style.min.css" />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Usage
|
|
37
|
+
|
|
38
|
+
Quick example with Lemonade
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import lemonade from 'lemonadejs';
|
|
42
|
+
import Tabs from '@lemonadejs/tabs';
|
|
43
|
+
import '@lemonadejs/tabs/dist/style.css';
|
|
44
|
+
|
|
45
|
+
export default function App() {
|
|
46
|
+
const self = this;
|
|
47
|
+
|
|
48
|
+
return `<div>
|
|
49
|
+
<Tabs :selected="0">
|
|
50
|
+
<div title="Tab 1">Content of the first tab</div>
|
|
51
|
+
<div title="Tab 2">Content of the second tab</div>
|
|
52
|
+
</Tabs>
|
|
53
|
+
</div>`;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Quick example with React
|
|
58
|
+
|
|
59
|
+
```jsx
|
|
60
|
+
import React, { useRef } from 'react';
|
|
61
|
+
import Tabs from '@lemonadejs/tabs/dist/react';
|
|
62
|
+
import '@lemonadejs/tabs/dist/style.css';
|
|
63
|
+
|
|
64
|
+
const data = [
|
|
65
|
+
{ title: 'Tab 1', content: 'Content of first tab' },
|
|
66
|
+
{ title: 'Tab 2', content: 'Content of second tab' },
|
|
67
|
+
];
|
|
68
|
+
export default function App() {
|
|
69
|
+
const tabs = useRef();
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
<Tabs selected={0} ref={tabs} data={data} />
|
|
73
|
+
</>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Quick example with React
|
|
79
|
+
|
|
80
|
+
```vue
|
|
81
|
+
<template>
|
|
82
|
+
<Tabs :selected="0">
|
|
83
|
+
<div title="Tab 1">Content of the first tab</div>
|
|
84
|
+
<div title="Tab 2">Content of the second tab</div>
|
|
85
|
+
</Tabs>
|
|
86
|
+
</template>
|
|
87
|
+
|
|
88
|
+
<script>
|
|
89
|
+
import Tabs from '@lemonadejs/tabs/dist/vue';
|
|
90
|
+
import '@lemonadejs/tabs/dist/style.css';
|
|
91
|
+
|
|
92
|
+
export default {
|
|
93
|
+
name: 'App',
|
|
94
|
+
components: {
|
|
95
|
+
Tabs,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
</script>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
[You can find more examples here in the official documentation.](https://lemonadejs.net/components/tabs)
|
|
102
|
+
|
|
103
|
+
### Configuration
|
|
104
|
+
|
|
105
|
+
You can configure things such as tabs titles, tabs contents and selected tab.
|
|
106
|
+
|
|
107
|
+
#### Settings
|
|
108
|
+
|
|
109
|
+
| Property | Type | Description |
|
|
110
|
+
| --------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
111
|
+
| selected? | number | The index of the initially selected tab. Starts from 0. |
|
|
112
|
+
| position? | string | The position of the tabs bar within the parent element. Use 'center' to center-align the tabs. |
|
|
113
|
+
| data? | tabItem[] | An optional alternative method to provide the title and content that will serve as the basis for rendering the tabs. See more about the `tabItem` object in the Tab Item section below. |
|
|
114
|
+
| round? | boolean | Dictates whether the tab style will feature rounded corners. |
|
|
115
|
+
| onopen? | function | When a new tabs is opened. |
|
|
116
|
+
|
|
117
|
+
#### Tab Item
|
|
118
|
+
|
|
119
|
+
| Property | Description |
|
|
120
|
+
| -------- | ------------------------------------------------------------------------ |
|
|
121
|
+
| title | The title of the tab, serving as the label displayed on the tab options. |
|
|
122
|
+
| content | The HTML content intended for this specific tab. |
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
The [LemonadeJS](https://lemonadejs.net) Tabs is released under the MIT.
|
|
127
|
+
|
|
128
|
+
## Other Tools
|
|
129
|
+
|
|
130
|
+
- [jSuites](https://jsuites.net)
|
|
131
|
+
- [Jspreadsheet Data Grid](https://jspreadsheet.com)
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ declare namespace Tabs {
|
|
|
10
10
|
interface Item {
|
|
11
11
|
// Reference to an external DOM
|
|
12
12
|
el: HTMLElement,
|
|
13
|
+
/** Material icon keyword */
|
|
14
|
+
icon?: string;
|
|
13
15
|
// Tab header
|
|
14
16
|
title: string,
|
|
15
17
|
// HTML template
|
|
@@ -22,13 +24,21 @@ declare namespace Tabs {
|
|
|
22
24
|
/** Selected tab */
|
|
23
25
|
selected?: number;
|
|
24
26
|
/** Tabs position */
|
|
25
|
-
position?: 'center' | undefined;
|
|
27
|
+
position?: 'default' | 'center' | 'bottom' | undefined;
|
|
26
28
|
/** Activate round borders */
|
|
27
29
|
round?: boolean;
|
|
28
|
-
/** On open event */
|
|
29
|
-
onopen?: (instance: object, index: number) => void;
|
|
30
30
|
/** Allow to create new tab button */
|
|
31
31
|
allowCreate?: boolean;
|
|
32
|
+
/** Change tabs */
|
|
33
|
+
onchange?: (instance: object, value: number) => void;
|
|
34
|
+
/** On open event */
|
|
35
|
+
onopen?: (instance: object, index: number) => void;
|
|
36
|
+
/** Before create new tab */
|
|
37
|
+
onbeforecreate?: (instance: object, item: Item, position: number) => boolean | void;
|
|
38
|
+
/** On create new tab event */
|
|
39
|
+
oncreate?: (instance: object, item: Item, position: number) => void;
|
|
40
|
+
/** On change tab position event */
|
|
41
|
+
onchangeposition: (instance: object, fromIndex: number, toIndex: number) => void;
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
interface Instance {
|
|
@@ -42,8 +52,8 @@ declare namespace Tabs {
|
|
|
42
52
|
position?: 'center' | undefined;
|
|
43
53
|
/** Activate round borders */
|
|
44
54
|
round?: boolean;
|
|
45
|
-
/**
|
|
46
|
-
|
|
55
|
+
/** Allow to create new tab button */
|
|
56
|
+
allowCreate?: boolean;
|
|
47
57
|
}
|
|
48
58
|
}
|
|
49
59
|
|
package/dist/index.js
CHANGED
|
@@ -8,12 +8,33 @@ if (! lemonade && typeof (require) === 'function') {
|
|
|
8
8
|
global.Tabs = factory();
|
|
9
9
|
}(this, (function () {
|
|
10
10
|
|
|
11
|
+
class CustomEvents extends Event {
|
|
12
|
+
constructor(type, props, options) {
|
|
13
|
+
super(type, {
|
|
14
|
+
bubbles: true,
|
|
15
|
+
composed: true,
|
|
16
|
+
...options,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (props) {
|
|
20
|
+
for (const key in props) {
|
|
21
|
+
// Avoid assigning if property already exists anywhere on `this`
|
|
22
|
+
if (! (key in this)) {
|
|
23
|
+
this[key] = props[key];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
// Dispatcher
|
|
12
|
-
const Dispatch = function(type){
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
31
|
+
const Dispatch = function(method, type, options) {
|
|
32
|
+
// Try calling the method directly if provided
|
|
33
|
+
if (typeof method === 'function') {
|
|
34
|
+
let a = Object.values(options);
|
|
35
|
+
return method(...a);
|
|
36
|
+
} else if (this.tagName) {
|
|
37
|
+
this.dispatchEvent(new CustomEvents(type, options));
|
|
17
38
|
}
|
|
18
39
|
}
|
|
19
40
|
|
|
@@ -37,9 +58,109 @@ if (! lemonade && typeof (require) === 'function') {
|
|
|
37
58
|
}
|
|
38
59
|
}
|
|
39
60
|
|
|
40
|
-
const
|
|
61
|
+
const sorting = function(el, options) {
|
|
62
|
+
const obj = {};
|
|
63
|
+
|
|
64
|
+
let dragElement = null;
|
|
65
|
+
|
|
66
|
+
el.addEventListener('dragstart', function(e) {
|
|
67
|
+
let target = e.target;
|
|
68
|
+
if (target.nodeType === 3) {
|
|
69
|
+
if (target.parentNode.getAttribute('draggable') === 'true') {
|
|
70
|
+
target = target.parentNode;
|
|
71
|
+
} else {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
e.stopPropagation();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (target.getAttribute('draggable') === 'true') {
|
|
79
|
+
let position = Array.prototype.indexOf.call(target.parentNode.children, target);
|
|
80
|
+
dragElement = {
|
|
81
|
+
element: target,
|
|
82
|
+
o: position,
|
|
83
|
+
d: position
|
|
84
|
+
}
|
|
85
|
+
target.style.opacity = '0.25';
|
|
86
|
+
e.dataTransfer.setDragImage(target,0,0);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
el.addEventListener('dragover', function(e) {
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
|
|
93
|
+
if (dragElement && getElement(e.target) && e.target.getAttribute('draggable') == 'true' && dragElement.element != e.target) {
|
|
94
|
+
let element = e.target.clientWidth / 2 > e.offsetX ? e.target : e.target.nextSibling;
|
|
95
|
+
e.target.parentNode.insertBefore(dragElement.element, element);
|
|
96
|
+
dragElement.d = Array.prototype.indexOf.call(e.target.parentNode.children, dragElement.element);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
el.addEventListener('dragleave', function(e) {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
el.addEventListener('dragend', function(e) {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
|
|
107
|
+
if (dragElement) {
|
|
108
|
+
let element = dragElement.o < dragElement.d ? e.target.parentNode.children[dragElement.o] : e.target.parentNode.children[dragElement.o].nextSibling
|
|
109
|
+
e.target.parentNode.insertBefore(dragElement.element, element);
|
|
110
|
+
dragElement.element.style.opacity = '';
|
|
111
|
+
dragElement = null;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
el.addEventListener('drop', function(e) {
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
|
|
118
|
+
if (dragElement) {
|
|
119
|
+
if (dragElement.o !== dragElement.d) {
|
|
120
|
+
if (typeof(options.ondrop) == 'function') {
|
|
121
|
+
options.ondrop(el, dragElement.o, dragElement.d, dragElement.element, e.target, e);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
dragElement.element.style.opacity = '';
|
|
126
|
+
dragElement = null;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const getElement = function(element) {
|
|
131
|
+
var sorting = false;
|
|
132
|
+
|
|
133
|
+
function path (element) {
|
|
134
|
+
if (element === el) {
|
|
135
|
+
sorting = true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (! sorting) {
|
|
139
|
+
path(element.parentNode);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
path(element);
|
|
144
|
+
|
|
145
|
+
return sorting;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
149
|
+
if (! el.children[i].hasAttribute('draggable')) {
|
|
150
|
+
el.children[i].setAttribute('draggable', 'true');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return el;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const Tabs = function(children, { onchange, onload }) {
|
|
41
158
|
let self = this
|
|
42
159
|
|
|
160
|
+
// Event
|
|
161
|
+
let change = self.onchange;
|
|
162
|
+
self.onchange = null;
|
|
163
|
+
|
|
43
164
|
// Add new tab
|
|
44
165
|
let createButton;
|
|
45
166
|
|
|
@@ -77,7 +198,9 @@ if (! lemonade && typeof (require) === 'function') {
|
|
|
77
198
|
}
|
|
78
199
|
}
|
|
79
200
|
|
|
80
|
-
|
|
201
|
+
let props = ['title', 'selected', 'data-icon'];
|
|
202
|
+
|
|
203
|
+
const select = function(index) {
|
|
81
204
|
// Make sure the index is a number
|
|
82
205
|
index = parseInt(index);
|
|
83
206
|
// Do not select tabs that does not exist
|
|
@@ -93,19 +216,19 @@ if (! lemonade && typeof (require) === 'function') {
|
|
|
93
216
|
|
|
94
217
|
const init = function(selected) {
|
|
95
218
|
let tabs = [];
|
|
219
|
+
|
|
96
220
|
for (let i = 0; i < self.data.length; i++) {
|
|
97
221
|
// Extract meta information from the DOM
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
self.data[i]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
222
|
+
if (props) {
|
|
223
|
+
props.forEach((prop) => {
|
|
224
|
+
let short = prop.replace('data-', '');
|
|
225
|
+
if (! self.data[i][short]) {
|
|
226
|
+
let ret = self.data[i].el.getAttribute(prop);
|
|
227
|
+
if (ret != null) {
|
|
228
|
+
self.data[i][short] = ret;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
109
232
|
}
|
|
110
233
|
// Create tabs object
|
|
111
234
|
tabs[i] = {
|
|
@@ -115,30 +238,68 @@ if (! lemonade && typeof (require) === 'function') {
|
|
|
115
238
|
if (self.data[i].selected) {
|
|
116
239
|
selected = i;
|
|
117
240
|
}
|
|
241
|
+
if (self.data[i].icon) {
|
|
242
|
+
tabs[i].icon = self.data[i].icon;
|
|
243
|
+
}
|
|
118
244
|
|
|
119
|
-
self.root.appendChild(self.data[i].el)
|
|
245
|
+
self.root.appendChild(self.data[i].el);
|
|
120
246
|
}
|
|
247
|
+
|
|
121
248
|
// Create headers
|
|
122
249
|
self.tabs = tabs;
|
|
250
|
+
|
|
123
251
|
// Default selected
|
|
124
252
|
if (typeof(selected) !== 'undefined') {
|
|
125
253
|
self.selected = selected;
|
|
126
254
|
}
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
255
|
+
|
|
256
|
+
if (props) {
|
|
257
|
+
// Add create new tab button
|
|
258
|
+
if (createButton) {
|
|
259
|
+
self.headers.appendChild(createButton);
|
|
260
|
+
}
|
|
261
|
+
// Add sorting
|
|
262
|
+
sorting(self.el.firstChild.firstChild, {
|
|
263
|
+
ondrop: (el, fromIndex, toIndex) => {
|
|
264
|
+
// Remove the item from its original position
|
|
265
|
+
const [movedItem] = self.data.splice(fromIndex, 1);
|
|
266
|
+
// Insert it into the new position
|
|
267
|
+
self.data.splice(toIndex, 0, movedItem);
|
|
268
|
+
// Make sure correct order
|
|
269
|
+
for (let i = 0; i < self.data.length; i++) {
|
|
270
|
+
self.root.appendChild(self.data[i].el);
|
|
271
|
+
}
|
|
272
|
+
// Select new position
|
|
273
|
+
self.selected = toIndex;
|
|
274
|
+
// Dispatch event
|
|
275
|
+
Dispatch.call(self, self.onchangeposition, 'changeposition', {
|
|
276
|
+
instance: self,
|
|
277
|
+
fromIndex: fromIndex,
|
|
278
|
+
toIndex: toIndex,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
})
|
|
130
282
|
}
|
|
283
|
+
|
|
284
|
+
props = null;
|
|
131
285
|
}
|
|
132
286
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
287
|
+
const create = function() {
|
|
288
|
+
// Create a new item
|
|
289
|
+
self.create({ title: 'Untitled' }, null, true);
|
|
290
|
+
}
|
|
137
291
|
|
|
138
|
-
|
|
292
|
+
const open = function(e) {
|
|
293
|
+
if (e.target.tagName === 'LI') {
|
|
294
|
+
// Avoid select something already selected
|
|
295
|
+
let index = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
|
|
296
|
+
if (index !== self.selected) {
|
|
297
|
+
self.selected = index;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
139
300
|
}
|
|
140
301
|
|
|
141
|
-
|
|
302
|
+
const keydown = function(e, s) {
|
|
142
303
|
let index = null;
|
|
143
304
|
if (e.key === 'Enter') {
|
|
144
305
|
self.click(e, s);
|
|
@@ -160,14 +321,32 @@ if (! lemonade && typeof (require) === 'function') {
|
|
|
160
321
|
}
|
|
161
322
|
}
|
|
162
323
|
|
|
163
|
-
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
let index = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
|
|
167
|
-
if (index !== self.selected) {
|
|
168
|
-
self.selected = index;
|
|
169
|
-
}
|
|
324
|
+
onload(() => {
|
|
325
|
+
if (template) {
|
|
326
|
+
extract(self.root, self);
|
|
170
327
|
}
|
|
328
|
+
|
|
329
|
+
init(self.selected || 0);
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
onchange((property) => {
|
|
333
|
+
if (property === 'selected') {
|
|
334
|
+
select(self.selected);
|
|
335
|
+
|
|
336
|
+
Dispatch.call(self, self.onopen, 'open', {
|
|
337
|
+
instance: self,
|
|
338
|
+
selected: self.selected,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
Dispatch.call(self, change, 'change', {
|
|
342
|
+
instance: self,
|
|
343
|
+
value: self.selected,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
self.open = function (index) {
|
|
349
|
+
self.selected = index;
|
|
171
350
|
}
|
|
172
351
|
|
|
173
352
|
self.create = function(item, position, select) {
|
|
@@ -175,6 +354,17 @@ if (! lemonade && typeof (require) === 'function') {
|
|
|
175
354
|
if (typeof(item) !== 'object') {
|
|
176
355
|
console.error('Item must be an object');
|
|
177
356
|
} else {
|
|
357
|
+
|
|
358
|
+
let ret = Dispatch.call(self, self.onbeforecreate, 'beforecreate', {
|
|
359
|
+
instance: self,
|
|
360
|
+
item: item,
|
|
361
|
+
position: position,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
if (ret === false) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
|
|
178
368
|
// Create DOM
|
|
179
369
|
item.el = document.createElement('div');
|
|
180
370
|
// Create from content
|
|
@@ -198,28 +388,25 @@ if (! lemonade && typeof (require) === 'function') {
|
|
|
198
388
|
} else {
|
|
199
389
|
init(self.selected);
|
|
200
390
|
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
self.click = function() {
|
|
205
|
-
// Create a new item
|
|
206
|
-
self.create({ title: 'Untitled' }, null, true);
|
|
207
|
-
}
|
|
208
391
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
392
|
+
self.tabs.forEach(item => {
|
|
393
|
+
item.el.setAttribute('draggable', 'true');
|
|
394
|
+
})
|
|
212
395
|
|
|
213
|
-
Dispatch.call(self, '
|
|
396
|
+
Dispatch.call(self, self.oncreate, 'create', {
|
|
397
|
+
instance: self,
|
|
398
|
+
item: item,
|
|
399
|
+
position: position,
|
|
400
|
+
});
|
|
214
401
|
}
|
|
215
402
|
}
|
|
216
403
|
|
|
217
404
|
self.allowCreate = !! self.allowCreate;
|
|
218
405
|
|
|
219
|
-
return `<div class="lm-tabs" data-position="{{self.position}}" data-round="{{self.round}}">
|
|
406
|
+
return render => render`<div class="lm-tabs" data-position="{{self.position}}" data-round="{{self.round}}">
|
|
220
407
|
<div role="tabs" class="lm-tabs-headers">
|
|
221
|
-
<ul :ref="self.headers" :loop="self.tabs" :selected="self.selected" onclick="
|
|
222
|
-
<div data-visible="{{self.allowCreate}}" class="lm-tabs-insert-
|
|
408
|
+
<ul :ref="self.headers" :loop="self.tabs" :selected="self.selected" onclick="${open}" onkeydown="${keydown}" onfocusin="${open}"><li class="lm-tab" tabindex="0" role="tab" data-icon="{{self.icon}}">{{self.title}}</li></ul>
|
|
409
|
+
<div data-visible="{{self.allowCreate}}" class="lm-tabs-insert-button" role="insert-tab" onclick="${create}">add</div>
|
|
223
410
|
</div>
|
|
224
411
|
<div :ref="self.root" class="lm-tabs-content">${template}</div>
|
|
225
412
|
</div>`
|
package/dist/style.css
CHANGED
|
@@ -1,78 +1,104 @@
|
|
|
1
|
+
.lm-tabs[data-position="bottom"] {
|
|
2
|
+
display: flex;
|
|
3
|
+
justify-content: center;
|
|
4
|
+
flex-direction: column-reverse;
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
.lm-tabs .lm-tabs-content > div {
|
|
2
|
-
|
|
8
|
+
padding: 15px 5px;
|
|
3
9
|
}
|
|
4
10
|
|
|
5
11
|
.lm-tabs .lm-tabs-headers {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
width: 100%;
|
|
9
15
|
}
|
|
10
16
|
|
|
11
17
|
.lm-tabs[data-position="center"] .lm-tabs-headers {
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
justify-content: center;
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
.lm-tabs .lm-tabs-headers > ul {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
list-style-type: none;
|
|
24
|
+
display: flex;
|
|
25
|
+
margin: 0;
|
|
26
|
+
padding: 0;
|
|
27
|
+
align-items: center;
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
.lm-tabs .lm-tabs-headers > ul > li {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
user-select: none;
|
|
33
|
+
padding: 4px 24px;
|
|
34
|
+
border: 1px solid #ccc;
|
|
35
|
+
background-position: center;
|
|
36
|
+
transition: background 0.8s;
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
align-items: center;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
.lm-tabs .lm-tabs-headers > ul > li.selected {
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
background-color: #eee;
|
|
44
|
+
color: #000;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.lm-tabs .lm-tabs-headers > ul > li[data-icon]:before {
|
|
48
|
+
content: attr(data-icon);
|
|
49
|
+
font-family: "Material Symbols Outlined", "Material Icons", "FontAwesome";
|
|
50
|
+
font-size: 24px;
|
|
51
|
+
width: 24px;
|
|
52
|
+
height: 24px;
|
|
53
|
+
text-align: center;
|
|
54
|
+
color: var(--lm-icon-color, #777);
|
|
55
|
+
margin-bottom: 5px;
|
|
36
56
|
}
|
|
37
57
|
|
|
38
58
|
.lm-tabs[data-round="true"] .lm-tabs-headers > ul > li:first-of-type {
|
|
39
|
-
|
|
40
|
-
|
|
59
|
+
border-top-left-radius: 3px;
|
|
60
|
+
border-bottom-left-radius: 3px;
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
.lm-tabs[data-round="true"] .lm-tabs-headers > ul > li:last-of-type {
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
border-top-right-radius: 3px;
|
|
65
|
+
border-bottom-right-radius: 3px;
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
.lm-tabs .lm-tabs-headers > ul > li:not(:first-child) {
|
|
49
|
-
|
|
69
|
+
border-left: 1px solid transparent;
|
|
50
70
|
}
|
|
51
71
|
|
|
52
72
|
.lm-tabs .lm-tabs-headers > ul > li:hover {
|
|
53
|
-
|
|
73
|
+
background: #eee radial-gradient(circle, transparent 1%, #eee 1%) center/15000%;
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
.lm-tabs .lm-tabs-headers > ul > li:active {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
background-color: #ddd;
|
|
78
|
+
background-size: 100%;
|
|
79
|
+
transition: background 0s;
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
.lm-tabs .lm-tabs-content > div {
|
|
63
|
-
|
|
83
|
+
display: none;
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
.lm-tabs .lm-tabs-content > div.selected {
|
|
67
|
-
|
|
87
|
+
display: block;
|
|
68
88
|
}
|
|
69
89
|
|
|
70
|
-
.lm-tabs-insert-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
90
|
+
.lm-tabs-insert-button {
|
|
91
|
+
margin-left: 5px;
|
|
92
|
+
color: #555;
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
font-family: "Material Symbols Outlined", "Material Icons", "FontAwesome";
|
|
95
|
+
font-size: 24px;
|
|
96
|
+
width: 24px;
|
|
97
|
+
height: 24px;
|
|
98
|
+
text-align: center;
|
|
99
|
+
line-height: 24px;
|
|
74
100
|
}
|
|
75
101
|
|
|
76
|
-
.lm-tabs div[data-visible=
|
|
77
|
-
|
|
78
|
-
}
|
|
102
|
+
.lm-tabs div[data-visible="false"] {
|
|
103
|
+
display: none;
|
|
104
|
+
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@lemonadejs/tabs",
|
|
3
|
-
"title": "JavaScript Tabs",
|
|
4
|
-
"description": "LemonadeJS tabs is a JavaScript component to create tabs.",
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "Contact <contact@lemonadejs.net>",
|
|
7
|
-
"url": "https://lemonadejs.net"
|
|
8
|
-
},
|
|
9
|
-
"keywords": [
|
|
10
|
-
"javascript tabs",
|
|
11
|
-
"lemonadejs tabs",
|
|
12
|
-
"js tabs",
|
|
13
|
-
"tabs js"
|
|
14
|
-
],
|
|
15
|
-
"dependencies": {
|
|
16
|
-
"lemonadejs": "^5.
|
|
17
|
-
},
|
|
18
|
-
"main": "dist/index.js",
|
|
19
|
-
"version": "5.
|
|
20
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@lemonadejs/tabs",
|
|
3
|
+
"title": "JavaScript Tabs",
|
|
4
|
+
"description": "LemonadeJS tabs is a JavaScript component to create tabs.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Contact <contact@lemonadejs.net>",
|
|
7
|
+
"url": "https://lemonadejs.net"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"javascript tabs",
|
|
11
|
+
"lemonadejs tabs",
|
|
12
|
+
"js tabs",
|
|
13
|
+
"tabs js"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"lemonadejs": "^5.3.2"
|
|
17
|
+
},
|
|
18
|
+
"main": "dist/index.js",
|
|
19
|
+
"version": "5.8.0"
|
|
20
|
+
}
|