@lemonadejs/tabs 2.2.0 → 2.3.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/dist/index.d.ts CHANGED
@@ -7,14 +7,18 @@ declare function Tabs(el: HTMLElement, options?: Tabs.Options): Tabs.Instance;
7
7
 
8
8
  declare namespace Tabs {
9
9
 
10
- interface Items {
10
+ interface Item {
11
+ // Reference to an external DOM
12
+ el: HTMLElement,
13
+ // Tab header
11
14
  title: string,
15
+ // HTML template
12
16
  content: string,
13
17
  }
14
18
 
15
19
  interface Options {
16
20
  /** Programmatically content */
17
- data?: Items[];
21
+ data?: Item[];
18
22
  /** Selected tab */
19
23
  selected?: number;
20
24
  /** Tabs position */
@@ -22,12 +26,16 @@ declare namespace Tabs {
22
26
  /** Activate round borders */
23
27
  round?: boolean;
24
28
  /** On open event */
25
- onopen?: (index: number) => void;
29
+ onopen?: (instance: object, index: number) => void;
30
+ /** Allow to create new tab button */
31
+ allowCreate?: boolean;
26
32
  }
27
33
 
28
34
  interface Instance {
35
+ /** Create a new option */
36
+ create: (item: Item, position?: number, select?: boolean) => void;
29
37
  /** Programmatically content */
30
- data?: Items[];
38
+ data: Item[];
31
39
  /** Selected tab */
32
40
  selected: number;
33
41
  /** Tabs position */
@@ -35,7 +43,7 @@ declare namespace Tabs {
35
43
  /** Activate round borders */
36
44
  round?: boolean;
37
45
  /** On open event */
38
- onopen?: (index: number) => void;
46
+ onopen?: (instance: object, index: number) => void;
39
47
  }
40
48
  }
41
49
 
package/dist/index.js CHANGED
@@ -17,76 +17,140 @@ if (! lemonade && typeof (require) === 'function') {
17
17
  }
18
18
  }
19
19
 
20
- const Tabs = function (html) {
20
+ const extract = function(root, self) {
21
+ if (! Array.isArray(self.data)) {
22
+ self.data = [];
23
+ }
24
+
25
+ if (root.tagName) {
26
+ for (let i = 0; i < root.children.length; i++) {
27
+ self.data.push({
28
+ el: root.children[i],
29
+ })
30
+ }
31
+ } else {
32
+ root.forEach((child) => {
33
+ self.data.push({
34
+ el: child.element,
35
+ })
36
+ });
37
+ }
38
+ }
39
+
40
+ const Tabs = function(children) {
21
41
  let self = this
22
- let elements = [];
23
42
 
43
+ // Add new tab
44
+ let createButton;
45
+
46
+ // Get the references from the root web component
47
+ let root;
24
48
  if (this.tagName) {
25
- // Remove elements from the DOM
26
- elements = removeElements(this);
49
+ root = this;
50
+ } else {
51
+ // References from LemonadeJS
52
+ if (typeof(children) === 'string') {
53
+ // Version 4
54
+ root = document.createElement('div');
55
+ root.innerHTML = children;
56
+ } else if (children && children.length) {
57
+ // Version 5
58
+ root = children;
59
+ }
60
+ }
61
+
62
+ if (root) {
63
+ extract(root, self);
27
64
  }
28
65
 
66
+ // Process the data
29
67
  if (self.data) {
30
68
  for (let i = 0; i < self.data.length; i++) {
31
- let d = document.createElement('div');
32
- d.title = self.data[i].title;
33
- d.innerHTML = self.data[i].content;
34
- elements.push(d);
69
+ if (self.data[i].el) {
70
+ if (! self.data[i].title) {
71
+ self.data[i].title = self.data[i].el.getAttribute('title');
72
+ }
73
+ if (! self.data[i].selected) {
74
+ self.data[i].selected = self.data[i].el.getAttribute('selected');
75
+ }
76
+ } else {
77
+ // Create element
78
+ self.data[i].el = document.createElement('div');
79
+ // Create from content
80
+ if (self.data[i].content) {
81
+ self.data[i].el.innerHTML = self.data[i].content;
82
+ }
83
+ }
35
84
  }
36
85
  }
37
86
 
38
- if (! html) {
39
- html = '';
40
- }
41
-
42
- self.tabs = [];
43
-
44
87
  const select = function (index) {
88
+ // Make sure the index is a number
45
89
  index = parseInt(index);
46
-
47
90
  // Do not select tabs that does not exist
48
- if (index >= 0 && index < self.tabs.length) {
49
- for (let i = 0; i < self.content.children.length; i++) {
91
+ if (index >= 0 && index < self.data.length) {
92
+ for (let i = 0; i < self.root.children.length; i++) {
50
93
  self.headers.children[i].classList.remove('selected');
51
- self.content.children[i].classList.remove('selected');
94
+ self.root.children[i].classList.remove('selected');
52
95
  }
53
96
  self.headers.children[index].classList.add('selected');
54
- self.content.children[index].classList.add('selected');
97
+ self.root.children[index].classList.add('selected');
55
98
  }
56
99
  }
57
100
 
58
- self.onload = function () {
59
- // Append elements to the container
60
- appendElements(self.el.children[1], elements);
101
+ const init = function(selected) {
102
+ let tabs = [];
103
+ for (let i = 0; i < self.data.length; i++) {
104
+ // Create tabs object
105
+ tabs[i] = {
106
+ title: self.data[i].title,
107
+ }
108
+ // Which one is selected by default
109
+ if (self.data[i].selected) {
110
+ selected = i;
111
+ }
61
112
 
62
- for (let i = 0; i < self.content.children.length; i++) {
63
- self.tabs.push({ title: self.content.children[i].title });
113
+ self.root.appendChild(self.data[i].el)
64
114
  }
65
- self.refresh('tabs');
66
-
67
- let index = 0;
68
- if (! isNaN(parseInt(self.selected))) {
69
- index = parseInt(self.selected);
115
+ // Create headers
116
+ self.tabs = tabs;
117
+ // Default selected
118
+ if (typeof(selected) !== 'undefined') {
119
+ self.selected = selected;
120
+ }
121
+ // Add create new tab button
122
+ if (createButton) {
123
+ self.headers.appendChild(createButton);
70
124
  }
71
- select(index);
72
125
  }
73
126
 
74
-
75
- self.onchange = function (property) {
76
- if (property === 'selected') {
77
- select(self.selected);
78
-
79
- Dispatch.call(self, 'onopen', self, self.selected);
80
- }
127
+ self.onload = function () {
128
+ init(self.selected || 0);
81
129
  }
82
130
 
83
- self.keydown = function(e) {
131
+ self.keydown = function(e, s) {
132
+ let index = null;
84
133
  if (e.key === 'Enter') {
85
- self.click(e);
134
+ self.click(e, s);
135
+ } else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
136
+ index = self.selected - 1;
137
+ if (index < 0) {
138
+ index = 0;
139
+ }
140
+ } else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
141
+ index = self.selected + 1;
142
+ if (index > self.tabs.length-1) {
143
+ index = self.tabs.length-1;
144
+ }
145
+ }
146
+
147
+ // Make selection
148
+ if (index !== null) {
149
+ self.tabs[index].el.focus();
86
150
  }
87
151
  }
88
152
 
89
- self.click = function (e) {
153
+ self.open = function (e) {
90
154
  if (e.target.tagName === 'LI') {
91
155
  // Avoid select something already selected
92
156
  let index = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
@@ -96,50 +160,78 @@ if (! lemonade && typeof (require) === 'function') {
96
160
  }
97
161
  }
98
162
 
99
- return `<div class="lm-tabs" data-position="{{self.position}}" data-round="{{self.round}}">
100
- <ul :ref="self.headers" :loop="self.tabs" :selected="self.selected" onclick="self.click" onkeydown="self.keydown" role="tabs"><li class="lm-tab-list-item" tabindex="0" role="tab">{{self.title}}</li></ul>
101
- <div :ref="self.content" class="lm-tabs-content">${html}</div>
102
- </div>`
103
- }
163
+ self.create = function(item, position, select) {
164
+ // Create element
165
+ if (typeof(item) !== 'object') {
166
+ console.error('Item must be an object');
167
+ } else {
168
+ // Create DOM
169
+ item.el = document.createElement('div');
170
+ // Create from content
171
+ if (item.content) {
172
+ item.el.innerHTML = item.content;
173
+ }
104
174
 
105
- const removeElements = function(root) {
106
- // Keep the DOM elements
107
- let elements = [];
108
- if (root) {
109
- while (root.firstChild) {
110
- elements.push(root.firstChild);
111
- root.firstChild.remove();
175
+ // Add the new item in the end
176
+ if (typeof(position) === 'undefined' || position === null) {
177
+ // Mew item
178
+ position = self.data.length;
179
+ // Add in the end
180
+ self.data.push(item);
181
+ } else {
182
+ self.data.splice(position, 0, item);
183
+ }
184
+ // New position
185
+ if (select) {
186
+ // Refresh
187
+ init(self.data.indexOf(item));
188
+ } else {
189
+ init(self.selected);
190
+ }
112
191
  }
113
192
  }
114
- return elements;
115
- }
116
193
 
117
- const appendElements = function(root, elements) {
118
- if (elements && elements.length) {
119
- while (elements[0]) {
120
- root.appendChild(elements.shift());
121
- }
194
+ self.click = function() {
195
+ // Create a new item
196
+ self.create({ title: 'Untitled' }, null, true);
122
197
  }
123
- }
124
198
 
125
- const Component = function (root, options) {
126
- if (typeof (root) === 'object') {
127
- // Remove elements from the DOM
128
- let elements = removeElements(root);
129
- // Create the modal
130
- let e = lemonade.render(Tabs, root, options);
131
- // Add elements to the container
132
- appendElements(e.children[1], elements);
199
+ self.onchange = function (property) {
200
+ if (property === 'selected') {
201
+ select(self.selected);
133
202
 
134
- return options;
135
- } else {
136
- return Tabs.call(this);
203
+ Dispatch.call(self, 'onopen', self, self.selected);
204
+ }
137
205
  }
206
+
207
+ self.allowCreate = !! self.allowCreate;
208
+
209
+ return `<div class="lm-tabs" data-position="{{self.position}}" data-round="{{self.round}}">
210
+ <div role="tabs" class="lm-tabs-headers">
211
+ <ul :ref="self.headers" :loop="self.tabs" :selected="self.selected" onclick="self.open" onkeydown="self.keydown" onfocusin="self.open"><li class="lm-tab" tabindex="0" role="tab">{{self.title}}</li></ul>
212
+ <div data-visible="{{self.allowCreate}}" class="lm-tabs-insert-tab material-icons" role="insert-tab" onclick="self.click">add</div>
213
+ </div>
214
+ <div :ref="self.root" class="lm-tabs-content"></div>
215
+ </div>`
138
216
  }
139
217
 
140
218
  lemonade.setComponents({ Tabs: Tabs });
141
219
 
142
220
  lemonade.createWebComponent('tabs', Tabs);
143
221
 
144
- return Component;
222
+ return function (root, options) {
223
+ if (typeof (root) === 'object') {
224
+ if (typeof(options) !== 'object') {
225
+ options = {};
226
+ }
227
+ // Extract DOM references
228
+ extract(root, options);
229
+ // Create the modal
230
+ lemonade.render(Tabs, root, options);
231
+ // Return self
232
+ return options;
233
+ } else {
234
+ return Tabs.call(this);
235
+ }
236
+ };
145
237
  })));
package/dist/style.css CHANGED
@@ -1,48 +1,59 @@
1
1
  .lm-tabs .lm-tabs-content > div {
2
- padding: 15px 5px;
2
+ padding: 6px;
3
3
  }
4
4
 
5
- .lm-tabs > ul {
5
+ .lm-tabs .lm-tabs-headers {
6
+ display: flex;
7
+ align-items: center;
8
+ width: 100%;
9
+ }
10
+
11
+ .lm-tabs[data-position="center"] .lm-tabs-headers {
12
+ margin: 0 auto;
13
+ justify-content: center;
14
+ }
15
+
16
+ .lm-tabs .lm-tabs-headers > ul {
6
17
  list-style-type: none;
7
18
  display: flex;
8
19
  margin: 0;
9
20
  padding: 0;
10
- width: 100%;
21
+ align-items: center;
11
22
  }
12
23
 
13
- .lm-tabs > ul > li {
24
+ .lm-tabs .lm-tabs-headers > ul > li {
14
25
  cursor: pointer;
15
26
  user-select: none;
16
- padding: 6px 24px;
27
+ padding: 4px 24px;
17
28
  border: 1px solid #ccc;
18
29
  background-position: center;
19
30
  transition: background 0.8s;
20
31
  }
21
32
 
22
- .lm-tabs > ul > li.selected {
33
+ .lm-tabs .lm-tabs-headers > ul > li.selected {
23
34
  background-color: #eee;
24
35
  color: #000;
25
36
  }
26
37
 
27
- .lm-tabs[data-round="true"] > ul > li:first-child {
38
+ .lm-tabs[data-round="true"] .lm-tabs-headers > ul > li:first-of-type {
28
39
  border-top-left-radius: 3px;
29
40
  border-bottom-left-radius: 3px;
30
41
  }
31
42
 
32
- .lm-tabs[data-round="true"] > ul > li:last-child {
43
+ .lm-tabs[data-round="true"] .lm-tabs-headers > ul > li:last-of-type {
33
44
  border-top-right-radius: 3px;
34
45
  border-bottom-right-radius: 3px;
35
46
  }
36
47
 
37
- .lm-tabs > ul > li:not(:first-child) {
38
- border-left: 0;
48
+ .lm-tabs .lm-tabs-headers > ul > li:not(:first-child) {
49
+ border-left: 1px solid transparent;
39
50
  }
40
51
 
41
- .lm-tabs > ul > li:hover {
52
+ .lm-tabs .lm-tabs-headers > ul > li:hover {
42
53
  background: #eee radial-gradient(circle, transparent 1%, #eee 1%) center/15000%;
43
54
  }
44
55
 
45
- .lm-tabs > ul > li:active {
56
+ .lm-tabs .lm-tabs-headers > ul > li:active {
46
57
  background-color: #ddd;
47
58
  background-size: 100%;
48
59
  transition: background 0s;
@@ -56,7 +67,12 @@
56
67
  display: block;
57
68
  }
58
69
 
59
- .lm-tabs[data-position="center"] > ul {
60
- margin: 0 auto;
61
- justify-content: center;
70
+ .lm-tabs-insert-tab {
71
+ margin-left: 5px;
72
+ color: #555;
73
+ cursor: pointer;
74
+ }
75
+
76
+ .lm-tabs div[data-visible='false'] {
77
+ display: none;
62
78
  }
package/package.json CHANGED
@@ -16,5 +16,5 @@
16
16
  "lemonadejs": "^4.3.3"
17
17
  },
18
18
  "main": "dist/index.js",
19
- "version": "2.2.0"
19
+ "version": "2.3.0"
20
20
  }