solara 0.3.0 → 0.5.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 (57) 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_switcher.rb +58 -1
  5. data/solara/lib/core/dashboard/.DS_Store +0 -0
  6. data/solara/lib/core/dashboard/brand/BrandDetail.js +34 -2
  7. data/solara/lib/core/dashboard/brand/BrandDetailController.js +23 -206
  8. data/solara/lib/core/dashboard/brand/BrandDetailModel.js +13 -5
  9. data/solara/lib/core/dashboard/brand/BrandDetailView.js +16 -178
  10. data/solara/lib/core/dashboard/brand/SectionsFormManager.js +232 -0
  11. data/solara/lib/core/dashboard/brand/brand.html +199 -199
  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 +5 -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/doctor/schema/brand_configurations.json +0 -8
  27. data/solara/lib/core/doctor/schema/platform/global/resources_manifest.json +30 -0
  28. data/solara/lib/core/doctor/schema/platform/json_manifest.json +57 -0
  29. data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +35 -1
  30. data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +30 -1
  31. data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +35 -1
  32. data/solara/lib/core/doctor/validator/template/template_validator.rb +9 -9
  33. data/solara/lib/core/scripts/brand_config_manager.rb +1 -1
  34. data/solara/lib/core/scripts/brand_configurations_manager.rb +41 -0
  35. data/solara/lib/core/scripts/code_generator.rb +342 -118
  36. data/solara/lib/core/scripts/file_path.rb +21 -1
  37. data/solara/lib/core/scripts/gitignore_manager.rb +11 -3
  38. data/solara/lib/core/scripts/json_manifest_processor.rb +95 -0
  39. data/solara/lib/core/scripts/platform/ios/infoplist_string_catalog_manager.rb +11 -1
  40. data/solara/lib/core/scripts/resource_manifest_processor.rb +151 -0
  41. data/solara/lib/core/scripts/solara_status_manager.rb +1 -1
  42. data/solara/lib/core/scripts/theme_generator.rb +21 -242
  43. data/solara/lib/core/solara_configurator.rb +1 -1
  44. data/solara/lib/core/template/brands/global/resources_manifest.json +10 -0
  45. data/solara/lib/core/template/brands/json/Json-Manifest.md +61 -0
  46. data/solara/lib/core/template/brands/json/json_manifest.json +18 -0
  47. data/solara/lib/core/template/brands/shared/theme.json +213 -29
  48. data/solara/lib/core/template/config/android_template_config.json +50 -0
  49. data/solara/lib/core/template/config/flutter_template_config.json +35 -0
  50. data/solara/lib/core/template/config/ios_template_config.json +50 -0
  51. data/solara/lib/core/template/configurations.json +46 -0
  52. data/solara/lib/core/template/project_template_generator.rb +2 -0
  53. data/solara/lib/solara/version.rb +1 -1
  54. data/solara/lib/solara.rb +19 -0
  55. metadata +13 -4
  56. data/solara/lib/core/dashboard/component/AddFieldSheet.js +0 -175
  57. 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,177 +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
- populateSection(sectionItem, sectionElement, content, inputType) {
96
- for (const [key, value] of Object.entries(content)) {
97
- if (Array.isArray(value)) {
98
- sectionElement.appendChild(this.createInputField(sectionItem, key, value, 'array'));
99
- } else if (typeof value === 'object' && value !== null) {
100
- for (const [subKey, subValue] of Object.entries(value)) {
101
- const subInputType = subValue === true || subValue === false ? 'boolean' : inputType;
102
- sectionElement.appendChild(this.createInputField(sectionItem, `${key}.${subKey}`, subValue, subInputType));
103
- }
104
- } else {
105
- const fieldInputType = value === true || value === false ? 'boolean' : inputType;
106
- sectionElement.appendChild(this.createInputField(sectionItem, key, value, fieldInputType));
107
- }
108
- }
109
- }
110
-
111
- createInputField(sectionItem, key, value, inputType) {
112
- const container = document.createElement('div');
113
- container.className = 'input-group';
114
- const label = document.createElement('label');
115
- label.textContent = key;
116
- container.appendChild(label);
117
-
118
- const inputWrapper = document.createElement('div');
119
- inputWrapper.className = 'input-wrapper';
120
-
121
- if (inputType === 'array') {
122
- const arrayInputContainer = document.createElement('div');
123
- arrayInputContainer.className = 'array-input-container';
124
-
125
- const input = document.createElement('input');
126
- input.type = 'text';
127
- input.id = key;
128
- input.className = 'array-input';
129
- input.placeholder = 'Enter array value';
130
-
131
- const addButton = document.createElement('button');
132
- addButton.className = 'add-array-item';
133
- addButton.textContent = '+';
134
-
135
- arrayInputContainer.appendChild(input);
136
- arrayInputContainer.appendChild(addButton);
137
-
138
- const arrayItemsContainer = document.createElement('div');
139
- arrayItemsContainer.className = 'array-items-container';
140
-
141
- inputWrapper.appendChild(arrayInputContainer);
142
- inputWrapper.appendChild(arrayItemsContainer);
143
-
144
- if (Array.isArray(value)) {
145
- value.forEach(item => {
146
- this.addArrayItem(sectionItem, arrayItemsContainer, item);
147
- });
148
- }
149
-
150
- addButton.addEventListener('click', () => {
151
- this.addArrayItem(sectionItem, arrayItemsContainer, input.value.trim());
152
- input.value = '';
153
- });
154
-
155
- } else if (inputType === 'boolean') {
156
- const checkbox = document.createElement('input');
157
- checkbox.type = 'checkbox';
158
- checkbox.id = key;
159
- checkbox.checked = value;
160
-
161
- const checkboxLabel = document.createElement('label');
162
- checkboxLabel.className = 'checkbox-label';
163
- checkboxLabel.htmlFor = key;
164
- checkboxLabel.textContent = value ? 'True' : 'False';
165
-
166
- checkbox.addEventListener('change', () => {
167
- checkboxLabel.textContent = checkbox.checked ? 'True' : 'False';
168
- this.onSectionChanged(sectionItem, container.closest('.section'));
169
- });
170
-
171
- inputWrapper.appendChild(checkbox);
172
- inputWrapper.appendChild(checkboxLabel);
173
- } else {
174
- const input = document.createElement('input');
175
- input.type = inputType;
176
- input.id = key;
177
-
178
- console.log(value)
179
- if (inputType === 'color') {
180
- input.value = value.startsWith('#') ? value : `#${value.substring(4)}`;
181
- } else {
182
- input.value = value;
183
- }
184
-
185
- inputWrapper.appendChild(input);
186
- }
187
-
188
- const deleteIcon = document.createElement('span');
189
- deleteIcon.className = 'delete-icon';
190
- deleteIcon.textContent = '×';
191
- deleteIcon.onclick = () => this.onDeleteField(sectionItem, container);
192
- inputWrapper.appendChild(deleteIcon);
193
-
194
- container.appendChild(inputWrapper);
195
-
196
- container.addEventListener('change', () => this.onSectionChanged(sectionItem, container.closest('.section')));
197
-
198
- return container;
199
- }
200
-
201
- addArrayItem(sectionItem, container, value) {
202
- const itemContainer = document.createElement('div');
203
- itemContainer.classList.add('array-item');
204
-
205
- const itemInput = document.createElement('input');
206
- itemInput.type = 'text';
207
- itemInput.classList.add('array-item-input');
208
- itemInput.value = value;
209
-
210
- const deleteButton = document.createElement('button');
211
- deleteButton.classList.add('delete-array-item');
212
- deleteButton.textContent = '×';
213
- deleteButton.addEventListener('click', () => {
214
- itemContainer.remove();
215
- this.onSectionChanged(sectionItem, container.closest('.section'));
216
- });
217
-
218
- itemContainer.appendChild(itemInput);
219
- itemContainer.appendChild(deleteButton);
220
- container.appendChild(itemContainer);
221
-
222
- this.onSectionChanged(sectionItem, container.closest('.section'));
223
- }
224
-
225
- showAddFieldForm(sectionItem, sectionElement, inputType) {
226
- this.addFieldSheet.show(inputType, (name, value) => {
227
- const newField = this.createInputField(sectionItem, name, value, inputType);
228
- sectionElement.insertBefore(newField, sectionElement.lastElementChild);
229
- this.onSectionChanged(sectionItem, sectionElement);
230
- })
231
- }
232
-
233
70
  showConfirmationDialog(message, onConfirm) {
234
71
  this.confirmationDialog.showDialog(message, onConfirm);
235
72
  }
236
73
 
237
- showApplyChangesButton() {
238
- const applyChangesButton = document.getElementById('applyChangesButton');
239
- applyChangesButton.style.display = 'block';
240
- this.header.style.backgroundColor = '#ff4136';
74
+ setupSyncBrandButton(color) {
75
+ this.syncBrandButton.style.display = 'block';
241
76
  }
242
77
 
243
78
  showSwitchButton() {
@@ -254,14 +89,6 @@ class BrandDetailView {
254
89
  this.messageBottomSheet.showMessage(message);
255
90
  }
256
91
 
257
- setOnSectionChangedHandler(handler) {
258
- this.onSectionChanged = handler;
259
- }
260
-
261
- setOnDeleteFieldHandler(handler) {
262
- this.onDeleteField = handler;
263
- }
264
-
265
92
  showOnboardBrandForm(onSubmit) {
266
93
  this.onboardSheet.show('Brand Details', 'Add Brand', onSubmit);
267
94
  }
@@ -287,6 +114,17 @@ class BrandDetailView {
287
114
  this.onboardSheet.hide();
288
115
  }
289
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
+
290
128
  }
291
129
 
292
130
  export default BrandDetailView;
@@ -0,0 +1,232 @@
1
+ import '../component/EditJsonSheet.js';
2
+
3
+ class SectionsFormManager {
4
+
5
+ constructor() {
6
+ this.sections = [];
7
+ this.sectionsContainer = document.getElementById('sections');
8
+ }
9
+
10
+ display(sections, onChange) {
11
+ this.sections = sections.map((section) => {
12
+ return new SectionItemManager(
13
+ section,
14
+ this.createSection(section),
15
+ onChange)
16
+ })
17
+ this.sections.forEach((item => {
18
+ item.displayJSONCards()
19
+ }))
20
+ }
21
+
22
+ createSection(section) {
23
+ const sectionElement = document.createElement('div');
24
+ sectionElement.className = 'section';
25
+
26
+ sectionElement.dataset.key = section.key
27
+ sectionElement.dataset.name = section.name
28
+
29
+ const titleContainer = document.createElement('div');
30
+ titleContainer.className = 'section-title-container';
31
+
32
+ const title = document.createElement('h2');
33
+ title.className = "section-title";
34
+ title.textContent = section.name;
35
+ titleContainer.appendChild(title);
36
+
37
+ sectionElement.appendChild(titleContainer);
38
+
39
+ sectionElement.id = section.key;
40
+
41
+ this.sectionsContainer.appendChild(sectionElement);
42
+
43
+ return sectionElement
44
+ }
45
+
46
+ data() {
47
+ const data = this.sections.map((section) => {
48
+ return section.section
49
+ })
50
+ return Array.from(data);
51
+ }
52
+
53
+ }
54
+
55
+ class SectionItemManager {
56
+ constructor(section, container, onChange) {
57
+ this.section = section
58
+ this.container = container
59
+ this.onChange = onChange
60
+ this.editJsonSheet = document.getElementById('editJsonSheet');
61
+ }
62
+
63
+ displayJSONCards() {
64
+ let cardContent = this.container.querySelector('.card-content')
65
+ if (cardContent !== null) this.container.innerHTML = ''
66
+ this.container.appendChild(this.createCard(this.section.content, 'root', null, this.section.key));
67
+ }
68
+
69
+ createCard(obj, key, parent, cardTitle) {
70
+ const card = document.createElement('div');
71
+ card.className = 'card';
72
+
73
+ const header = document.createElement('div');
74
+ header.className = 'card-header';
75
+ header.textContent = key === 'root' ? cardTitle : key;
76
+ header.onclick = () => {
77
+ if (key !== 'root') {
78
+ this.editKey(parent, key)
79
+ return
80
+ }
81
+ this.editJsonSheet.show(
82
+ JSON.stringify(this.section.content, null, 2),
83
+ cardTitle,
84
+ (value) => {
85
+ this.section.content = value
86
+ this.displayJSONCards()
87
+ this.notifyChange()
88
+ })
89
+ };
90
+
91
+ const actions = document.createElement('div');
92
+ actions.className = 'card-actions';
93
+
94
+ const addBtn = document.createElement('button');
95
+ addBtn.className = 'add-property-btn';
96
+ addBtn.innerHTML = '<i class="fas fa-plus"></i>';
97
+ addBtn.onclick = () => this.addProperty(obj);
98
+ actions.appendChild(addBtn);
99
+
100
+ if (key !== 'root') {
101
+ const deleteCardBtn = document.createElement('button');
102
+ deleteCardBtn.className = 'delete-btn';
103
+ deleteCardBtn.innerHTML = '<i class="fas fa-times"></i>';
104
+ deleteCardBtn.onclick = () => this.confirmDeleteProperty(parent, key);
105
+ actions.appendChild(deleteCardBtn);
106
+ }
107
+
108
+ header.appendChild(actions);
109
+ card.appendChild(header);
110
+
111
+ const content = document.createElement('div');
112
+ content.className = 'card-content';
113
+
114
+ const isArray = Array.isArray(obj)
115
+
116
+ for (const [k, v] of Object.entries(obj)) {
117
+ const item = document.createElement('div');
118
+
119
+ const cardValueContainer = document.createElement('div');
120
+ cardValueContainer.className = 'card-value-container';
121
+ item.appendChild(cardValueContainer);
122
+
123
+ const itemKey = document.createElement('span');
124
+ itemKey.className = 'card-key';
125
+ itemKey.onclick = () => {
126
+ if (isArray) return
127
+ this.editKey(obj, k)
128
+ };
129
+ cardValueContainer.appendChild(itemKey);
130
+
131
+ if (typeof v === 'object' && v !== null) {
132
+ item.appendChild(this.createCard(v, k, obj, null));
133
+ itemKey.textContent = isArray ? `${key}[${k}]` : ''
134
+ } else {
135
+ item.className = 'card-item';
136
+ itemKey.textContent = k.replace(/_/g, ' ')
137
+
138
+ if (this.isColorValue(v)) {
139
+ const itemValue = document.createElement('input');
140
+ itemValue.type = 'color';
141
+ itemValue.className = 'card-value';
142
+ itemValue.value = v;
143
+ itemValue.onchange = () => this.updateValue(obj, k, itemValue.value);
144
+ cardValueContainer.appendChild(itemValue);
145
+ } else {
146
+ const itemValue = document.createElement('textarea');
147
+ itemValue.className = 'card-value';
148
+ itemValue.value = v;
149
+ itemValue.onchange = () => this.updateValue(obj, k, itemValue.value);
150
+ cardValueContainer.appendChild(itemValue);
151
+ }
152
+
153
+ const deleteBtn = document.createElement('button');
154
+ deleteBtn.className = 'delete-btn';
155
+ deleteBtn.innerHTML = '<i class="fas fa-times"></i>';
156
+ deleteBtn.onclick = () => this.confirmDeleteProperty(obj, k);
157
+ cardValueContainer.appendChild(deleteBtn);
158
+ }
159
+
160
+ content.appendChild(item);
161
+ }
162
+
163
+ card.appendChild(content);
164
+ return card;
165
+ }
166
+
167
+ isColorValue(value) {
168
+ // Check if the value is a valid color (hex with opacity, RGBA, or RGB)
169
+ const hexPattern = /^#([0-9A-F]{3}){1,2}([0-9A-F]{2})?$/i; // 3, 6, or 8 hex digits
170
+ const rgbaPattern = /^rgba?\(\s*(\d{1,3}\s*,\s*){2}\d{1,3}\s*,?\s*(0|1|0?\.\d+|1?\.\d+)\s*\)$/;
171
+
172
+ return hexPattern.test(value) || rgbaPattern.test(value);
173
+ }
174
+
175
+ editKey(obj, oldKey) {
176
+ const newKey = prompt('Edit property name:', oldKey);
177
+ if (newKey && newKey !== oldKey) {
178
+ obj[newKey] = obj[oldKey];
179
+ delete obj[oldKey];
180
+ this.displayJSONCards();
181
+ }
182
+ this.notifyChange()
183
+ }
184
+
185
+ updateValue(obj, key, value) {
186
+ try {
187
+ obj[key] = JSON.parse(value);
188
+ } catch {
189
+ obj[key] = value;
190
+ }
191
+ this.displayJSONCards();
192
+ this.notifyChange()
193
+ }
194
+
195
+ confirmDeleteProperty(obj, key) {
196
+ const confirmationDialog = document.getElementById('confirmationDialog');
197
+ confirmationDialog.showDialog(`Are you sure you need to delete: ${key}?`,
198
+ async () => {
199
+ this.deleteProperty(obj, key)
200
+ });
201
+ }
202
+
203
+ deleteProperty(obj, key) {
204
+ if (Array.isArray(obj)) {
205
+ obj.splice(key, 1);
206
+ } else {
207
+ delete obj[key];
208
+ }
209
+ this.displayJSONCards();
210
+ this.notifyChange()
211
+ }
212
+
213
+ addProperty(obj) {
214
+ if (Array.isArray(obj)) {
215
+ obj.push('');
216
+ } else {
217
+ const key = prompt('Enter new property name:');
218
+ if (key) {
219
+ obj[key] = '';
220
+ }
221
+ }
222
+ this.displayJSONCards();
223
+ this.notifyChange()
224
+ }
225
+
226
+ notifyChange() {
227
+ this.onChange(this.section, this.container)
228
+ }
229
+ }
230
+
231
+
232
+ export default SectionsFormManager;