@zentto/studio 0.6.0 → 0.6.1
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/dist/designer/zs-app-wizard.js +562 -562
- package/dist/designer/zs-page-designer.d.ts +8 -0
- package/dist/designer/zs-page-designer.d.ts.map +1 -1
- package/dist/designer/zs-page-designer.js +1053 -991
- package/dist/designer/zs-page-designer.js.map +1 -1
- package/dist/fields/zs-field-datagrid.js +58 -58
- package/dist/fields/zs-field-report.js +52 -52
- package/dist/zentto-studio-renderer.js +307 -307
- package/package.json +74 -70
|
@@ -78,233 +78,233 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
78
78
|
this.codeModalTitle = '';
|
|
79
79
|
this.codeModalContent = '';
|
|
80
80
|
}
|
|
81
|
-
static { this.styles = [studioTokens, fieldBaseStyles, css `
|
|
82
|
-
:host { display: block; font-family: var(--zs-font-family); }
|
|
83
|
-
|
|
84
|
-
.wizard {
|
|
85
|
-
max-width: 900px; margin: 0 auto;
|
|
86
|
-
background: var(--zs-bg); border-radius: 12px;
|
|
87
|
-
border: 1px solid var(--zs-border);
|
|
88
|
-
overflow: hidden;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/* Steps bar */
|
|
92
|
-
.wizard-steps {
|
|
93
|
-
display: flex; background: var(--zs-bg-secondary);
|
|
94
|
-
border-bottom: 1px solid var(--zs-border); padding: 0;
|
|
95
|
-
overflow-x: auto;
|
|
96
|
-
}
|
|
97
|
-
.wizard-step {
|
|
98
|
-
flex: 1; display: flex; align-items: center; gap: 6px;
|
|
99
|
-
padding: 14px 12px; cursor: pointer;
|
|
100
|
-
color: var(--zs-text-muted); font-size: 12px;
|
|
101
|
-
border-bottom: 3px solid transparent;
|
|
102
|
-
transition: all 150ms; min-width: 0; white-space: nowrap;
|
|
103
|
-
}
|
|
104
|
-
.wizard-step:hover { color: var(--zs-text-secondary); }
|
|
105
|
-
.wizard-step--active {
|
|
106
|
-
color: var(--zs-primary); border-bottom-color: var(--zs-primary);
|
|
107
|
-
font-weight: 500;
|
|
108
|
-
}
|
|
109
|
-
.wizard-step--done { color: var(--zs-success); }
|
|
110
|
-
.wizard-step-icon { font-size: 16px; }
|
|
111
|
-
.wizard-step-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
112
|
-
|
|
113
|
-
/* Body */
|
|
114
|
-
.wizard-body { padding: 32px; min-height: 400px; }
|
|
115
|
-
.wizard-title { font-size: 22px; font-weight: 600; margin: 0 0 4px; color: var(--zs-text); }
|
|
116
|
-
.wizard-desc { font-size: 14px; color: var(--zs-text-secondary); margin: 0 0 24px; }
|
|
117
|
-
|
|
118
|
-
/* Footer */
|
|
119
|
-
.wizard-footer {
|
|
120
|
-
display: flex; justify-content: space-between;
|
|
121
|
-
padding: 16px 32px; border-top: 1px solid var(--zs-border);
|
|
122
|
-
background: var(--zs-bg-secondary);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/* Template cards */
|
|
126
|
-
.template-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
|
127
|
-
.template-card {
|
|
128
|
-
padding: 24px; border: 2px solid var(--zs-border);
|
|
129
|
-
border-radius: 12px; cursor: pointer;
|
|
130
|
-
transition: all 200ms; text-align: center;
|
|
131
|
-
}
|
|
132
|
-
.template-card:hover { border-color: var(--zs-primary); transform: translateY(-2px); box-shadow: 0 4px 12px var(--zs-shadow); }
|
|
133
|
-
.template-card--selected { border-color: var(--zs-primary); background: var(--zs-primary-light); }
|
|
134
|
-
.template-icon { font-size: 40px; margin-bottom: 12px; }
|
|
135
|
-
.template-title { font-size: 16px; font-weight: 600; color: var(--zs-text); }
|
|
136
|
-
.template-desc { font-size: 13px; color: var(--zs-text-secondary); margin-top: 4px; }
|
|
137
|
-
|
|
138
|
-
/* Form groups */
|
|
139
|
-
.form-group { margin-bottom: 20px; }
|
|
140
|
-
.form-label { font-size: 13px; font-weight: 500; color: var(--zs-text); margin-bottom: 6px; display: block; }
|
|
141
|
-
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
142
|
-
.form-row-3 { grid-template-columns: 1fr 1fr 1fr; }
|
|
143
|
-
.form-row-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
|
|
144
|
-
|
|
145
|
-
/* Color picker grid */
|
|
146
|
-
.color-grid { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
147
|
-
.color-swatch {
|
|
148
|
-
width: 36px; height: 36px; border-radius: 8px;
|
|
149
|
-
cursor: pointer; border: 3px solid transparent;
|
|
150
|
-
transition: all 150ms;
|
|
151
|
-
}
|
|
152
|
-
.color-swatch:hover { transform: scale(1.15); }
|
|
153
|
-
.color-swatch--selected { border-color: var(--zs-text); box-shadow: 0 0 0 2px white, 0 0 0 4px var(--zs-text); }
|
|
154
|
-
|
|
155
|
-
/* Sidebar style picker */
|
|
156
|
-
.style-grid { display: flex; gap: 12px; }
|
|
157
|
-
.style-option {
|
|
158
|
-
flex: 1; padding: 16px; text-align: center;
|
|
159
|
-
border: 2px solid var(--zs-border); border-radius: 8px;
|
|
160
|
-
cursor: pointer; transition: all 150ms;
|
|
161
|
-
}
|
|
162
|
-
.style-option:hover { border-color: var(--zs-primary); }
|
|
163
|
-
.style-option--selected { border-color: var(--zs-primary); background: var(--zs-primary-light); }
|
|
164
|
-
.style-preview {
|
|
165
|
-
width: 60px; height: 40px; border-radius: 4px;
|
|
166
|
-
margin: 0 auto 8px; border: 1px solid var(--zs-border);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/* Nav editor */
|
|
170
|
-
.nav-list { border: 1px solid var(--zs-border); border-radius: 8px; overflow: hidden; }
|
|
171
|
-
.nav-item-row {
|
|
172
|
-
display: flex; align-items: center; gap: 8px;
|
|
173
|
-
padding: 10px 12px; border-bottom: 1px solid var(--zs-border);
|
|
174
|
-
font-size: 14px;
|
|
175
|
-
}
|
|
176
|
-
.nav-item-row:last-child { border-bottom: none; }
|
|
177
|
-
.nav-item-icon { font-size: 18px; cursor: pointer; }
|
|
178
|
-
.nav-item-title { flex: 1; }
|
|
179
|
-
.nav-item-actions { display: flex; gap: 4px; }
|
|
180
|
-
.nav-item-btn {
|
|
181
|
-
border: none; background: none; cursor: pointer;
|
|
182
|
-
font-size: 14px; padding: 2px 6px; border-radius: 4px;
|
|
183
|
-
color: var(--zs-text-muted); transition: all 100ms;
|
|
184
|
-
}
|
|
185
|
-
.nav-item-btn:hover { background: var(--zs-bg-hover); color: var(--zs-text); }
|
|
186
|
-
.nav-item-btn--danger:hover { color: var(--zs-danger); }
|
|
187
|
-
.add-btn {
|
|
188
|
-
display: flex; align-items: center; gap: 6px;
|
|
189
|
-
padding: 8px 16px; margin-top: 12px;
|
|
190
|
-
border: 1px dashed var(--zs-border); border-radius: 8px;
|
|
191
|
-
background: none; cursor: pointer;
|
|
192
|
-
font-family: var(--zs-font-family); font-size: 13px;
|
|
193
|
-
color: var(--zs-text-secondary); transition: all 150ms;
|
|
194
|
-
}
|
|
195
|
-
.add-btn:hover { border-color: var(--zs-primary); color: var(--zs-primary); }
|
|
196
|
-
|
|
197
|
-
/* Icon picker */
|
|
198
|
-
.icon-grid { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
199
|
-
.icon-btn {
|
|
200
|
-
width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
|
|
201
|
-
border: 1px solid transparent; border-radius: 6px;
|
|
202
|
-
cursor: pointer; font-size: 18px; background: none;
|
|
203
|
-
transition: all 100ms;
|
|
204
|
-
}
|
|
205
|
-
.icon-btn:hover { background: var(--zs-bg-hover); border-color: var(--zs-border); }
|
|
206
|
-
.icon-btn--selected { background: var(--zs-primary-light); border-color: var(--zs-primary); }
|
|
207
|
-
|
|
208
|
-
/* Preview */
|
|
209
|
-
.preview-container {
|
|
210
|
-
border: 1px solid var(--zs-border); border-radius: 8px;
|
|
211
|
-
height: 500px; overflow: hidden;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/* Buttons */
|
|
215
|
-
.btn {
|
|
216
|
-
padding: 10px 24px; border-radius: var(--zs-radius);
|
|
217
|
-
font-family: var(--zs-font-family); font-size: 14px;
|
|
218
|
-
font-weight: 500; cursor: pointer; border: 1px solid transparent;
|
|
219
|
-
transition: all 150ms;
|
|
220
|
-
}
|
|
221
|
-
.btn--primary { background: var(--zs-primary); color: white; }
|
|
222
|
-
.btn--primary:hover { background: var(--zs-primary-hover); }
|
|
223
|
-
.btn--secondary { background: var(--zs-bg); color: var(--zs-text); border-color: var(--zs-border); }
|
|
224
|
-
.btn--secondary:hover { background: var(--zs-bg-hover); }
|
|
225
|
-
.btn--success { background: var(--zs-success); color: white; }
|
|
226
|
-
.btn--success:hover { opacity: 0.9; }
|
|
227
|
-
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
228
|
-
|
|
229
|
-
/* Data source rows */
|
|
230
|
-
.ds-card {
|
|
231
|
-
border: 1px solid var(--zs-border); border-radius: 8px;
|
|
232
|
-
padding: 16px; margin-bottom: 12px;
|
|
233
|
-
background: var(--zs-bg);
|
|
234
|
-
}
|
|
235
|
-
.ds-card-header {
|
|
236
|
-
display: flex; align-items: center; justify-content: space-between;
|
|
237
|
-
margin-bottom: 12px;
|
|
238
|
-
}
|
|
239
|
-
.ds-card-title { font-size: 14px; font-weight: 500; color: var(--zs-text); }
|
|
240
|
-
.ds-method-badge {
|
|
241
|
-
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
|
242
|
-
font-size: 11px; font-weight: 600; color: white;
|
|
243
|
-
}
|
|
244
|
-
.ds-method-GET { background: #27ae60; }
|
|
245
|
-
.ds-method-POST { background: #3498db; }
|
|
246
|
-
.ds-method-PUT { background: #e67e22; }
|
|
247
|
-
.ds-method-PATCH { background: #9b59b6; }
|
|
248
|
-
.ds-method-DELETE { background: #e74c3c; }
|
|
249
|
-
|
|
250
|
-
/* Theme preview card */
|
|
251
|
-
.theme-preview-card {
|
|
252
|
-
border: 1px solid var(--zs-border); border-radius: 12px;
|
|
253
|
-
padding: 24px; margin-top: 20px;
|
|
254
|
-
transition: all 200ms;
|
|
255
|
-
}
|
|
256
|
-
.theme-preview-header {
|
|
257
|
-
font-size: 16px; font-weight: 600; margin-bottom: 8px;
|
|
258
|
-
}
|
|
259
|
-
.theme-preview-text {
|
|
260
|
-
font-size: 13px; margin-bottom: 16px; opacity: 0.7;
|
|
261
|
-
}
|
|
262
|
-
.theme-preview-btn {
|
|
263
|
-
display: inline-block; padding: 8px 20px;
|
|
264
|
-
border: none; border-radius: 8px;
|
|
265
|
-
color: white; font-size: 13px; font-weight: 500;
|
|
266
|
-
}
|
|
267
|
-
.theme-preview-input {
|
|
268
|
-
display: inline-block; padding: 6px 12px;
|
|
269
|
-
border: 1px solid; border-radius: 8px;
|
|
270
|
-
font-size: 13px; margin-left: 8px; width: 150px;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/* Code modal */
|
|
274
|
-
.code-modal-overlay {
|
|
275
|
-
position: fixed; inset: 0; z-index: 9999;
|
|
276
|
-
background: rgba(0,0,0,0.5); display: flex;
|
|
277
|
-
align-items: center; justify-content: center;
|
|
278
|
-
}
|
|
279
|
-
.code-modal {
|
|
280
|
-
background: var(--zs-bg); border-radius: 12px;
|
|
281
|
-
width: 90%; max-width: 800px; max-height: 80vh;
|
|
282
|
-
display: flex; flex-direction: column;
|
|
283
|
-
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
284
|
-
}
|
|
285
|
-
.code-modal-header {
|
|
286
|
-
display: flex; align-items: center; justify-content: space-between;
|
|
287
|
-
padding: 16px 20px; border-bottom: 1px solid var(--zs-border);
|
|
288
|
-
}
|
|
289
|
-
.code-modal-header h3 { margin: 0; font-size: 16px; }
|
|
290
|
-
.code-modal-body {
|
|
291
|
-
flex: 1; overflow: auto; padding: 0;
|
|
292
|
-
}
|
|
293
|
-
.code-modal-body pre {
|
|
294
|
-
margin: 0; padding: 20px; font-size: 13px;
|
|
295
|
-
font-family: 'Fira Code', 'Cascadia Code', monospace;
|
|
296
|
-
line-height: 1.5; white-space: pre-wrap; word-break: break-word;
|
|
297
|
-
background: var(--zs-bg-secondary); min-height: 200px;
|
|
298
|
-
}
|
|
299
|
-
.code-modal-footer {
|
|
300
|
-
display: flex; gap: 8px; justify-content: flex-end;
|
|
301
|
-
padding: 12px 20px; border-top: 1px solid var(--zs-border);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/* Export buttons row */
|
|
305
|
-
.export-row {
|
|
306
|
-
display: flex; gap: 8px; flex-wrap: wrap;
|
|
307
|
-
}
|
|
81
|
+
static { this.styles = [studioTokens, fieldBaseStyles, css `
|
|
82
|
+
:host { display: block; font-family: var(--zs-font-family); }
|
|
83
|
+
|
|
84
|
+
.wizard {
|
|
85
|
+
max-width: 900px; margin: 0 auto;
|
|
86
|
+
background: var(--zs-bg); border-radius: 12px;
|
|
87
|
+
border: 1px solid var(--zs-border);
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Steps bar */
|
|
92
|
+
.wizard-steps {
|
|
93
|
+
display: flex; background: var(--zs-bg-secondary);
|
|
94
|
+
border-bottom: 1px solid var(--zs-border); padding: 0;
|
|
95
|
+
overflow-x: auto;
|
|
96
|
+
}
|
|
97
|
+
.wizard-step {
|
|
98
|
+
flex: 1; display: flex; align-items: center; gap: 6px;
|
|
99
|
+
padding: 14px 12px; cursor: pointer;
|
|
100
|
+
color: var(--zs-text-muted); font-size: 12px;
|
|
101
|
+
border-bottom: 3px solid transparent;
|
|
102
|
+
transition: all 150ms; min-width: 0; white-space: nowrap;
|
|
103
|
+
}
|
|
104
|
+
.wizard-step:hover { color: var(--zs-text-secondary); }
|
|
105
|
+
.wizard-step--active {
|
|
106
|
+
color: var(--zs-primary); border-bottom-color: var(--zs-primary);
|
|
107
|
+
font-weight: 500;
|
|
108
|
+
}
|
|
109
|
+
.wizard-step--done { color: var(--zs-success); }
|
|
110
|
+
.wizard-step-icon { font-size: 16px; }
|
|
111
|
+
.wizard-step-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
112
|
+
|
|
113
|
+
/* Body */
|
|
114
|
+
.wizard-body { padding: 32px; min-height: 400px; }
|
|
115
|
+
.wizard-title { font-size: 22px; font-weight: 600; margin: 0 0 4px; color: var(--zs-text); }
|
|
116
|
+
.wizard-desc { font-size: 14px; color: var(--zs-text-secondary); margin: 0 0 24px; }
|
|
117
|
+
|
|
118
|
+
/* Footer */
|
|
119
|
+
.wizard-footer {
|
|
120
|
+
display: flex; justify-content: space-between;
|
|
121
|
+
padding: 16px 32px; border-top: 1px solid var(--zs-border);
|
|
122
|
+
background: var(--zs-bg-secondary);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Template cards */
|
|
126
|
+
.template-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
|
127
|
+
.template-card {
|
|
128
|
+
padding: 24px; border: 2px solid var(--zs-border);
|
|
129
|
+
border-radius: 12px; cursor: pointer;
|
|
130
|
+
transition: all 200ms; text-align: center;
|
|
131
|
+
}
|
|
132
|
+
.template-card:hover { border-color: var(--zs-primary); transform: translateY(-2px); box-shadow: 0 4px 12px var(--zs-shadow); }
|
|
133
|
+
.template-card--selected { border-color: var(--zs-primary); background: var(--zs-primary-light); }
|
|
134
|
+
.template-icon { font-size: 40px; margin-bottom: 12px; }
|
|
135
|
+
.template-title { font-size: 16px; font-weight: 600; color: var(--zs-text); }
|
|
136
|
+
.template-desc { font-size: 13px; color: var(--zs-text-secondary); margin-top: 4px; }
|
|
137
|
+
|
|
138
|
+
/* Form groups */
|
|
139
|
+
.form-group { margin-bottom: 20px; }
|
|
140
|
+
.form-label { font-size: 13px; font-weight: 500; color: var(--zs-text); margin-bottom: 6px; display: block; }
|
|
141
|
+
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
142
|
+
.form-row-3 { grid-template-columns: 1fr 1fr 1fr; }
|
|
143
|
+
.form-row-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
|
|
144
|
+
|
|
145
|
+
/* Color picker grid */
|
|
146
|
+
.color-grid { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
147
|
+
.color-swatch {
|
|
148
|
+
width: 36px; height: 36px; border-radius: 8px;
|
|
149
|
+
cursor: pointer; border: 3px solid transparent;
|
|
150
|
+
transition: all 150ms;
|
|
151
|
+
}
|
|
152
|
+
.color-swatch:hover { transform: scale(1.15); }
|
|
153
|
+
.color-swatch--selected { border-color: var(--zs-text); box-shadow: 0 0 0 2px white, 0 0 0 4px var(--zs-text); }
|
|
154
|
+
|
|
155
|
+
/* Sidebar style picker */
|
|
156
|
+
.style-grid { display: flex; gap: 12px; }
|
|
157
|
+
.style-option {
|
|
158
|
+
flex: 1; padding: 16px; text-align: center;
|
|
159
|
+
border: 2px solid var(--zs-border); border-radius: 8px;
|
|
160
|
+
cursor: pointer; transition: all 150ms;
|
|
161
|
+
}
|
|
162
|
+
.style-option:hover { border-color: var(--zs-primary); }
|
|
163
|
+
.style-option--selected { border-color: var(--zs-primary); background: var(--zs-primary-light); }
|
|
164
|
+
.style-preview {
|
|
165
|
+
width: 60px; height: 40px; border-radius: 4px;
|
|
166
|
+
margin: 0 auto 8px; border: 1px solid var(--zs-border);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Nav editor */
|
|
170
|
+
.nav-list { border: 1px solid var(--zs-border); border-radius: 8px; overflow: hidden; }
|
|
171
|
+
.nav-item-row {
|
|
172
|
+
display: flex; align-items: center; gap: 8px;
|
|
173
|
+
padding: 10px 12px; border-bottom: 1px solid var(--zs-border);
|
|
174
|
+
font-size: 14px;
|
|
175
|
+
}
|
|
176
|
+
.nav-item-row:last-child { border-bottom: none; }
|
|
177
|
+
.nav-item-icon { font-size: 18px; cursor: pointer; }
|
|
178
|
+
.nav-item-title { flex: 1; }
|
|
179
|
+
.nav-item-actions { display: flex; gap: 4px; }
|
|
180
|
+
.nav-item-btn {
|
|
181
|
+
border: none; background: none; cursor: pointer;
|
|
182
|
+
font-size: 14px; padding: 2px 6px; border-radius: 4px;
|
|
183
|
+
color: var(--zs-text-muted); transition: all 100ms;
|
|
184
|
+
}
|
|
185
|
+
.nav-item-btn:hover { background: var(--zs-bg-hover); color: var(--zs-text); }
|
|
186
|
+
.nav-item-btn--danger:hover { color: var(--zs-danger); }
|
|
187
|
+
.add-btn {
|
|
188
|
+
display: flex; align-items: center; gap: 6px;
|
|
189
|
+
padding: 8px 16px; margin-top: 12px;
|
|
190
|
+
border: 1px dashed var(--zs-border); border-radius: 8px;
|
|
191
|
+
background: none; cursor: pointer;
|
|
192
|
+
font-family: var(--zs-font-family); font-size: 13px;
|
|
193
|
+
color: var(--zs-text-secondary); transition: all 150ms;
|
|
194
|
+
}
|
|
195
|
+
.add-btn:hover { border-color: var(--zs-primary); color: var(--zs-primary); }
|
|
196
|
+
|
|
197
|
+
/* Icon picker */
|
|
198
|
+
.icon-grid { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
199
|
+
.icon-btn {
|
|
200
|
+
width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
|
|
201
|
+
border: 1px solid transparent; border-radius: 6px;
|
|
202
|
+
cursor: pointer; font-size: 18px; background: none;
|
|
203
|
+
transition: all 100ms;
|
|
204
|
+
}
|
|
205
|
+
.icon-btn:hover { background: var(--zs-bg-hover); border-color: var(--zs-border); }
|
|
206
|
+
.icon-btn--selected { background: var(--zs-primary-light); border-color: var(--zs-primary); }
|
|
207
|
+
|
|
208
|
+
/* Preview */
|
|
209
|
+
.preview-container {
|
|
210
|
+
border: 1px solid var(--zs-border); border-radius: 8px;
|
|
211
|
+
height: 500px; overflow: hidden;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Buttons */
|
|
215
|
+
.btn {
|
|
216
|
+
padding: 10px 24px; border-radius: var(--zs-radius);
|
|
217
|
+
font-family: var(--zs-font-family); font-size: 14px;
|
|
218
|
+
font-weight: 500; cursor: pointer; border: 1px solid transparent;
|
|
219
|
+
transition: all 150ms;
|
|
220
|
+
}
|
|
221
|
+
.btn--primary { background: var(--zs-primary); color: white; }
|
|
222
|
+
.btn--primary:hover { background: var(--zs-primary-hover); }
|
|
223
|
+
.btn--secondary { background: var(--zs-bg); color: var(--zs-text); border-color: var(--zs-border); }
|
|
224
|
+
.btn--secondary:hover { background: var(--zs-bg-hover); }
|
|
225
|
+
.btn--success { background: var(--zs-success); color: white; }
|
|
226
|
+
.btn--success:hover { opacity: 0.9; }
|
|
227
|
+
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
228
|
+
|
|
229
|
+
/* Data source rows */
|
|
230
|
+
.ds-card {
|
|
231
|
+
border: 1px solid var(--zs-border); border-radius: 8px;
|
|
232
|
+
padding: 16px; margin-bottom: 12px;
|
|
233
|
+
background: var(--zs-bg);
|
|
234
|
+
}
|
|
235
|
+
.ds-card-header {
|
|
236
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
237
|
+
margin-bottom: 12px;
|
|
238
|
+
}
|
|
239
|
+
.ds-card-title { font-size: 14px; font-weight: 500; color: var(--zs-text); }
|
|
240
|
+
.ds-method-badge {
|
|
241
|
+
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
|
242
|
+
font-size: 11px; font-weight: 600; color: white;
|
|
243
|
+
}
|
|
244
|
+
.ds-method-GET { background: #27ae60; }
|
|
245
|
+
.ds-method-POST { background: #3498db; }
|
|
246
|
+
.ds-method-PUT { background: #e67e22; }
|
|
247
|
+
.ds-method-PATCH { background: #9b59b6; }
|
|
248
|
+
.ds-method-DELETE { background: #e74c3c; }
|
|
249
|
+
|
|
250
|
+
/* Theme preview card */
|
|
251
|
+
.theme-preview-card {
|
|
252
|
+
border: 1px solid var(--zs-border); border-radius: 12px;
|
|
253
|
+
padding: 24px; margin-top: 20px;
|
|
254
|
+
transition: all 200ms;
|
|
255
|
+
}
|
|
256
|
+
.theme-preview-header {
|
|
257
|
+
font-size: 16px; font-weight: 600; margin-bottom: 8px;
|
|
258
|
+
}
|
|
259
|
+
.theme-preview-text {
|
|
260
|
+
font-size: 13px; margin-bottom: 16px; opacity: 0.7;
|
|
261
|
+
}
|
|
262
|
+
.theme-preview-btn {
|
|
263
|
+
display: inline-block; padding: 8px 20px;
|
|
264
|
+
border: none; border-radius: 8px;
|
|
265
|
+
color: white; font-size: 13px; font-weight: 500;
|
|
266
|
+
}
|
|
267
|
+
.theme-preview-input {
|
|
268
|
+
display: inline-block; padding: 6px 12px;
|
|
269
|
+
border: 1px solid; border-radius: 8px;
|
|
270
|
+
font-size: 13px; margin-left: 8px; width: 150px;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* Code modal */
|
|
274
|
+
.code-modal-overlay {
|
|
275
|
+
position: fixed; inset: 0; z-index: 9999;
|
|
276
|
+
background: rgba(0,0,0,0.5); display: flex;
|
|
277
|
+
align-items: center; justify-content: center;
|
|
278
|
+
}
|
|
279
|
+
.code-modal {
|
|
280
|
+
background: var(--zs-bg); border-radius: 12px;
|
|
281
|
+
width: 90%; max-width: 800px; max-height: 80vh;
|
|
282
|
+
display: flex; flex-direction: column;
|
|
283
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
284
|
+
}
|
|
285
|
+
.code-modal-header {
|
|
286
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
287
|
+
padding: 16px 20px; border-bottom: 1px solid var(--zs-border);
|
|
288
|
+
}
|
|
289
|
+
.code-modal-header h3 { margin: 0; font-size: 16px; }
|
|
290
|
+
.code-modal-body {
|
|
291
|
+
flex: 1; overflow: auto; padding: 0;
|
|
292
|
+
}
|
|
293
|
+
.code-modal-body pre {
|
|
294
|
+
margin: 0; padding: 20px; font-size: 13px;
|
|
295
|
+
font-family: 'Fira Code', 'Cascadia Code', monospace;
|
|
296
|
+
line-height: 1.5; white-space: pre-wrap; word-break: break-word;
|
|
297
|
+
background: var(--zs-bg-secondary); min-height: 200px;
|
|
298
|
+
}
|
|
299
|
+
.code-modal-footer {
|
|
300
|
+
display: flex; gap: 8px; justify-content: flex-end;
|
|
301
|
+
padding: 12px 20px; border-top: 1px solid var(--zs-border);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* Export buttons row */
|
|
305
|
+
.export-row {
|
|
306
|
+
display: flex; gap: 8px; flex-wrap: wrap;
|
|
307
|
+
}
|
|
308
308
|
`]; }
|
|
309
309
|
connectedCallback() {
|
|
310
310
|
super.connectedCallback();
|
|
@@ -353,57 +353,57 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
353
353
|
}
|
|
354
354
|
// ─── Render ───────────────────────────────────────
|
|
355
355
|
render() {
|
|
356
|
-
return html `
|
|
357
|
-
<div class="wizard">
|
|
358
|
-
<div class="wizard-steps">
|
|
359
|
-
${STEPS.map((step, i) => html `
|
|
360
|
-
<div class="wizard-step ${i === this.currentStep ? 'wizard-step--active' : i < this.currentStep ? 'wizard-step--done' : ''}"
|
|
356
|
+
return html `
|
|
357
|
+
<div class="wizard">
|
|
358
|
+
<div class="wizard-steps">
|
|
359
|
+
${STEPS.map((step, i) => html `
|
|
360
|
+
<div class="wizard-step ${i === this.currentStep ? 'wizard-step--active' : i < this.currentStep ? 'wizard-step--done' : ''}"
|
|
361
361
|
@click="${() => { if (i <= this.currentStep || this.config)
|
|
362
|
-
this.currentStep = i; }}">
|
|
363
|
-
<span class="wizard-step-icon">${i < this.currentStep ? '✓' : step.icon}</span>
|
|
364
|
-
<span class="wizard-step-title">${step.title}</span>
|
|
365
|
-
</div>
|
|
366
|
-
`)}
|
|
367
|
-
</div>
|
|
368
|
-
|
|
369
|
-
<div class="wizard-body">
|
|
370
|
-
<h2 class="wizard-title">${STEPS[this.currentStep].title}</h2>
|
|
371
|
-
<p class="wizard-desc">${STEPS[this.currentStep].description}</p>
|
|
372
|
-
|
|
362
|
+
this.currentStep = i; }}">
|
|
363
|
+
<span class="wizard-step-icon">${i < this.currentStep ? '✓' : step.icon}</span>
|
|
364
|
+
<span class="wizard-step-title">${step.title}</span>
|
|
365
|
+
</div>
|
|
366
|
+
`)}
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
<div class="wizard-body">
|
|
370
|
+
<h2 class="wizard-title">${STEPS[this.currentStep].title}</h2>
|
|
371
|
+
<p class="wizard-desc">${STEPS[this.currentStep].description}</p>
|
|
372
|
+
|
|
373
373
|
${this.currentStep === 0 ? this.renderTemplateStep() :
|
|
374
374
|
this.currentStep === 1 ? this.renderBrandingStep() :
|
|
375
375
|
this.currentStep === 2 ? this.renderNavStep() :
|
|
376
376
|
this.currentStep === 3 ? this.renderDataSourcesStep() :
|
|
377
377
|
this.currentStep === 4 ? this.renderPagesStep() :
|
|
378
378
|
this.currentStep === 5 ? this.renderThemeStep() :
|
|
379
|
-
this.renderPreviewStep()}
|
|
380
|
-
</div>
|
|
381
|
-
|
|
382
|
-
<div class="wizard-footer">
|
|
383
|
-
<button class="btn btn--secondary" ?disabled="${this.currentStep === 0}" @click="${this.prev}">Anterior</button>
|
|
379
|
+
this.renderPreviewStep()}
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<div class="wizard-footer">
|
|
383
|
+
<button class="btn btn--secondary" ?disabled="${this.currentStep === 0}" @click="${this.prev}">Anterior</button>
|
|
384
384
|
${this.currentStep < STEPS.length - 1
|
|
385
385
|
? html `<button class="btn btn--primary" ?disabled="${!this.canNext()}" @click="${this.next}">Siguiente</button>`
|
|
386
|
-
: html `<button class="btn btn--success" @click="${this.finish}">Crear Aplicacion</button>`}
|
|
387
|
-
</div>
|
|
388
|
-
</div>
|
|
389
|
-
|
|
390
|
-
${this.codeModalOpen ? this.renderCodeModal() : nothing}
|
|
386
|
+
: html `<button class="btn btn--success" @click="${this.finish}">Crear Aplicacion</button>`}
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
${this.codeModalOpen ? this.renderCodeModal() : nothing}
|
|
391
391
|
`;
|
|
392
392
|
}
|
|
393
393
|
// ─── Step 1: Template Selection ──────────────────
|
|
394
394
|
renderTemplateStep() {
|
|
395
395
|
const templates = listAppTemplates();
|
|
396
|
-
return html `
|
|
397
|
-
<div class="template-grid">
|
|
398
|
-
${templates.map(t => html `
|
|
399
|
-
<div class="template-card ${this.selectedTemplate === t.id ? 'template-card--selected' : ''}"
|
|
400
|
-
@click="${() => { this.selectedTemplate = t.id; }}">
|
|
401
|
-
<div class="template-icon">${t.icon}</div>
|
|
402
|
-
<div class="template-title">${t.title}</div>
|
|
403
|
-
<div class="template-desc">${t.description}</div>
|
|
404
|
-
</div>
|
|
405
|
-
`)}
|
|
406
|
-
</div>
|
|
396
|
+
return html `
|
|
397
|
+
<div class="template-grid">
|
|
398
|
+
${templates.map(t => html `
|
|
399
|
+
<div class="template-card ${this.selectedTemplate === t.id ? 'template-card--selected' : ''}"
|
|
400
|
+
@click="${() => { this.selectedTemplate = t.id; }}">
|
|
401
|
+
<div class="template-icon">${t.icon}</div>
|
|
402
|
+
<div class="template-title">${t.title}</div>
|
|
403
|
+
<div class="template-desc">${t.description}</div>
|
|
404
|
+
</div>
|
|
405
|
+
`)}
|
|
406
|
+
</div>
|
|
407
407
|
`;
|
|
408
408
|
}
|
|
409
409
|
// ─── Step 2: Branding ─────────────────────────────
|
|
@@ -411,92 +411,92 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
411
411
|
if (!this.config)
|
|
412
412
|
return nothing;
|
|
413
413
|
const b = this.config.branding;
|
|
414
|
-
return html `
|
|
415
|
-
<div class="form-row">
|
|
416
|
-
<div class="form-group">
|
|
417
|
-
<label class="form-label">Nombre de la App</label>
|
|
418
|
-
<input class="zs-input" .value="${b.title ?? ''}" @input="${(e) => { this.config.branding.title = e.target.value; this.requestUpdate(); }}" />
|
|
419
|
-
</div>
|
|
420
|
-
<div class="form-group">
|
|
421
|
-
<label class="form-label">Subtitulo</label>
|
|
422
|
-
<input class="zs-input" .value="${b.subtitle ?? ''}" @input="${(e) => { this.config.branding.subtitle = e.target.value; this.requestUpdate(); }}" />
|
|
423
|
-
</div>
|
|
424
|
-
</div>
|
|
425
|
-
|
|
426
|
-
<div class="form-group">
|
|
427
|
-
<label class="form-label">Color Principal</label>
|
|
428
|
-
<div class="color-grid">
|
|
429
|
-
${COLOR_PALETTE.map(color => html `
|
|
430
|
-
<div class="color-swatch ${b.primaryColor === color ? 'color-swatch--selected' : ''}"
|
|
431
|
-
style="background: ${color}"
|
|
432
|
-
@click="${() => { this.config.branding.primaryColor = color; this.requestUpdate(); }}"
|
|
433
|
-
></div>
|
|
434
|
-
`)}
|
|
435
|
-
</div>
|
|
436
|
-
</div>
|
|
437
|
-
|
|
438
|
-
<div class="form-group">
|
|
439
|
-
<label class="form-label">Estilo del Sidebar</label>
|
|
440
|
-
<div class="style-grid">
|
|
441
|
-
${SIDEBAR_STYLES.map(s => html `
|
|
442
|
-
<div class="style-option ${b.sidebarStyle === s.value ? 'style-option--selected' : ''}"
|
|
443
|
-
@click="${() => { this.config.branding.sidebarStyle = s.value; this.requestUpdate(); }}">
|
|
444
|
-
<div class="style-preview" style="background: ${s.preview}"></div>
|
|
445
|
-
<div style="font-size:13px;font-weight:500;">${s.label}</div>
|
|
446
|
-
</div>
|
|
447
|
-
`)}
|
|
448
|
-
</div>
|
|
449
|
-
</div>
|
|
450
|
-
|
|
451
|
-
<div class="form-group">
|
|
452
|
-
<label class="form-label">URL del Logo (opcional)</label>
|
|
453
|
-
<input class="zs-input" .value="${b.logo ?? ''}" placeholder="https://..." @input="${(e) => { this.config.branding.logo = e.target.value; this.requestUpdate(); }}" />
|
|
454
|
-
</div>
|
|
414
|
+
return html `
|
|
415
|
+
<div class="form-row">
|
|
416
|
+
<div class="form-group">
|
|
417
|
+
<label class="form-label">Nombre de la App</label>
|
|
418
|
+
<input class="zs-input" .value="${b.title ?? ''}" @input="${(e) => { this.config.branding.title = e.target.value; this.requestUpdate(); }}" />
|
|
419
|
+
</div>
|
|
420
|
+
<div class="form-group">
|
|
421
|
+
<label class="form-label">Subtitulo</label>
|
|
422
|
+
<input class="zs-input" .value="${b.subtitle ?? ''}" @input="${(e) => { this.config.branding.subtitle = e.target.value; this.requestUpdate(); }}" />
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<div class="form-group">
|
|
427
|
+
<label class="form-label">Color Principal</label>
|
|
428
|
+
<div class="color-grid">
|
|
429
|
+
${COLOR_PALETTE.map(color => html `
|
|
430
|
+
<div class="color-swatch ${b.primaryColor === color ? 'color-swatch--selected' : ''}"
|
|
431
|
+
style="background: ${color}"
|
|
432
|
+
@click="${() => { this.config.branding.primaryColor = color; this.requestUpdate(); }}"
|
|
433
|
+
></div>
|
|
434
|
+
`)}
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
<div class="form-group">
|
|
439
|
+
<label class="form-label">Estilo del Sidebar</label>
|
|
440
|
+
<div class="style-grid">
|
|
441
|
+
${SIDEBAR_STYLES.map(s => html `
|
|
442
|
+
<div class="style-option ${b.sidebarStyle === s.value ? 'style-option--selected' : ''}"
|
|
443
|
+
@click="${() => { this.config.branding.sidebarStyle = s.value; this.requestUpdate(); }}">
|
|
444
|
+
<div class="style-preview" style="background: ${s.preview}"></div>
|
|
445
|
+
<div style="font-size:13px;font-weight:500;">${s.label}</div>
|
|
446
|
+
</div>
|
|
447
|
+
`)}
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
|
|
451
|
+
<div class="form-group">
|
|
452
|
+
<label class="form-label">URL del Logo (opcional)</label>
|
|
453
|
+
<input class="zs-input" .value="${b.logo ?? ''}" placeholder="https://..." @input="${(e) => { this.config.branding.logo = e.target.value; this.requestUpdate(); }}" />
|
|
454
|
+
</div>
|
|
455
455
|
`;
|
|
456
456
|
}
|
|
457
457
|
// ─── Step 3: Navigation Editor ────────────────────
|
|
458
458
|
renderNavStep() {
|
|
459
459
|
if (!this.config)
|
|
460
460
|
return nothing;
|
|
461
|
-
return html `
|
|
462
|
-
<div class="nav-list">
|
|
463
|
-
${this.config.navigation.map((item, i) => html `
|
|
464
|
-
<div class="nav-item-row">
|
|
465
|
-
<span class="nav-item-icon" @click="${() => { this.editingNavIndex = i; this.showIconPicker = !this.showIconPicker; }}">${item.icon ?? (item.kind === 'header' ? '📌' : item.kind === 'divider' ? '—' : '📄')}</span>
|
|
461
|
+
return html `
|
|
462
|
+
<div class="nav-list">
|
|
463
|
+
${this.config.navigation.map((item, i) => html `
|
|
464
|
+
<div class="nav-item-row">
|
|
465
|
+
<span class="nav-item-icon" @click="${() => { this.editingNavIndex = i; this.showIconPicker = !this.showIconPicker; }}">${item.icon ?? (item.kind === 'header' ? '📌' : item.kind === 'divider' ? '—' : '📄')}</span>
|
|
466
466
|
${item.kind === 'divider'
|
|
467
467
|
? html `<span class="nav-item-title" style="color:var(--zs-text-muted);font-style:italic;">Separador</span>`
|
|
468
|
-
: html `<input class="zs-input" style="height:32px;font-size:13px;" .value="${item.title ?? ''}" @input="${(e) => { this.config.navigation[i].title = e.target.value; this.requestUpdate(); }}" />`}
|
|
469
|
-
<div class="nav-item-actions">
|
|
470
|
-
<button class="nav-item-btn" title="Subir" @click="${() => this.moveNav(i, -1)}">↑</button>
|
|
471
|
-
<button class="nav-item-btn" title="Bajar" @click="${() => this.moveNav(i, 1)}">↓</button>
|
|
472
|
-
<button class="nav-item-btn nav-item-btn--danger" title="Eliminar" @click="${() => { this.config.navigation.splice(i, 1); this.requestUpdate(); }}">✕</button>
|
|
473
|
-
</div>
|
|
474
|
-
</div>
|
|
475
|
-
`)}
|
|
476
|
-
</div>
|
|
477
|
-
|
|
478
|
-
${this.showIconPicker ? html `
|
|
479
|
-
<div style="margin-top:12px;padding:12px;border:1px solid var(--zs-border);border-radius:8px;">
|
|
480
|
-
<div style="font-size:12px;color:var(--zs-text-secondary);margin-bottom:8px;">Selecciona un icono:</div>
|
|
481
|
-
<div class="icon-grid">
|
|
482
|
-
${ICON_PALETTE.map(icon => html `
|
|
468
|
+
: html `<input class="zs-input" style="height:32px;font-size:13px;" .value="${item.title ?? ''}" @input="${(e) => { this.config.navigation[i].title = e.target.value; this.requestUpdate(); }}" />`}
|
|
469
|
+
<div class="nav-item-actions">
|
|
470
|
+
<button class="nav-item-btn" title="Subir" @click="${() => this.moveNav(i, -1)}">↑</button>
|
|
471
|
+
<button class="nav-item-btn" title="Bajar" @click="${() => this.moveNav(i, 1)}">↓</button>
|
|
472
|
+
<button class="nav-item-btn nav-item-btn--danger" title="Eliminar" @click="${() => { this.config.navigation.splice(i, 1); this.requestUpdate(); }}">✕</button>
|
|
473
|
+
</div>
|
|
474
|
+
</div>
|
|
475
|
+
`)}
|
|
476
|
+
</div>
|
|
477
|
+
|
|
478
|
+
${this.showIconPicker ? html `
|
|
479
|
+
<div style="margin-top:12px;padding:12px;border:1px solid var(--zs-border);border-radius:8px;">
|
|
480
|
+
<div style="font-size:12px;color:var(--zs-text-secondary);margin-bottom:8px;">Selecciona un icono:</div>
|
|
481
|
+
<div class="icon-grid">
|
|
482
|
+
${ICON_PALETTE.map(icon => html `
|
|
483
483
|
<button class="icon-btn" @click="${() => {
|
|
484
484
|
if (this.editingNavIndex >= 0) {
|
|
485
485
|
this.config.navigation[this.editingNavIndex].icon = icon;
|
|
486
486
|
this.showIconPicker = false;
|
|
487
487
|
this.requestUpdate();
|
|
488
488
|
}
|
|
489
|
-
}}">${icon}</button>
|
|
490
|
-
`)}
|
|
491
|
-
</div>
|
|
492
|
-
</div>
|
|
493
|
-
` : ''}
|
|
494
|
-
|
|
495
|
-
<div style="display:flex;gap:8px;">
|
|
496
|
-
<button class="add-btn" @click="${() => this.addNavItem('page')}">➕ Pagina</button>
|
|
497
|
-
<button class="add-btn" @click="${() => this.addNavItem('header')}">📌 Seccion</button>
|
|
498
|
-
<button class="add-btn" @click="${() => this.addNavItem('divider')}">— Separador</button>
|
|
499
|
-
</div>
|
|
489
|
+
}}">${icon}</button>
|
|
490
|
+
`)}
|
|
491
|
+
</div>
|
|
492
|
+
</div>
|
|
493
|
+
` : ''}
|
|
494
|
+
|
|
495
|
+
<div style="display:flex;gap:8px;">
|
|
496
|
+
<button class="add-btn" @click="${() => this.addNavItem('page')}">➕ Pagina</button>
|
|
497
|
+
<button class="add-btn" @click="${() => this.addNavItem('header')}">📌 Seccion</button>
|
|
498
|
+
<button class="add-btn" @click="${() => this.addNavItem('divider')}">— Separador</button>
|
|
499
|
+
</div>
|
|
500
500
|
`;
|
|
501
501
|
}
|
|
502
502
|
addNavItem(kind) {
|
|
@@ -531,77 +531,77 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
531
531
|
if (!this.config)
|
|
532
532
|
return nothing;
|
|
533
533
|
const sources = this.ensureDataSources();
|
|
534
|
-
return html `
|
|
535
|
-
${sources.length === 0 ? html `
|
|
536
|
-
<div style="text-align:center;padding:40px 20px;color:var(--zs-text-muted);">
|
|
537
|
-
<div style="font-size:40px;margin-bottom:12px;">🔗</div>
|
|
538
|
-
<div style="font-size:14px;">No hay fuentes de datos configuradas.</div>
|
|
539
|
-
<div style="font-size:13px;margin-top:4px;">Agrega APIs REST para conectar tus paginas con datos reales.</div>
|
|
540
|
-
</div>
|
|
541
|
-
` : nothing}
|
|
542
|
-
|
|
543
|
-
${sources.map((ds, i) => html `
|
|
544
|
-
<div class="ds-card">
|
|
545
|
-
<div class="ds-card-header">
|
|
546
|
-
<span class="ds-card-title">${ds.name || `Fuente ${i + 1}`}</span>
|
|
547
|
-
<div style="display:flex;gap:8px;align-items:center;">
|
|
548
|
-
<span class="ds-method-badge ds-method-${ds.method ?? 'GET'}">${ds.method ?? 'GET'}</span>
|
|
549
|
-
<button class="nav-item-btn nav-item-btn--danger" title="Eliminar"
|
|
550
|
-
@click="${() => { sources.splice(i, 1); this.requestUpdate(); }}">✕</button>
|
|
551
|
-
</div>
|
|
552
|
-
</div>
|
|
553
|
-
|
|
554
|
-
<div class="form-row" style="margin-bottom:12px;">
|
|
555
|
-
<div class="form-group" style="margin-bottom:0;">
|
|
556
|
-
<label class="form-label">ID</label>
|
|
534
|
+
return html `
|
|
535
|
+
${sources.length === 0 ? html `
|
|
536
|
+
<div style="text-align:center;padding:40px 20px;color:var(--zs-text-muted);">
|
|
537
|
+
<div style="font-size:40px;margin-bottom:12px;">🔗</div>
|
|
538
|
+
<div style="font-size:14px;">No hay fuentes de datos configuradas.</div>
|
|
539
|
+
<div style="font-size:13px;margin-top:4px;">Agrega APIs REST para conectar tus paginas con datos reales.</div>
|
|
540
|
+
</div>
|
|
541
|
+
` : nothing}
|
|
542
|
+
|
|
543
|
+
${sources.map((ds, i) => html `
|
|
544
|
+
<div class="ds-card">
|
|
545
|
+
<div class="ds-card-header">
|
|
546
|
+
<span class="ds-card-title">${ds.name || `Fuente ${i + 1}`}</span>
|
|
547
|
+
<div style="display:flex;gap:8px;align-items:center;">
|
|
548
|
+
<span class="ds-method-badge ds-method-${ds.method ?? 'GET'}">${ds.method ?? 'GET'}</span>
|
|
549
|
+
<button class="nav-item-btn nav-item-btn--danger" title="Eliminar"
|
|
550
|
+
@click="${() => { sources.splice(i, 1); this.requestUpdate(); }}">✕</button>
|
|
551
|
+
</div>
|
|
552
|
+
</div>
|
|
553
|
+
|
|
554
|
+
<div class="form-row" style="margin-bottom:12px;">
|
|
555
|
+
<div class="form-group" style="margin-bottom:0;">
|
|
556
|
+
<label class="form-label">ID</label>
|
|
557
557
|
<input class="zs-input" .value="${ds.id}" @input="${(e) => {
|
|
558
558
|
ds.id = e.target.value;
|
|
559
559
|
this.requestUpdate();
|
|
560
|
-
}}" placeholder="customers" />
|
|
561
|
-
</div>
|
|
562
|
-
<div class="form-group" style="margin-bottom:0;">
|
|
563
|
-
<label class="form-label">Nombre</label>
|
|
560
|
+
}}" placeholder="customers" />
|
|
561
|
+
</div>
|
|
562
|
+
<div class="form-group" style="margin-bottom:0;">
|
|
563
|
+
<label class="form-label">Nombre</label>
|
|
564
564
|
<input class="zs-input" .value="${ds.name}" @input="${(e) => {
|
|
565
565
|
ds.name = e.target.value;
|
|
566
566
|
this.requestUpdate();
|
|
567
|
-
}}" placeholder="Clientes" />
|
|
568
|
-
</div>
|
|
569
|
-
</div>
|
|
570
|
-
|
|
571
|
-
<div class="form-row" style="margin-bottom:12px;">
|
|
572
|
-
<div class="form-group" style="margin-bottom:0;grid-column:span 1;">
|
|
573
|
-
<label class="form-label">Metodo</label>
|
|
567
|
+
}}" placeholder="Clientes" />
|
|
568
|
+
</div>
|
|
569
|
+
</div>
|
|
570
|
+
|
|
571
|
+
<div class="form-row" style="margin-bottom:12px;">
|
|
572
|
+
<div class="form-group" style="margin-bottom:0;grid-column:span 1;">
|
|
573
|
+
<label class="form-label">Metodo</label>
|
|
574
574
|
<select class="zs-input" .value="${ds.method ?? 'GET'}" @change="${(e) => {
|
|
575
575
|
ds.method = e.target.value;
|
|
576
576
|
this.requestUpdate();
|
|
577
|
-
}}">
|
|
578
|
-
${HTTP_METHODS.map(m => html `<option value="${m}" ?selected="${ds.method === m}">${m}</option>`)}
|
|
579
|
-
</select>
|
|
580
|
-
</div>
|
|
581
|
-
<div class="form-group" style="margin-bottom:0;">
|
|
582
|
-
<label class="form-label">Intervalo de refresco (ms)</label>
|
|
583
|
-
<input class="zs-input" type="number" .value="${String(ds.refreshInterval ?? '')}" placeholder="0 = sin polling"
|
|
577
|
+
}}">
|
|
578
|
+
${HTTP_METHODS.map(m => html `<option value="${m}" ?selected="${ds.method === m}">${m}</option>`)}
|
|
579
|
+
</select>
|
|
580
|
+
</div>
|
|
581
|
+
<div class="form-group" style="margin-bottom:0;">
|
|
582
|
+
<label class="form-label">Intervalo de refresco (ms)</label>
|
|
583
|
+
<input class="zs-input" type="number" .value="${String(ds.refreshInterval ?? '')}" placeholder="0 = sin polling"
|
|
584
584
|
@input="${(e) => {
|
|
585
585
|
const v = parseInt(e.target.value);
|
|
586
586
|
ds.refreshInterval = isNaN(v) || v <= 0 ? undefined : v;
|
|
587
587
|
this.requestUpdate();
|
|
588
|
-
}}" />
|
|
589
|
-
</div>
|
|
590
|
-
</div>
|
|
591
|
-
|
|
592
|
-
<div class="form-group" style="margin-bottom:12px;">
|
|
593
|
-
<label class="form-label">URL</label>
|
|
588
|
+
}}" />
|
|
589
|
+
</div>
|
|
590
|
+
</div>
|
|
591
|
+
|
|
592
|
+
<div class="form-group" style="margin-bottom:12px;">
|
|
593
|
+
<label class="form-label">URL</label>
|
|
594
594
|
<input class="zs-input" .value="${ds.url ?? ''}" @input="${(e) => {
|
|
595
595
|
ds.url = e.target.value;
|
|
596
596
|
this.requestUpdate();
|
|
597
|
-
}}" placeholder="https://api.ejemplo.com/v1/resource" />
|
|
598
|
-
</div>
|
|
599
|
-
|
|
600
|
-
<div class="form-group" style="margin-bottom:0;">
|
|
601
|
-
<label class="form-label">Headers (JSON, opcional)</label>
|
|
602
|
-
<textarea class="zs-input" rows="2" style="font-family:monospace;font-size:12px;resize:vertical;"
|
|
603
|
-
.value="${ds.headers ? JSON.stringify(ds.headers, null, 2) : ''}"
|
|
604
|
-
placeholder='{ "Authorization": "Bearer ..." }'
|
|
597
|
+
}}" placeholder="https://api.ejemplo.com/v1/resource" />
|
|
598
|
+
</div>
|
|
599
|
+
|
|
600
|
+
<div class="form-group" style="margin-bottom:0;">
|
|
601
|
+
<label class="form-label">Headers (JSON, opcional)</label>
|
|
602
|
+
<textarea class="zs-input" rows="2" style="font-family:monospace;font-size:12px;resize:vertical;"
|
|
603
|
+
.value="${ds.headers ? JSON.stringify(ds.headers, null, 2) : ''}"
|
|
604
|
+
placeholder='{ "Authorization": "Bearer ..." }'
|
|
605
605
|
@change="${(e) => {
|
|
606
606
|
const raw = e.target.value.trim();
|
|
607
607
|
if (!raw) {
|
|
@@ -614,12 +614,12 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
614
614
|
}
|
|
615
615
|
catch { /* ignore invalid JSON */ }
|
|
616
616
|
this.requestUpdate();
|
|
617
|
-
}}"
|
|
618
|
-
></textarea>
|
|
619
|
-
</div>
|
|
620
|
-
</div>
|
|
621
|
-
`)}
|
|
622
|
-
|
|
617
|
+
}}"
|
|
618
|
+
></textarea>
|
|
619
|
+
</div>
|
|
620
|
+
</div>
|
|
621
|
+
`)}
|
|
622
|
+
|
|
623
623
|
<button class="add-btn" @click="${() => {
|
|
624
624
|
const id = `ds-${Date.now()}`;
|
|
625
625
|
this.ensureDataSources().push({
|
|
@@ -631,44 +631,44 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
631
631
|
autoFetch: true,
|
|
632
632
|
});
|
|
633
633
|
this.requestUpdate();
|
|
634
|
-
}}">🔗 Nueva Fuente de Datos</button>
|
|
634
|
+
}}">🔗 Nueva Fuente de Datos</button>
|
|
635
635
|
`;
|
|
636
636
|
}
|
|
637
637
|
// ─── Step 5: Pages ────────────────────────────────
|
|
638
638
|
renderPagesStep() {
|
|
639
639
|
if (!this.config)
|
|
640
640
|
return nothing;
|
|
641
|
-
return html `
|
|
642
|
-
<div class="nav-list">
|
|
643
|
-
${this.config.pages.map((page, i) => html `
|
|
644
|
-
<div class="nav-item-row">
|
|
645
|
-
<span class="nav-item-icon">${this.getContentIcon(page.content)}</span>
|
|
646
|
-
<div style="flex:1;">
|
|
647
|
-
<input class="zs-input" style="height:30px;font-size:13px;margin-bottom:4px;" .value="${page.title}" @input="${(e) => { this.config.pages[i].title = e.target.value; this.requestUpdate(); }}" />
|
|
648
|
-
<div style="display:flex;gap:8px;align-items:center;">
|
|
649
|
-
<span style="font-size:11px;color:var(--zs-text-muted);">/${page.segment}</span>
|
|
650
|
-
<select style="font-size:11px;padding:2px 6px;border:1px solid var(--zs-border);border-radius:4px;" .value="${page.content}" @change="${(e) => { this.config.pages[i].content = e.target.value; this.requestUpdate(); }}">
|
|
651
|
-
<option value="empty">Vacia</option>
|
|
652
|
-
<option value="cards">Cards/Dashboard</option>
|
|
653
|
-
<option value="datagrid">Grid de Datos</option>
|
|
654
|
-
<option value="schema">Formulario</option>
|
|
655
|
-
<option value="chart">Graficos</option>
|
|
656
|
-
<option value="html">HTML</option>
|
|
657
|
-
<option value="iframe">iFrame</option>
|
|
658
|
-
<option value="tabs">Tabs</option>
|
|
659
|
-
<option value="custom">Custom</option>
|
|
660
|
-
</select>
|
|
661
|
-
</div>
|
|
662
|
-
</div>
|
|
663
|
-
<button class="nav-item-btn nav-item-btn--danger" @click="${() => { this.config.pages.splice(i, 1); this.requestUpdate(); }}">✕</button>
|
|
664
|
-
</div>
|
|
665
|
-
`)}
|
|
666
|
-
</div>
|
|
641
|
+
return html `
|
|
642
|
+
<div class="nav-list">
|
|
643
|
+
${this.config.pages.map((page, i) => html `
|
|
644
|
+
<div class="nav-item-row">
|
|
645
|
+
<span class="nav-item-icon">${this.getContentIcon(page.content)}</span>
|
|
646
|
+
<div style="flex:1;">
|
|
647
|
+
<input class="zs-input" style="height:30px;font-size:13px;margin-bottom:4px;" .value="${page.title}" @input="${(e) => { this.config.pages[i].title = e.target.value; this.requestUpdate(); }}" />
|
|
648
|
+
<div style="display:flex;gap:8px;align-items:center;">
|
|
649
|
+
<span style="font-size:11px;color:var(--zs-text-muted);">/${page.segment}</span>
|
|
650
|
+
<select style="font-size:11px;padding:2px 6px;border:1px solid var(--zs-border);border-radius:4px;" .value="${page.content}" @change="${(e) => { this.config.pages[i].content = e.target.value; this.requestUpdate(); }}">
|
|
651
|
+
<option value="empty">Vacia</option>
|
|
652
|
+
<option value="cards">Cards/Dashboard</option>
|
|
653
|
+
<option value="datagrid">Grid de Datos</option>
|
|
654
|
+
<option value="schema">Formulario</option>
|
|
655
|
+
<option value="chart">Graficos</option>
|
|
656
|
+
<option value="html">HTML</option>
|
|
657
|
+
<option value="iframe">iFrame</option>
|
|
658
|
+
<option value="tabs">Tabs</option>
|
|
659
|
+
<option value="custom">Custom</option>
|
|
660
|
+
</select>
|
|
661
|
+
</div>
|
|
662
|
+
</div>
|
|
663
|
+
<button class="nav-item-btn nav-item-btn--danger" @click="${() => { this.config.pages.splice(i, 1); this.requestUpdate(); }}">✕</button>
|
|
664
|
+
</div>
|
|
665
|
+
`)}
|
|
666
|
+
</div>
|
|
667
667
|
<button class="add-btn" @click="${() => {
|
|
668
668
|
const id = `page-${Date.now()}`;
|
|
669
669
|
this.config.pages.push({ id, segment: id, title: 'Nueva Pagina', content: 'empty' });
|
|
670
670
|
this.requestUpdate();
|
|
671
|
-
}}">➕ Nueva Pagina</button>
|
|
671
|
+
}}">➕ Nueva Pagina</button>
|
|
672
672
|
`;
|
|
673
673
|
}
|
|
674
674
|
getContentIcon(type) {
|
|
@@ -684,116 +684,116 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
684
684
|
if (!this.config)
|
|
685
685
|
return nothing;
|
|
686
686
|
const theme = this.ensureTheme();
|
|
687
|
-
return html `
|
|
688
|
-
<!-- Theme mode -->
|
|
689
|
-
<div class="form-group">
|
|
690
|
-
<label class="form-label">Modo de Tema</label>
|
|
691
|
-
<div class="style-grid">
|
|
692
|
-
${THEME_MODES.map(m => html `
|
|
693
|
-
<div class="style-option ${theme.mode === m.value ? 'style-option--selected' : ''}"
|
|
694
|
-
@click="${() => { theme.mode = m.value; this.requestUpdate(); }}">
|
|
695
|
-
<div style="font-size:28px;margin-bottom:6px;">${m.icon}</div>
|
|
696
|
-
<div style="font-size:13px;font-weight:500;">${m.label}</div>
|
|
697
|
-
</div>
|
|
698
|
-
`)}
|
|
699
|
-
</div>
|
|
700
|
-
</div>
|
|
701
|
-
|
|
702
|
-
<!-- Primary color -->
|
|
703
|
-
<div class="form-group">
|
|
704
|
-
<label class="form-label">Color Primario (--zs-primary)</label>
|
|
705
|
-
<div class="color-grid">
|
|
706
|
-
${COLOR_PALETTE.map(color => html `
|
|
707
|
-
<div class="color-swatch ${theme.primaryColor === color ? 'color-swatch--selected' : ''}"
|
|
708
|
-
style="background: ${color}"
|
|
709
|
-
@click="${() => { theme.primaryColor = color; this.requestUpdate(); }}"
|
|
710
|
-
></div>
|
|
711
|
-
`)}
|
|
712
|
-
</div>
|
|
713
|
-
</div>
|
|
714
|
-
|
|
715
|
-
<!-- Font family -->
|
|
716
|
-
<div class="form-row">
|
|
717
|
-
<div class="form-group">
|
|
718
|
-
<label class="form-label">Tipografia (--zs-font)</label>
|
|
687
|
+
return html `
|
|
688
|
+
<!-- Theme mode -->
|
|
689
|
+
<div class="form-group">
|
|
690
|
+
<label class="form-label">Modo de Tema</label>
|
|
691
|
+
<div class="style-grid">
|
|
692
|
+
${THEME_MODES.map(m => html `
|
|
693
|
+
<div class="style-option ${theme.mode === m.value ? 'style-option--selected' : ''}"
|
|
694
|
+
@click="${() => { theme.mode = m.value; this.requestUpdate(); }}">
|
|
695
|
+
<div style="font-size:28px;margin-bottom:6px;">${m.icon}</div>
|
|
696
|
+
<div style="font-size:13px;font-weight:500;">${m.label}</div>
|
|
697
|
+
</div>
|
|
698
|
+
`)}
|
|
699
|
+
</div>
|
|
700
|
+
</div>
|
|
701
|
+
|
|
702
|
+
<!-- Primary color -->
|
|
703
|
+
<div class="form-group">
|
|
704
|
+
<label class="form-label">Color Primario (--zs-primary)</label>
|
|
705
|
+
<div class="color-grid">
|
|
706
|
+
${COLOR_PALETTE.map(color => html `
|
|
707
|
+
<div class="color-swatch ${theme.primaryColor === color ? 'color-swatch--selected' : ''}"
|
|
708
|
+
style="background: ${color}"
|
|
709
|
+
@click="${() => { theme.primaryColor = color; this.requestUpdate(); }}"
|
|
710
|
+
></div>
|
|
711
|
+
`)}
|
|
712
|
+
</div>
|
|
713
|
+
</div>
|
|
714
|
+
|
|
715
|
+
<!-- Font family -->
|
|
716
|
+
<div class="form-row">
|
|
717
|
+
<div class="form-group">
|
|
718
|
+
<label class="form-label">Tipografia (--zs-font)</label>
|
|
719
719
|
<select class="zs-input" .value="${theme.fontFamily ?? 'Inter, sans-serif'}" @change="${(e) => {
|
|
720
720
|
theme.fontFamily = e.target.value;
|
|
721
721
|
this.requestUpdate();
|
|
722
|
-
}}">
|
|
723
|
-
${FONT_OPTIONS.map(f => html `<option value="${f}" ?selected="${theme.fontFamily === f}">${f.split(',')[0]}</option>`)}
|
|
724
|
-
</select>
|
|
725
|
-
</div>
|
|
726
|
-
<div class="form-group">
|
|
727
|
-
<label class="form-label">Border Radius (--zs-radius)</label>
|
|
722
|
+
}}">
|
|
723
|
+
${FONT_OPTIONS.map(f => html `<option value="${f}" ?selected="${theme.fontFamily === f}">${f.split(',')[0]}</option>`)}
|
|
724
|
+
</select>
|
|
725
|
+
</div>
|
|
726
|
+
<div class="form-group">
|
|
727
|
+
<label class="form-label">Border Radius (--zs-radius)</label>
|
|
728
728
|
<select class="zs-input" .value="${String(theme.borderRadius ?? 8)}" @change="${(e) => {
|
|
729
729
|
theme.borderRadius = parseInt(e.target.value);
|
|
730
730
|
this.requestUpdate();
|
|
731
|
-
}}">
|
|
732
|
-
${RADIUS_OPTIONS.map(r => html `<option value="${r.value}" ?selected="${theme.borderRadius === r.value}">${r.label}</option>`)}
|
|
733
|
-
</select>
|
|
734
|
-
</div>
|
|
735
|
-
</div>
|
|
736
|
-
|
|
737
|
-
<!-- Accent color -->
|
|
738
|
-
<div class="form-group">
|
|
739
|
-
<label class="form-label">Color de Acento (--zs-accent, opcional)</label>
|
|
740
|
-
<div class="color-grid">
|
|
741
|
-
${COLOR_PALETTE.map(color => html `
|
|
742
|
-
<div class="color-swatch ${theme.accentColor === color ? 'color-swatch--selected' : ''}"
|
|
743
|
-
style="background: ${color}"
|
|
744
|
-
@click="${() => { theme.accentColor = color; this.requestUpdate(); }}"
|
|
745
|
-
></div>
|
|
746
|
-
`)}
|
|
747
|
-
</div>
|
|
748
|
-
</div>
|
|
749
|
-
|
|
750
|
-
<!-- Font size & spacing -->
|
|
751
|
-
<div class="form-row">
|
|
752
|
-
<div class="form-group">
|
|
753
|
-
<label class="form-label">Tamano de Fuente Base (px)</label>
|
|
731
|
+
}}">
|
|
732
|
+
${RADIUS_OPTIONS.map(r => html `<option value="${r.value}" ?selected="${theme.borderRadius === r.value}">${r.label}</option>`)}
|
|
733
|
+
</select>
|
|
734
|
+
</div>
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
<!-- Accent color -->
|
|
738
|
+
<div class="form-group">
|
|
739
|
+
<label class="form-label">Color de Acento (--zs-accent, opcional)</label>
|
|
740
|
+
<div class="color-grid">
|
|
741
|
+
${COLOR_PALETTE.map(color => html `
|
|
742
|
+
<div class="color-swatch ${theme.accentColor === color ? 'color-swatch--selected' : ''}"
|
|
743
|
+
style="background: ${color}"
|
|
744
|
+
@click="${() => { theme.accentColor = color; this.requestUpdate(); }}"
|
|
745
|
+
></div>
|
|
746
|
+
`)}
|
|
747
|
+
</div>
|
|
748
|
+
</div>
|
|
749
|
+
|
|
750
|
+
<!-- Font size & spacing -->
|
|
751
|
+
<div class="form-row">
|
|
752
|
+
<div class="form-group">
|
|
753
|
+
<label class="form-label">Tamano de Fuente Base (px)</label>
|
|
754
754
|
<input class="zs-input" type="number" min="10" max="22" .value="${String(theme.fontSize ?? 14)}" @input="${(e) => {
|
|
755
755
|
theme.fontSize = parseInt(e.target.value) || 14;
|
|
756
756
|
this.requestUpdate();
|
|
757
|
-
}}" />
|
|
758
|
-
</div>
|
|
759
|
-
<div class="form-group">
|
|
760
|
-
<label class="form-label">Espaciado Base (px)</label>
|
|
757
|
+
}}" />
|
|
758
|
+
</div>
|
|
759
|
+
<div class="form-group">
|
|
760
|
+
<label class="form-label">Espaciado Base (px)</label>
|
|
761
761
|
<input class="zs-input" type="number" min="2" max="16" .value="${String(theme.spacing ?? 8)}" @input="${(e) => {
|
|
762
762
|
theme.spacing = parseInt(e.target.value) || 8;
|
|
763
763
|
this.requestUpdate();
|
|
764
|
-
}}" />
|
|
765
|
-
</div>
|
|
766
|
-
</div>
|
|
767
|
-
|
|
768
|
-
<!-- Mini preview -->
|
|
769
|
-
<div class="theme-preview-card" style="
|
|
770
|
-
background: ${theme.mode === 'dark' ? '#1e1e2d' : '#ffffff'};
|
|
771
|
-
color: ${theme.mode === 'dark' ? '#e0e0e0' : '#333333'};
|
|
772
|
-
font-family: ${theme.fontFamily ?? 'Inter, sans-serif'};
|
|
773
|
-
font-size: ${theme.fontSize ?? 14}px;
|
|
774
|
-
border-radius: ${theme.borderRadius ?? 8}px;
|
|
775
|
-
">
|
|
776
|
-
<div class="theme-preview-header" style="color: ${theme.primaryColor ?? '#3498db'};">
|
|
777
|
-
Vista previa del tema
|
|
778
|
-
</div>
|
|
779
|
-
<div class="theme-preview-text">
|
|
780
|
-
Asi se vera tu aplicacion con estos ajustes de estilo.
|
|
781
|
-
</div>
|
|
782
|
-
<span class="theme-preview-btn" style="
|
|
783
|
-
background: ${theme.primaryColor ?? '#3498db'};
|
|
784
|
-
border-radius: ${theme.borderRadius ?? 8}px;
|
|
785
|
-
">Boton Primario</span>
|
|
786
|
-
<input class="theme-preview-input" value="Campo de texto"
|
|
787
|
-
style="
|
|
788
|
-
border-color: ${theme.mode === 'dark' ? '#444' : '#ccc'};
|
|
789
|
-
border-radius: ${theme.borderRadius ?? 8}px;
|
|
790
|
-
background: ${theme.mode === 'dark' ? '#2a2a3d' : '#f9f9f9'};
|
|
791
|
-
color: ${theme.mode === 'dark' ? '#e0e0e0' : '#333'};
|
|
792
|
-
font-family: ${theme.fontFamily ?? 'Inter, sans-serif'};
|
|
793
|
-
"
|
|
794
|
-
readonly
|
|
795
|
-
/>
|
|
796
|
-
</div>
|
|
764
|
+
}}" />
|
|
765
|
+
</div>
|
|
766
|
+
</div>
|
|
767
|
+
|
|
768
|
+
<!-- Mini preview -->
|
|
769
|
+
<div class="theme-preview-card" style="
|
|
770
|
+
background: ${theme.mode === 'dark' ? '#1e1e2d' : '#ffffff'};
|
|
771
|
+
color: ${theme.mode === 'dark' ? '#e0e0e0' : '#333333'};
|
|
772
|
+
font-family: ${theme.fontFamily ?? 'Inter, sans-serif'};
|
|
773
|
+
font-size: ${theme.fontSize ?? 14}px;
|
|
774
|
+
border-radius: ${theme.borderRadius ?? 8}px;
|
|
775
|
+
">
|
|
776
|
+
<div class="theme-preview-header" style="color: ${theme.primaryColor ?? '#3498db'};">
|
|
777
|
+
Vista previa del tema
|
|
778
|
+
</div>
|
|
779
|
+
<div class="theme-preview-text">
|
|
780
|
+
Asi se vera tu aplicacion con estos ajustes de estilo.
|
|
781
|
+
</div>
|
|
782
|
+
<span class="theme-preview-btn" style="
|
|
783
|
+
background: ${theme.primaryColor ?? '#3498db'};
|
|
784
|
+
border-radius: ${theme.borderRadius ?? 8}px;
|
|
785
|
+
">Boton Primario</span>
|
|
786
|
+
<input class="theme-preview-input" value="Campo de texto"
|
|
787
|
+
style="
|
|
788
|
+
border-color: ${theme.mode === 'dark' ? '#444' : '#ccc'};
|
|
789
|
+
border-radius: ${theme.borderRadius ?? 8}px;
|
|
790
|
+
background: ${theme.mode === 'dark' ? '#2a2a3d' : '#f9f9f9'};
|
|
791
|
+
color: ${theme.mode === 'dark' ? '#e0e0e0' : '#333'};
|
|
792
|
+
font-family: ${theme.fontFamily ?? 'Inter, sans-serif'};
|
|
793
|
+
"
|
|
794
|
+
readonly
|
|
795
|
+
/>
|
|
796
|
+
</div>
|
|
797
797
|
`;
|
|
798
798
|
}
|
|
799
799
|
// ─── Step 7: Preview ──────────────────────────────
|
|
@@ -802,34 +802,34 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
802
802
|
return nothing;
|
|
803
803
|
const navPageCount = this.config.navigation.filter(n => n.kind !== 'divider' && n.kind !== 'header').length;
|
|
804
804
|
const dsCount = this.config.dataSources?.length ?? 0;
|
|
805
|
-
return html `
|
|
806
|
-
<div style="display:flex;gap:16px;margin-bottom:16px;align-items:center;flex-wrap:wrap;">
|
|
807
|
-
<div style="flex:1;min-width:200px;">
|
|
808
|
-
<div style="font-size:13px;color:var(--zs-text-secondary);margin-bottom:4px;">Resumen:</div>
|
|
809
|
-
<div style="font-size:14px;">
|
|
810
|
-
<strong>${this.config.branding.title}</strong> —
|
|
811
|
-
${this.config.pages.length} paginas,
|
|
812
|
-
${navPageCount} items en menu${dsCount > 0 ? `, ${dsCount} fuentes de datos` : ''}
|
|
813
|
-
</div>
|
|
814
|
-
</div>
|
|
815
|
-
</div>
|
|
816
|
-
|
|
817
|
-
<!-- Export buttons -->
|
|
818
|
-
<div class="export-row" style="margin-bottom:16px;">
|
|
819
|
-
<button class="btn btn--secondary" @click="${this.exportJson}">📋 Exportar JSON</button>
|
|
820
|
-
<button class="btn btn--secondary" @click="${this.exportReactCode}">⚛️ Exportar Codigo React</button>
|
|
821
|
-
<button class="btn btn--secondary" @click="${this.exportNextCode}">▲ Exportar Codigo Next.js</button>
|
|
805
|
+
return html `
|
|
806
|
+
<div style="display:flex;gap:16px;margin-bottom:16px;align-items:center;flex-wrap:wrap;">
|
|
807
|
+
<div style="flex:1;min-width:200px;">
|
|
808
|
+
<div style="font-size:13px;color:var(--zs-text-secondary);margin-bottom:4px;">Resumen:</div>
|
|
809
|
+
<div style="font-size:14px;">
|
|
810
|
+
<strong>${this.config.branding.title}</strong> —
|
|
811
|
+
${this.config.pages.length} paginas,
|
|
812
|
+
${navPageCount} items en menu${dsCount > 0 ? `, ${dsCount} fuentes de datos` : ''}
|
|
813
|
+
</div>
|
|
814
|
+
</div>
|
|
815
|
+
</div>
|
|
816
|
+
|
|
817
|
+
<!-- Export buttons -->
|
|
818
|
+
<div class="export-row" style="margin-bottom:16px;">
|
|
819
|
+
<button class="btn btn--secondary" @click="${this.exportJson}">📋 Exportar JSON</button>
|
|
820
|
+
<button class="btn btn--secondary" @click="${this.exportReactCode}">⚛️ Exportar Codigo React</button>
|
|
821
|
+
<button class="btn btn--secondary" @click="${this.exportNextCode}">▲ Exportar Codigo Next.js</button>
|
|
822
822
|
<button class="btn btn--secondary" @click="${() => {
|
|
823
823
|
const json = JSON.stringify(this.config, null, 2);
|
|
824
824
|
navigator.clipboard.writeText(json).then(() => {
|
|
825
825
|
alert('JSON copiado al portapapeles');
|
|
826
826
|
});
|
|
827
|
-
}}">📎 Copiar JSON</button>
|
|
828
|
-
</div>
|
|
829
|
-
|
|
830
|
-
<div class="preview-container">
|
|
831
|
-
<zentto-studio-app .config="${this.config}"></zentto-studio-app>
|
|
832
|
-
</div>
|
|
827
|
+
}}">📎 Copiar JSON</button>
|
|
828
|
+
</div>
|
|
829
|
+
|
|
830
|
+
<div class="preview-container">
|
|
831
|
+
<zentto-studio-app .config="${this.config}"></zentto-studio-app>
|
|
832
|
+
</div>
|
|
833
833
|
`;
|
|
834
834
|
}
|
|
835
835
|
// ─── Export Handlers ──────────────────────────────
|
|
@@ -888,25 +888,25 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
|
|
|
888
888
|
}
|
|
889
889
|
// ─── Code Modal ───────────────────────────────────
|
|
890
890
|
renderCodeModal() {
|
|
891
|
-
return html `
|
|
891
|
+
return html `
|
|
892
892
|
<div class="code-modal-overlay" @click="${(e) => {
|
|
893
893
|
if (e.target === e.currentTarget)
|
|
894
894
|
this.closeCodeModal();
|
|
895
|
-
}}">
|
|
896
|
-
<div class="code-modal">
|
|
897
|
-
<div class="code-modal-header">
|
|
898
|
-
<h3>${this.codeModalTitle}</h3>
|
|
899
|
-
<button class="nav-item-btn" @click="${this.closeCodeModal}" style="font-size:18px;">✕</button>
|
|
900
|
-
</div>
|
|
901
|
-
<div class="code-modal-body">
|
|
902
|
-
<pre>${this.codeModalContent}</pre>
|
|
903
|
-
</div>
|
|
904
|
-
<div class="code-modal-footer">
|
|
905
|
-
<button class="btn btn--secondary" @click="${this.downloadCode}">💾 Descargar</button>
|
|
906
|
-
<button class="btn btn--primary" @click="${this.copyCodeToClipboard}">📋 Copiar</button>
|
|
907
|
-
</div>
|
|
908
|
-
</div>
|
|
909
|
-
</div>
|
|
895
|
+
}}">
|
|
896
|
+
<div class="code-modal">
|
|
897
|
+
<div class="code-modal-header">
|
|
898
|
+
<h3>${this.codeModalTitle}</h3>
|
|
899
|
+
<button class="nav-item-btn" @click="${this.closeCodeModal}" style="font-size:18px;">✕</button>
|
|
900
|
+
</div>
|
|
901
|
+
<div class="code-modal-body">
|
|
902
|
+
<pre>${this.codeModalContent}</pre>
|
|
903
|
+
</div>
|
|
904
|
+
<div class="code-modal-footer">
|
|
905
|
+
<button class="btn btn--secondary" @click="${this.downloadCode}">💾 Descargar</button>
|
|
906
|
+
<button class="btn btn--primary" @click="${this.copyCodeToClipboard}">📋 Copiar</button>
|
|
907
|
+
</div>
|
|
908
|
+
</div>
|
|
909
|
+
</div>
|
|
910
910
|
`;
|
|
911
911
|
}
|
|
912
912
|
};
|