plutonium 0.58.0 → 0.58.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.
@@ -44,12 +44,10 @@ module Plutonium
44
44
  # preferences read from localStorage:
45
45
  # - Color mode: applies `dark` class on <html> so dark theme renders
46
46
  # from the first frame instead of flashing light.
47
- # - Rail-pin: the rail is pinned by default, so this applies
48
- # `pu-rail-pinned` on <body> (when present) and on every incoming
49
- # body via turbo:before-render unless the user explicitly collapsed
50
- # it (localStorage "false"), so a Turbo.visit (e.g. the redirect
51
- # after a form submit) doesn't flash the rail into its collapsed
52
- # state before the icon-rail Stimulus controller can restore it.
47
+ # - Rail-pin: a turbo:before-render listener adds/removes `pu-rail-pinned`
48
+ # on <html> based on whether the incoming body contains an icon-rail,
49
+ # preventing layout shift on Turbo navigations between rail and non-rail
50
+ # pages. Initial-load rail-pinned is handled by ResourceLayout.
53
51
  def render_pre_paint_scripts
54
52
  script do
55
53
  raw(safe(<<~JS))
@@ -63,11 +61,12 @@ module Plutonium
63
61
  } catch (e) {}
64
62
 
65
63
  try {
66
- if (localStorage.getItem("pu_rail_pinned") === "false") return;
67
- if (document.body) document.body.classList.add("pu-rail-pinned");
68
- document.addEventListener("turbo:before-render", function (event) {
69
- if (localStorage.getItem("pu_rail_pinned") !== "false") {
70
- event.detail.newBody.classList.add("pu-rail-pinned");
64
+ document.addEventListener("turbo:before-render", function (e) {
65
+ var hasRail = !!e.detail.newBody.querySelector('[data-controller~="icon-rail"]');
66
+ if (hasRail && localStorage.getItem("pu_rail_pinned") !== "false") {
67
+ document.documentElement.classList.add("pu-rail-pinned");
68
+ } else {
69
+ document.documentElement.classList.remove("pu-rail-pinned");
71
70
  }
72
71
  });
73
72
  } catch (e) {}
@@ -4,6 +4,24 @@ module Plutonium
4
4
  class ResourceLayout < Base
5
5
  private
6
6
 
7
+ # Sets pu-rail-pinned immediately on initial page load so the rail
8
+ # renders in its pinned state from the first frame. Turbo navigations
9
+ # are handled by the turbo:before-render listener in Base.
10
+ def render_pre_paint_scripts
11
+ super
12
+ script do
13
+ raw(safe(<<~JS))
14
+ (function () {
15
+ try {
16
+ if (localStorage.getItem("pu_rail_pinned") !== "false") {
17
+ document.documentElement.classList.add("pu-rail-pinned");
18
+ }
19
+ } catch (e) {}
20
+ })();
21
+ JS
22
+ end
23
+ end
24
+
7
25
  def main_attributes
8
26
  classes = case Plutonium.configuration.shell
9
27
  when :modern
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- VERSION = "0.58.0"
2
+ VERSION = "0.58.1"
3
3
  NEXT_MAJOR_VERSION = VERSION.split(".").tap { |v|
4
4
  v[1] = v[1].to_i + 1
5
5
  v[2] = 0
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radioactive-labs/plutonium",
3
- "version": "0.58.0",
3
+ "version": "0.58.1",
4
4
  "description": "Build production-ready Rails apps in minutes, not days. Convention-driven, fully customizable, AI-ready.",
5
5
  "type": "module",
6
6
  "main": "src/js/core.js",
@@ -689,16 +689,16 @@ aside[data-controller~="icon-rail"] {
689
689
  }
690
690
 
691
691
  /* Pinned mode: rail expands, labels appear */
692
- body.pu-rail-pinned aside[data-controller~="icon-rail"] {
692
+ html.pu-rail-pinned aside[data-controller~="icon-rail"] {
693
693
  width: 14rem !important; /* 224px */
694
694
  }
695
695
 
696
- body.pu-rail-pinned .icon-rail-label {
696
+ html.pu-rail-pinned .icon-rail-label {
697
697
  display: inline-flex !important;
698
698
  }
699
699
 
700
- body.pu-rail-pinned .icon-rail-leaf,
701
- body.pu-rail-pinned .icon-rail-parent-trigger {
700
+ html.pu-rail-pinned .icon-rail-leaf,
701
+ html.pu-rail-pinned .icon-rail-parent-trigger {
702
702
  width: 100%;
703
703
  justify-content: flex-start;
704
704
  gap: 8px;
@@ -707,7 +707,7 @@ body.pu-rail-pinned .icon-rail-parent-trigger {
707
707
 
708
708
  /* When pinned, chevron flows inline after the label (right-aligned)
709
709
  and rotates to point down — mirrors a typical disclosure caret. */
710
- body.pu-rail-pinned .icon-rail-chevron {
710
+ html.pu-rail-pinned .icon-rail-chevron {
711
711
  position: static;
712
712
  background: transparent;
713
713
  box-shadow: none;
@@ -720,7 +720,7 @@ body.pu-rail-pinned .icon-rail-chevron {
720
720
  }
721
721
 
722
722
  /* When pinned, items stretch full width so labels read left-to-right */
723
- body.pu-rail-pinned #sidebar-navigation-content {
723
+ html.pu-rail-pinned #sidebar-navigation-content {
724
724
  align-items: stretch;
725
725
  padding-left: 8px;
726
726
  padding-right: 8px;
@@ -728,31 +728,31 @@ body.pu-rail-pinned #sidebar-navigation-content {
728
728
 
729
729
  /* When pinned, brand area aligns to the start so it reads as part of the
730
730
  left-aligned column instead of staying centered while items go left. */
731
- body.pu-rail-pinned aside[data-controller~="icon-rail"] > div:first-child {
731
+ html.pu-rail-pinned aside[data-controller~="icon-rail"] > div:first-child {
732
732
  justify-content: flex-start;
733
733
  padding-left: 14px;
734
734
  }
735
735
 
736
736
  /* When pinned, pin button right-aligns to anchor the collapse affordance
737
737
  to the rail edge it'll snap toward. */
738
- body.pu-rail-pinned aside[data-controller~="icon-rail"] > div:last-child {
738
+ html.pu-rail-pinned aside[data-controller~="icon-rail"] > div:last-child {
739
739
  justify-content: flex-end;
740
740
  padding-right: 8px;
741
741
  }
742
742
 
743
743
  /* Pin button icon swap */
744
- body.pu-rail-pinned .icon-rail-pin-collapse {
744
+ html.pu-rail-pinned .icon-rail-pin-collapse {
745
745
  display: inline-flex !important;
746
746
  }
747
747
 
748
- body.pu-rail-pinned .icon-rail-pin-expand {
748
+ html.pu-rail-pinned .icon-rail-pin-expand {
749
749
  display: none !important;
750
750
  }
751
751
 
752
752
  /* Main content padding when pinned: rail width + 1.5rem breathing room
753
753
  (matches the collapsed-state lg:pl-20 = rail 3.5rem + 1.5rem gap). */
754
754
  @media (min-width: 1024px) {
755
- body.pu-rail-pinned main {
755
+ html.pu-rail-pinned main {
756
756
  padding-left: 15.5rem !important;
757
757
  }
758
758
  }
@@ -11,11 +11,20 @@ export default class extends Controller {
11
11
  connect() {
12
12
  // Pinned is the default; only an explicit "false" collapses the rail.
13
13
  const pinned = localStorage.getItem(this.storageKeyValue) !== "false"
14
- document.body.classList.toggle("pu-rail-pinned", pinned)
14
+ document.documentElement.classList.toggle("pu-rail-pinned", pinned)
15
+ }
16
+
17
+ disconnect() {
18
+ // Guard: if another icon-rail is already in the DOM (Turbo swapped to a
19
+ // page that also has a rail), leave the class alone — the new controller's
20
+ // connect() will assert the correct value immediately after.
21
+ if (!document.querySelector('[data-controller~="icon-rail"]')) {
22
+ document.documentElement.classList.remove("pu-rail-pinned")
23
+ }
15
24
  }
16
25
 
17
26
  togglePin() {
18
- const pinned = document.body.classList.toggle("pu-rail-pinned")
27
+ const pinned = document.documentElement.classList.toggle("pu-rail-pinned")
19
28
  localStorage.setItem(this.storageKeyValue, pinned)
20
29
  }
21
30
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plutonium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.58.0
4
+ version: 0.58.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich