solara 0.4.0 → 0.6.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/solara/lib/.DS_Store +0 -0
  3. data/solara/lib/core/.DS_Store +0 -0
  4. data/solara/lib/core/brands/brand_onboarder.rb +1 -1
  5. data/solara/lib/core/brands/brand_switcher.rb +92 -1
  6. data/solara/lib/core/dashboard/brand/BrandDetail.js +34 -2
  7. data/solara/lib/core/dashboard/brand/BrandDetailController.js +27 -234
  8. data/solara/lib/core/dashboard/brand/BrandDetailModel.js +14 -5
  9. data/solara/lib/core/dashboard/brand/BrandDetailView.js +16 -200
  10. data/solara/lib/core/dashboard/brand/SectionsFormManager.js +293 -0
  11. data/solara/lib/core/dashboard/brand/brand.html +223 -174
  12. data/solara/lib/core/dashboard/brand/source/BrandLocalSource.js +2 -5
  13. data/solara/lib/core/dashboard/brand/source/BrandRemoteSource.js +36 -133
  14. data/solara/lib/core/dashboard/brands/Brands.js +31 -0
  15. data/solara/lib/core/dashboard/brands/BrandsController.js +0 -5
  16. data/solara/lib/core/dashboard/brands/BrandsView.js +2 -2
  17. data/solara/lib/core/dashboard/brands/brands.html +71 -52
  18. data/solara/lib/core/dashboard/component/AliasesBottomSheet.js +6 -6
  19. data/solara/lib/core/dashboard/component/BrandOptionsBottomSheet.js +4 -4
  20. data/solara/lib/core/dashboard/component/ConfirmationDialog.js +15 -10
  21. data/solara/lib/core/dashboard/component/EditJsonSheet.js +160 -0
  22. data/solara/lib/core/dashboard/component/MessageBottomSheet.js +5 -5
  23. data/solara/lib/core/dashboard/component/OnboardBrandBottomSheet.js +9 -3
  24. data/solara/lib/core/dashboard/handler/base_handler.rb +1 -0
  25. data/solara/lib/core/dashboard/handler/edit_section_handler.rb +1 -5
  26. data/solara/lib/core/dashboard/handler/onboard_brand_handler.rb +0 -15
  27. data/solara/lib/core/doctor/schema/brand_configurations.json +0 -8
  28. data/solara/lib/core/doctor/schema/platform/global/resources_manifest.json +30 -0
  29. data/solara/lib/core/doctor/schema/platform/json_manifest.json +39 -0
  30. data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +35 -1
  31. data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +30 -1
  32. data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +35 -1
  33. data/solara/lib/core/doctor/validator/template/template_validator.rb +9 -9
  34. data/solara/lib/core/scripts/brand_config_manager.rb +1 -1
  35. data/solara/lib/core/scripts/brand_configurations_manager.rb +41 -0
  36. data/solara/lib/core/scripts/code_generator.rb +342 -118
  37. data/solara/lib/core/scripts/file_manager.rb +11 -15
  38. data/solara/lib/core/scripts/file_path.rb +21 -1
  39. data/solara/lib/core/scripts/gitignore_manager.rb +12 -6
  40. data/solara/lib/core/scripts/json_manifest_processor.rb +136 -0
  41. data/solara/lib/core/scripts/platform/ios/infoplist_string_catalog_manager.rb +11 -1
  42. data/solara/lib/core/scripts/resource_manifest_processor.rb +151 -0
  43. data/solara/lib/core/scripts/solara_status_manager.rb +1 -1
  44. data/solara/lib/core/scripts/theme_generator.rb +21 -242
  45. data/solara/lib/core/solara_configurator.rb +1 -1
  46. data/solara/lib/core/template/brands/global/resources_manifest.json +10 -0
  47. data/solara/lib/core/template/brands/json/Json-Manifest.md +59 -0
  48. data/solara/lib/core/template/brands/json/json_manifest.json +16 -0
  49. data/solara/lib/core/template/brands/shared/theme.json +213 -29
  50. data/solara/lib/core/template/config/android_template_config.json +50 -0
  51. data/solara/lib/core/template/config/flutter_template_config.json +35 -0
  52. data/solara/lib/core/template/config/ios_template_config.json +50 -0
  53. data/solara/lib/core/template/configurations.json +46 -0
  54. data/solara/lib/core/template/project_template_generator.rb +2 -0
  55. data/solara/lib/solara/version.rb +1 -1
  56. data/solara/lib/solara.rb +19 -0
  57. data/solara/lib/solara_manager.rb +21 -13
  58. metadata +13 -4
  59. data/solara/lib/core/dashboard/component/AddFieldSheet.js +0 -175
  60. data/solara/lib/core/dashboard/handler/brand_configurations_manager.rb +0 -73
@@ -1,6 +1,6 @@
1
1
  import {DataSource} from './BrandDetailModel.js';
2
+ import SectionsFormManager from './SectionsFormManager.js';
2
3
  import '../component/OnboardBrandBottomSheet.js';
3
- import '../component/AddFieldSheet.js';
4
4
  import '../component/ConfirmationDialog.js';
5
5
  import '../component/MessageBottomSheet.js';
6
6
 
@@ -15,7 +15,6 @@ class BrandDetailView {
15
15
  this.addBrandContainer = document.getElementById('add-brand-container');
16
16
 
17
17
  this.sectionsContainer = document.getElementById('sections');
18
- this.addFieldSheet = document.getElementById('addFieldSheet');
19
18
  this.confirmationDialog = document.getElementById('confirmationDialog');
20
19
  this.messageBottomSheet = document.getElementById('messageBottomSheet');
21
20
 
@@ -24,8 +23,9 @@ class BrandDetailView {
24
23
  this.addNewBrandBtn = document.getElementById('newBrandBtn');
25
24
  this.exportBrandBtn = document.getElementById('exportBrandBtn');
26
25
  this.allBrandsButton = document.getElementById('allBrandsButton');
27
-
26
+ this.syncBrandButton = document.getElementById('syncBrandButton');
28
27
  this.onboardSheet = document.getElementById('onboardBottomSheet');
28
+ this.sectionsFormManager = new SectionsFormManager();
29
29
 
30
30
  this.initializeApp();
31
31
  }
@@ -67,199 +67,12 @@ class BrandDetailView {
67
67
  }
68
68
  }
69
69
 
70
- createSection(key, name, inputType) {
71
- const section = document.createElement('div');
72
- section.className = 'section';
73
-
74
- section.dataset.key = key
75
- section.dataset.name = name
76
- section.dataset.inputType = inputType
77
-
78
- const titleContainer = document.createElement('div');
79
- titleContainer.className = 'section-title-container';
80
-
81
- const title = document.createElement('h2');
82
- title.textContent = name;
83
- titleContainer.appendChild(title);
84
-
85
- const subtitleElement = document.createElement('p');
86
- subtitleElement.className = 'section-subtitle';
87
- subtitleElement.textContent = key;
88
- titleContainer.appendChild(subtitleElement);
89
-
90
- section.appendChild(titleContainer);
91
-
92
- return section;
93
- }
94
-
95
- populateJsonFields(data, container, content, inputType, level = 0) {
96
- container.dataset.key = data.key
97
- container.dataset.level = `${level}`
98
-
99
- for (const [key, value] of Object.entries(content)) {
100
- if (Array.isArray(value)) {
101
- this.populateJsonArray(key, value, data, container, content, inputType, level)
102
- continue
103
- }
104
-
105
- if (value !== null && typeof value === 'object') {
106
- this.populateJsonObject(key, value, data, container, content, inputType, level)
107
- continue
108
- }
109
-
110
- const fieldElement = this.createJsonField(data, key, value, inputType, level)
111
- container.appendChild(fieldElement);
112
- }
113
- }
114
-
115
- populateJsonArray(key, value, data, container, content, inputType, level) {
116
- const arrayContainer = document.createElement('div');
117
- arrayContainer.className = 'json-array';
118
- arrayContainer.classList.add(`json-array-${level}`);
119
-
120
- const labelContainer = document.createElement('div');
121
- labelContainer.className = 'json-array-label-group';
122
- const label = document.createElement('label');
123
- label.textContent = key;
124
- labelContainer.appendChild(label);
125
-
126
- // TODO: to be implemented later
127
- if (false) {
128
- const addButton = document.createElement('button');
129
- addButton.className = 'add-array-item';
130
- addButton.textContent = '+';
131
- let lastItemIndex = value.length - 1
132
- addButton.addEventListener('click', () => {
133
- const itemContainer = document.createElement('div');
134
- itemContainer.className = 'json-array-item';
135
-
136
- lastItemIndex += 1
137
- let fieldKey = `${key}[${lastItemIndex}]`
138
-
139
- const indexed = container.querySelectorAll(`.json-array-item-indexed-${level}`).length !== 0;
140
- if (indexed) {
141
- fieldKey = lastItemIndex
142
- }
143
- const field = this.createJsonField(data, fieldKey, '', inputType, level + 1)
144
- field.classList.add(`json-array-item-${level}`);
145
- itemContainer.appendChild(field);
146
-
147
- arrayContainer.insertBefore(itemContainer, arrayContainer.lastElementChild);
148
-
149
- this.onSectionChanged(data, container.closest('.section'));
150
- });
151
- }
152
- arrayContainer.appendChild(labelContainer);
153
-
154
- value.forEach((item, index) => {
155
- const itemContainer = document.createElement('div');
156
- itemContainer.className = 'json-array-item';
157
- itemContainer.classList.add(`json-array-item-${level}`);
158
- if (typeof item === 'object' && item !== null) {
159
- this.populateJsonFields(data, itemContainer, item, inputType, level + 1);
160
- } else {
161
- itemContainer.dataset.level = `${level + 1}`
162
- const field = this.createJsonField(data, `${index}`, item, inputType, level + 1)
163
- itemContainer.classList.add(`json-array-item-indexed-${level}`);
164
- itemContainer.appendChild(field);
165
- }
166
- arrayContainer.appendChild(itemContainer);
167
- });
168
-
169
- // TODO: to be implemented later
170
- // arrayContainer.appendChild(addButton);
171
- container.appendChild(arrayContainer);
172
- }
173
-
174
- populateJsonObject(key, value, data, container, content, inputType, level) {
175
- const objectContainer = document.createElement('div');
176
- objectContainer.className = 'json-object';
177
- objectContainer.classList.add(`json-object-${level}`);
178
- const objectLabel = document.createElement('label');
179
- objectLabel.className = 'json-object-title';
180
- objectLabel.textContent = key;
181
- objectContainer.appendChild(objectLabel);
182
-
183
- this.populateJsonFields(data, objectContainer, value, inputType, level + 1);
184
-
185
- container.appendChild(objectContainer);
186
- }
187
-
188
- createJsonField(data, key, value, inputType, level) {
189
- const fieldInputType = typeof value === 'boolean' ? 'boolean' : inputType;
190
- const container = document.createElement('div');
191
- container.className = 'input-group';
192
- const label = document.createElement('label');
193
- label.textContent = key;
194
- container.appendChild(label);
195
-
196
- const inputWrapper = document.createElement('div');
197
- inputWrapper.className = 'input-wrapper';
198
-
199
- if (fieldInputType === 'boolean') {
200
- const checkbox = document.createElement('input');
201
- checkbox.type = 'checkbox';
202
- checkbox.id = key;
203
- checkbox.checked = value;
204
-
205
- const checkboxLabel = document.createElement('label');
206
- checkboxLabel.className = 'checkbox-label';
207
- checkboxLabel.htmlFor = key;
208
- checkboxLabel.textContent = value ? 'True' : 'False';
209
-
210
- checkbox.addEventListener('change', () => {
211
- checkboxLabel.textContent = checkbox.checked ? 'True' : 'False';
212
- this.onSectionChanged(data, container.closest('.section'));
213
- });
214
-
215
- inputWrapper.appendChild(checkbox);
216
- inputWrapper.appendChild(checkboxLabel);
217
- } else {
218
- const input = document.createElement('input');
219
- input.type = fieldInputType;
220
- input.id = key;
221
-
222
- console.log(value)
223
- if (fieldInputType === 'color') {
224
- input.value = value.startsWith('#') ? value : `#${value.substring(4)}`;
225
- } else {
226
- input.value = value;
227
- }
228
-
229
- inputWrapper.appendChild(input);
230
- }
231
-
232
- const deleteIcon = document.createElement('span');
233
- deleteIcon.className = 'delete-icon';
234
- deleteIcon.textContent = '×';
235
- deleteIcon.onclick = () => this.onDeleteField(data, container);
236
- inputWrapper.appendChild(deleteIcon);
237
-
238
- container.appendChild(inputWrapper);
239
-
240
- container.addEventListener('change', () => this.onSectionChanged(data, container.closest('.section')));
241
-
242
- container.classList.add(`json-field-${level}`);
243
-
244
- return container;
245
- }
246
-
247
- showAddFieldForm(data, sectionElement, inputType) {
248
- this.addFieldSheet.show(inputType, (name, value) => {
249
- const newField = this.createJsonField(data, name, value, inputType, 0);
250
- sectionElement.insertBefore(newField, sectionElement.lastElementChild);
251
- this.onSectionChanged(data, sectionElement);
252
- })
253
- }
254
-
255
70
  showConfirmationDialog(message, onConfirm) {
256
71
  this.confirmationDialog.showDialog(message, onConfirm);
257
72
  }
258
73
 
259
- showApplyChangesButton() {
260
- const applyChangesButton = document.getElementById('applyChangesButton');
261
- applyChangesButton.style.display = 'block';
262
- this.header.style.backgroundColor = '#ff4136';
74
+ setupSyncBrandButton(color) {
75
+ this.syncBrandButton.style.display = 'block';
263
76
  }
264
77
 
265
78
  showSwitchButton() {
@@ -276,14 +89,6 @@ class BrandDetailView {
276
89
  this.messageBottomSheet.showMessage(message);
277
90
  }
278
91
 
279
- setOnSectionChangedHandler(handler) {
280
- this.onSectionChanged = handler;
281
- }
282
-
283
- setOnDeleteFieldHandler(handler) {
284
- this.onDeleteField = handler;
285
- }
286
-
287
92
  showOnboardBrandForm(onSubmit) {
288
93
  this.onboardSheet.show('Brand Details', 'Add Brand', onSubmit);
289
94
  }
@@ -309,6 +114,17 @@ class BrandDetailView {
309
114
  this.onboardSheet.hide();
310
115
  }
311
116
 
117
+ async toast(message) {
118
+ const toastElement = document.getElementById('toast');
119
+ toastElement.textContent = message;
120
+ toastElement.style.display = 'block';
121
+
122
+ setTimeout(() => {
123
+ toastElement.style.display = 'none';
124
+ }, 3000);
125
+ }
126
+
127
+
312
128
  }
313
129
 
314
130
  export default BrandDetailView;
@@ -0,0 +1,293 @@
1
+ import '../component/EditJsonSheet.js';
2
+
3
+ class SectionsFormManager {
4
+ constructor() {
5
+ this.sections = [];
6
+ this.sectionsContainer = document.getElementById('sections');
7
+ }
8
+
9
+ display(sections, onChange) {
10
+ this.sections = sections.map((section) => {
11
+ return new SectionItemManager(
12
+ section,
13
+ this.createSection(section),
14
+ onChange)
15
+ })
16
+ this.sections.forEach((item => {
17
+ item.displayJSONCards()
18
+ }))
19
+ }
20
+
21
+ createSection(section) {
22
+ const sectionElement = document.createElement('div');
23
+ sectionElement.className = 'section';
24
+
25
+ sectionElement.dataset.key = section.key
26
+ sectionElement.dataset.name = section.name
27
+
28
+ const titleContainer = document.createElement('div');
29
+ titleContainer.className = 'section-title-container';
30
+
31
+ const title = document.createElement('h2');
32
+ title.className = "section-title";
33
+ title.textContent = section.name;
34
+ titleContainer.appendChild(title);
35
+
36
+ sectionElement.appendChild(titleContainer);
37
+
38
+ sectionElement.id = section.key;
39
+
40
+ this.sectionsContainer.appendChild(sectionElement);
41
+
42
+ return sectionElement
43
+ }
44
+
45
+ data() {
46
+ const data = this.sections.map((section) => {
47
+ return section.section
48
+ })
49
+ return Array.from(data);
50
+ }
51
+ }
52
+
53
+ class SectionItemManager {
54
+ constructor(section, container, onChange) {
55
+ this.section = section
56
+ this.container = container
57
+ this.onChange = onChange
58
+ this.editJsonSheet = document.getElementById('editJsonSheet');
59
+ }
60
+
61
+ displayJSONCards() {
62
+ let cardContent = this.container.querySelector('.card-content')
63
+ if (cardContent !== null) this.container.innerHTML = ''
64
+ this.container.appendChild(this.createCard(this.section.content, 'root', null, this.section.key));
65
+ }
66
+
67
+ createCard(obj, key, parent, cardTitle) {
68
+ const card = document.createElement('div');
69
+ card.className = 'card';
70
+
71
+ const header = document.createElement('div');
72
+ header.className = 'card-header';
73
+ header.textContent = key === 'root' ? cardTitle : key;
74
+ header.onclick = () => {
75
+ if (key !== 'root') {
76
+ this.editKey(parent, key)
77
+ return
78
+ }
79
+ this.editJsonSheet.show(
80
+ JSON.stringify(this.section.content, null, 2),
81
+ cardTitle,
82
+ (value) => {
83
+ this.section.content = value
84
+ this.displayJSONCards()
85
+ this.notifyChange()
86
+ })
87
+ };
88
+
89
+ const actions = document.createElement('div');
90
+ actions.className = 'card-actions';
91
+
92
+ const addBtn = document.createElement('button');
93
+ addBtn.className = 'add-property-btn';
94
+ addBtn.innerHTML = '<i class="fas fa-plus"></i>';
95
+ addBtn.onclick = () => this.addProperty(obj);
96
+ actions.appendChild(addBtn);
97
+
98
+ if (key !== 'root') {
99
+ const deleteCardBtn = document.createElement('button');
100
+ deleteCardBtn.className = 'delete-btn';
101
+ deleteCardBtn.innerHTML = '<i class="fas fa-times"></i>';
102
+ deleteCardBtn.onclick = () => this.confirmDeleteProperty(parent, key);
103
+ actions.appendChild(deleteCardBtn);
104
+ }
105
+
106
+ header.appendChild(actions);
107
+ card.appendChild(header);
108
+
109
+ const content = document.createElement('div');
110
+ content.className = 'card-content';
111
+
112
+ const isArray = Array.isArray(obj)
113
+
114
+ for (const [k, v] of Object.entries(obj)) {
115
+ const item = document.createElement('div');
116
+
117
+ const cardValueContainer = document.createElement('div');
118
+ cardValueContainer.className = 'card-value-container';
119
+ item.appendChild(cardValueContainer);
120
+
121
+ const itemKey = document.createElement('span');
122
+ itemKey.className = 'card-key';
123
+ itemKey.onclick = () => {
124
+ if (isArray) return
125
+ this.editKey(obj, k)
126
+ };
127
+ cardValueContainer.appendChild(itemKey);
128
+
129
+ if (typeof v === 'object' && v !== null) {
130
+ item.appendChild(this.createCard(v, k, obj, null));
131
+ itemKey.textContent = isArray ? `${key}[${k}]` : ''
132
+ } else {
133
+ item.className = 'card-item';
134
+ itemKey.textContent = k.replace(/_/g, ' ')
135
+
136
+ if (typeof v === 'boolean') {
137
+ // Create a container for the entire boolean input
138
+ const booleanContainer = document.createElement('div');
139
+ booleanContainer.className = 'boolean-container';
140
+
141
+ const checkboxContainer = document.createElement('div');
142
+ checkboxContainer.className = 'card-value checkbox-container';
143
+
144
+ const itemValue = document.createElement('input');
145
+ itemValue.type = 'checkbox';
146
+ itemValue.className = 'card-value checkbox';
147
+ itemValue.checked = v;
148
+
149
+ const valueLabel = document.createElement('span');
150
+ valueLabel.className = 'checkbox-value';
151
+ valueLabel.textContent = v.toString();
152
+
153
+ const updateValue = () => {
154
+ const newValue = !itemValue.checked;
155
+ itemValue.checked = newValue;
156
+ valueLabel.textContent = newValue.toString();
157
+ this.updateValue(obj, k, newValue, typeof v);
158
+ };
159
+
160
+ // Add click handlers to both container and checkbox
161
+ booleanContainer.onclick = (e) => {
162
+ if (e.target !== itemValue) { // Prevent double-toggle when clicking checkbox
163
+ updateValue();
164
+ }
165
+ };
166
+
167
+ itemValue.onchange = () => {
168
+ valueLabel.textContent = itemValue.checked.toString();
169
+ this.updateValue(obj, k, itemValue.checked, typeof v);
170
+ };
171
+
172
+ checkboxContainer.appendChild(itemValue);
173
+ checkboxContainer.appendChild(valueLabel);
174
+
175
+ // Move the key inside the boolean container
176
+ booleanContainer.appendChild(itemKey);
177
+ booleanContainer.appendChild(checkboxContainer);
178
+
179
+ cardValueContainer.appendChild(booleanContainer);
180
+ } else if (this.isColorValue(v)) {
181
+ const itemValue = document.createElement('input');
182
+ itemValue.type = 'color';
183
+ itemValue.className = 'card-value';
184
+ itemValue.value = v;
185
+ itemValue.onchange = () => this.updateValue(obj, k, itemValue.value, typeof v);
186
+ cardValueContainer.appendChild(itemValue);
187
+ } else {
188
+ const itemValue = document.createElement('textarea');
189
+ itemValue.className = 'card-value';
190
+ itemValue.value = v;
191
+ itemValue.onchange = () => this.updateValue(obj, k, itemValue.value, typeof v);
192
+ cardValueContainer.appendChild(itemValue);
193
+ }
194
+
195
+ const deleteBtn = document.createElement('button');
196
+ deleteBtn.className = 'delete-btn';
197
+ deleteBtn.innerHTML = '<i class="fas fa-times"></i>';
198
+ deleteBtn.onclick = () => this.confirmDeleteProperty(obj, k);
199
+ cardValueContainer.appendChild(deleteBtn);
200
+ }
201
+
202
+ content.appendChild(item);
203
+ }
204
+
205
+ card.appendChild(content);
206
+ return card;
207
+ }
208
+
209
+ isColorValue(value) {
210
+ const hexPattern = /^#([0-9A-F]{3}){1,2}([0-9A-F]{2})?$/i;
211
+ const rgbaPattern = /^rgba?\(\s*(\d{1,3}\s*,\s*){2}\d{1,3}\s*,?\s*(0|1|0?\.\d+|1?\.\d+)\s*\)$/;
212
+
213
+ return hexPattern.test(value) || rgbaPattern.test(value);
214
+ }
215
+
216
+ editKey(obj, oldKey) {
217
+ const newKey = prompt('Edit property name:', oldKey);
218
+ if (newKey && newKey !== oldKey) {
219
+ obj[newKey] = obj[oldKey];
220
+ delete obj[oldKey];
221
+ this.displayJSONCards();
222
+ }
223
+ this.notifyChange()
224
+ }
225
+
226
+ updateValue(obj, key, value, originalType) {
227
+ try {
228
+ // Handle different types based on the original value's type
229
+ switch (originalType) {
230
+ case 'string':
231
+ obj[key] = String(value);
232
+ break;
233
+ case 'number':
234
+ // If the original was a number, keep it number
235
+ if (!isNaN(value) || value === '') {
236
+ obj[key] = Number(value);
237
+ }
238
+ break;
239
+ case 'boolean':
240
+ obj[key] = value.toLowerCase() === 'true';
241
+ break;
242
+ default:
243
+ // Try to parse as JSON, fallback to string if it fails
244
+ try {
245
+ obj[key] = JSON.parse(value);
246
+ } catch {
247
+ obj[key] = value;
248
+ }
249
+ }
250
+ } catch {
251
+ obj[key] = value;
252
+ }
253
+ this.displayJSONCards();
254
+ this.notifyChange();
255
+ }
256
+
257
+ confirmDeleteProperty(obj, key) {
258
+ const confirmationDialog = document.getElementById('confirmationDialog');
259
+ confirmationDialog.showDialog(`Are you sure you need to delete: ${key}?`,
260
+ async () => {
261
+ this.deleteProperty(obj, key)
262
+ });
263
+ }
264
+
265
+ deleteProperty(obj, key) {
266
+ if (Array.isArray(obj)) {
267
+ obj.splice(key, 1);
268
+ } else {
269
+ delete obj[key];
270
+ }
271
+ this.displayJSONCards();
272
+ this.notifyChange()
273
+ }
274
+
275
+ addProperty(obj) {
276
+ if (Array.isArray(obj)) {
277
+ obj.push('');
278
+ } else {
279
+ const key = prompt('Enter new property name:');
280
+ if (key) {
281
+ obj[key] = '';
282
+ }
283
+ }
284
+ this.displayJSONCards();
285
+ this.notifyChange()
286
+ }
287
+
288
+ notifyChange() {
289
+ this.onChange(this.section, this.container)
290
+ }
291
+ }
292
+
293
+ export default SectionsFormManager;