]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/layout/container/Dashboard.js
bump version to 7.0.0-4
[extjs.git] / extjs / classic / classic / src / layout / container / Dashboard.js
CommitLineData
947f0963
TL
1/**
2 * This layout extends `Ext.layout.container.Column` and adds splitters between adjacent
3 * columns allowing the user to resize them.
4 * @private
5 */
6Ext.define('Ext.layout.container.Dashboard', {
7 extend: 'Ext.layout.container.Column',
8 alias: 'layout.dashboard',
9
10 requires: [
11 'Ext.layout.container.ColumnSplitter'
12 ],
13
14 type: 'dashboard',
15
16 firstColumnCls: Ext.baseCSSPrefix + 'dashboard-column-first',
17
18 lastColumnCls: Ext.baseCSSPrefix + 'dashboard-column-last',
19
20 /*
21 * The geometry of a Column layout with splitters between respective items:
22 *
23 * 0 1 2 3 4
24 * +-----------------------------------------------+
25 * | +-----------+ || +---------+ || +-----------+ | \
26 * | | | || | | || | | | \
27 * | | | || | | || | | | \
28 * | | | || | | || | | | \
29 * | +-----------+ || | | || | | | row[0]
30 * | || | | || | | | /
31 * | || | | || | | | /
32 * | || | | || +-----------+ | /
33 * | || | | || | /
34 * | || +---------+ || |
35 * | +-------------------+ || +------------------+ | \
36 * | | | || | | | \
37 * | | | || | | | \
38 * | | | || | | | row[1]
39 * | | | || | | | /
40 * | | | || +------------------+ | /
41 * | +-------------------+ || | /
42 * +-----------------------------------------------+
43 * 6 7 8
44 *
45 * The splitter between 4 and 6 will be hidden but still present in the items. It is
46 * considered part of row[0].
47 */
48
49 getSplitterConfig: function() {
50 return {
51 xtype: 'columnsplitter'
52 };
53 },
54
55 /**
56 * @private
57 * Returns a filtered item list sans splitters
58 * @param items
59 * @return {Array|*}
60 */
61 getColumns: function(items) {
62 var array = Ext.Array;
63
64 return array.filter(array.from(items), function(item) {
65 return item.target && item.target.isSplitter !== true;
66 });
67 },
68
69 beginLayout: function(ownerContext) {
70 var me = this;
71
72 me.callParent([ownerContext]);
73
74 // We need to reset the heights of the splitters so that they don't influence the
75 // layout (mostly overflow management).
76 // eslint-disable-next-line vars-on-top, one-var
77 var childItems = ownerContext.childItems,
78 rows = (ownerContext.rows = []),
79 length = childItems.length,
80 totalWidth = 2,
81 columnTargets = 0,
82 lastRow = 0,
83 maxColumns = me.owner.getMaxColumns(),
84 child, i, prev, row, splitter, target, width;
85
86 for (i = 0; i < length; ++i) {
87 target = (child = childItems[i]).target;
88 splitter = target && target.isSplitter;
89 columnTargets += (splitter ? 0 : 1);
90 width = splitter ? 0 : target.columnWidth || 1;
91
92 if (totalWidth + width > 1 || (maxColumns && (columnTargets > maxColumns))) {
93 if (prev) {
94 // We have wrapped and we have a previous item which is a splitter by
95 // definition. We have previously seen that splitter and setHeight(0)
96 // on it. We now setHeight(0) to effectively hide it.
97 prev.orphan = 1;
98 prev.el.setHeight(0);
99 }
100
101 totalWidth = 0;
102 columnTargets = 1;
103
104 if (rows.length) {
105 // We have encountered a row break condition
106 // As this is floating layout, classify the current row
107 // before proceeding
108 lastRow = rows.length - 1;
109 me.syncFirstLast(
110 me.getColumns(rows[lastRow].items)
111 );
112 }
113
114 rows.push(row = {
115 index: rows.length,
116 items: [],
117 maxHeight: 0
118 });
119 }
120
121 totalWidth += width;
122 row.items.push(child);
123 child.row = row;
124 target.rowIndex = row.index;
125
126 if (splitter) {
127 child.el.setHeight(1);
128 }
129
130 prev = child;
131 }
132
133 if (rows.length) {
134 me.syncFirstLast(
135 me.getColumns(rows[rows.length - 1].items)
136 );
137 }
138 },
139
140 beforeLayoutCycle: function(ownerContext) {
141 var me = this,
142 items = me.owner.items;
143
144 // We need to do this in beforeLayoutCycle because this changes the child items
145 // and hence needs to be considered before recursing.
146 if (me.splitterGen !== items.generation) {
147 me.syncSplitters();
148
149 // The syncSplitters call will change items.generation so do this last.
150 me.splitterGen = items.generation;
151 }
152
153 me.callParent(arguments);
154 },
155
156 finishedLayout: function(ownerContext) {
157 var items = ownerContext.childItems,
158 len = items.length,
159 box, child, i, target, row;
160
161 this.callParent([ownerContext]);
162
163 for (i = 0; i < len; i += 2) {
164 target = (child = items[i]).target;
165 box = target.lastBox;
166 row = child.row;
167 row.maxHeight = Math.max(row.maxHeight, box.height);
168
169 // Put this on the component so that it gets saved (we use this to fix up
170 // columnWidth on restore)
171 target.width = box.width;
172 }
173
174 for (i = 0; i < len; i ++) {
175 target = (child = items[i]).target;
176
177 if (!child.orphan) {
178 if (i % 2 === 0) {
179 // Set min height of column to the max height
180 // So that it can increase on adding placeholder
181 target.el.setMinHeight(child.row.maxHeight);
182 }
183 else {
184 // Set height for splitter
185 target.el.setHeight(child.row.maxHeight);
186 }
187 }
188 else {
189 // since this is an orphan child, set its width to 0
190 target.el.setWidth(0);
191 }
192 }
193 },
194
195 /**
196 * This method synchronizes the splitters so that we have exactly one between each
197 * column.
198 * @private
199 */
200 syncSplitters: function() {
201 var me = this,
202 owner = me.owner,
203 items = owner.items.items,
204 index = items.length,
205 ok = true,
206 shouldBeSplitter = false,
207 item, splitter; // eslint-disable-line no-unused-vars
208
209 // Walk backwards over the items so that an insertion index is stable.
210 while (index-- > 0) {
211 item = items[index];
212
213 if (shouldBeSplitter) {
214 if (item.isSplitter) {
215 shouldBeSplitter = false;
216 }
217 else {
218 // An item is adjacent to an item, so inject a splitter beyond
219 // the current item to separate the columns. Keep shouldBeSplitter
220 // at true since we just encountered an item.
221 if (ok) {
222 ok = false;
223 owner.suspendLayouts();
224 }
225
226 splitter = owner.add(index + 1, me.getSplitterConfig());
227 }
228 }
229 else {
230 if (item.isSplitter) {
231 // A splitter is adjacent to a splitter so we remove this one. We
232 // leave shouldBeSplitter at false because the next thing we see
233 // should still not be a splitter.
234 if (ok) {
235 ok = false;
236 owner.suspendLayouts();
237 }
238
239 owner.remove(item);
240 }
241 else {
242 shouldBeSplitter = true;
243 }
244 }
245 }
246
247 // It is possible to exit the above with a splitter as the first item, but
248 // this is invalid so remove any such splitters.
249 while (items.length && (item = items[0]).isSplitter) {
250 if (ok) {
251 ok = false;
252 owner.suspendLayouts();
253 }
254
255 owner.remove(item);
256 }
257
258 if (!ok) {
259 owner.resumeLayouts();
260 }
261 },
262
263 syncFirstLast: function(items) {
264 var me = this,
265 firstCls = me.firstColumnCls,
266 lastCls = me.lastColumnCls,
267 len,
268 firstAndLast = [firstCls, lastCls],
269 i, item, last;
270
271 items = Ext.Array.from(items);
272 len = items.length;
273
274 for (i = 0; i < len; ++i) {
275 item = items[i].target;
276 last = (i === len - 1);
277
278 if (!i) { // if (first)
279 if (last) {
280 item.addCls(firstAndLast);
281 }
282 else {
283 item.addCls(firstCls);
284 item.removeCls(lastCls);
285 }
286 }
287 else if (last) {
288 item.addCls(lastCls);
289 item.removeCls(firstCls);
290 }
291 else {
292 item.removeCls(firstAndLast);
293 }
294 }
295 },
296
297 calculateItemSizeWithContent: function(availableWidth, contentWidth, items) {
298 var itemMarginWidth, itemContext,
299 splitterItemWidth = 0,
300 halfSplitterItemWidth = 0,
301 itemWidth, i,
302 len = items.length,
303 rowindex, rowLen;
304
305 availableWidth = (availableWidth < contentWidth) ? 0 : availableWidth;
306
307 for (i = 0; i < len; i += 2) {
308 itemContext = items[i];
309 rowLen = itemContext.row.items.length;
310 rowindex = itemContext.row.items.indexOf(itemContext);
311
312 itemMarginWidth = itemContext.marginInfo.width; // always set by above loop
313 itemWidth = itemContext.target.columnWidth;
314 itemWidth = Math.floor(itemWidth * availableWidth) - itemMarginWidth;
315
316 // Get the width of splitter item. We calculate the half value for width
317 // since splitter must form a part of both items between which it lies equally.
318 if (splitterItemWidth === 0 && (rowindex + 1 < rowLen)) {
319 splitterItemWidth = items[i + 1].getProp('width');
320 halfSplitterItemWidth = Math.ceil(splitterItemWidth / 2);
321 }
322
323 if (halfSplitterItemWidth) {
324 // if there exists a splitter to the right and this splitter
325 // is not the last item of this row, reduce the width of the
326 // column since splitter will take half width from the item
327 if (rowindex + 2 < rowLen) {
328 itemWidth -= halfSplitterItemWidth;
329 }
330
331 // if there exists a splitter to the left, reduce the width
332 // of the column since splitter will take half width from the item
333 if (rowindex > 0) {
334 itemWidth -= halfSplitterItemWidth;
335 }
336 }
337
338 itemWidth = itemContext.setWidth(itemWidth); // constrains to min/maxWidth
339 contentWidth += itemWidth + itemMarginWidth;
340 }
341
342 return contentWidth;
343 }
344});