bm-core-ui 2.7.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/CHANGELOG.md +1624 -0
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/build/@types/index.d.ts +14078 -0
- package/build/BMCodeEditor/BMCodeEditor.js +707 -0
- package/build/BMCollectionView/BMCollectionView.js +5861 -0
- package/build/BMCollectionView/BMCollectionViewCell.js +688 -0
- package/build/BMCollectionView/BMCollectionViewFlowLayout.js +4467 -0
- package/build/BMCollectionView/BMCollectionViewLayout.js +830 -0
- package/build/BMCollectionView/BMCollectionViewLayoutAttributes.js +673 -0
- package/build/BMCollectionView/BMCollectionViewMasonryLayout.js +491 -0
- package/build/BMCollectionView/BMCollectionViewStackLayout.js +634 -0
- package/build/BMCollectionView/BMCollectionViewTileLayout.js +1121 -0
- package/build/BMCoreUI.css +2833 -0
- package/build/BMView/BMAttributedLabelView.js +310 -0
- package/build/BMView/BMLayoutConstraint_v2.5.js +1813 -0
- package/build/BMView/BMLayoutGuide.js +192 -0
- package/build/BMView/BMLayoutSizeClass.js +466 -0
- package/build/BMView/BMMenu.js +574 -0
- package/build/BMView/BMScrollView.js +247 -0
- package/build/BMView/BMTextField.js +512 -0
- package/build/BMView/BMTextFieldDelegate.js +71 -0
- package/build/BMView/BMView_v2.5.js +3572 -0
- package/build/BMView/BMViewport.js +221 -0
- package/build/BMViewLayoutEditor/BMLayoutEditor.js +6117 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorConstraintSettings.js +417 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorDelegate.js +59 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingCells.js +1653 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettings.js +1480 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingsComplexCells.js +431 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingsDelegate.js +46 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorVariablesController.js +460 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorViewGroupSettings.js +293 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorViewSettings.js +382 -0
- package/build/BMViewLayoutEditor/BMLayoutVariableProvider.js +202 -0
- package/build/BMWindow/BMConfirmationPopup.js +477 -0
- package/build/BMWindow/BMKeyboardShortcut.js +151 -0
- package/build/BMWindow/BMPopover/BMPopover.js +492 -0
- package/build/BMWindow/BMToolWindow.js +71 -0
- package/build/BMWindow/BMWindow.js +2000 -0
- package/build/Core/BMAnimationContext.js +1181 -0
- package/build/Core/BMColor.js +991 -0
- package/build/Core/BMCoreUI.js +470 -0
- package/build/Core/BMFunctionCollection.js +110 -0
- package/build/Core/BMIndexPath.js +165 -0
- package/build/Core/BMInset.js +138 -0
- package/build/Core/BMKeyPath.js +100 -0
- package/build/Core/BMPoint.js +291 -0
- package/build/Core/BMRect.js +556 -0
- package/build/Core/BMSize.js +137 -0
- package/build/iScroll/LICENSE +22 -0
- package/build/iScroll/iscroll-probe.js +2154 -0
- package/build/images/AlignBottom.png +0 -0
- package/build/images/AlignCenterX.png +0 -0
- package/build/images/AlignCenterY.png +0 -0
- package/build/images/AlignLeading.png +0 -0
- package/build/images/AlignTop.png +0 -0
- package/build/images/AlignTrailing.png +0 -0
- package/build/images/AllConstraints.png +0 -0
- package/build/images/BottomConstraint.png +0 -0
- package/build/images/CenterXConstraint.png +0 -0
- package/build/images/CenterYConstraint.png +0 -0
- package/build/images/CoreUI2.png +0 -0
- package/build/images/CoreUI2@2x.png +0 -0
- package/build/images/Desktop.png +0 -0
- package/build/images/DesktopMini.png +0 -0
- package/build/images/EqualHeight.png +0 -0
- package/build/images/EqualHorizontalSpacing.png +0 -0
- package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
- package/build/images/EqualVerticalSpacing.png +0 -0
- package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
- package/build/images/EqualWidth.png +0 -0
- package/build/images/HeightConstraint.png +0 -0
- package/build/images/InactiveConstraints.png +0 -0
- package/build/images/Layout.png +0 -0
- package/build/images/LayoutVariables.png +0 -0
- package/build/images/LeftConstraint.png +0 -0
- package/build/images/OwnConstraints.png +0 -0
- package/build/images/Phone.png +0 -0
- package/build/images/PhoneLandscape.png +0 -0
- package/build/images/PhoneLandscapeMini.png +0 -0
- package/build/images/PhoneMini.png +0 -0
- package/build/images/PhonePortrait.png +0 -0
- package/build/images/PhonePortraitMini.png +0 -0
- package/build/images/Properties.png +0 -0
- package/build/images/RightConstraint.png +0 -0
- package/build/images/SubviewConstraints.png +0 -0
- package/build/images/Tablet.png +0 -0
- package/build/images/TabletLandscape.png +0 -0
- package/build/images/TabletLandscapeMini.png +0 -0
- package/build/images/TabletMini.png +0 -0
- package/build/images/TabletPortrait.png +0 -0
- package/build/images/TabletPortraitMini.png +0 -0
- package/build/images/TopConstraint.png +0 -0
- package/build/images/WidthConstraint.png +0 -0
- package/build/index.js +40 -0
- package/lib/@types/BMCoreUI.min.d.ts +14078 -0
- package/lib/BMCoreUI.min.js +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {YES, NO} from '../Core/BMCoreUI'
|
|
4
|
+
import {BMSizeMake} from '../Core/BMSize'
|
|
5
|
+
import {BMRectMakeWithNodeFrame} from '../Core/BMRect'
|
|
6
|
+
import {BMIndexPathMakeWithRow} from '../Core/BMIndexPath'
|
|
7
|
+
import {__BMVelocityAnimate} from '../Core/BMAnimationContext'
|
|
8
|
+
import {BMMenuKind} from '../BMView/BMMenu'
|
|
9
|
+
import {BMCollectionViewCell} from '../BMCollectionView/BMCollectionViewCell'
|
|
10
|
+
import { _BMLayoutEditorSettingsView } from './BMLayoutEditorSettings'
|
|
11
|
+
|
|
12
|
+
// @type BMLayoutEditorVariableCell extends BMCollectionViewCell
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A collection view cell that represents a layout variable, used by the layout editor.
|
|
16
|
+
*/
|
|
17
|
+
export class BMLayoutEditorVariableCell extends BMCollectionViewCell {
|
|
18
|
+
|
|
19
|
+
initWithCollectionView(collectionView, args) {
|
|
20
|
+
super.initWithCollectionView(collectionView, args);
|
|
21
|
+
|
|
22
|
+
const node = args.node;
|
|
23
|
+
|
|
24
|
+
node.classList.add('BMLayoutEditorVariable');
|
|
25
|
+
|
|
26
|
+
const sizeClassBadge = document.createElement('div');
|
|
27
|
+
sizeClassBadge.className = 'BMLayoutEditorDetailsItemBadgeIcon';
|
|
28
|
+
this.sizeClassBadge = sizeClassBadge;
|
|
29
|
+
node.appendChild(sizeClassBadge);
|
|
30
|
+
|
|
31
|
+
const name = document.createElement('input');
|
|
32
|
+
name.type = 'text';
|
|
33
|
+
name.className = 'BMWindowInput BMLayoutEditorVariableName';
|
|
34
|
+
this.variableName = name;
|
|
35
|
+
node.appendChild(name);
|
|
36
|
+
this._initNameEditor();
|
|
37
|
+
|
|
38
|
+
const value = document.createElement('input');
|
|
39
|
+
value.type = 'number';
|
|
40
|
+
value.className = 'BMWindowInput BMLayoutEditorVariableValue';
|
|
41
|
+
this.variableValue = value;
|
|
42
|
+
node.appendChild(value);
|
|
43
|
+
this._initValueEditor();
|
|
44
|
+
|
|
45
|
+
const addVariationButton = document.createElement('div');
|
|
46
|
+
addVariationButton.className = 'BMWindowToolbarButton BMLayoutEditorToolbarButton BMLayoutEditorVariableButton BMLayoutEditorAddVariationButton';
|
|
47
|
+
addVariationButton.innerHTML = '<i class="material-icons">add_circle</i>';
|
|
48
|
+
node.appendChild(addVariationButton);
|
|
49
|
+
this.addVariationButton = addVariationButton;
|
|
50
|
+
this._initAddVariationButton();
|
|
51
|
+
|
|
52
|
+
const removeVariationButton = document.createElement('div');
|
|
53
|
+
removeVariationButton.className = 'BMWindowToolbarButton BMLayoutEditorToolbarButton BMLayoutEditorVariableButton BMLayoutEditorRemoveVariationButton';
|
|
54
|
+
removeVariationButton.innerHTML = '<i class="material-icons">remove_circle</i>';
|
|
55
|
+
node.appendChild(removeVariationButton);
|
|
56
|
+
this.removeVariationButton = removeVariationButton;
|
|
57
|
+
this._initRemoveVariationButton();
|
|
58
|
+
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Initializes the action that occurs upon clicking on the add variation button.
|
|
64
|
+
*/
|
|
65
|
+
_initAddVariationButton() {
|
|
66
|
+
this.addVariationButton.addEventListener('click', event => {
|
|
67
|
+
// TODO check if a variation already exists for this size class; ideally filter the menu
|
|
68
|
+
this.editor._showSizeClassMenuAtPoint(BMRectMakeWithNodeFrame(this.addVariationButton).center, {kind: BMMenuKind.Menu, action: sizeClass => {
|
|
69
|
+
// If this variation already exists, do nothing
|
|
70
|
+
const variables = this.collectionView.dataSet.variables;
|
|
71
|
+
const variable = variables.find(v => {
|
|
72
|
+
return v.name == this.indexPath.object.name && sizeClass.isEqualToSizeClass(v.sizeClass);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (variable) return;
|
|
76
|
+
|
|
77
|
+
// Update the UI with the new value
|
|
78
|
+
this.collectionView.dataSet.beginUpdates();
|
|
79
|
+
this.collectionView.dataSet.variables.splice(this.indexPath.row + 1, 0, {name: this.indexPath.object.name, sizeClass, value: this.indexPath.object.value});
|
|
80
|
+
const newRow = this.indexPath.row + 1;
|
|
81
|
+
this.collectionView.dataSet.commitUpdatesWithCompletionHandler(() => {
|
|
82
|
+
// After the animation finishes, focus the new property's name
|
|
83
|
+
var newVariableIndexPath = this.collectionView.indexPathForObjectAtRow(newRow, {inSectionAtIndex: 0});
|
|
84
|
+
var newVariableCell = this.collectionView.cellAtIndexPath(newVariableIndexPath);
|
|
85
|
+
|
|
86
|
+
if (newVariableCell) {
|
|
87
|
+
newVariableCell.node.querySelector('.BMLayoutEditorVariableValue').focus();
|
|
88
|
+
|
|
89
|
+
this.collectionView.scrollToCellAtIndexPath(newVariableIndexPath, {animated: YES});
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// If the newly created property is not in view, its cell must be created before it can be focused
|
|
93
|
+
newVariableCell = propertiesCollection.retainCellForIndexPath(newVariableIndexPath);
|
|
94
|
+
newVariableCell.node.querySelector('.BMLayoutEditorVariableValue').focus();
|
|
95
|
+
|
|
96
|
+
// Because any input acquiring focus will cause its owning cell to be retained, the cell may be released from here
|
|
97
|
+
newVariableCell.release();
|
|
98
|
+
|
|
99
|
+
this.collectionView.scrollToCellAtIndexPath(newVariableIndexPath, {animated: YES});
|
|
100
|
+
}
|
|
101
|
+
});;
|
|
102
|
+
|
|
103
|
+
// Push this update to the backing variable provider
|
|
104
|
+
this.editor.layoutVariableProvider.setLayoutVariableValue(this.indexPath.object.value, {named: this.indexPath.object.name, inSizeClass: sizeClass});
|
|
105
|
+
}});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Initializes the action that ocdurs upon clicking on the remove variation or remove variable button.
|
|
111
|
+
*/
|
|
112
|
+
_initRemoveVariationButton() {
|
|
113
|
+
this.removeVariationButton.addEventListener('click', event => {
|
|
114
|
+
const variables = this.collectionView.dataSet.variables;
|
|
115
|
+
// This button behaves differently depending on whether it is pressed on a variation or a variable definition
|
|
116
|
+
// For variations, this should just remove the variation
|
|
117
|
+
if (this.indexPath.object.sizeClass) {
|
|
118
|
+
// Update the UI
|
|
119
|
+
this.collectionView.dataSet.beginUpdates();
|
|
120
|
+
variables.splice(this.indexPath.row, 1);
|
|
121
|
+
this.collectionView.dataSet.commitUpdates();
|
|
122
|
+
|
|
123
|
+
// And unregister the variation from the variable provider
|
|
124
|
+
this.editor.layoutVariableProvider.removeVariationForLayoutVariableNamed(this.indexPath.object.name, {inSizeClass: this.indexPath.object.sizeClass});
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// For variable definitions, this should unregister all size class variations and also remove the variable itself
|
|
128
|
+
// TODO: Verify that the variable is not used referened and provide a warning if it is
|
|
129
|
+
this.collectionView.dataSet.beginUpdates();
|
|
130
|
+
for (let i = 0; i < variables.length; i++) {
|
|
131
|
+
const variable = variables[i];
|
|
132
|
+
if (variable.name == this.indexPath.object.name) {
|
|
133
|
+
// Remove the variation or variable from the provider
|
|
134
|
+
if (variable.sizeClass) {
|
|
135
|
+
this.editor.layoutVariableProvider.removeVariationForLayoutVariableNamed(variable.name, {inSizeClass: variable.sizeClass});
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
this.editor.layoutVariableProvider.unregisterLayoutVariableNamed(variable.name);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Update the UI's backing data set
|
|
142
|
+
variables.splice(i, 1);
|
|
143
|
+
i--;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.collectionView.dataSet.commitUpdates();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Initializes the name editor behaviours.
|
|
154
|
+
*/
|
|
155
|
+
_initNameEditor() {
|
|
156
|
+
let initialName;
|
|
157
|
+
|
|
158
|
+
this.variableName.addEventListener('focus', async event => {
|
|
159
|
+
initialName = this._variable.name;
|
|
160
|
+
this.variableName.select();
|
|
161
|
+
this.retain();
|
|
162
|
+
const fn = event => event.preventDefault();
|
|
163
|
+
this.variableName.addEventListener('mouseup', fn);
|
|
164
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
165
|
+
this.variableName.removeEventListener('mouseup', fn);
|
|
166
|
+
});
|
|
167
|
+
this.variableName.addEventListener('blur', async event => {
|
|
168
|
+
let newName = this.variableName.value.trim();
|
|
169
|
+
if (newName != initialName) {
|
|
170
|
+
// Check that this new name is not used by any other variable
|
|
171
|
+
let hasNewName = NO;
|
|
172
|
+
for (const variable of this.collectionView.dataSet.variables) {
|
|
173
|
+
if (variable.name == newName) {
|
|
174
|
+
hasNewName = YES;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!hasNewName) {
|
|
180
|
+
// Instruct the variable provider to rename the layout variable
|
|
181
|
+
this.editor.layoutVariableProvider.renameLayoutVariableNamed(initialName, {toName: newName});
|
|
182
|
+
|
|
183
|
+
// Also update the data set item backing the UI
|
|
184
|
+
for (const variable of this.collectionView.dataSet.variables) {
|
|
185
|
+
if (variable.name == initialName) {
|
|
186
|
+
variable.name = newName;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// If there are naming collisions, restore the initial name
|
|
192
|
+
this.variableName.value = initialName;
|
|
193
|
+
|
|
194
|
+
await __BMVelocityAnimate(this.variableName, {translateX: '20px'}, {duration: 50, ease: 'easeOutQuad'});
|
|
195
|
+
await __BMVelocityAnimate(this.variableName, {translateX: '-20px'}, {duration: 100, ease: 'easeInOutQuad'});
|
|
196
|
+
__BMVelocityAnimate(this.variableName, {translateX: '0px'}, {duration: 50, ease: 'easeInQuad'})
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
this.release();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
_initValueEditor() {
|
|
204
|
+
this.variableValue.addEventListener('focus', async event => {
|
|
205
|
+
this.variableValue.select();
|
|
206
|
+
this.retain();
|
|
207
|
+
const fn = event => event.preventDefault();
|
|
208
|
+
this.variableValue.addEventListener('mouseup', fn);
|
|
209
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
210
|
+
this.variableValue.removeEventListener('mouseup', fn);
|
|
211
|
+
});
|
|
212
|
+
this.variableValue.addEventListener('input', event => {
|
|
213
|
+
let newValue = parseFloat(this.variableValue.value.trim());
|
|
214
|
+
if (!isNaN(newValue)) {
|
|
215
|
+
// Update the local value of the variable
|
|
216
|
+
this._variable.value = newValue;
|
|
217
|
+
// Instruct the provider to update the value as well
|
|
218
|
+
if (this._variable.sizeClass) {
|
|
219
|
+
this.editor.layoutVariableProvider.setLayoutVariableValue(newValue, {named: this._variable.name, inSizeClass: this._variable.sizeClass});
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
this.editor.layoutVariableProvider.registerLayoutVariableNamed(this._variable.name, {withValue: newValue});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
this.variableValue.addEventListener('blur', async event => this.release());
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The represented layout variable variation.
|
|
231
|
+
*/
|
|
232
|
+
get variable() { // <BMLayoutVariableVariation>
|
|
233
|
+
return this._variable;
|
|
234
|
+
}
|
|
235
|
+
set variable(variable) {
|
|
236
|
+
this._variable = variable;
|
|
237
|
+
|
|
238
|
+
if (variable.sizeClass) {
|
|
239
|
+
this.addVariationButton.style.visibility = 'hidden';
|
|
240
|
+
this.sizeClassBadge.style.display = 'block';
|
|
241
|
+
this.variableName.style.display = 'none';
|
|
242
|
+
|
|
243
|
+
// TODO: Optimize this
|
|
244
|
+
const badge = this.editor.settingsBadgeForSizeClass(variable.sizeClass);
|
|
245
|
+
this.sizeClassBadge.className = badge.className;
|
|
246
|
+
this.sizeClassBadge.innerText = badge.innerText;
|
|
247
|
+
|
|
248
|
+
this.node.classList.remove('BMLayoutEditorVariableMain');
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
this.addVariationButton.style.visibility = 'visible';
|
|
252
|
+
this.sizeClassBadge.style.display = 'none';
|
|
253
|
+
this.variableName.style.display = 'block';
|
|
254
|
+
|
|
255
|
+
this.node.classList.add('BMLayoutEditorVariableMain');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
this.variableValue.value = variable.value;
|
|
259
|
+
this.variableName.value = variable.name;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// @endtype
|
|
265
|
+
|
|
266
|
+
// @type BMLayoutEditorEmptyVariablesCell extends BMCollectionViewCell
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* A collection view cell that displays a message to the user when no layout variables are displayed.
|
|
270
|
+
*/
|
|
271
|
+
export class BMLayoutEditorEmptyVariablesCell extends BMCollectionViewCell {
|
|
272
|
+
initWithCollectionView(collectionView, args) {
|
|
273
|
+
super.initWithCollectionView(collectionView, args);
|
|
274
|
+
|
|
275
|
+
const layoutVariablesAvailable = collectionView.dataSet.provider.canUseLayoutVariables();
|
|
276
|
+
|
|
277
|
+
const title = document.createElement('div');
|
|
278
|
+
title.innerText = layoutVariablesAvailable ? 'No Layout Variables Defined' : 'Layout Variables Unavailable';
|
|
279
|
+
title.className = 'BMWindowLabel BMLayoutEditorMessageTitle';
|
|
280
|
+
this.node.appendChild(title);
|
|
281
|
+
|
|
282
|
+
const description = document.createElement('div');
|
|
283
|
+
description.innerText = layoutVariablesAvailable ? 'Use the "Add Variable" button below to add a layout variable.' : collectionView.dataSet.provider.unavailableLayoutVariablesUserLabel();
|
|
284
|
+
description.className = 'BMWindowLabel BMLayoutEditorMessageDescription';
|
|
285
|
+
this.node.appendChild(description);
|
|
286
|
+
|
|
287
|
+
this.node.classList.add('BMLayoutEditorEmptyVariableMessage');
|
|
288
|
+
|
|
289
|
+
return this;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// @endtype
|
|
294
|
+
|
|
295
|
+
// @type BMLayoutEditorVariablesDataSet implements BMCollectionViewDataSet, BMCollectionViewDelegate
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* A data set object that manages the display of layout variables and their variations in a layout editor.
|
|
299
|
+
*/
|
|
300
|
+
export class BMLayoutEditorVariablesDataSet {
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Initializes this data set with the given layout variable provider object.
|
|
304
|
+
* @param provider <BMLayoutVariableProvider> The layout variable provider to use.
|
|
305
|
+
* {
|
|
306
|
+
* @param collectionView <BMCollectionView> The collection view using this data set.
|
|
307
|
+
* @param editor <BMLayoutEditor> The layout editor using this data set.
|
|
308
|
+
* }
|
|
309
|
+
* @return <BMLayoutEditorVariablesDataSet> This data set object.
|
|
310
|
+
*/
|
|
311
|
+
initWithLayoutVariableProvider(provider, {collectionView, editor}) {
|
|
312
|
+
this.provider = provider;
|
|
313
|
+
this.variables = [];
|
|
314
|
+
|
|
315
|
+
// Build the flat list of layout variables which can be used by collection view
|
|
316
|
+
const declaredVariables = provider.layoutVariables;
|
|
317
|
+
for (const variable in declaredVariables) {
|
|
318
|
+
this.variables.push({name: variable, value: declaredVariables[variable]});
|
|
319
|
+
|
|
320
|
+
const variations = provider.variationsForLayoutVariableNamed(variable);
|
|
321
|
+
for (const variation of variations) {
|
|
322
|
+
this.variables.push(variation);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.collectionView = collectionView;
|
|
327
|
+
this.editor = editor;
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
numberOfSections() {
|
|
332
|
+
return Math.min(this.variables.length, 1);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
numberOfObjectsInSectionAtIndex(index) {
|
|
336
|
+
return this.variables.length;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
indexPathForObjectAtRow(row) {
|
|
340
|
+
return BMIndexPathMakeWithRow(row, {section: 0, forObject: this.variables[row]});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
indexPathForObject(object) {
|
|
344
|
+
for (let i = 0; i < this.variables.length; i++) {
|
|
345
|
+
const variable = this.variables[i];
|
|
346
|
+
|
|
347
|
+
if (variable.name == object.name && variable.sizeClass == object.sizeClass) {
|
|
348
|
+
return BMIndexPathMakeWithRow(i, {section: 0, object: variable});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
cellForItemAtIndexPath(indexPath) {
|
|
354
|
+
const cell = this.collectionView.dequeueCellForReuseIdentifier('Variable');
|
|
355
|
+
cell.editor = this.editor;
|
|
356
|
+
cell.variable = indexPath.object;
|
|
357
|
+
return cell;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
cellForSupplementaryViewWithIdentifier(identifier, args) {
|
|
361
|
+
return this.collectionView.dequeueCellForSupplementaryViewWithIdentifier(identifier);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
beginUpdates() {
|
|
365
|
+
this._oldData = this.variables.slice();
|
|
366
|
+
this._newData = this.variables;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
useOldData(use) {
|
|
370
|
+
if (use) {
|
|
371
|
+
this.variables = this._oldData;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
this.variables = this._newData;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
isUsingOldData() {
|
|
379
|
+
return this.variables === this._newData;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
commitUpdates() {
|
|
383
|
+
return this.commitUpdatesWithCompletionHandler();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
commitUpdatesWithCompletionHandler(handler) {
|
|
387
|
+
this.collectionView.updateEntireDataAnimated(YES, {completionHandler: handler});
|
|
388
|
+
this.variables = this._newData;
|
|
389
|
+
this._oldData = undefined;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
addVariable() {
|
|
393
|
+
this.beginUpdates();
|
|
394
|
+
|
|
395
|
+
let name = 'new variable';
|
|
396
|
+
let i = 0;
|
|
397
|
+
|
|
398
|
+
// Find a unique name for this new layout variable
|
|
399
|
+
while (YES) {
|
|
400
|
+
let hasName = NO;
|
|
401
|
+
|
|
402
|
+
for (var j = 0; j < this.variables.length; j++) {
|
|
403
|
+
if (this.variables[j].name == name) {
|
|
404
|
+
hasName = YES;
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (!hasName) {
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
i++;
|
|
414
|
+
name = 'new variable ' + i;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Add it to the UI and to the provider
|
|
419
|
+
this.variables.push({name: name, value: 0});
|
|
420
|
+
this.provider.registerLayoutVariableNamed(name, {withValue: 0});
|
|
421
|
+
|
|
422
|
+
this.commitUpdatesWithCompletionHandler(() => {
|
|
423
|
+
// After the animation finishes, focus the new property's name
|
|
424
|
+
var newVariableIndexPath = this.indexPathForObjectAtRow(this.variables.length - 1, {inSectionAtIndex: 0});
|
|
425
|
+
var newVariableCell = this.collectionView.cellAtIndexPath(newVariableIndexPath);
|
|
426
|
+
|
|
427
|
+
if (newVariableCell) {
|
|
428
|
+
newVariableCell.node.querySelector('.BMLayoutEditorVariableName').focus();
|
|
429
|
+
|
|
430
|
+
this.collectionView.scrollToCellAtIndexPath(newVariableIndexPath, {animated: YES});
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
// If the newly created property is not in view, its cell must be created before it can be focused
|
|
434
|
+
newVariableCell = this.collectionView.retainCellForIndexPath(newVariableIndexPath);
|
|
435
|
+
newVariableCell.node.querySelector('.BMLayoutEditorVariableName').focus();
|
|
436
|
+
|
|
437
|
+
// Because any input acquiring focus will cause its owning cell to be retained, the cell may be released from here
|
|
438
|
+
newVariableCell.release();
|
|
439
|
+
|
|
440
|
+
this.collectionView.scrollToCellAtIndexPath(newVariableIndexPath, {animated: YES});
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
collectionViewCanSelectCellAtIndexPath() {
|
|
446
|
+
return NO;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
collectionViewSizeForCellAtIndexPath(collectionView, indexPath) {
|
|
450
|
+
if (!indexPath.object.sizeClass) {
|
|
451
|
+
return BMSizeMake(1, 48);
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
return BMSizeMake(1, 32);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// @endtype
|