maglevcms 3.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/maglev/tailwind.css +123 -105
  3. data/app/assets/javascripts/maglev/editor/controllers/app/forms/section_form_controller.js +31 -6
  4. data/app/assets/javascripts/maglev/editor/controllers/app/page_preview_controller.js +20 -6
  5. data/app/assets/stylesheets/maglev/application.css +47 -3
  6. data/app/components/maglev/uikit/app_layout/sidebar_component.html.erb +1 -1
  7. data/app/components/maglev/uikit/app_layout/topbar/logo_component.html.erb +1 -1
  8. data/app/components/maglev/uikit/app_layout/topbar/page_info_component.html.erb +2 -2
  9. data/app/components/maglev/uikit/breadcrumbs/breadcrumbs_component.html.erb +2 -2
  10. data/app/components/maglev/uikit/breadcrumbs/link_component.html.erb +2 -2
  11. data/app/components/maglev/uikit/button_group_component/button_group_component.html.erb +1 -1
  12. data/app/components/maglev/uikit/collapsible/item_component.html.erb +1 -1
  13. data/app/components/maglev/uikit/dropdown_component/dropdown_controller.js +13 -2
  14. data/app/components/maglev/uikit/form/checkbox_component.html.erb +2 -2
  15. data/app/components/maglev/uikit/form/color_field_component.html.erb +3 -3
  16. data/app/components/maglev/uikit/form/combobox/list_component.html.erb +1 -1
  17. data/app/components/maglev/uikit/form/combobox/option_component.html.erb +1 -1
  18. data/app/components/maglev/uikit/form/combobox_component.html.erb +1 -1
  19. data/app/components/maglev/uikit/form/link/email_link_component.html.erb +2 -2
  20. data/app/components/maglev/uikit/form/link/page_link_component.html.erb +2 -2
  21. data/app/components/maglev/uikit/form/link/static_page_link_component.html.erb +2 -2
  22. data/app/components/maglev/uikit/form/link/url_link_component.html.erb +2 -2
  23. data/app/components/maglev/uikit/form/richtext/block_button_component.html.erb +1 -1
  24. data/app/components/maglev/uikit/form/richtext/button_component.html.erb +2 -2
  25. data/app/components/maglev/uikit/form/richtext/toolbar_component.html.erb +1 -1
  26. data/app/components/maglev/uikit/form/search_form_component.html.erb +1 -1
  27. data/app/components/maglev/uikit/form/search_form_component.rb +1 -1
  28. data/app/components/maglev/uikit/form/select_component.html.erb +1 -1
  29. data/app/components/maglev/uikit/image_library/card_component.html.erb +1 -1
  30. data/app/components/maglev/uikit/list/insert_button_component.html.erb +1 -1
  31. data/app/components/maglev/uikit/list/list_item_component.html.erb +7 -5
  32. data/app/components/maglev/uikit/list/list_item_component.rb +2 -0
  33. data/app/components/maglev/uikit/locale_switcher_component/locale_switcher_component.html.erb +1 -1
  34. data/app/components/maglev/uikit/menu_dropdown_component.rb +3 -3
  35. data/app/components/maglev/uikit/modal_component/modal_component.html.erb +3 -3
  36. data/app/components/maglev/uikit/modal_component.rb +1 -1
  37. data/app/components/maglev/uikit/page_layout_component/back_link_component.html.erb +1 -1
  38. data/app/components/maglev/uikit/page_layout_component/page_layout_component.html.erb +2 -2
  39. data/app/components/maglev/uikit/page_layout_component.rb +3 -3
  40. data/app/components/maglev/uikit/pagination_component/pagination_component.html.erb +4 -4
  41. data/app/components/maglev/uikit/section_toolbar/top_left_corner_component.html.erb +4 -4
  42. data/app/components/maglev/uikit/section_toolbar/top_right_corner_component.html.erb +3 -3
  43. data/app/controllers/concerns/maglev/editor/errors_concern.rb +1 -1
  44. data/app/controllers/concerns/maglev/editor/lock_version_concern.rb +27 -0
  45. data/app/controllers/maglev/editor/section_blocks_controller.rb +3 -9
  46. data/app/controllers/maglev/editor/sections_controller.rb +3 -9
  47. data/app/helpers/maglev/application_helper.rb +6 -0
  48. data/app/services/concerns/maglev/content/helpers_concern.rb +1 -1
  49. data/app/views/layouts/maglev/editor/application.html.erb +1 -1
  50. data/app/views/maglev/editor/assets/index.html.erb +1 -1
  51. data/app/views/maglev/editor/pages/_preview.html.erb +3 -3
  52. data/app/views/maglev/editor/pages/_preview_empty_message.html.erb +1 -1
  53. data/app/views/maglev/editor/section_blocks/index/_tree.html.erb +3 -3
  54. data/app/views/maglev/editor/sections/_toolbar_list.html.erb +1 -1
  55. data/app/views/maglev/editor/sections/theme/_list.html.erb +4 -2
  56. data/lib/maglev/version.rb +1 -1
  57. metadata +12 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 862336e896c5cea49b17ff58d5837b8d3e301580f8dbe5966d35e9ab378cbf04
4
- data.tar.gz: 2d49e3d1f3ddf480906efbffc2477073a65f66694593bfe347b9faf75ec3c798
3
+ metadata.gz: 7b3c21664099214944c91eb07e3aa563b6043e9ab1e7af4d6a0443b0d83972a9
4
+ data.tar.gz: b2618b80cdec94c2b2574ce61845c783a0593748a1498c097107213d80c60677
5
5
  SHA512:
6
- metadata.gz: 232bde061cabd3d356e6623cfdddfc4c26c2f82c3a4ace19eafe52667afe1cb94afbf7705d03519ec77a65145ab5a3fddbc41ee9e0ff10960973fe8282fe2010
7
- data.tar.gz: 3fc4998c42adb572d2f930eafb255872e90560d4330517181fd6457da0f7d8ea4f09e84ea43669ffba5dcd2b8187b43e0f263d9fa93801e07de79dcaec207d3e
6
+ metadata.gz: 7352a60fca5460da127d428cfc52cc430d887cb794879238e8a34a545e67a4a39c2fd002dd9de79a71e48f6e4e434194ffc2309e3c4b0a3c7f64084c2226d324
7
+ data.tar.gz: 16a58a3770b2be147e894d5630837ee5c4cc6de0cfaa4d81b1cd601a887f2d3b81f2f67d01821ef453b252287a8abbd83d801df59a55059545f8650ca9dc61ee
@@ -301,6 +301,27 @@
301
301
  .inset-y-0 {
302
302
  inset-block: calc(var(--spacing) * 0);
303
303
  }
304
+ .-start-1 {
305
+ inset-inline-start: calc(var(--spacing) * -1);
306
+ }
307
+ .start-0 {
308
+ inset-inline-start: calc(var(--spacing) * 0);
309
+ }
310
+ .start-3 {
311
+ inset-inline-start: calc(var(--spacing) * 3);
312
+ }
313
+ .start-16 {
314
+ inset-inline-start: calc(var(--spacing) * 16);
315
+ }
316
+ .-end-2 {
317
+ inset-inline-end: calc(var(--spacing) * -2);
318
+ }
319
+ .end-0 {
320
+ inset-inline-end: calc(var(--spacing) * 0);
321
+ }
322
+ .end-3 {
323
+ inset-inline-end: calc(var(--spacing) * 3);
324
+ }
304
325
  .top-0 {
305
326
  top: calc(var(--spacing) * 0);
306
327
  }
@@ -319,15 +340,9 @@
319
340
  .top-16 {
320
341
  top: calc(var(--spacing) * 16);
321
342
  }
322
- .-right-2 {
323
- right: calc(var(--spacing) * -2);
324
- }
325
343
  .right-0 {
326
344
  right: calc(var(--spacing) * 0);
327
345
  }
328
- .right-3 {
329
- right: calc(var(--spacing) * 3);
330
- }
331
346
  .-bottom-0\.25 {
332
347
  bottom: calc(var(--spacing) * -0.25);
333
348
  }
@@ -337,18 +352,9 @@
337
352
  .bottom-0\.5 {
338
353
  bottom: calc(var(--spacing) * 0.5);
339
354
  }
340
- .-left-1 {
341
- left: calc(var(--spacing) * -1);
342
- }
343
355
  .left-0 {
344
356
  left: calc(var(--spacing) * 0);
345
357
  }
346
- .left-3 {
347
- left: calc(var(--spacing) * 3);
348
- }
349
- .left-16 {
350
- left: calc(var(--spacing) * 16);
351
- }
352
358
  .isolate {
353
359
  isolation: isolate;
354
360
  }
@@ -439,6 +445,27 @@
439
445
  .my-20 {
440
446
  margin-block: calc(var(--spacing) * 20);
441
447
  }
448
+ .ms-1 {
449
+ margin-inline-start: calc(var(--spacing) * 1);
450
+ }
451
+ .ms-2 {
452
+ margin-inline-start: calc(var(--spacing) * 2);
453
+ }
454
+ .ms-3 {
455
+ margin-inline-start: calc(var(--spacing) * 3);
456
+ }
457
+ .ms-5 {
458
+ margin-inline-start: calc(var(--spacing) * 5);
459
+ }
460
+ .ms-auto {
461
+ margin-inline-start: auto;
462
+ }
463
+ .me-2 {
464
+ margin-inline-end: calc(var(--spacing) * 2);
465
+ }
466
+ .me-3 {
467
+ margin-inline-end: calc(var(--spacing) * 3);
468
+ }
442
469
  .-mt-0\.5 {
443
470
  margin-top: calc(var(--spacing) * -0.5);
444
471
  }
@@ -475,12 +502,6 @@
475
502
  .-mr-1 {
476
503
  margin-right: calc(var(--spacing) * -1);
477
504
  }
478
- .mr-2 {
479
- margin-right: calc(var(--spacing) * 2);
480
- }
481
- .mr-3 {
482
- margin-right: calc(var(--spacing) * 3);
483
- }
484
505
  .-mb-5 {
485
506
  margin-bottom: calc(var(--spacing) * -5);
486
507
  }
@@ -514,18 +535,6 @@
514
535
  .ml-1 {
515
536
  margin-left: calc(var(--spacing) * 1);
516
537
  }
517
- .ml-2 {
518
- margin-left: calc(var(--spacing) * 2);
519
- }
520
- .ml-3 {
521
- margin-left: calc(var(--spacing) * 3);
522
- }
523
- .ml-5 {
524
- margin-left: calc(var(--spacing) * 5);
525
- }
526
- .ml-auto {
527
- margin-left: auto;
528
- }
529
538
  .box-content {
530
539
  box-sizing: content-box;
531
540
  }
@@ -911,6 +920,9 @@
911
920
  .gap-1 {
912
921
  gap: calc(var(--spacing) * 1);
913
922
  }
923
+ .gap-1\.5 {
924
+ gap: calc(var(--spacing) * 1.5);
925
+ }
914
926
  .gap-2 {
915
927
  gap: calc(var(--spacing) * 2);
916
928
  }
@@ -970,34 +982,6 @@
970
982
  .gap-x-8 {
971
983
  column-gap: calc(var(--spacing) * 8);
972
984
  }
973
- .space-x-0\.5 {
974
- :where(& > :not(:last-child)) {
975
- --tw-space-x-reverse: 0;
976
- margin-inline-start: calc(calc(var(--spacing) * 0.5) * var(--tw-space-x-reverse));
977
- margin-inline-end: calc(calc(var(--spacing) * 0.5) * calc(1 - var(--tw-space-x-reverse)));
978
- }
979
- }
980
- .space-x-1 {
981
- :where(& > :not(:last-child)) {
982
- --tw-space-x-reverse: 0;
983
- margin-inline-start: calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));
984
- margin-inline-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)));
985
- }
986
- }
987
- .space-x-1\.5 {
988
- :where(& > :not(:last-child)) {
989
- --tw-space-x-reverse: 0;
990
- margin-inline-start: calc(calc(var(--spacing) * 1.5) * var(--tw-space-x-reverse));
991
- margin-inline-end: calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-x-reverse)));
992
- }
993
- }
994
- .space-x-2 {
995
- :where(& > :not(:last-child)) {
996
- --tw-space-x-reverse: 0;
997
- margin-inline-start: calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));
998
- margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
999
- }
1000
- }
1001
985
  .space-x-4 {
1002
986
  :where(& > :not(:last-child)) {
1003
987
  --tw-space-x-reverse: 0;
@@ -1077,13 +1061,13 @@
1077
1061
  .rounded-xs {
1078
1062
  border-radius: var(--radius-xs);
1079
1063
  }
1080
- .rounded-l-2xl {
1081
- border-top-left-radius: var(--radius-2xl);
1082
- border-bottom-left-radius: var(--radius-2xl);
1064
+ .rounded-s-2xl {
1065
+ border-start-start-radius: var(--radius-2xl);
1066
+ border-end-start-radius: var(--radius-2xl);
1083
1067
  }
1084
- .rounded-r-2xl {
1085
- border-top-right-radius: var(--radius-2xl);
1086
- border-bottom-right-radius: var(--radius-2xl);
1068
+ .rounded-e-2xl {
1069
+ border-start-end-radius: var(--radius-2xl);
1070
+ border-end-end-radius: var(--radius-2xl);
1087
1071
  }
1088
1072
  .rounded-b {
1089
1073
  border-bottom-right-radius: 0.25rem;
@@ -1105,6 +1089,10 @@
1105
1089
  border-style: var(--tw-border-style);
1106
1090
  border-width: 4px;
1107
1091
  }
1092
+ .border-e {
1093
+ border-inline-end-style: var(--tw-border-style);
1094
+ border-inline-end-width: 1px;
1095
+ }
1108
1096
  .border-t {
1109
1097
  border-top-style: var(--tw-border-style);
1110
1098
  border-top-width: 1px;
@@ -1117,10 +1105,6 @@
1117
1105
  border-top-style: var(--tw-border-style);
1118
1106
  border-top-width: 4px;
1119
1107
  }
1120
- .border-r {
1121
- border-right-style: var(--tw-border-style);
1122
- border-right-width: 1px;
1123
- }
1124
1108
  .border-b {
1125
1109
  border-bottom-style: var(--tw-border-style);
1126
1110
  border-bottom-width: 1px;
@@ -1409,6 +1393,24 @@
1409
1393
  .py-24 {
1410
1394
  padding-block: calc(var(--spacing) * 24);
1411
1395
  }
1396
+ .ps-2 {
1397
+ padding-inline-start: calc(var(--spacing) * 2);
1398
+ }
1399
+ .ps-3 {
1400
+ padding-inline-start: calc(var(--spacing) * 3);
1401
+ }
1402
+ .ps-11 {
1403
+ padding-inline-start: calc(var(--spacing) * 11);
1404
+ }
1405
+ .pe-4 {
1406
+ padding-inline-end: calc(var(--spacing) * 4);
1407
+ }
1408
+ .pe-8 {
1409
+ padding-inline-end: calc(var(--spacing) * 8);
1410
+ }
1411
+ .pe-9 {
1412
+ padding-inline-end: calc(var(--spacing) * 9);
1413
+ }
1412
1414
  .pt-2 {
1413
1415
  padding-top: calc(var(--spacing) * 2);
1414
1416
  }
@@ -1424,15 +1426,6 @@
1424
1426
  .pt-32 {
1425
1427
  padding-top: calc(var(--spacing) * 32);
1426
1428
  }
1427
- .pr-4 {
1428
- padding-right: calc(var(--spacing) * 4);
1429
- }
1430
- .pr-8 {
1431
- padding-right: calc(var(--spacing) * 8);
1432
- }
1433
- .pr-9 {
1434
- padding-right: calc(var(--spacing) * 9);
1435
- }
1436
1429
  .pb-0 {
1437
1430
  padding-bottom: calc(var(--spacing) * 0);
1438
1431
  }
@@ -1454,21 +1447,15 @@
1454
1447
  .pb-16 {
1455
1448
  padding-bottom: calc(var(--spacing) * 16);
1456
1449
  }
1457
- .pl-2 {
1458
- padding-left: calc(var(--spacing) * 2);
1459
- }
1460
- .pl-3 {
1461
- padding-left: calc(var(--spacing) * 3);
1462
- }
1463
- .pl-11 {
1464
- padding-left: calc(var(--spacing) * 11);
1465
- }
1466
1450
  .text-center {
1467
1451
  text-align: center;
1468
1452
  }
1469
1453
  .text-left {
1470
1454
  text-align: left;
1471
1455
  }
1456
+ .text-start {
1457
+ text-align: start;
1458
+ }
1472
1459
  .font-nunito {
1473
1460
  font-family: var(--font-nunito);
1474
1461
  }
@@ -1867,14 +1854,14 @@
1867
1854
  width: calc(1024px * var(--page-preview-scale-ratio));
1868
1855
  }
1869
1856
  }
1870
- .group-data-\[expanded-page-layout\]\/page-layout\:left-104 {
1857
+ .group-data-\[expanded-page-layout\]\/page-layout\:start-104 {
1871
1858
  &:is(:where(.group\/page-layout)[data-expanded-page-layout] *) {
1872
- left: calc(var(--spacing) * 104);
1859
+ inset-inline-start: calc(var(--spacing) * 104);
1873
1860
  }
1874
1861
  }
1875
- .group-data-\[expanded-page-layout\]\/page-layout\:pl-88 {
1862
+ .group-data-\[expanded-page-layout\]\/page-layout\:ps-88 {
1876
1863
  &:is(:where(.group\/page-layout)[data-expanded-page-layout] *) {
1877
- padding-left: calc(var(--spacing) * 88);
1864
+ padding-inline-start: calc(var(--spacing) * 88);
1878
1865
  }
1879
1866
  }
1880
1867
  .group-data-\[focus\]\:text-white {
@@ -2046,16 +2033,16 @@
2046
2033
  display: flex;
2047
2034
  }
2048
2035
  }
2049
- .first\:rounded-l-sm {
2036
+ .first\:rounded-s-sm {
2050
2037
  &:first-child {
2051
- border-top-left-radius: var(--radius-sm);
2052
- border-bottom-left-radius: var(--radius-sm);
2038
+ border-start-start-radius: var(--radius-sm);
2039
+ border-end-start-radius: var(--radius-sm);
2053
2040
  }
2054
2041
  }
2055
- .last\:rounded-r-sm {
2042
+ .last\:rounded-e-sm {
2056
2043
  &:last-child {
2057
- border-top-right-radius: var(--radius-sm);
2058
- border-bottom-right-radius: var(--radius-sm);
2044
+ border-start-end-radius: var(--radius-sm);
2045
+ border-end-end-radius: var(--radius-sm);
2059
2046
  }
2060
2047
  }
2061
2048
  .hover\:z-10 {
@@ -2783,6 +2770,37 @@
2783
2770
  padding-inline: calc(var(--spacing) * 16);
2784
2771
  }
2785
2772
  }
2773
+ .rtl\:origin-top-right {
2774
+ &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) {
2775
+ transform-origin: 100% 0;
2776
+ }
2777
+ }
2778
+ .rtl\:-translate-x-\[50\%\] {
2779
+ &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) {
2780
+ --tw-translate-x: calc(50% * -1);
2781
+ translate: var(--tw-translate-x) var(--tw-translate-y);
2782
+ }
2783
+ }
2784
+ .rtl\:translate-x-\[50\%\] {
2785
+ &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) {
2786
+ --tw-translate-x: 50%;
2787
+ translate: var(--tw-translate-x) var(--tw-translate-y);
2788
+ }
2789
+ }
2790
+ .rtl\:-scale-x-100 {
2791
+ &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) {
2792
+ --tw-scale-x: calc(100% * -1);
2793
+ scale: var(--tw-scale-x) var(--tw-scale-y);
2794
+ }
2795
+ }
2796
+ .rtl\:group-has-checked\:-translate-x-4 {
2797
+ &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) {
2798
+ &:is(:where(.group):has(*:checked) *) {
2799
+ --tw-translate-x: calc(var(--spacing) * -4);
2800
+ translate: var(--tw-translate-x) var(--tw-translate-y);
2801
+ }
2802
+ }
2803
+ }
2786
2804
  .dark\:text-editor-primary {
2787
2805
  @media (prefers-color-scheme: dark) {
2788
2806
  color: var(--color-editor-primary);
@@ -2881,16 +2899,16 @@
2881
2899
  }
2882
2900
  }
2883
2901
  }
2884
- .\[\&\>\*\:first-child\]\:rounded-l-sm {
2902
+ .\[\&\>\*\:first-child\]\:rounded-s-sm {
2885
2903
  &>*:first-child {
2886
- border-top-left-radius: var(--radius-sm);
2887
- border-bottom-left-radius: var(--radius-sm);
2904
+ border-start-start-radius: var(--radius-sm);
2905
+ border-end-start-radius: var(--radius-sm);
2888
2906
  }
2889
2907
  }
2890
- .\[\&\>\*\:last-child\]\:rounded-r-sm {
2908
+ .\[\&\>\*\:last-child\]\:rounded-e-sm {
2891
2909
  &>*:last-child {
2892
- border-top-right-radius: var(--radius-sm);
2893
- border-bottom-right-radius: var(--radius-sm);
2910
+ border-start-end-radius: var(--radius-sm);
2911
+ border-end-end-radius: var(--radius-sm);
2894
2912
  }
2895
2913
  }
2896
2914
  }
@@ -9,8 +9,10 @@ export default class extends Controller {
9
9
  static debounces = ['afterSettingUpdate']
10
10
 
11
11
  connect() {
12
- useDebounce(this)
12
+ useDebounce(this)
13
+
13
14
  this.sourceId = this.sectionBlockIdValue !== '' ? this.sectionBlockIdValue : this.sectionIdValue
15
+
14
16
  requestAnimationFrame(() => {
15
17
  this.dispatch('connected', { bubbles: true, detail: {
16
18
  sectionId: this.sectionIdValue,
@@ -18,6 +20,12 @@ export default class extends Controller {
18
20
  lockVersion: this.sectionLockVersionValue }
19
21
  })
20
22
  })
23
+
24
+ // we need to keep track of the submitting state to avoid multiple requests being sent with a wrong lock version
25
+ this.submitting = false
26
+ this.element.addEventListener('turbo:submit-end', (event) => {
27
+ if (!event.detail.success) this.submitting = false
28
+ })
21
29
  }
22
30
 
23
31
  onSettingChange(event) {
@@ -33,15 +41,32 @@ export default class extends Controller {
33
41
  onPersist(event) {
34
42
  log('[SectionForm::onPersist]', event.detail, typeof event.detail)
35
43
  this.lockVersionTarget.value = event.detail.lockVersion
44
+
45
+ // we are done submitting, so we can reset the submitting flag
46
+ this.submitting = false
36
47
  }
37
48
 
38
- async afterSettingUpdate(event) {
49
+ afterSettingUpdate(event) {
50
+ // if the request is still running (flight), we need to debounce the call
51
+ // to avoid multiple requests being sent with a wrong lock version
52
+ if (this.submitting) {
53
+ this.afterSettingUpdate(event)
54
+ return
55
+ }
56
+
39
57
  log('[SectionForm::afterSettingUpdate] 🙌🙌', event.detail)
40
- await this.element.requestSubmit()
41
-
58
+
59
+ // we couldn't update the content just by modifying the DOM, so we have to refresh the whole section.
60
+ // The right time to do this is when the request is finished, so we listen for the turbo:submit-end event.
42
61
  if (!event.detail.updated) {
43
- // we couldn't update the content just by modifying the DOM, so we have to refresh the whole section
44
- this.dispatch('updateSection', { detail: { sectionId: this.sectionIdValue } })
62
+ this.element.addEventListener('turbo:submit-end', () => {
63
+ this.dispatch('updateSection', { detail: { sectionId: this.sectionIdValue } })
64
+ }, { once: true })
45
65
  }
66
+
67
+ // better to set it to true instead of relying on the turbo:submit-start event
68
+ this.submitting = true
69
+
70
+ this.element.requestSubmit()
46
71
  }
47
72
  }
@@ -164,13 +164,14 @@ export default class extends Controller {
164
164
 
165
165
  setupTransformations() {
166
166
  this.calculateTransformations = this.calculateTransformations.bind(this)
167
- this.ro = new ResizeObserver(this.calculateTransformations)
168
-
167
+ this.scheduleTransformations = this.scheduleTransformations.bind(this)
168
+ this.resizeObserver = new ResizeObserver(this.scheduleTransformations)
169
+
169
170
  const sidebar = this.appLayoutSidebar
170
171
  const pageLayout = document.querySelector('#page-layout')
171
- if (sidebar) this.ro.observe(this.appLayoutSidebar())
172
- if (pageLayout) this.ro.observe(pageLayout)
173
- this.ro.observe(document.documentElement)
172
+ if (sidebar) this.resizeObserver.observe(this.appLayoutSidebar())
173
+ if (pageLayout) this.resizeObserver.observe(pageLayout)
174
+ this.resizeObserver.observe(document.documentElement)
174
175
 
175
176
  // Recompute on page layout toggles / VT renders
176
177
  addEventListener("turbo:render", this.calculateTransformations)
@@ -180,7 +181,11 @@ export default class extends Controller {
180
181
  }
181
182
 
182
183
  teardownTransformations() {
183
- this.ro?.disconnect()
184
+ if (this.transformationsFrame) {
185
+ cancelAnimationFrame(this.transformationsFrame)
186
+ this.transformationsFrame = null
187
+ }
188
+ this.resizeObserver?.disconnect()
184
189
  removeEventListener("turbo:render", this.calculateTransformations)
185
190
  removeEventListener("turbo:before-render", this.calculateTransformations)
186
191
  }
@@ -230,6 +235,15 @@ export default class extends Controller {
230
235
  this.dispatch('scale-ratio-updated', { detail: { value: scaleRatio } })
231
236
  }
232
237
 
238
+ scheduleTransformations() {
239
+ if (this.transformationsFrame) return
240
+
241
+ this.transformationsFrame = requestAnimationFrame(() => {
242
+ this.transformationsFrame = null
243
+ this.calculateTransformations()
244
+ })
245
+ }
246
+
233
247
  calculateTransformX() {
234
248
  if (!this.hasExpandedPageLayout()) return 0
235
249
  return this.expandedPageLayout().offsetWidth - this.appLayoutSidebarWidth()
@@ -32,6 +32,27 @@
32
32
  }
33
33
  }
34
34
 
35
+ /* RTL: the page-layout panel lives on the right, so it slides off to / in from the right edge */
36
+ @keyframes slide-out-rtl {
37
+ from {
38
+ transform: translateX(0%);
39
+ }
40
+
41
+ to {
42
+ transform: translateX(100%);
43
+ }
44
+ }
45
+
46
+ @keyframes slide-in-rtl {
47
+ from {
48
+ transform: translateX(100%);
49
+ }
50
+
51
+ to {
52
+ transform: translateX(0%);
53
+ }
54
+ }
55
+
35
56
  html[data-turbo-visit-direction="forward"]::view-transition-old(page-layout),
36
57
  html[data-turbo-visit-direction="back"]::view-transition-old(page-layout),
37
58
  html[data-turbo-visit-direction="forward"]::view-transition-old(expanded-page-layout),
@@ -46,6 +67,20 @@ html[data-turbo-visit-direction="back"]::view-transition-new(expanded-page-layou
46
67
  animation: 0.2s cubic-bezier(0.55, 0, 0.1, 1) both slide-in;
47
68
  }
48
69
 
70
+ html[dir="rtl"][data-turbo-visit-direction="forward"]::view-transition-old(page-layout),
71
+ html[dir="rtl"][data-turbo-visit-direction="back"]::view-transition-old(page-layout),
72
+ html[dir="rtl"][data-turbo-visit-direction="forward"]::view-transition-old(expanded-page-layout),
73
+ html[dir="rtl"][data-turbo-visit-direction="back"]::view-transition-old(expanded-page-layout) {
74
+ animation-name: slide-out-rtl;
75
+ }
76
+
77
+ html[dir="rtl"][data-turbo-visit-direction="forward"]::view-transition-new(page-layout),
78
+ html[dir="rtl"][data-turbo-visit-direction="back"]::view-transition-new(page-layout),
79
+ html[dir="rtl"][data-turbo-visit-direction="forward"]::view-transition-new(expanded-page-layout),
80
+ html[dir="rtl"][data-turbo-visit-direction="back"]::view-transition-new(expanded-page-layout) {
81
+ animation-name: slide-in-rtl;
82
+ }
83
+
49
84
  html[data-page-layout-vt-pair="already-present"]::view-transition-old(page-layout),
50
85
  html[data-page-layout-vt-pair="already-present"]::view-transition-old(expanded-page-layout),
51
86
  html[data-page-layout-vt-pair="already-present"]::view-transition-new(page-layout),
@@ -61,8 +96,12 @@ html[data-page-layout-vt-pair="already-present"]::view-transition-new(expanded-p
61
96
  z-index: 30;
62
97
  }
63
98
 
64
- ::view-transition-group(page-preview) {
65
- transform-origin: right center;
99
+ ::view-transition-group(page-preview) {
100
+ transform-origin: right center;
101
+ }
102
+
103
+ html[dir="rtl"]::view-transition-group(page-preview) {
104
+ transform-origin: left center;
66
105
  }
67
106
 
68
107
  #page-layout[data-not-expanded] {
@@ -95,7 +134,12 @@ html[data-page-layout-vt-pair="already-present"]::view-transition-new(expanded-p
95
134
 
96
135
  /* couldn't get this done in Tailwind custom classes */
97
136
  #page-preview {
98
- transform: translateX(var(--page-preview-transform-x, 0px)) scale(var(--page-preview-scale-ratio, 1));
137
+ transform: translateX(var(--page-preview-transform-x, 0px)) scale(var(--page-preview-scale-ratio, 1));
99
138
  height: calc(100% * 1 / 1 / var(--page-preview-scale-ratio, 1));
100
139
  width: var(--page-preview-width, 100%);
140
+ }
141
+
142
+ /* RTL: the expanded panel is on the right, so the canvas must shift the opposite way to clear it */
143
+ html[dir="rtl"] #page-preview {
144
+ transform: translateX(calc(-1 * var(--page-preview-transform-x, 0px))) scale(var(--page-preview-scale-ratio, 1));
101
145
  }
@@ -1,6 +1,6 @@
1
1
  <aside
2
2
  id="app-layout-sidebar"
3
- class="w-16 flex-shrink-0 flex content-center h-full border-r border-gray-200 relative z-20 bg-white"
3
+ class="w-16 flex-shrink-0 flex content-center h-full border-e border-gray-200 relative z-20 bg-white"
4
4
  >
5
5
  <nav class="flex h-full w-16 flex-col justify-between py-2">
6
6
  <ol class="flex flex-col gap-1 px-2">
@@ -1,3 +1,3 @@
1
- <%= link_to root_path, class: "w-16 h-full flex justify-center items-center border-r border-gray-200 shrink-0", data: { controller: 'prevent-same-path' } do %>
1
+ <%= link_to root_path, class: "w-16 h-full flex justify-center items-center border-e border-gray-200 shrink-0", data: { controller: 'prevent-same-path' } do %>
2
2
  <%= image_tag logo_url, class: "w-2/4", id: 'editor-logo-image', data: { turbo_permanent: true } %>
3
3
  <% end %>
@@ -5,7 +5,7 @@
5
5
  <div class="flex flex-col leading-none overflow-hidden">
6
6
  <div class="flex items-center overflow-hidden">
7
7
  <%= helpers.maglev_page_icon(page, size: '1.1rem', wrapper_class_names: 'text-gray-900 shrink-0') %>
8
- <div class="text-base font-semibold truncate ml-1 mr-3 min-w-0">
8
+ <div class="text-base font-semibold truncate ms-1 me-3 min-w-0">
9
9
  <%= page.title.presence || page.default_title %>
10
10
  </div>
11
11
 
@@ -16,7 +16,7 @@
16
16
  %>
17
17
  </div>
18
18
 
19
- <div class="text-xs text-gray-500 flex items-center space-x-1 overflow-hidden">
19
+ <div class="text-xs text-gray-500 flex items-center gap-1 overflow-hidden">
20
20
  <p class="truncate text-gray-700 min-w-0">
21
21
  <span class="text-gray-900 font-semibold">/</span><%= page_path %>
22
22
  </p>
@@ -1,9 +1,9 @@
1
1
  <nav class="flex justify-between">
2
- <ol class="inline-flex items-center space-x-0.5 text-xs text-gray-500">
2
+ <ol class="inline-flex items-center gap-0.5 text-xs text-gray-500">
3
3
  <% breadcrumbs.each_with_index do |breadcrumb, index| %>
4
4
  <%= breadcrumb %>
5
5
  <% if index < breadcrumbs.size - 1 %>
6
- <%= render Maglev::Uikit::IconComponent.new(name: 'arrow_right', size: '1rem', class_names: 'text-gray-400') %>
6
+ <%= render Maglev::Uikit::IconComponent.new(name: 'arrow_right', size: '1rem', class_names: 'text-gray-400 rtl:-scale-x-100') %>
7
7
  <% end %>
8
8
  <% end %>
9
9
  </ol>
@@ -1,12 +1,12 @@
1
1
  <li class="flex items-center h-full">
2
2
  <% if url? %>
3
- <%= link_to url, class: 'inline-flex items-center text-xs space-x-1.5 rounded-sm hover:text-gray-900', data: { turbo_frame: '_top' } do %>
3
+ <%= link_to url, class: 'inline-flex items-center text-xs gap-1.5 rounded-sm hover:text-gray-900', data: { turbo_frame: '_top' } do %>
4
4
  <% if icon? %>
5
5
  <%= render Maglev::Uikit::IconComponent.new(name: icon, size: '0.85rem') %>
6
6
  <% end %>
7
7
  <span><%= label %></span>
8
8
  <% end %>
9
9
  <% else %>
10
- <span class="inline-flex items-center text-xs space-x-1.5 rounded-sm"><%= label %></span>
10
+ <span class="inline-flex items-center text-xs gap-1.5 rounded-sm"><%= label %></span>
11
11
  <% end %>
12
12
  </li>
@@ -1,4 +1,4 @@
1
- <div class="[&>*:first-child]:rounded-l-sm [&>*:last-child]:rounded-r-sm inline-flex items-center text-white divide-x divide-white/15">
1
+ <div class="[&>*:first-child]:rounded-s-sm [&>*:last-child]:rounded-e-sm inline-flex items-center text-white divide-x divide-white/15">
2
2
  <% buttons.each do |button| %>
3
3
  <%= button %>
4
4
  <% end %>
@@ -2,7 +2,7 @@
2
2
  <div class="flex items-center cursor-pointer select-none px-3 py-3 bg-editor-primary text-white rounded-xs" data-action="click->uikit-collapsible#toggle">
3
3
  <div class="flex items-center"><%= title %></div>
4
4
 
5
- <div class="ml-auto">
5
+ <div class="ms-auto">
6
6
  <%= render Maglev::Uikit::IconComponent.new(name: :arrow_down, size: '1.15rem', class_names: 'transition-transform duration-200 rotate-180 group-[.is-collapsed]:rotate-0') %>
7
7
  </div>
8
8
  </div>
@@ -9,10 +9,11 @@ export default class extends Controller {
9
9
  connect() {
10
10
  const button = this.buttonTarget
11
11
  const content = this.contentTarget
12
+ const placement = this.directionAwarePlacement(this.placementValue || 'bottom-start')
12
13
 
13
- this.cleanup = autoUpdate(button, content, () => computePosition(button, content, {
14
+ this.cleanup = autoUpdate(button, content, () => computePosition(button, content, {
14
15
  strategy: 'fixed',
15
- placement: this.placementValue || 'bottom-start',
16
+ placement,
16
17
  middleware: [
17
18
  flip(),
18
19
  shift(),
@@ -71,4 +72,14 @@ export default class extends Controller {
71
72
  hide() {
72
73
  this.leave()
73
74
  }
75
+
76
+ // Floating UI already flips the -start/-end alignment for RTL, but the physical
77
+ // left/right main side is direction-agnostic. Mirror it so submenus/side popovers
78
+ // open toward the reading direction (e.g. right-start -> left-start in RTL).
79
+ directionAwarePlacement(placement) {
80
+ if (getComputedStyle(this.element).direction !== 'rtl') return placement
81
+ if (placement.startsWith('left')) return placement.replace('left', 'right')
82
+ if (placement.startsWith('right')) return placement.replace('right', 'left')
83
+ return placement
84
+ }
74
85
  }