blob: 9cf52051a541b393e898f7eca0dd0c72647c167e [file] [log] [blame]
Leo Reppd162b2e2021-06-30 13:51:07 +02001/**
2 * Container for several containerItem style items. Functions like a mini menu with next, prev, add etc propagation,
3 * but no event handling or slider or lengthField. Supposed to be subelement to a (container)menu class item.
4 *
5 * @author Leo Repp
6 */
7
8"use strict";
9define([
10 'container/containeritem' //TODO why does this not work!!!
11], function (
12 defaultContainerItemClass
13) {
14
15 return {
16 /**
17 *
18 * @param {Array<object>} listOfContainerItems List of items that will be placed within the container and that realise some of the functions supplied in containeritem.js
19 * @param {object} params May contain attribute containerItemClass for a base class all containerItems build upon
20 * @returns The container object
21 */
22 create : function (listOfContainerItems, params) {
23 var obj = Object.create(this);
24 obj._init(listOfContainerItems, params);
25 return obj;
26 },
27
28 _init : function (listOfContainerItems, params){
29 if (params !== undefined && params["containerItemClass"] !== undefined){
30 this._containerItemClass = params["containerItemClass"];
31 } else {
32 this._containerItemClass = defaultContainerItemClass;
33 };
34 var el = document.createElement("ul");
35 el.style.outline = 0;
36 el.setAttribute('tabindex', 0);
37 el.classList.add('menu', 'container'); //container class allows for more stylesheet changes
38
39 this._el = el;
40 this._prefix = undefined; //required for re-setting the menus pointer correctly
41 // after having upgraded a new item scss style to the prefix object.
42
43 this.items = new Array();
Leo Repp050a7342021-10-25 11:05:32 +020044 //items are stored in the order they are added in. This includes the prefix.
Leo Reppd162b2e2021-06-30 13:51:07 +020045 if (listOfContainerItems !== undefined) {
46 for (let item of listOfContainerItems) {
47 this.addItem(item);
48 }
49 }
50
Leo Repp050a7342021-10-25 11:05:32 +020051 this.position = undefined; //undefined = not in container,
52 // 0 to length-1 = in container
53
54 this._prefixPosition = undefined; //Required so that switching
55 // to prefix by default is supported
56
Leo Reppd162b2e2021-06-30 13:51:07 +020057
58
59 //t._el.classList.add('visible'); //Done by containermenu
60
61
62 },
Leo Reppd162b2e2021-06-30 13:51:07 +020063
64 addItem : function (item) {
65 var cItem = this._containerItemClass.create().upgradeTo(item);
66 cItem._menu = this._menu; //if not set then undefined, but thats OK
67 this.items.push(cItem);
68 this._el.appendChild(cItem.element());
69 return cItem;
70 },
71
72 addMenu : function (menu) {
73 this._menu = menu;
74 if (this._prefix !== undefined) {
75 this._menu._prefix = this._prefix; // better than going via classList or something
76 };
77 for (let item of this.items) {
78 item._menu=menu;
79 }
80 },
81
82 addPrefix : function (prefix) {
83 prefix.isSelectable = function () {
84 return this.isSet(); //TODO check!
85 }
Leo Repp050a7342021-10-25 11:05:32 +020086 this._prefixPosition = this.items.length;
Leo Reppd162b2e2021-06-30 13:51:07 +020087 var prefItem = this.addItem(prefix);
88 this._prefix = prefItem;
89 if (this._menu !== undefined){
90 this._menu._prefix=prefItem;
91 }
92 },
93
94 /**
95 * Exit the container unconditionally. Required so that active returns the
96 * correct result. Called when the prefix or similar resets the currently visual
97 * field.
98 */
99 exit : function () {
100 if (this.position !== undefined) {
101 this.item().active(false);
102 this.position = undefined;
103 };
104 },
105
106 element : function () {
107 return this._el;
108 },
109
110 destroy : function () {
111 for (let item of this.items){
112 delete item['_menu'];
113 }
114 },
115
116 /**
117 * @returns whether an item within the container is active (by checking this.position)
118 */
119 active : function () {
120 return this.position !== undefined;
121 },
122
123 /**
124 * Getter for items
125 * @param {Integer} index [optional] Index of to select item. If left blank this.position.
126 * @returns item at location index
127 */
128 item : function (index) {
129 if (index === undefined) return this.items[this.position];
130 return this.items[index];
131 },
132
133 /**
Leo Repp050a7342021-10-25 11:05:32 +0200134 *
135 * Make the container active without having called prev or next.
136 * Occurs whenever the prefix makes the
137 * menus list empty while we had it selected.
138 * This potentially requires adjusting this.position.
139 */
140 makeActive : function () {
141 if (this.position === undefined) {
142 if (this._prefix.isSelectable()) {
143 this.position = this._prefixPosition; //make prefix active if it exists
144 this.item().active(true);
145 } else if (this.liveLength() > 0) {
146 this.position = 0;
147 this._prefix.active(false); // usually the menu makes the prefix active anyway.
148 this.item().active(true);
149 }
150 }
151 },
152
153 /**
Leo Reppd162b2e2021-06-30 13:51:07 +0200154 * Move on to the next item in container. Returns true if we then leave the container, false otherwise.
155 */
156 next : function() {
157 if (this.position !== undefined){
158 this.item().active(false);
159 this.position++;
160 } else {
161 this.position = 0;
162 };
163 if (this.position >= this.length()) {
164 this.position=undefined;
165 return true;
166 };
167 while (!this.item().isSelectable()) {
168 this.position++;
169 if (this.position >= this.length()) {
170 this.position=undefined;
171 return true;
172 }
173 };
174 this.item().active(true);
175 return false;
176 },
177
178 /**
179 * Move on to the previous item in container. Returns true if we then leave the container, false otherwise.
180 */
181 prev : function() {
182 if (this.position !== undefined){
183 this.item().active(false);
184 this.position = (this.position-1)
185 } else {
186 this.position = (this.items.length-1);
187 }
188 if (this.position<0) {
189 this.position=undefined;
190 return true;
191 }
192 while (!this.item().isSelectable()) {
193 this.position--;
194 if (this.position<0){
195 this.position=undefined;
196 return true;
197 };
198 };
199 this.item().active(true);
200 return false;
201 },
202
203 further : function () {
204 const item = this.item();
205 if (item["further"] !== undefined) {
206 item["further"].bind(item).apply();
207 };
208 },
209
210 enter : function (event) {
211 this.item().onclick(event);
212 },
213
214 chop : function () {
215 for (let item of this.items) {
216 item.chop();
217 }
218 },
219
220 add : function (letter) {
221 for (let item of this.items) {
222 item.add(letter);
223 }
224 },
225
226 length : function () {
227 return this.items.length;
228 },
229
230 /**
231 *
232 * @returns The number of items that are selectable. Is the actual length of the list.
233 */
234 liveLength : function () {
235 var ll = 0;
236 for (let item of this.items){
237 if (item.isSelectable()){
238 ll++;
239 }
240 }
241 return ll;
242 }
243
244};
Akronb7a005a2021-09-21 17:43:02 +0200245});