@salla.sa/twilight-bundles 0.1.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.
@@ -0,0 +1,1678 @@
1
+ import { findComponentFiles as I } from "./build.js";
2
+ import * as a from "fs";
3
+ import * as c from "path";
4
+ const L = process.env.TWILIGHT_BUNDLES_URL || "https://cdn.salla.network/js/twilight-bundles/latest/twilight-bundles.js";
5
+ let i = process.env.TWILIGHT_FORM_BUILDER_MOCK_BASE_URL || "https://salla.design";
6
+ i = i.replace(/\/$/, "") + "/api/v1/form-builder-mock";
7
+ function G(t, e) {
8
+ var s, l;
9
+ const o = {
10
+ ar: {
11
+ toggleTheme: "تغيير المظهر",
12
+ toggleLang: "English",
13
+ dir: "rtl",
14
+ lang: "ar",
15
+ title: "حزم العناصر",
16
+ noSettings: "لا توجد إعدادات لهذا العنصر",
17
+ addSettings: "إضافة إعدادات لهذا العنصر",
18
+ saveSettings: "حفظ التغييرات"
19
+ },
20
+ en: {
21
+ toggleTheme: "Toggle Theme",
22
+ toggleLang: "Arabic",
23
+ dir: "ltr",
24
+ lang: "en",
25
+ title: "Twilight Bundles",
26
+ noSettings: "No settings for this component",
27
+ addSettings: "Add settings for this component",
28
+ saveSettings: "Save changes"
29
+ }
30
+ }, r = [
31
+ "https://cdn.assets.salla.network/prod/admin/cp/assets/css/icons/sallaicons/style.css?v0.21-languages2",
32
+ "https://cdn.assets.salla.network/prod/admin/vendor/form-builder/form-builder.a58a1e74d158c6a9cd3aeffe2feb6674.css",
33
+ "https://cdn.assets.salla.network/prod/admin/vendor/theme-dashboard/form-builder-theme.198b7a49c2f8cc9bae22de21569b1f42.css"
34
+ ];
35
+ return `<!DOCTYPE html>
36
+ <html lang="ar" dir="rtl">
37
+ <head>
38
+ <meta charset="UTF-8">
39
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
40
+ <title>${o.ar.title}</title>
41
+ <script>
42
+ localStorage.setItem('FormBuilder::debugger', 1);
43
+ window.customComponents = ${JSON.stringify(t.map((n) => n.url))};
44
+ window.customComponentsSchema = ${JSON.stringify(Object.fromEntries(t.map((n) => [n.name, n.schema])))};
45
+ function schemaForComponent(componentName){
46
+ if(localStorage.getItem('form-builder::'+componentName)){
47
+ return htmlSafeString(localStorage.getItem('form-builder::'+componentName));
48
+ }
49
+
50
+ return htmlSafeString(window.customComponentsSchema[componentName]);
51
+ }
52
+ function getComponentData(componentName){
53
+ return htmlSafeString(localStorage.getItem('form-builder::data_' + componentName));
54
+ }
55
+ function htmlSafeString(str) {
56
+ return str?.replace(/&/g, '&amp;')
57
+ .replace(/</g, '&lt;')
58
+ .replace(/>/g, '&gt;')
59
+ .replace(/"/g, '&quot;')
60
+ .replace(/'/g, '&#039;')||'';
61
+ }
62
+
63
+ function renderComponent(componentName, existingComponent){
64
+ if(Salla.storage.get('hidden-salla-components', []).includes(componentName)){
65
+ return;
66
+ }
67
+ const tempDom = document.createElement('div');
68
+ const config = getComponentData(componentName);
69
+ tempDom.innerHTML=\`<div class="component-card" data-component="\${componentName}">
70
+ <div class="component-card-header">
71
+ <h2>
72
+ <i class="sicon-tag"></i>
73
+ \${componentName}
74
+ </h2>
75
+ <div class="component-card-actions">
76
+ <button class="component-visibility-btn" aria-label="Toggle visibility"
77
+ onclick="hideComponent('\${componentName}')"
78
+ title="Hide component">
79
+ <i class="sicon-eye"></i>
80
+ </button>
81
+ <button class="component-settings-btn" aria-label="Open settings"
82
+ data-component="\${componentName}"
83
+ data-schema="\${config}">
84
+ <i class="sicon-settings"></i>
85
+ </button>
86
+ </div>
87
+ </div>
88
+ <salla-custom-component \${config?'config="'+config+'"':''} component-name="\${componentName}"></salla-custom-component>
89
+ </div>
90
+ </div>\`;
91
+ tempDom.querySelector('.component-settings-btn').addEventListener('click', () => openDrawer(componentName));
92
+ const grid = document.getElementById('componentsGrid');
93
+ existingComponent
94
+ ? grid.insertBefore(tempDom.firstElementChild, existingComponent.nextSibling)
95
+ : grid.appendChild(tempDom.firstElementChild);
96
+ tempDom.remove();
97
+ }
98
+ function reRenderComponent(componentName){
99
+ const existingComponent = document.querySelector('.component-card[data-component="' + componentName + '"]');
100
+ renderComponent(componentName, existingComponent);
101
+ existingComponent.remove();
102
+ }
103
+
104
+
105
+ // Save component visibility to localStorage
106
+ function hideComponent(componentName) {
107
+ const hiddenComponents = Salla.storage.get('hidden-salla-components', []);
108
+ if(!hiddenComponents?.includes(componentName)){
109
+ hiddenComponents.push(componentName);
110
+ }
111
+ Salla.storage.set('hidden-salla-components', hiddenComponents);
112
+ document.querySelector('.component-card[data-component="' + componentName + '"]')?.remove();
113
+ }
114
+
115
+ function showComponent(componentName) {
116
+ let hiddenComponents = Salla.storage.get('hidden-salla-components', []);
117
+ if(hiddenComponents.includes(componentName)){
118
+ hiddenComponents = hiddenComponents.filter(component => component !== componentName);
119
+ }
120
+ Salla.storage.set('hidden-salla-components', hiddenComponents);
121
+ renderComponent(componentName);
122
+ }
123
+
124
+ function toggleComponentVisibility(element) {
125
+ const componentName = element.value;
126
+ element.checked = Salla.storage.get('hidden-salla-components', []).includes(componentName);
127
+ element.checked ?showComponent(componentName) : hideComponent(componentName);
128
+ }
129
+ // Get the updated gap value from localStorage
130
+ function getSavedGrid() {
131
+ return Salla.storage.get('salla_demo_grid', {
132
+ columns: '${e.grid.columns}',
133
+ gap: '${e.grid.gap}',
134
+ minWidth: '${e.grid.minWidth}'
135
+ });
136
+ }
137
+ function getSavedLanguages(){
138
+ return Salla.storage.get('salla_demo_formbuilder', {
139
+ languages: ${JSON.stringify(e.formbuilder.languages)},
140
+ defaultLanguage: ${JSON.stringify(e.formbuilder.defaultLanguage)}
141
+ });
142
+ }
143
+
144
+ // ==================== BEGIN SETTINGS DRAWER ====================
145
+ function openSettingsDrawer(){
146
+ const settingsDrawer = document.getElementById('settingsDrawer');
147
+ if(!settingsDrawer){
148
+ return;
149
+ }
150
+ settingsDrawer.classList.add('active');
151
+
152
+ drawerOverlay?.classList.add('active');
153
+ document.body.style.overflow = 'hidden';
154
+
155
+ if(settingsDrawer.is_rendered){
156
+ return;
157
+ }
158
+ settingsDrawer.is_rendered = true;
159
+
160
+ // Set current visibility state for components
161
+ settingsDrawer.querySelectorAll('.visibility-checkbox')
162
+ .forEach(checkbox => checkbox.checked = !Salla.storage.get('hidden-salla-components', []).includes(checkbox.value));
163
+
164
+ // Set current grid settings
165
+ const savedGrid = getSavedGrid();
166
+
167
+ // Set grid columns preset
168
+ const gridPresetBtns = document.querySelectorAll('.grid-preset-btn');
169
+ let activePresetFound = false;
170
+
171
+ gridPresetBtns.forEach(btn => {
172
+ btn.classList.remove('active');
173
+ const columns = btn.getAttribute('data-columns');
174
+
175
+ if (columns === savedGrid.columns) {
176
+ btn.classList.add('active');
177
+ activePresetFound = true;
178
+ }
179
+ });
180
+
181
+ // If no preset matches, activate custom option
182
+ if (!activePresetFound) {
183
+ const customBtn = document.querySelector('.grid-preset-btn[data-columns="custom"]');
184
+ customBtn?.classList.add('active');
185
+
186
+ // Store the current value as custom value
187
+ const gridColumnsInput = document.getElementById('gridColumns');
188
+ if (gridColumnsInput) {
189
+ gridColumnsInput.setAttribute('data-custom-value', savedGrid.columns);
190
+ gridColumnsInput.readOnly = false;
191
+ }
192
+ }
193
+
194
+ // Initialize grid columns input
195
+ const gridColumnsInput = document.getElementById('gridColumns');
196
+ if (gridColumnsInput) {
197
+ // Initialize with saved value
198
+ gridColumnsInput.value = savedGrid.columns;
199
+ gridColumnsInput.addEventListener('input', () => {
200
+ // Store custom value when user edits
201
+ const customPreset = document.querySelector('.grid-preset-btn[data-columns="custom"]');
202
+ if (customPreset && customPreset.classList.contains('active')) {
203
+ gridColumnsInput.setAttribute('data-custom-value', gridColumnsInput.value);
204
+ }
205
+ // Apply settings immediately for live update
206
+ applySettings();
207
+ });
208
+ }
209
+ const gridGapInput = document.getElementById('gridGapValue');
210
+ const gridGapUnitInput = document.getElementById('gridGapUnit');
211
+ if(gridGapInput && savedGrid.gap){
212
+ gridGapInput.value = savedGrid.gap.match(/[\\d\\.]+/)?.[0];
213
+ gridGapUnitInput.value = savedGrid.gap.match(/[a-z%]+/)?.[0];
214
+ }
215
+ // Add event listeners to grid gap inputs for live updates
216
+ gridGapInput?.addEventListener('input', () => {
217
+ const gridGapValue = gridGapInput?.value || '1';
218
+ const gridGapUnit = gridGapUnitInput?.value || 'rem';
219
+ const gridGap = gridGapValue + gridGapUnit;
220
+
221
+ // Update the gap in real-time
222
+ const grid = document.getElementById('componentsGrid');
223
+ if (grid) {
224
+ grid.style.gap = gridGap;
225
+ }
226
+
227
+ const savedGrid = getSavedGrid();
228
+
229
+
230
+ Salla.storage.set('salla_demo_grid', {...savedGrid, gap: gridGap});
231
+ });
232
+
233
+ gridGapUnitInput?.addEventListener('change', () => {
234
+ const gridGapValue = gridGapInput.value || '1';
235
+ const gridGapUnit = gridGapUnitInput.value || 'rem';
236
+ const gridGap = gridGapValue + gridGapUnit;
237
+
238
+ // Update the gap in real-time
239
+ const grid = document.getElementById('componentsGrid');
240
+ if (grid) {
241
+ grid.style.gap = gridGap;
242
+ }
243
+
244
+ Salla.storage.set('salla_demo_grid', {...getSavedGrid(), gap: gridGap});
245
+ });
246
+
247
+ document.getElementById('customCSS')?.addEventListener('input', debounce(() => {
248
+ applySettings();
249
+ }, 500));
250
+
251
+ document.getElementById('customJS')?.addEventListener('input', debounce(() => {
252
+ applySettings();
253
+ }, 500));
254
+
255
+ document.getElementById('formbuilderLanguages')?.addEventListener('change', () => applySettings());
256
+ document.getElementById('formbuilderDefaultLang')?.addEventListener('change', () => applySettings());
257
+
258
+
259
+ // Set current custom CSS and JS
260
+ const customCSS = document.getElementById('customCSS');
261
+ const customJS = document.getElementById('customJS');
262
+
263
+ if (customCSS && customJS) {
264
+ customCSS.value = Salla.storage.get('salla_demo_custom_css', '${((s = e.css) == null ? void 0 : s.replace(/'/g, "\\'")) || ""}');
265
+ customJS.value = Salla.storage.get('salla_demo_custom_js', '${((l = e.js) == null ? void 0 : l.replace(/'/g, "\\'")) || ""}');
266
+ }
267
+
268
+ // Set current formbuilder settings
269
+ const savedFormbuilder = getSavedLanguages();
270
+
271
+ // Set language checkboxes
272
+ document.querySelectorAll('#formbuilderLanguages option').forEach(option => option.selected = savedFormbuilder.languages.includes(option.value));
273
+
274
+ document.getElementById('formbuilderDefaultLang').value = savedFormbuilder.defaultLanguage;
275
+ }
276
+ // ==================== END SETTINGS DRAWER ====================
277
+
278
+ function parseValueAndUnit(cssValue) {
279
+ const match = cssValue.match(/^([d.]+)([a-z%]*)$/);
280
+ if (match) {
281
+ return [match[1], match[2] || 'px']; // Default to px if no unit
282
+ }
283
+ return ['0', 'px']; // Default fallback
284
+ }
285
+
286
+ // Helper function to set selected option in a select element
287
+ function setSelectedUnit(selectElement, unit) {
288
+ for (let i = 0; i < selectElement.options.length; i++) {
289
+ if (selectElement.options[i].value === unit) {
290
+ selectElement.selectedIndex = i;
291
+ break;
292
+ }
293
+ }
294
+ }
295
+
296
+ function getGridColumns() {
297
+ const gridColumnsInput = document.getElementById('gridColumns');
298
+ return gridColumnsInput?.value || '${e.grid.columns}';
299
+ }
300
+
301
+ function updateGridBasedOnItemCount() {
302
+ const grid = document.getElementById('componentsGrid');
303
+ if (!grid) return;
304
+
305
+ const gridItems = grid.querySelectorAll(':scope > *');
306
+
307
+ if (gridItems.length <= 2) {
308
+ // For 1-2 items, use auto-fit with full width
309
+ grid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(0, 1fr))';
310
+ // Reset all items grid-column property
311
+ gridItems.forEach(item => item.style.gridColumn = '');
312
+ } else {
313
+ // For 3+ items, use 3 columns with first item spanning all columns
314
+ grid.style.gridTemplateColumns = 'repeat(3, 1fr)';
315
+ // Reset all items first
316
+ gridItems.forEach(item => item.style.gridColumn = '');
317
+ // Make first item span 3 columns
318
+ if (gridItems[0]) gridItems[0].style.gridColumn = 'span 3';
319
+ }
320
+ }
321
+
322
+ function applySettings() {
323
+ const activePreset = document.querySelector('.grid-preset-btn.active');
324
+ const grid = document.getElementById('componentsGrid');
325
+ if (!grid) return;
326
+
327
+ // Apply special auto-fill behavior if selected
328
+ if (activePreset && activePreset.getAttribute('data-columns') === 'auto-fill') {
329
+ updateGridBasedOnItemCount();
330
+ } else {
331
+ // Apply normal grid columns
332
+ const gridColumns = getGridColumns();
333
+ grid.style.gridTemplateColumns = gridColumns;
334
+ // Reset all items grid-column property
335
+ const gridItems = grid.querySelectorAll(':scope > *');
336
+ gridItems.forEach(item => item.style.gridColumn = '');
337
+ }
338
+
339
+ // Get grid gap with unit
340
+ const gridGapValue = document.getElementById('gridGapValue')?.value || '1';
341
+ const gridGapUnit = document.getElementById('gridGapUnit')?.value || 'rem';
342
+ const gridGap = gridGapValue + gridGapUnit;
343
+
344
+ // Apply grid gap
345
+ if (grid) {
346
+ grid.style.gap = gridGap;
347
+
348
+ // Ensure grid gap is saved in localStorage
349
+ const savedGrid = Salla.storage.get('salla_demo_grid', {
350
+ columns: grid.style.gridTemplateColumns || '${e.grid.columns}',
351
+ gap: '${e.grid.gap}',
352
+ minWidth: '${e.grid.minWidth}'
353
+ });
354
+
355
+ // Update only the gap property
356
+ Salla.storage.set('salla_demo_grid', {
357
+ columns: savedGrid.columns,
358
+ gap: gridGap,
359
+ minWidth: savedGrid.minWidth
360
+ });
361
+ }
362
+
363
+ // Apply custom CSS
364
+ const customCSS = document.getElementById('customCSS')?.value || '';
365
+ let customCSSElement = document.getElementById('custom-css-element');
366
+ if (!customCSSElement) {
367
+ customCSSElement = document.createElement('style');
368
+ customCSSElement.id = 'custom-css-element';
369
+ document.head.appendChild(customCSSElement);
370
+ }
371
+ customCSSElement.textContent = customCSS;
372
+
373
+ // Apply custom JS
374
+ const customJS = document.getElementById('customJS')?.value || '';
375
+ try {
376
+ if (customJS) {
377
+ eval(customJS);
378
+ }
379
+ } catch (error) {
380
+ console.error('Error executing custom JS:', error);
381
+ }
382
+
383
+ // Save settings to localStorage
384
+ Salla.storage.set('salla_demo_grid', {
385
+ columns: activePreset && activePreset.getAttribute('data-columns') === 'auto-fill' ? 'auto-fill' : getGridColumns(),
386
+ gap: gridGap,
387
+ minWidth: '${e.grid.minWidth}' // Use backend value only
388
+ });
389
+
390
+ Salla.storage.set('salla_demo_custom_css', customCSS);
391
+ Salla.storage.set('salla_demo_custom_js', customJS);
392
+
393
+ // Get selected languages from multi-select
394
+ const formbuilderLanguagesSelect = document.getElementById('formbuilderLanguages');
395
+ const selectedLanguages = Array.from(formbuilderLanguagesSelect?.selectedOptions || []).map(option => option.value);
396
+
397
+ // Ensure at least one language is selected
398
+ if (selectedLanguages.length === 0) {
399
+ selectedLanguages.push('ar'); // Default to English if nothing selected
400
+ }
401
+
402
+ // Get default language
403
+ const formbuilderDefaultLang = document.getElementById('formbuilderDefaultLang')?.value || '${e.formbuilder.defaultLanguage}';
404
+
405
+ // Ensure default language is in the selected languages
406
+ if (!selectedLanguages.includes(formbuilderDefaultLang)) {
407
+ selectedLanguages.push(formbuilderDefaultLang);
408
+ // Also select it in the UI
409
+ const option = Array.from(formbuilderLanguagesSelect?.options || []).find(opt => opt.value === formbuilderDefaultLang);
410
+ if (option) option.selected = true;
411
+ }
412
+
413
+ // Save formbuilder settings
414
+ Salla.storage.set('salla_demo_formbuilder', {
415
+ languages: selectedLanguages,
416
+ defaultLanguage: formbuilderDefaultLang
417
+ });
418
+
419
+ // Reset component settings drawer on form-builder changes
420
+ resetComponentSettings();
421
+ }
422
+
423
+ window.addEventListener('FormBuilder::form-builder-3::request-success',async ({detail:payload}) => {
424
+ const ignoredKeys = ['static-', '$','twilight-bundles-component-name'];
425
+ const data = Object.fromEntries(
426
+ Object.entries(payload).filter(([key]) => !ignoredKeys.some(ignoredKey=>key.startsWith(ignoredKey)))
427
+ );
428
+ const componentName = payload['twilight-bundles-component-name'];
429
+ Salla.storage.set('form-builder::data_' + componentName, data);
430
+ if (componentName && window.customComponentsSchema && window.customComponentsSchema[componentName]) {
431
+ // Inject the data into the schema
432
+ const schema = window.customComponentsSchema[componentName];
433
+ await fetch('${i}/schema-injector', {
434
+ method: 'POST',
435
+ headers: {'Content-Type': 'application/json'},
436
+ body: JSON.stringify({ schema, data }),
437
+ }).then(res=>res.json())
438
+ .then(data=>Salla.storage.set('form-builder::'+componentName, data))
439
+ .then(()=>reRenderComponent(componentName))
440
+ .then(()=>closeDrawer())
441
+ .catch(err=>console.error('Error injecting data into schema:', err));
442
+ }
443
+ });
444
+ <\/script>
445
+ <link rel="icon" type="image/png" media="(prefers-color-scheme: light)" href="https://cdn.salla.network/images/logo/logo-square.png" />
446
+ <link rel="icon" type="image/png" media="(prefers-color-scheme: dark)" href="https://cdn.salla.network/images/logo/logo-light-square.png" />
447
+ <script type="module" src="https://cdn.salla.network/js/twilight/latest/twilight.esm.js" async><\/script>
448
+ <script type="module" src="${L}" demo-mode defer><\/script>
449
+ <link rel="stylesheet" href="https://cdn.salla.network/fonts/pingarlt.css">
450
+ <link rel="stylesheet" href="https://cdn.salla.network/fonts/sallaicons.css?v=2.0.5">
451
+
452
+ <!-- Preload form builder resources for faster loading -->
453
+
454
+ <style>
455
+ :root {
456
+ --font-main: "PingARLT";
457
+ --color-primary-50: rgb(186, 243, 230); /* #BAF3E6 */
458
+ --color-primary-100: rgb(120, 232, 206); /* #78E8CE */
459
+ --color-primary-900: rgb(0, 73, 86); /* #004956 */
460
+ --color-primary: rgb(0, 78, 92);
461
+ }
462
+
463
+ :root[data-theme="dark"] {
464
+ --bg-primary: #1E1E1E;
465
+ --bg-secondary: #2A2A2A;
466
+ --text-primary: #FFFFFF;
467
+ --text-secondary: #9CA3AF;
468
+ --border-color: #333333;
469
+ --color-primary: #1d1e20;
470
+ --component-title: #baf3e5;
471
+ }
472
+
473
+ :root[data-theme="light"] {
474
+ --bg-primary: #FFFFFF;
475
+ --bg-secondary: #F8F9FA;
476
+ --text-primary: #1E1E1E;
477
+ --text-secondary: #4B5563;
478
+ --border-color: #E5E7EB;
479
+ --component-title: #004e5c;
480
+ }
481
+
482
+ * {
483
+ margin: 0;
484
+ padding: 0;
485
+ box-sizing: border-box;
486
+ font-family: var(--font-main);
487
+ }
488
+
489
+ body {
490
+ min-height: 100vh;
491
+ background-color: var(--bg-secondary);
492
+ }
493
+
494
+ header {
495
+ background-color: var(--color-primary);
496
+ padding: 0.75rem 0;
497
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
498
+ }
499
+
500
+ .container {
501
+ max-width: 1200px;
502
+ margin: 0 auto;
503
+ padding: 0 0.75rem;
504
+ }
505
+
506
+ .header-content {
507
+ display: flex;
508
+ align-items: center;
509
+ justify-content: space-between;
510
+ }
511
+
512
+ .logo-container {
513
+ display: flex;
514
+ align-items: center;
515
+ gap: 1rem;
516
+ }
517
+
518
+ .logo {
519
+ height: 40px;
520
+ }
521
+
522
+ .logo img {
523
+ height: 100%;
524
+ width: auto;
525
+ }
526
+
527
+ .title {
528
+ color: white;
529
+ font-size: 1.25rem;
530
+ font-weight: 500;
531
+ margin: 0;
532
+ }
533
+
534
+ .actions {
535
+ display: flex;
536
+ gap: 1rem;
537
+ align-items: center;
538
+ }
539
+
540
+ .actions button {
541
+ background: transparent;
542
+ border: none;
543
+ color: white;
544
+ cursor: pointer;
545
+ padding: 0.5rem;
546
+ border-radius: 4px;
547
+ transition: color 0.2s ease;
548
+ font-size: 1rem;
549
+ display: flex;
550
+ align-items: center;
551
+ gap: 0.5rem;
552
+ }
553
+
554
+ .actions button:hover {
555
+ color: rgb(var(--color-primary-100));
556
+ }
557
+
558
+ .theme-icon {
559
+ transition: transform 0.3s ease;
560
+ }
561
+
562
+ [data-theme="dark"] .theme-icon.moon {
563
+ display: none;
564
+ }
565
+
566
+ [data-theme="light"] .theme-icon.sun {
567
+ display: none;
568
+ }
569
+
570
+ .lang-icon {
571
+ transition: transform 0.3s ease;
572
+ }
573
+
574
+ [dir="rtl"] .lang-icon {
575
+ transform: scaleX(-1);
576
+ }
577
+
578
+ .lang-code {
579
+ font-size: 0.875rem;
580
+ font-weight: 500;
581
+ text-transform: uppercase;
582
+ }
583
+
584
+ main {
585
+ padding: 1.5rem 0;
586
+ }
587
+
588
+ .components-grid {
589
+ display: grid;
590
+ grid-template-columns: ${e.grid.columns};
591
+ gap: ${e.grid.gap};
592
+ margin-top: 0;
593
+ }
594
+
595
+ @media (max-width: ${e.grid.minWidth}) {
596
+ .components-grid {
597
+ grid-template-columns: 1fr;
598
+ }
599
+ }
600
+
601
+ .component-card {
602
+ background: var(--bg-primary);
603
+ border: 1px solid var(--border-color);
604
+ border-radius: 8px;
605
+ padding: 0.75rem;
606
+ }
607
+
608
+ .component-card-header {
609
+ display: flex;
610
+ justify-content: space-between;
611
+ align-items: center;
612
+ margin-bottom: 0.75rem;
613
+ }
614
+
615
+ .component-card-header h2 {
616
+ color: var(--component-title);
617
+ font-size: 1.125rem;
618
+ margin: 0;
619
+ display: flex;
620
+ align-items: center;
621
+ gap: 0.5rem;
622
+ }
623
+
624
+ .component-card-header h2 i {
625
+ font-size: 1.25rem;
626
+ }
627
+
628
+ .component-card-actions {
629
+ display: flex;
630
+ gap: 0.5rem;
631
+ }
632
+
633
+ .component-visibility-btn,
634
+ .component-settings-btn {
635
+ background: transparent;
636
+ border: 1px solid var(--border-color);
637
+ color: var(--text-secondary);
638
+ cursor: pointer;
639
+ width: 32px;
640
+ height: 32px;
641
+ border-radius: 4px;
642
+ display: flex;
643
+ align-items: center;
644
+ justify-content: center;
645
+ transition: all 0.2s ease;
646
+ }
647
+
648
+ .component-visibility-btn:hover,
649
+ .component-settings-btn:hover {
650
+ background-color: var(--bg-secondary);
651
+ color: var(--text-primary);
652
+ }
653
+
654
+ .component-card.hidden {
655
+ opacity: 0.5;
656
+ position: relative;
657
+ }
658
+
659
+ .component-card.hidden::after {
660
+ content: "Hidden";
661
+ position: absolute;
662
+ top: 0;
663
+ left: 0;
664
+ right: 0;
665
+ bottom: 0;
666
+ display: flex;
667
+ align-items: center;
668
+ justify-content: center;
669
+ background-color: rgba(0, 0, 0, 0.1);
670
+ font-size: 1.5rem;
671
+ font-weight: bold;
672
+ color: var(--text-secondary);
673
+ z-index: 10;
674
+ }
675
+
676
+ /* Drawer styles */
677
+ .drawer-overlay {
678
+ position: fixed;
679
+ top: 0;
680
+ left: 0;
681
+ right: 0;
682
+ bottom: 0;
683
+ background-color: rgba(0, 0, 0, 0.5);
684
+ z-index: 999;
685
+ opacity: 0;
686
+ visibility: hidden;
687
+ transition: all 0.3s ease;
688
+ }
689
+
690
+ .drawer-overlay.active {
691
+ opacity: 1;
692
+ visibility: visible;
693
+ }
694
+
695
+ .drawer {
696
+ position: fixed;
697
+ top: 0;
698
+ bottom: 0;
699
+ background-color: var(--bg-primary);
700
+ width: 400px;
701
+ max-width: 100%;
702
+ z-index: 1000;
703
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
704
+ transition: transform 0.3s ease;
705
+ overflow-y: auto;
706
+ opacity: 0;
707
+ visibility: hidden;
708
+ }
709
+ #settingsDrawer {
710
+ width: 500px;
711
+ }
712
+ .drawer.active {
713
+ opacity: 1;
714
+ visibility: visible;
715
+ }
716
+
717
+ [dir="ltr"] .drawer {
718
+ right: 0;
719
+ transform: translateX(100%);
720
+ }
721
+
722
+ [dir="rtl"] .drawer {
723
+ left: 0;
724
+ transform: translateX(-100%);
725
+ }
726
+
727
+ .drawer.active {
728
+ transform: translateX(0);
729
+ }
730
+
731
+ .drawer-header {
732
+ display: flex;
733
+ justify-content: space-between;
734
+ align-items: center;
735
+ padding: 1.25rem 1.5rem;
736
+ border-bottom: 1px solid var(--border-color);
737
+ background-color: var(--bg-secondary);
738
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
739
+ }
740
+
741
+ .drawer-header h3 {
742
+ margin: 0;
743
+ color: var(--text-primary);
744
+ flex: 1;
745
+ font-size: 1.25rem;
746
+ font-weight: 600;
747
+ letter-spacing: 0.01em;
748
+ }
749
+
750
+ .drawer-actions {
751
+ display: flex;
752
+ align-items: center;
753
+ gap: 0.75rem;
754
+ }
755
+
756
+ .drawer-close,
757
+ .drawer-reset {
758
+ background-color: var(--bg-primary);
759
+ border: 1px solid var(--border-color);
760
+ border-radius: 6px;
761
+ cursor: pointer;
762
+ font-size: 1.25rem;
763
+ color: var(--text-secondary);
764
+ padding: 0.5rem;
765
+ width: 36px;
766
+ height: 36px;
767
+ display: flex;
768
+ align-items: center;
769
+ justify-content: center;
770
+ transition: all 0.2s ease;
771
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
772
+ }
773
+
774
+ .drawer-close:hover,
775
+ .drawer-reset:hover {
776
+ background-color: var(--bg-secondary);
777
+ color: var(--text-primary);
778
+ transform: translateY(-1px);
779
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
780
+ }
781
+
782
+ .drawer-close:active,
783
+ .drawer-reset:active {
784
+ transform: translateY(0);
785
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
786
+ }
787
+
788
+ .drawer-reset {
789
+ color: var(--color-primary);
790
+ }
791
+
792
+ .drawer-close {
793
+ color: var(--color-danger, var(--text-secondary));
794
+ }
795
+
796
+ .drawer-content {
797
+ padding: 1rem;
798
+ }
799
+
800
+ /* Filter drawer styles */
801
+ .filter-actions {
802
+ display: flex;
803
+ gap: 1rem;
804
+ margin-bottom: 1rem;
805
+ }
806
+
807
+ .btn {
808
+ padding: 0.5rem 1rem;
809
+ border-radius: 4px;
810
+ border: none;
811
+ cursor: pointer;
812
+ font-weight: 500;
813
+ transition: all 0.2s ease;
814
+ }
815
+
816
+ .btn-primary {
817
+ background-color: var(--color-primary-100);
818
+ color: var(--color-primary-900);
819
+ }
820
+
821
+ .btn-primary:hover {
822
+ background-color: var(--color-primary-50);
823
+ }
824
+
825
+ .btn-secondary {
826
+ background-color: var(--bg-secondary);
827
+ color: var(--text-primary);
828
+ border: 1px solid var(--border-color);
829
+ }
830
+
831
+ .btn-secondary:hover {
832
+ background-color: var(--bg-primary);
833
+ }
834
+
835
+ .component-visibility-list {
836
+ display: flex;
837
+ flex-direction: column;
838
+ gap: 0.75rem;
839
+ overflow-y: auto;
840
+ }
841
+
842
+ .visibility-item {
843
+ padding: 0.5rem;
844
+ border-radius: 4px;
845
+ transition: background-color 0.2s ease;
846
+ }
847
+
848
+ .visibility-item:hover {
849
+ background-color: var(--bg-secondary);
850
+ }
851
+
852
+ .visibility-item label {
853
+ display: flex;
854
+ align-items: center;
855
+ gap: 0.75rem;
856
+ cursor: pointer;
857
+ color: var(--text-primary);
858
+ }
859
+
860
+ .visibility-checkbox {
861
+ width: 1.25rem;
862
+ height: 1.25rem;
863
+ cursor: pointer;
864
+ }
865
+
866
+ /* Settings drawer styles */
867
+ .settings-tabs {
868
+ display: flex;
869
+ flex-direction: column;
870
+ gap: 1.5rem;
871
+ }
872
+
873
+ .settings-tab-headers {
874
+ display: flex;
875
+ border-bottom: 1px solid var(--border-color);
876
+ margin-bottom: 1rem;
877
+ overflow-x: auto;
878
+ -webkit-overflow-scrolling: touch;
879
+ }
880
+
881
+ .settings-tab-btn {
882
+ padding: 0.75rem 1rem;
883
+ background: none;
884
+ border: none;
885
+ border-bottom: 2px solid transparent;
886
+ color: var(--text-secondary);
887
+ cursor: pointer;
888
+ font-weight: 500;
889
+ white-space: nowrap;
890
+ transition: all 0.2s ease;
891
+ }
892
+
893
+ .settings-tab-btn:hover {
894
+ color: var(--text-primary);
895
+ }
896
+
897
+ .settings-tab-btn.active {
898
+ color: var(--color-primary);
899
+ border-bottom-color: var(--color-primary);
900
+ }
901
+
902
+ .settings-tab-content {
903
+ display: none;
904
+ animation: fadeIn 0.3s ease;
905
+ }
906
+
907
+ .settings-tab-content.active {
908
+ display: block;
909
+ }
910
+
911
+ @keyframes fadeIn {
912
+ from { opacity: 0; }
913
+ to { opacity: 1; }
914
+ }
915
+
916
+ .settings-section-title {
917
+ margin: 0 0 0.5rem;
918
+ font-size: 1.1rem;
919
+ font-weight: 600;
920
+ color: var(--text-primary);
921
+ }
922
+
923
+ .settings-section-desc {
924
+ margin: 0 0 1.5rem;
925
+ font-size: 0.9rem;
926
+ color: var(--text-secondary);
927
+ }
928
+
929
+ .settings-form-group {
930
+ margin-bottom: 1.25rem;
931
+ }
932
+
933
+ .settings-form-group label {
934
+ display: block;
935
+ margin-bottom: 0.5rem;
936
+ font-weight: 500;
937
+ color: var(--text-primary);
938
+ }
939
+
940
+ .settings-input,
941
+ .settings-textarea {
942
+ width: 100%;
943
+ padding: 0.75rem;
944
+ border: 1px solid var(--border-color);
945
+ border-radius: 4px;
946
+ background-color: var(--bg-primary);
947
+ color: var(--text-primary);
948
+ font-family: inherit;
949
+ font-size: 0.9rem;
950
+ transition: border-color 0.2s ease;
951
+ }
952
+
953
+ .settings-input:focus,
954
+ .settings-textarea:focus {
955
+ outline: none;
956
+ border-color: var(--color-primary);
957
+ box-shadow: 0 0 0 2px var(--color-primary-50);
958
+ }
959
+
960
+ .settings-textarea {
961
+ resize: vertical;
962
+ min-height: 100px;
963
+ direction: ltr;
964
+ font-family: monospace;
965
+ color: #888;
966
+ }
967
+
968
+ .settings-form-group small {
969
+ display: block;
970
+ margin-top: 0.25rem;
971
+ font-size: 0.8rem;
972
+ color: var(--text-secondary);
973
+ }
974
+
975
+ .settings-actions {
976
+ display: flex;
977
+ justify-content: flex-end;
978
+ gap: 1rem;
979
+ margin-top: 2rem;
980
+ padding-top: 1rem;
981
+ border-top: 1px solid var(--border-color);
982
+ }
983
+
984
+ /* Language selection styles */
985
+ .settings-languages-container {
986
+ max-height: 200px;
987
+ overflow-y: auto;
988
+ border: 1px solid var(--border-color);
989
+ border-radius: 4px;
990
+ background-color: var(--bg-primary);
991
+ }
992
+
993
+ .settings-languages-list {
994
+ display: grid;
995
+ grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
996
+ gap: 0.5rem;
997
+ padding: 0.75rem;
998
+ }
999
+
1000
+ .settings-language-item {
1001
+ display: flex;
1002
+ align-items: center;
1003
+ gap: 0.25rem;
1004
+ padding: 0.25rem;
1005
+ border-radius: 4px;
1006
+ cursor: pointer;
1007
+ transition: background-color 0.2s ease;
1008
+ }
1009
+
1010
+ .settings-language-item:hover {
1011
+ background-color: var(--bg-secondary);
1012
+ }
1013
+
1014
+ .settings-language-item input {
1015
+ margin: 0;
1016
+ }
1017
+
1018
+ .settings-language-item span {
1019
+ font-size: 0.85rem;
1020
+ }
1021
+
1022
+ /* Grid presets styles */
1023
+ .grid-columns-presets {
1024
+ display: flex;
1025
+ flex-wrap: wrap;
1026
+ gap: 0.5rem;
1027
+ margin-bottom: 0.75rem;
1028
+ }
1029
+
1030
+ .grid-preset-btn {
1031
+ display: flex;
1032
+ align-items: center;
1033
+ justify-content: center;
1034
+ width: 60px;
1035
+ height: 40px;
1036
+ border: 1px solid var(--border-color);
1037
+ border-radius: 4px;
1038
+ background-color: var(--bg-primary);
1039
+ cursor: pointer;
1040
+ transition: all 0.2s ease;
1041
+ }
1042
+
1043
+ .grid-preset-btn:hover {
1044
+ background-color: var(--bg-secondary);
1045
+ transform: translateY(-1px);
1046
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
1047
+ }
1048
+
1049
+ .grid-preset-btn.active {
1050
+ border-color: var(--color-primary);
1051
+ background-color: var(--color-primary-50);
1052
+ }
1053
+
1054
+ .grid-preset-icon {
1055
+ font-size: 1.25rem;
1056
+ color: var(--text-primary);
1057
+ letter-spacing: -2px;
1058
+ }
1059
+
1060
+ .custom-grid-columns-container {
1061
+ margin-top: 0.75rem;
1062
+ }
1063
+
1064
+ /* Input with unit styles */
1065
+ .settings-input-with-unit {
1066
+ display: flex;
1067
+ align-items: center;
1068
+ gap: 0.5rem;
1069
+ }
1070
+
1071
+ .settings-input-number {
1072
+ flex: 1;
1073
+ }
1074
+
1075
+ .settings-input-unit {
1076
+ width: 80px;
1077
+ padding: 0.75rem;
1078
+ border: 1px solid var(--border-color);
1079
+ border-radius: 4px;
1080
+ background-color: var(--bg-primary);
1081
+ color: var(--text-primary);
1082
+ font-family: inherit;
1083
+ font-size: 0.9rem;
1084
+ }
1085
+ </style>
1086
+ </head>
1087
+ <body>
1088
+ <header>
1089
+ <div class="container">
1090
+ <div class="header-content">
1091
+ <div class="logo-container">
1092
+ <div class="logo">
1093
+ <img src="https://cdn.salla.network/images/logo/logo-light-wide.png" alt="Salla">
1094
+ </div>
1095
+ <h1 class="title" id="pageTitle">${o.ar.title}</h1>
1096
+ </div>
1097
+ <div class="actions">
1098
+ <button id="toggleSettings" title="Settings" onclick="openSettingsDrawer()" class="sicon-settings"></button>
1099
+ <button id="toggleTheme" title="Toggle theme">
1100
+ <i class="theme-icon moon sicon-moon"></i>
1101
+ <i class="theme-icon sun sicon-lightbulb"></i>
1102
+ </button>
1103
+ <button id="toggleLang" title="Toggle language">
1104
+ <i class="lang-icon sicon-world"></i>
1105
+ <span class="lang-code">EN</span>
1106
+ </button>
1107
+ </div>
1108
+ </div>
1109
+ </div>
1110
+ </header>
1111
+ <main>
1112
+ <div class="container">
1113
+ <div class="components-grid" id="componentsGrid"></div>
1114
+ </div>
1115
+ </main>
1116
+
1117
+ <!-- Drawer overlay -->
1118
+ <div class="drawer-overlay" id="drawerOverlay"></div>
1119
+
1120
+ <!-- Component settings drawer -->
1121
+ <div id="componentDrawer" class="drawer">
1122
+ <div class="drawer-header">
1123
+ <h3 class="drawer-title">Component Settings</h3>
1124
+ <div class="drawer-actions">
1125
+ <button id="resetForm" class="drawer-reset" title="Reset to default">
1126
+ <i class="sicon-rotate"></i>
1127
+ </button>
1128
+ <button id="closeDrawer" class="drawer-close">
1129
+ <i class="sicon-cancel"></i>
1130
+ </button>
1131
+ </div>
1132
+ </div>
1133
+ <div class="drawer-content">
1134
+ <div id="formContainer"></div>
1135
+ </div>
1136
+ </div>
1137
+
1138
+ <!-- Settings drawer -->
1139
+ <div id="settingsDrawer" class="drawer">
1140
+ <div class="drawer-header">
1141
+ <h3>Demo Settings</h3>
1142
+ <div class="drawer-actions">
1143
+ <button class="drawer-close" onclick="closeDrawer()">
1144
+ <i class="sicon-cancel"></i>
1145
+ </button>
1146
+ </div>
1147
+ </div>
1148
+ <div class="drawer-content">
1149
+ <div class="settings-tabs">
1150
+ <div class="settings-tab-headers">
1151
+ <button class="settings-tab-btn active" data-tab="components">Components</button>
1152
+ <button class="settings-tab-btn" data-tab="grid">Grid</button>
1153
+ <button class="settings-tab-btn" data-tab="custom-code">Custom Code</button>
1154
+ <button class="settings-tab-btn" data-tab="formbuilder">Form Builder</button>
1155
+ </div>
1156
+
1157
+ <!-- Components Tab -->
1158
+ <div class="settings-tab-content active" id="components-tab">
1159
+ <h4 class="settings-section-title">Component Visibility</h4>
1160
+ <p class="settings-section-desc">Select which components to display in the demo</p>
1161
+
1162
+ <div class="component-visibility-list">
1163
+ ${t.map((n) => `
1164
+ <div class="visibility-item">
1165
+ <label>
1166
+ <input type="checkbox"
1167
+ name="component-visibility"
1168
+ class="visibility-checkbox"
1169
+ value="${n.name}"
1170
+ onchange="toggleComponentVisibility(this)">
1171
+ <span>${n.name}</span>
1172
+ </label>
1173
+ </div>
1174
+ `).join("")}
1175
+ </div>
1176
+ </div>
1177
+
1178
+ <!-- Grid Tab -->
1179
+ <div class="settings-tab-content" id="grid-tab">
1180
+ <h4 class="settings-section-title">Grid Settings</h4>
1181
+ <p class="settings-section-desc">Customize the component grid layout</p>
1182
+
1183
+ <div class="settings-form-group">
1184
+ <label for="gridColumnsPreset">Grid Columns Layout</label>
1185
+ <div class="grid-columns-presets">
1186
+ <button type="button" class="grid-preset-btn" data-columns="repeat(1, 1fr)" title="1 column">
1187
+ <span class="grid-preset-icon sicon-inbox-multi"></span>
1188
+ </button>
1189
+ <button type="button" class="grid-preset-btn" data-columns="repeat(2, 1fr)" title="2 columns">
1190
+ <span class="grid-preset-icon sicon-layout-grid"></span>
1191
+ </button>
1192
+ <button type="button" class="grid-preset-btn" data-columns="repeat(3, 1fr)" title="3 columns">
1193
+ <span class="grid-preset-icon sicon-grid"></span>
1194
+ </button>
1195
+ <button type="button" class="grid-preset-btn" data-columns="repeat(4, 1fr)" title="4 columns">
1196
+ <span class="grid-preset-icon">▮▮▮▮</span>
1197
+ </button>
1198
+ <button type="button" class="grid-preset-btn" data-columns="auto-fill" title="Auto-fill">
1199
+ <span class="grid-preset-icon sicon-window-layout"></span>
1200
+ </button>
1201
+ <button type="button" class="grid-preset-btn grid-preset-custom" data-columns="custom" title="Custom">
1202
+ <span class="grid-preset-icon sicon-settings"></span>
1203
+ </button>
1204
+ </div>
1205
+ <div id="customGridColumnsContainer" class="custom-grid-columns-container">
1206
+ <input type="text" id="gridColumns" dir="ltr" class="settings-input" placeholder="repeat(3, 1fr)" value="${e.grid.columns}" data-custom-value="${e.grid.columns}">
1207
+ <small>CSS grid-template-columns value</small>
1208
+ </div>
1209
+ </div>
1210
+
1211
+ <div class="settings-form-group">
1212
+ <label for="gridGap">Grid Gap</label>
1213
+ <div class="settings-input-with-unit">
1214
+ <input type="number" id="gridGapValue" class="settings-input settings-input-number" placeholder="1" step="0.1" min="0">
1215
+ <select id="gridGapUnit" class="settings-input-unit">
1216
+ <option value="px">px</option>
1217
+ <option value="rem">rem</option>
1218
+ <option value="em">em</option>
1219
+ <option value="%">%</option>
1220
+ </select>
1221
+ </div>
1222
+ <small>Space between grid items</small>
1223
+ </div>
1224
+
1225
+ <!-- Min Width Breakpoint is now handled by backend only -->
1226
+ </div>
1227
+
1228
+ <!-- Custom Code Tab -->
1229
+ <div class="settings-tab-content" id="custom-code-tab">
1230
+ <h4 class="settings-section-title">Custom Code</h4>
1231
+ <p class="settings-section-desc">Add custom CSS and JavaScript</p>
1232
+
1233
+ <div class="settings-form-group">
1234
+ <label for="customCSS">Custom CSS</label>
1235
+ <textarea id="customCSS" dir="ltr" class="settings-textarea" rows="6" placeholder="/* Add your custom CSS here */">${e.css || ""}</textarea>
1236
+ </div>
1237
+
1238
+ <div class="settings-form-group">
1239
+ <label for="customJS">Custom JavaScript</label>
1240
+ <textarea id="customJS" dir="ltr" class="settings-textarea" rows="6" placeholder="// Add your custom JavaScript here">${e.js || ""}</textarea>
1241
+ </div>
1242
+ </div>
1243
+
1244
+ <!-- Form Builder Tab -->
1245
+ <div class="settings-tab-content" id="formbuilder-tab">
1246
+ <h4 class="settings-section-title">Form Builder Settings</h4>
1247
+ <p class="settings-section-desc">Configure form builder options</p>
1248
+
1249
+ <div class="settings-form-group">
1250
+ <label for="formbuilderLanguages">Languages</label>
1251
+ <select id="formbuilderLanguages" class="settings-input" multiple>
1252
+ ${["ar", "en", "bg", "cs", "da", "de", "el", "es", "et", "fa", "fi", "fr", "ga", "he", "hi", "hr", "hu", "hy", "ind", "it", "ja", "ko", "lv", "mt", "nl", "pl", "pt", "ro", "ru", "sl", "sq", "sv", "tl", "tr", "uk", "ur", "zh", "bn"].map((n) => `
1253
+ <option value="${n}" ${e.formbuilder.languages.includes(n) ? "selected" : ""}>${n}</option>
1254
+ `).join("")}
1255
+ </select>
1256
+ </div>
1257
+
1258
+ <div class="settings-form-group">
1259
+ <label for="formbuilderDefaultLang">Default Language</label>
1260
+ <select id="formbuilderDefaultLang" class="settings-input">
1261
+ ${["ar", "en", "bg", "cs", "da", "de", "el", "es", "et", "fa", "fi", "fr", "ga", "he", "hi", "hr", "hu", "hy", "ind", "it", "ja", "ko", "lv", "mt", "nl", "pl", "pt", "ro", "ru", "sl", "sq", "sv", "tl", "tr", "uk", "ur", "zh", "bn"].map((n) => `
1262
+ <option value="${n}" ${e.formbuilder.defaultLanguage === n ? "selected" : ""}>${n}</option>
1263
+ `).join("")}
1264
+ </select>
1265
+ <small>Default language for the form builder</small>
1266
+ </div>
1267
+ </div>
1268
+ </div>
1269
+
1270
+ <!-- Footer removed as requested -->
1271
+ </div>
1272
+ </div>
1273
+
1274
+ <script>
1275
+ const translations = ${JSON.stringify(o)};
1276
+ const toggleTheme = document.getElementById('toggleTheme');
1277
+ const toggleLang = document.getElementById('toggleLang');
1278
+
1279
+ // Simple debounce function to prevent too many updates
1280
+ function debounce(func, wait) {
1281
+ let timeout;
1282
+ return function(...args) {
1283
+ clearTimeout(timeout);
1284
+ timeout = setTimeout(() => func.apply(this, args), wait);
1285
+ };
1286
+ }
1287
+
1288
+ // Get stored preferences or use defaults
1289
+ let currentLang = localStorage.getItem('salla_demo_lang') || 'ar';
1290
+ let currentTheme = localStorage.getItem('salla_demo_theme') || 'light';
1291
+
1292
+ function __demoTrans(key){
1293
+ return translations[currentLang][key];
1294
+ }
1295
+ // Function to update language
1296
+ function updateLanguage(lang) {
1297
+ currentLang = lang;
1298
+ const dir = currentLang === 'ar' ? 'rtl' : 'ltr';
1299
+ document.documentElement.setAttribute('lang', currentLang);
1300
+ document.documentElement.setAttribute('dir', dir);
1301
+ localStorage.setItem('salla_demo_lang', currentLang);
1302
+
1303
+ // Update language code and title
1304
+ const langCode = toggleLang.querySelector('.lang-code');
1305
+ const pageTitle = document.getElementById('pageTitle');
1306
+ langCode.textContent = currentLang === 'ar' ? 'EN' : 'AR';
1307
+ pageTitle.textContent = __demoTrans('title');
1308
+ }
1309
+
1310
+ // Function to update theme
1311
+ function updateTheme(theme) {
1312
+ currentTheme = theme;
1313
+ document.documentElement.setAttribute('data-theme', currentTheme);
1314
+ localStorage.setItem('salla_demo_theme', currentTheme);
1315
+ }
1316
+
1317
+ // Initialize with stored or default values
1318
+ updateLanguage(currentLang);
1319
+ updateTheme(currentTheme);
1320
+
1321
+
1322
+ function initSettings(){
1323
+ // Initialize grid settings from localStorage
1324
+ const savedGrid = getSavedGrid();
1325
+
1326
+ const grid = document.getElementById('componentsGrid');
1327
+ if (grid) {
1328
+ // Apply grid settings immediately
1329
+ if (savedGrid.columns === 'auto-fill') {
1330
+ // Apply special auto-fill behavior
1331
+ updateGridBasedOnItemCount();
1332
+ } else {
1333
+ // Apply normal grid columns
1334
+ grid.style.gridTemplateColumns = savedGrid.columns;
1335
+ }
1336
+ grid.style.gap = savedGrid.gap;
1337
+ }
1338
+
1339
+ // Initialize grid gap inputs with saved values
1340
+ const gapMatch = savedGrid.gap.match(/([d.]+)([a-z%]+)/);
1341
+ if (gapMatch && document.getElementById('gridGapValue') && document.getElementById('gridGapUnit')) {
1342
+ const [_, gapValue, gapUnit] = gapMatch;
1343
+ document.getElementById('gridGapValue').value = gapValue;
1344
+ document.getElementById('gridGapUnit').value = gapUnit;
1345
+ }
1346
+
1347
+ // Initialize grid preset buttons based on saved columns
1348
+ const gridColumnsInput = document.getElementById('gridColumns');
1349
+ if (gridColumnsInput) {
1350
+ gridColumnsInput.value = savedGrid.columns;
1351
+
1352
+ // Set the active preset button
1353
+ document.querySelectorAll('.grid-preset-btn').forEach(btn => {
1354
+ const columns = btn.getAttribute('data-columns');
1355
+ if (columns === savedGrid.columns ||
1356
+ (columns === 'auto-fill' && savedGrid.columns === 'auto-fill') ||
1357
+ (columns === 'custom' && !['1', '2', '3', '4', 'auto-fill'].includes(savedGrid.columns))) {
1358
+
1359
+ // Remove active class from all buttons
1360
+ document.querySelectorAll('.grid-preset-btn').forEach(b => b.classList.remove('active'));
1361
+ // Add active class to this button
1362
+ btn.classList.add('active');
1363
+
1364
+ // Handle custom preset
1365
+ if (columns === 'custom') {
1366
+ gridColumnsInput.readOnly = false;
1367
+ gridColumnsInput.setAttribute('data-custom-value', savedGrid.columns);
1368
+ } else {
1369
+ gridColumnsInput.readOnly = true;
1370
+ }
1371
+ }
1372
+ });
1373
+ }
1374
+
1375
+ // Apply custom CSS if saved
1376
+ const savedCSS = Salla.storage.get('salla_demo_custom_css', '');
1377
+ if (savedCSS) {
1378
+ const customCSSElement = document.createElement('style');
1379
+ customCSSElement.id = 'custom-css-element';
1380
+ customCSSElement.textContent = savedCSS;
1381
+ document.head.appendChild(customCSSElement);
1382
+ }
1383
+
1384
+ // Apply custom JS if saved
1385
+ const savedJS = Salla.storage.get('salla_demo_custom_js', '');
1386
+ if (savedJS) {
1387
+ try {
1388
+ eval(savedJS);
1389
+ } catch (error) {
1390
+ console.error('Error executing custom JS:', error);
1391
+ }
1392
+ }
1393
+ }
1394
+ // Event listeners for theme and language toggles
1395
+ toggleTheme.addEventListener('click', () => {
1396
+ updateTheme(currentTheme === 'light' ? 'dark' : 'light');
1397
+ });
1398
+
1399
+ toggleLang.addEventListener('click', () => {
1400
+ updateLanguage(currentLang === 'ar' ? 'en' : 'ar');
1401
+ });
1402
+
1403
+ // Settings tabs functionality
1404
+ document.querySelectorAll('.settings-tab-btn').forEach(tab => {
1405
+ tab.addEventListener('click', () => {
1406
+ const tabId = tab.getAttribute('data-tab');
1407
+ if (!tabId) return;
1408
+
1409
+ // Update active tab
1410
+ document.querySelectorAll('.settings-tab-btn').forEach(t => {
1411
+ t.classList.remove('active');
1412
+ });
1413
+ tab.classList.add('active');
1414
+
1415
+ // Update active content
1416
+ document.querySelectorAll('.settings-tab-content').forEach(content => {
1417
+ content.classList.remove('active');
1418
+ });
1419
+ document.getElementById(tabId + '-tab')?.classList.add('active');
1420
+
1421
+ // Apply settings immediately when switching tabs
1422
+ applySettings();
1423
+ });
1424
+ });
1425
+
1426
+ // Grid preset buttons functionality
1427
+ document.querySelectorAll('.grid-preset-btn').forEach(btn => {
1428
+ btn.addEventListener('click', () => {
1429
+ // Remove active class from all buttons
1430
+ document.querySelectorAll('.grid-preset-btn').forEach(b => b.classList.remove('active'));
1431
+ // Add active class to clicked button
1432
+ btn.classList.add('active');
1433
+
1434
+ const columns = btn.getAttribute('data-columns');
1435
+ const gridColumns = document.getElementById('gridColumns');
1436
+
1437
+ if (gridColumns) {
1438
+ // If custom preset, enable input and set to custom value
1439
+ if (columns === 'custom') {
1440
+ gridColumns.readOnly = false;
1441
+ gridColumns.value = gridColumns.getAttribute('data-custom-value') || 'repeat(auto-fill, minmax(300px, 1fr))';
1442
+ }
1443
+ // If auto-fill preset, disable input and set special value
1444
+ else if (columns === 'auto-fill') {
1445
+ gridColumns.readOnly = true;
1446
+ gridColumns.value = 'auto-fill';
1447
+ }
1448
+ // For other presets, disable input and set preset value
1449
+ else {
1450
+ gridColumns.readOnly = true;
1451
+ gridColumns.value = columns;
1452
+ }
1453
+ }
1454
+
1455
+ // Apply settings immediately
1456
+ applySettings();
1457
+ });
1458
+ });
1459
+
1460
+ // Drawer functionality
1461
+ const componentDrawer = document.getElementById('componentDrawer');
1462
+ const settingsDrawer = document.getElementById('settingsDrawer');
1463
+ const drawerOverlay = document.getElementById('drawerOverlay');
1464
+ const drawerClose = componentDrawer?.querySelector('.drawer-close');
1465
+ const drawerReset = componentDrawer?.querySelector('.drawer-reset');
1466
+ const drawerTitle = componentDrawer?.querySelector('.drawer-title');
1467
+ const drawerContent = componentDrawer?.querySelector('.drawer-content');
1468
+
1469
+ // Function to open component drawer
1470
+ function openDrawer(componentName) {
1471
+ // Inject form builder script if it's not loaded yet
1472
+ if (!document.getElementById('form-builder-3-script')) {
1473
+ const script = document.createElement('script');
1474
+ script.id = 'form-builder-3-script';
1475
+ script.src = 'https://cdn.assets.salla.network/themes/default/temporary/form-builder-3.js';
1476
+ script.async = true;
1477
+ document.head.appendChild(script);
1478
+ }
1479
+
1480
+ if (drawerTitle) drawerTitle.textContent = componentName;
1481
+ componentDrawer?.classList.add('active');
1482
+ drawerOverlay?.classList.add('active');
1483
+ document.body.style.overflow = 'hidden';
1484
+ if(componentDrawer && componentDrawer.currentComponent === componentName){
1485
+ return;
1486
+ }
1487
+ if (componentDrawer) componentDrawer.currentComponent = componentName;
1488
+ const schema = schemaForComponent(componentName);
1489
+
1490
+ if(!schema){
1491
+ return drawerContent.innerHTML = \`
1492
+ <div style="padding: 2rem; text-align: center; color: #666;">
1493
+ <div style="margin-bottom: 1rem;">
1494
+ <i class="sicon-info" style="font-size: 3rem; color: #2377CD;"></i>
1495
+ </div>
1496
+ <h3 style="margin-bottom: 1rem; font-weight: 500;">\${__demoTrans('noSettings')}</h3>
1497
+ <p>\${__demoTrans('addSettings')}</p>
1498
+ <a href="/twilight-bundle.json" target="_blank">twilight-bundle.json</a>
1499
+ </div>
1500
+ \`;
1501
+ }
1502
+ const savedFormbuilder = getSavedLanguages();
1503
+ drawerContent.innerHTML = \`
1504
+ <form-builder-3
1505
+ form-key="form-builder-3"
1506
+ form-data='\${schema}'
1507
+ save-url="${i}"
1508
+ sources-url="${i}/sources"
1509
+ upload-url="${i}/uploader"
1510
+ direction="v"
1511
+ button="start"
1512
+ css-url="${r.join(",")}"
1513
+ language-list="\${savedFormbuilder.languages.join(',')}"
1514
+ default-language="\${savedFormbuilder.defaultLanguage}"
1515
+ submit-label="\${__demoTrans('saveSettings')}"></form-builder-3>
1516
+ \`;
1517
+ }
1518
+
1519
+ // Function to close drawer
1520
+ function closeDrawer() {
1521
+ document.querySelectorAll('.drawer.active').forEach(element => element.classList.remove('active'));
1522
+ drawerOverlay?.classList.remove('active');
1523
+ document.body.style.overflow = '';
1524
+ }
1525
+
1526
+ function resetComponentSettings() {
1527
+ // Reset any component settings that need to be updated when form-builder settings change
1528
+ // Find all form-builder components and reset them
1529
+ const formComponents = document.querySelectorAll('[data-component-type="form-builder"]');
1530
+ formComponents.forEach(component => {
1531
+ // Trigger a reset or re-render for the component
1532
+ component.classList.add('settings-updated');
1533
+ setTimeout(() => {
1534
+ component.classList.remove('settings-updated');
1535
+ }, 1000);
1536
+ });
1537
+
1538
+ // Trigger custom event for components to listen to
1539
+ document.dispatchEvent(new CustomEvent('formbuilder-settings-changed', {
1540
+ detail: {
1541
+ timestamp: new Date().getTime()
1542
+ }
1543
+ }));
1544
+
1545
+ // Close the component drawer if it's open
1546
+ const componentDrawer = document.getElementById('componentDrawer');
1547
+ if (componentDrawer?.classList.contains('active')) {
1548
+ componentDrawer.classList.remove('active');
1549
+ document.getElementById('drawerOverlay')?.classList.remove('active');
1550
+ document.body.style.overflow = '';
1551
+ }
1552
+ }
1553
+
1554
+ // Function to reset settings and reload page
1555
+ function resetSettings() {
1556
+ if (componentDrawer && componentDrawer.currentComponent) {
1557
+ localStorage.removeItem('form-builder::' + componentDrawer.currentComponent);
1558
+ localStorage.removeItem('form-builder::data_' + componentDrawer.currentComponent);
1559
+ reRenderComponent(componentDrawer.currentComponent);
1560
+ }
1561
+ }
1562
+
1563
+
1564
+ // Add event listeners for drawer close and reset
1565
+ drawerClose.addEventListener('click', closeDrawer);
1566
+ drawerOverlay.addEventListener('click', closeDrawer);
1567
+ drawerReset.addEventListener('click', resetSettings);
1568
+ (async () => {
1569
+ async function waitForCondition(callback, timeout, interval) {
1570
+ const start = Date.now();
1571
+
1572
+ while (Date.now() - start < timeout) {
1573
+ if (callback()) {
1574
+ return true;
1575
+ }
1576
+ console.log('waiting for window.Salla.onReady...'+(Date.now() - start));
1577
+ await new Promise((resolve) => setTimeout(resolve, interval));
1578
+ }
1579
+
1580
+ return false;
1581
+ }
1582
+ await waitForCondition(()=>window.Salla && window.Salla.onReady, 10000, 50);
1583
+ await window.Salla.onReady();
1584
+ Salla.lang.setLocale(currentLang);
1585
+ const hiddenComponents = Salla.storage.get('hidden-salla-components', []);
1586
+ Object.keys(window.customComponentsSchema || {}).forEach(name => renderComponent(name));
1587
+ initSettings();
1588
+ })();
1589
+
1590
+ <\/script>
1591
+ <style>${e.css}</style>
1592
+ <script>${e.js}<\/script>
1593
+ </body>
1594
+ </html>`;
1595
+ }
1596
+ function w(t) {
1597
+ t && a.existsSync(t) && a.unlinkSync(t);
1598
+ }
1599
+ function k(t) {
1600
+ try {
1601
+ const e = c.join(process.cwd(), "twilight-bundle.json"), r = JSON.parse(a.readFileSync(e, "utf-8")).components.find((s) => s.name === t);
1602
+ return r != null && r.fields ? (r.fields.push({
1603
+ type: "string",
1604
+ format: "hidden",
1605
+ id: "twilight-bundles-component-name",
1606
+ value: t
1607
+ }), JSON.stringify(r == null ? void 0 : r.fields)) : "";
1608
+ } catch (e) {
1609
+ return console.error("Error getting schema for component:", e), "";
1610
+ }
1611
+ }
1612
+ function B(t = {}) {
1613
+ let e;
1614
+ return {
1615
+ name: "salla-component-demo",
1616
+ enforce: "pre",
1617
+ configResolved(o) {
1618
+ var g, p, b, f, v, h;
1619
+ if ((g = o.server) != null && g.open)
1620
+ return;
1621
+ const r = I();
1622
+ let s = t.components ? Object.fromEntries(
1623
+ Object.entries(r).filter(([d]) => t.components.includes(d))
1624
+ ) : r;
1625
+ const l = ".salla-temp", n = c.resolve(process.cwd(), "node_modules", l), C = Object.entries(s).map(function([d, u]) {
1626
+ var y, S;
1627
+ const x = (y = o.server) != null && y.port ? u.replace(process.cwd(), `http://localhost:${(S = o.server) == null ? void 0 : S.port}`) : u, E = k(d);
1628
+ return { name: d, path: u, url: x, schema: E };
1629
+ });
1630
+ a.existsSync(n) || a.mkdirSync(n, { recursive: !0 }), e = c.resolve(n, "index.html"), a.writeFileSync(e, G(C, {
1631
+ grid: {
1632
+ columns: ((p = t.grid) == null ? void 0 : p.columns) || "repeat(auto-fill, minmax(300px, 1fr))",
1633
+ gap: ((b = t.grid) == null ? void 0 : b.gap) || "1rem",
1634
+ minWidth: ((f = t.grid) == null ? void 0 : f.minWidth) || "300px"
1635
+ },
1636
+ css: t.css || "",
1637
+ js: t.js || "",
1638
+ formbuilder: {
1639
+ languages: ((v = t.formbuilder) == null ? void 0 : v.languages) || ["ar", "en"],
1640
+ defaultLanguage: ((h = t.formbuilder) == null ? void 0 : h.defaultLanguage) || "ar"
1641
+ }
1642
+ }));
1643
+ const m = () => {
1644
+ w(e);
1645
+ try {
1646
+ a.rmdirSync(n);
1647
+ } catch {
1648
+ }
1649
+ };
1650
+ process.on("SIGINT", m), process.on("SIGTERM", m), process.on("exit", m), o.server.open = `/node_modules/${l}/index.html`;
1651
+ },
1652
+ configureServer(o) {
1653
+ },
1654
+ config(o) {
1655
+ return {
1656
+ ...o,
1657
+ server: {
1658
+ ...o.server,
1659
+ watch: {
1660
+ usePolling: !0,
1661
+ interval: 100
1662
+ }
1663
+ }
1664
+ };
1665
+ },
1666
+ closeBundle() {
1667
+ w(e);
1668
+ const o = c.resolve(process.cwd(), "node_modules/.salla-temp");
1669
+ try {
1670
+ a.rmdirSync(o);
1671
+ } catch {
1672
+ }
1673
+ }
1674
+ };
1675
+ }
1676
+ export {
1677
+ B as sallaDemoPlugin
1678
+ };