active_admin_prism 0.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 (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +133 -0
  3. data/INTEGRATION.md +1012 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +319 -0
  6. data/app/assets/javascripts/active_admin_prism/prism.js +1 -0
  7. data/app/assets/stylesheets/active_admin_prism/_variable_overrides.scss +43 -0
  8. data/app/assets/stylesheets/active_admin_prism/prism.css +1 -0
  9. data/app/views/active_admin/devise/confirmations/new.html.erb +26 -0
  10. data/app/views/active_admin/devise/passwords/edit.html.erb +29 -0
  11. data/app/views/active_admin/devise/passwords/new.html.erb +26 -0
  12. data/app/views/active_admin/devise/registrations/new.html.erb +32 -0
  13. data/app/views/active_admin/devise/sessions/new.html.erb +37 -0
  14. data/app/views/active_admin/devise/shared/_brand.html.erb +34 -0
  15. data/app/views/active_admin/devise/unlocks/new.html.erb +25 -0
  16. data/js-src/prism.js +270 -0
  17. data/lib/active_admin/views/filters_sidebar.rb +69 -0
  18. data/lib/active_admin/views/flash_messages.rb +77 -0
  19. data/lib/active_admin/views/index_table_actions.rb +46 -0
  20. data/lib/active_admin/views/prism_sidebar.rb +212 -0
  21. data/lib/active_admin_prism/configuration.rb +180 -0
  22. data/lib/active_admin_prism/engine.rb +18 -0
  23. data/lib/active_admin_prism/version.rb +5 -0
  24. data/lib/active_admin_prism.rb +33 -0
  25. data/lib/formtastic/inputs/prism_toggle_input.rb +31 -0
  26. data/lib/generators/active_admin_prism/error_pages/error_pages_generator.rb +49 -0
  27. data/lib/generators/active_admin_prism/error_pages/templates/404.html +164 -0
  28. data/lib/generators/active_admin_prism/error_pages/templates/500.html +173 -0
  29. data/lib/generators/active_admin_prism/install/install_generator.rb +69 -0
  30. data/lib/prism_icons.rb +82 -0
  31. data/lib/prism_login_helper.rb +31 -0
  32. data/lib/prism_toggle_tag.rb +35 -0
  33. data/scss-src/_base.scss +281 -0
  34. data/scss-src/_buttons.scss +134 -0
  35. data/scss-src/_forms.scss +272 -0
  36. data/scss-src/_icons.scss +27 -0
  37. data/scss-src/_login.scss +306 -0
  38. data/scss-src/_panels.scss +249 -0
  39. data/scss-src/_sidebar.scss +479 -0
  40. data/scss-src/_tables.scss +142 -0
  41. data/scss-src/_variables.scss +101 -0
  42. data/scss-src/prism.scss +15 -0
  43. metadata +157 -0
@@ -0,0 +1,164 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Prism — Page Not Found</title>
7
+ <style>
8
+ /*
9
+ Static public/ error page — Rails serves this straight off disk
10
+ (ActionDispatch::PublicExceptions), completely outside the asset
11
+ pipeline and any Rails view/layout, so it can't @import prism.css,
12
+ use ERB, or rely on any Ruby config. Colors below are copied from
13
+ this gem's scss-src/_variables.scss defaults (as of the version this
14
+ was generated from) rather than referencing them, and won't track a
15
+ host's own color customizations automatically — re-run the generator
16
+ (or hand-edit this file) after changing your palette if that matters
17
+ to you. No external requests (fonts, CDNs) on purpose — this page
18
+ needs to render even if the rest of the app/network is unhealthy.
19
+ */
20
+ :root{
21
+ --primary:#6d5bd0;
22
+ --primary-dark:#5949b8;
23
+ --primary-light:#f2ecfd;
24
+ --bg:#f4f5f7;
25
+ --card:#ffffff;
26
+ --border:#e6e8ec;
27
+ --text:#1a1f36;
28
+ --muted:#6b7280;
29
+ --radius:10px;
30
+ }
31
+ *{box-sizing:border-box;}
32
+ html,body{height:100%;}
33
+ body{
34
+ margin:0;
35
+ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
36
+ background:var(--bg);
37
+ color:var(--text);
38
+ display:flex;
39
+ align-items:center;
40
+ justify-content:center;
41
+ min-height:100vh;
42
+ overflow:hidden;
43
+ position:relative;
44
+ }
45
+
46
+ .bg-blobs{position:fixed;inset:0;z-index:0;overflow:hidden;}
47
+ .blob{position:absolute;border-radius:50%;filter:blur(70px);opacity:.35;will-change:transform;}
48
+ .blob-1{width:480px;height:480px;background:radial-gradient(circle at 30% 30%, var(--primary), transparent 70%);top:-160px;left:-140px;animation:float1 16s ease-in-out infinite;}
49
+ .blob-2{width:420px;height:420px;background:radial-gradient(circle at 70% 70%, #a78bfa, transparent 70%);bottom:-140px;right:-120px;animation:float2 18s ease-in-out infinite;}
50
+ .blob-3{width:300px;height:300px;background:radial-gradient(circle, #ff6b9a, transparent 70%);top:40%;right:10%;opacity:.18;animation:float3 14s ease-in-out infinite;}
51
+ @keyframes float1{0%,100%{transform:translate(0,0) scale(1);}50%{transform:translate(60px,40px) scale(1.1);}}
52
+ @keyframes float2{0%,100%{transform:translate(0,0) scale(1);}50%{transform:translate(-50px,-30px) scale(1.08);}}
53
+ @keyframes float3{0%,100%{transform:translate(0,0);}50%{transform:translate(-40px,30px);}}
54
+
55
+ .wrapper{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;animation:cardIn .7s cubic-bezier(.2,.8,.2,1) both;}
56
+ @keyframes cardIn{from{opacity:0;transform:translateY(18px) scale(.98);}to{opacity:1;transform:translateY(0) scale(1);}}
57
+
58
+ .card{
59
+ width:460px;
60
+ max-width:90vw;
61
+ background:var(--card);
62
+ border:1px solid var(--border);
63
+ border-radius:var(--radius);
64
+ box-shadow:0 20px 50px -20px rgba(76,63,180,.25), 0 2px 8px rgba(20,20,40,.04);
65
+ padding:44px 40px 36px;
66
+ text-align:center;
67
+ }
68
+
69
+ .prism-mark{width:88px;height:88px;margin:0 auto 8px;position:relative;}
70
+ .prism-mark svg{width:100%;height:100%;overflow:visible;}
71
+ .prism-tri{animation:drift 5s ease-in-out infinite;transform-origin:center;}
72
+ @keyframes drift{0%,100%{transform:translateY(0) rotate(0deg);}50%{transform:translateY(-4px) rotate(-2deg);}}
73
+ .beam{stroke-dasharray:40;animation:beamFlow 2.4s linear infinite;}
74
+ @keyframes beamFlow{to{stroke-dashoffset:-80;}}
75
+ .beam-dash{opacity:.55;animation:fadePulse 1.8s ease-in-out infinite;}
76
+ @keyframes fadePulse{0%,100%{opacity:.25;}50%{opacity:.7;}}
77
+ .qmark{animation:bob 2.4s ease-in-out infinite;transform-origin:center;}
78
+ @keyframes bob{0%,100%{transform:translateY(0);}50%{transform:translateY(-6px);}}
79
+
80
+ .error-code{
81
+ font-size:76px;
82
+ font-weight:800;
83
+ letter-spacing:-.03em;
84
+ line-height:1;
85
+ margin:6px 0 6px;
86
+ background:linear-gradient(135deg,var(--primary), #a78bfa, var(--primary));
87
+ background-size:200% 200%;
88
+ -webkit-background-clip:text;
89
+ background-clip:text;
90
+ color:transparent;
91
+ animation:gradientShift 6s ease infinite;
92
+ }
93
+ @keyframes gradientShift{0%,100%{background-position:0% 50%;}50%{background-position:100% 50%;}}
94
+
95
+ h1{margin:0 0 8px;font-size:19px;font-weight:700;}
96
+ p.desc{margin:0 0 28px;color:var(--muted);font-size:13.5px;line-height:1.6;}
97
+
98
+ .actions{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;}
99
+ .btn-primary,.btn-secondary{
100
+ padding:11px 20px;
101
+ border-radius:10px;
102
+ font-size:14px;
103
+ font-weight:700;
104
+ font-family:inherit;
105
+ cursor:pointer;
106
+ text-decoration:none;
107
+ display:inline-block;
108
+ transition:background .18s ease, transform .12s ease, box-shadow .18s ease, border-color .18s ease;
109
+ }
110
+ .btn-primary{
111
+ background:var(--primary);
112
+ color:#fff;
113
+ border:none;
114
+ box-shadow:0 8px 20px -8px rgba(109,91,208,.6);
115
+ }
116
+ .btn-primary:hover{background:var(--primary-dark);}
117
+ .btn-primary:active{transform:scale(.98);}
118
+ .btn-secondary{
119
+ background:#fff;
120
+ color:var(--text);
121
+ border:1.5px solid var(--border);
122
+ }
123
+ .btn-secondary:hover{border-color:var(--primary);background:var(--primary-light);}
124
+
125
+ .footer-note{margin-top:20px;font-size:12px;color:var(--muted);text-align:center;}
126
+ </style>
127
+ </head>
128
+ <body>
129
+
130
+ <div class="bg-blobs">
131
+ <div class="blob blob-1"></div>
132
+ <div class="blob blob-2"></div>
133
+ <div class="blob blob-3"></div>
134
+ </div>
135
+
136
+ <div class="wrapper">
137
+ <div class="card">
138
+ <div class="prism-mark">
139
+ <svg viewBox="0 0 100 100">
140
+ <text class="qmark" x="66" y="18" font-size="18" font-weight="800" fill="#a78bfa">?</text>
141
+ <polygon class="prism-tri" points="24,72 50,20 76,72" fill="rgba(109,91,208,.10)" stroke="#6d5bd0" stroke-width="3"/>
142
+ <line class="beam-dash" x1="2" y1="55" x2="34" y2="55" stroke="#6d5bd0" stroke-width="4" stroke-linecap="round" stroke-dasharray="6 6"/>
143
+ <line class="beam" x1="62" y1="46" x2="92" y2="18" stroke="#e53935" stroke-width="3" stroke-linecap="round"/>
144
+ <line class="beam" x1="62" y1="53" x2="94" y2="53" stroke="#fdd835" stroke-width="3" stroke-linecap="round" style="animation-delay:.15s"/>
145
+ <line class="beam" x1="62" y1="60" x2="90" y2="90" stroke="#1e88e5" stroke-width="3" stroke-linecap="round" style="animation-delay:.3s"/>
146
+ </svg>
147
+ </div>
148
+
149
+ <div class="error-code">404</div>
150
+ <h1>Page not found</h1>
151
+ <p class="desc">The page you're looking for got refracted somewhere along the way. It may have been moved, renamed, or never existed.</p>
152
+
153
+ <div class="actions">
154
+ <!-- Static file, no Rails route helpers available — point this at your own admin root. -->
155
+ <a href="/" class="btn-primary">Back to Dashboard</a>
156
+ <!-- Placeholder — point this at your own support inbox/issue tracker, or remove it. -->
157
+ <a href="mailto:support@example.com" class="btn-secondary">Report Issue</a>
158
+ </div>
159
+ </div>
160
+ <p class="footer-note">Prism theme for ActiveAdmin</p>
161
+ </div>
162
+
163
+ </body>
164
+ </html>
@@ -0,0 +1,173 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Prism — Server Error</title>
7
+ <style>
8
+ /* See 404.html's template for why this is plain, self-contained,
9
+ hand-copied CSS rather than anything referencing prism.css/Ruby. */
10
+ :root{
11
+ --primary:#6d5bd0;
12
+ --primary-dark:#5949b8;
13
+ --primary-light:#f2ecfd;
14
+ --bg:#f4f5f7;
15
+ --card:#ffffff;
16
+ --border:#e6e8ec;
17
+ --text:#1a1f36;
18
+ --muted:#6b7280;
19
+ --danger:#ef4444;
20
+ --warn:#f59e0b;
21
+ --radius:10px;
22
+ }
23
+ *{box-sizing:border-box;}
24
+ html,body{height:100%;}
25
+ body{
26
+ margin:0;
27
+ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
28
+ background:var(--bg);
29
+ color:var(--text);
30
+ display:flex;
31
+ align-items:center;
32
+ justify-content:center;
33
+ min-height:100vh;
34
+ overflow:hidden;
35
+ position:relative;
36
+ }
37
+
38
+ .bg-blobs{position:fixed;inset:0;z-index:0;overflow:hidden;}
39
+ .blob{position:absolute;border-radius:50%;filter:blur(70px);opacity:.35;will-change:transform;}
40
+ .blob-1{width:480px;height:480px;background:radial-gradient(circle at 30% 30%, var(--warn), transparent 70%);top:-160px;left:-140px;opacity:.2;animation:float1 16s ease-in-out infinite;}
41
+ .blob-2{width:420px;height:420px;background:radial-gradient(circle at 70% 70%, #a78bfa, transparent 70%);bottom:-140px;right:-120px;animation:float2 18s ease-in-out infinite;}
42
+ .blob-3{width:300px;height:300px;background:radial-gradient(circle, var(--danger), transparent 70%);top:40%;right:10%;opacity:.16;animation:float3 14s ease-in-out infinite;}
43
+ @keyframes float1{0%,100%{transform:translate(0,0) scale(1);}50%{transform:translate(60px,40px) scale(1.1);}}
44
+ @keyframes float2{0%,100%{transform:translate(0,0) scale(1);}50%{transform:translate(-50px,-30px) scale(1.08);}}
45
+ @keyframes float3{0%,100%{transform:translate(0,0);}50%{transform:translate(-40px,30px);}}
46
+
47
+ .wrapper{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;animation:cardIn .7s cubic-bezier(.2,.8,.2,1) both;}
48
+ @keyframes cardIn{from{opacity:0;transform:translateY(18px) scale(.98);}to{opacity:1;transform:translateY(0) scale(1);}}
49
+
50
+ .card{
51
+ width:460px;
52
+ max-width:90vw;
53
+ background:var(--card);
54
+ border:1px solid var(--border);
55
+ border-radius:var(--radius);
56
+ box-shadow:0 20px 50px -20px rgba(76,63,180,.25), 0 2px 8px rgba(20,20,40,.04);
57
+ padding:44px 40px 36px;
58
+ text-align:center;
59
+ }
60
+
61
+ .prism-mark{width:88px;height:88px;margin:0 auto 8px;position:relative;animation:shake 4.5s ease-in-out infinite;}
62
+ .prism-mark svg{width:100%;height:100%;overflow:visible;}
63
+ @keyframes shake{
64
+ 0%,92%,100%{transform:translate(0,0) rotate(0deg);}
65
+ 93%{transform:translate(-2px,0) rotate(-1deg);}
66
+ 94%{transform:translate(2px,0) rotate(1deg);}
67
+ 95%{transform:translate(-2px,0) rotate(-1deg);}
68
+ 96%{transform:translate(2px,0) rotate(1deg);}
69
+ 97%{transform:translate(0,0) rotate(0deg);}
70
+ }
71
+ .prism-tri{
72
+ animation:flicker 2.6s ease-in-out infinite;
73
+ transform-origin:center;
74
+ }
75
+ @keyframes flicker{
76
+ 0%,100%{filter:drop-shadow(0 0 0px rgba(239,68,68,0));}
77
+ 45%{filter:drop-shadow(0 0 0px rgba(239,68,68,0));}
78
+ 50%{filter:drop-shadow(0 0 8px rgba(239,68,68,.6));}
79
+ 55%{filter:drop-shadow(0 0 0px rgba(239,68,68,0));}
80
+ }
81
+ .beam{stroke-dasharray:40;animation:beamFlow 2.4s linear infinite;}
82
+ @keyframes beamFlow{to{stroke-dashoffset:-80;}}
83
+ .warn-badge{animation:pop 2.6s ease-in-out infinite;transform-origin:center;}
84
+ @keyframes pop{0%,40%,100%{transform:scale(1);}50%{transform:scale(1.18);}}
85
+
86
+ .error-code{
87
+ font-size:76px;
88
+ font-weight:800;
89
+ letter-spacing:-.03em;
90
+ line-height:1;
91
+ margin:6px 0 6px;
92
+ background:linear-gradient(135deg,var(--warn), var(--danger), var(--primary));
93
+ background-size:200% 200%;
94
+ -webkit-background-clip:text;
95
+ background-clip:text;
96
+ color:transparent;
97
+ animation:gradientShift 5s ease infinite;
98
+ }
99
+ @keyframes gradientShift{0%,100%{background-position:0% 50%;}50%{background-position:100% 50%;}}
100
+
101
+ h1{margin:0 0 8px;font-size:19px;font-weight:700;}
102
+ p.desc{margin:0 0 28px;color:var(--muted);font-size:13.5px;line-height:1.6;}
103
+
104
+ .actions{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;}
105
+ .btn-primary,.btn-secondary{
106
+ padding:11px 20px;
107
+ border-radius:10px;
108
+ font-size:14px;
109
+ font-weight:700;
110
+ font-family:inherit;
111
+ cursor:pointer;
112
+ text-decoration:none;
113
+ display:inline-block;
114
+ transition:background .18s ease, transform .12s ease, box-shadow .18s ease, border-color .18s ease;
115
+ }
116
+ .btn-primary{
117
+ background:var(--primary);
118
+ color:#fff;
119
+ border:none;
120
+ box-shadow:0 8px 20px -8px rgba(109,91,208,.6);
121
+ }
122
+ .btn-primary:hover{background:var(--primary-dark);}
123
+ .btn-primary:active{transform:scale(.98);}
124
+ .btn-secondary{
125
+ background:#fff;
126
+ color:var(--text);
127
+ border:1.5px solid var(--border);
128
+ }
129
+ .btn-secondary:hover{border-color:var(--primary);background:var(--primary-light);}
130
+
131
+ .footer-note{margin-top:20px;font-size:12px;color:var(--muted);text-align:center;}
132
+ </style>
133
+ </head>
134
+ <body>
135
+
136
+ <div class="bg-blobs">
137
+ <div class="blob blob-1"></div>
138
+ <div class="blob blob-2"></div>
139
+ <div class="blob blob-3"></div>
140
+ </div>
141
+
142
+ <div class="wrapper">
143
+ <div class="card">
144
+ <div class="prism-mark">
145
+ <svg viewBox="0 0 100 100">
146
+ <polygon class="prism-tri" points="24,72 50,20 76,72" fill="rgba(239,68,68,.10)" stroke="#ef4444" stroke-width="3"/>
147
+ <line class="beam" x1="2" y1="55" x2="38" y2="55" stroke="#6d5bd0" stroke-width="4" stroke-linecap="round"/>
148
+ <line class="beam" x1="62" y1="46" x2="99" y2="30" stroke="#f59e0b" stroke-width="3" stroke-linecap="round"/>
149
+ <line class="beam" x1="62" y1="53" x2="99" y2="46" stroke="#ef4444" stroke-width="3" stroke-linecap="round" style="animation-delay:.15s"/>
150
+ <line class="beam" x1="62" y1="60" x2="99" y2="62" stroke="#f59e0b" stroke-width="3" stroke-linecap="round" style="animation-delay:.3s"/>
151
+ <g class="warn-badge">
152
+ <circle cx="78" cy="18" r="12" fill="#f59e0b"/>
153
+ <rect x="76.5" y="11" width="3" height="8" rx="1.5" fill="#fff"/>
154
+ <circle cx="78" cy="23" r="1.6" fill="#fff"/>
155
+ </g>
156
+ </svg>
157
+ </div>
158
+
159
+ <div class="error-code">500</div>
160
+ <h1>Something broke on our end</h1>
161
+ <p class="desc">The signal scattered before it reached the server. Our team has been notified — try again in a moment.</p>
162
+
163
+ <div class="actions">
164
+ <a href="#" onclick="location.reload(); return false;" class="btn-primary">Retry</a>
165
+ <!-- Static file, no Rails route helpers available — point this at your own admin root. -->
166
+ <a href="/" class="btn-secondary">Back to Dashboard</a>
167
+ </div>
168
+ </div>
169
+ <p class="footer-note">Prism theme for ActiveAdmin</p>
170
+ </div>
171
+
172
+ </body>
173
+ </html>
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAdminPrism
4
+ module Generators
5
+ # rails g active_admin_prism:install
6
+ #
7
+ # The only manual install step this theme needs: enabling the sidebar.
8
+ # Stylesheet/javascript registration is automatic (see
9
+ # ActiveAdminPrism::Engine) — nothing to add to an asset manifest.
10
+ class InstallGenerator < ::Rails::Generators::Base
11
+ desc "Enables the Prism sidebar and Sass palette overrides"
12
+
13
+ ENABLE_LINE = "ActiveAdminPrism.enable!\n"
14
+ INITIALIZER_PATH = "config/initializers/active_admin.rb"
15
+
16
+ STYLESHEET_PATH = "app/assets/stylesheets/active_admin.scss"
17
+ VARIABLE_IMPORT_LINE = '@import "active_admin_prism/variable_overrides";'
18
+ MIXINS_IMPORT_PATTERN = /@import\s+["']active_admin\/mixins["'];?/.freeze
19
+
20
+ def enable_active_admin_prism
21
+ unless File.exist?(File.join(destination_root, INITIALIZER_PATH))
22
+ say_status :error,
23
+ "#{INITIALIZER_PATH} not found — run `rails g active_admin:install` first",
24
+ :red
25
+ return
26
+ end
27
+
28
+ if File.read(File.join(destination_root, INITIALIZER_PATH)).include?("ActiveAdminPrism.enable!")
29
+ say_status :identical, INITIALIZER_PATH, :blue
30
+ else
31
+ append_to_file INITIALIZER_PATH, "\n#{ENABLE_LINE}"
32
+ end
33
+ end
34
+
35
+ # Injects the palette override import just before
36
+ # `@import "active_admin/mixins"` — order matters, since every
37
+ # variable in variable_overrides.scss is `!default` and only wins if
38
+ # it runs first. See that file for what it overrides and why.
39
+ def override_active_admin_sass_variables
40
+ full_path = File.join(destination_root, STYLESHEET_PATH)
41
+
42
+ unless File.exist?(full_path)
43
+ say_status :skip,
44
+ "#{STYLESHEET_PATH} not found — if you add one later, put " \
45
+ "`#{VARIABLE_IMPORT_LINE}` before your \"active_admin/mixins\" import",
46
+ :yellow
47
+ return
48
+ end
49
+
50
+ contents = File.read(full_path)
51
+
52
+ if contents.include?("active_admin_prism/variable_overrides")
53
+ say_status :identical, STYLESHEET_PATH, :blue
54
+ return
55
+ end
56
+
57
+ unless contents.match?(MIXINS_IMPORT_PATTERN)
58
+ say_status :skip,
59
+ "couldn't find an `@import \"active_admin/mixins\";` line in #{STYLESHEET_PATH} — " \
60
+ "add `#{VARIABLE_IMPORT_LINE}` above it yourself",
61
+ :yellow
62
+ return
63
+ end
64
+
65
+ inject_into_file STYLESHEET_PATH, "#{VARIABLE_IMPORT_LINE}\n", before: MIXINS_IMPORT_PATTERN
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAdminPrism
4
+ # A small, self-contained set of outline icons in the common
5
+ # "24x24, currentColor stroke" line-icon style. Kept intentionally tiny —
6
+ # just what the sidebar chrome needs — rather than bundling an icon font.
7
+ module Icons
8
+ ICONS = {
9
+ dashboard: '<rect x="3" y="3" width="8" height="8" rx="1.5"/><rect x="13" y="3" width="8" height="5" rx="1.5"/>' \
10
+ '<rect x="13" y="10" width="8" height="11" rx="1.5"/><rect x="3" y="13" width="8" height="8" rx="1.5"/>',
11
+ users: '<circle cx="9" cy="7" r="3.25"/><path d="M3 20c0-3.3 2.7-6 6-6s6 2.7 6 6"/>' \
12
+ '<path d="M16 4.3a3.25 3.25 0 0 1 0 6.4"/><path d="M15 14c2.8.4 5 2.8 5 6"/>',
13
+ cart: '<circle cx="9" cy="20" r="1.5"/><circle cx="18" cy="20" r="1.5"/>' \
14
+ '<path d="M2.5 3h2.4l1.2 12.1a2 2 0 0 0 2 1.9h9.4a2 2 0 0 0 2-1.7L21 7.5H6.4"/>',
15
+ box: '<path d="M12 3 3 7.5 12 12l9-4.5Z"/><path d="M3 7.5v9L12 21l9-4.5v-9"/><path d="M12 12v9"/>',
16
+ receipt: '<path d="M6 2h12v20l-3-2-3 2-3-2-3 2Z"/><path d="M9 8h6M9 12h6M9 16h3"/>',
17
+ credit_card: '<rect x="2.5" y="5" width="19" height="14" rx="2"/><path d="M2.5 10h19"/><path d="M6 15h4"/>',
18
+ message: '<path d="M4 4h16v12H8l-4 4Z"/>',
19
+ settings: '<circle cx="12" cy="12" r="3.2"/>' \
20
+ '<path d="M12 3.5v2.4M12 18.1v2.4M20.5 12h-2.4M5.9 12H3.5' \
21
+ 'M17.7 6.3l-1.7 1.7M8 16l-1.7 1.7M17.7 17.7 16 16M8 8 6.3 6.3"/>',
22
+ chevron_down: '<polyline points="6 9 12 15 18 9"/>',
23
+ check: '<polyline points="4 12 9 17 20 6"/>',
24
+ x: '<path d="M5 5 19 19M19 5 5 19"/>',
25
+ home: '<path d="M4 11 12 4l8 7"/><path d="M6 10v10h12V10"/><path d="M10 20v-6h4v6"/>',
26
+ list: '<path d="M9 6h11M9 12h11M9 18h11"/><circle cx="4.5" cy="6" r="1.1"/>' \
27
+ '<circle cx="4.5" cy="12" r="1.1"/><circle cx="4.5" cy="18" r="1.1"/>',
28
+ folder: '<path d="M3 6.5a1 1 0 0 1 1-1h5l2 2.5h9a1 1 0 0 1 1 1V18a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1Z"/>',
29
+ bell: '<path d="M6 10a6 6 0 1 1 12 0c0 4 1.2 5.5 1.2 5.5H4.8S6 14 6 10Z"/><path d="M10 19a2 2 0 0 0 4 0"/>',
30
+ search: '<circle cx="10.5" cy="10.5" r="6.5"/><path d="M20 20l-4.7-4.7"/>',
31
+ menu: '<path d="M4 6h16M4 12h16M4 18h16"/>',
32
+ logout: '<path d="M9 4H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h4"/><path d="M14 8l4 4-4 4"/><path d="M18 12H9"/>',
33
+ eye: '<path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>',
34
+ pencil: '<path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/><path d="M14.5 5.5l3 3"/>',
35
+ trash: '<path d="M4 7h16"/><path d="M9 7V4h6v3"/><path d="M6 7l1 13h10l1-13"/>' \
36
+ '<path d="M10 11v6M14 11v6"/>',
37
+ filter: '<path d="M4 5h16l-6.5 7.5v6l-3 1.5v-7.5Z"/>',
38
+ globe: '<circle cx="12" cy="12" r="9"/><path d="M3 12h18"/>' \
39
+ '<path d="M12 3c2.5 2.4 4 5.6 4 9s-1.5 6.6-4 9c-2.5-2.4-4-5.6-4-9s1.5-6.6 4-9Z"/>'
40
+ }.freeze
41
+
42
+ # Returns an inline <svg>...</svg> string for `name`, or nil if unknown.
43
+ def self.svg(name, css_class: "prism-icon", size: 18)
44
+ inner = ICONS[name.to_sym]
45
+ return nil unless inner
46
+
47
+ %(<svg class="#{css_class}" width="#{size}" height="#{size}" viewBox="0 0 24 24" ) +
48
+ %(fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" ) +
49
+ %(stroke-linejoin="round" aria-hidden="true">#{inner}</svg>)
50
+ end
51
+
52
+ # The default sign-in page mark (see ActiveAdminPrism::Configuration
53
+ # #login_logo) — a generic glass-prism-with-spectrum motif distinct from
54
+ # the 24x24 line-icon set above (bigger, multi-color, animated via
55
+ # scss-src/_login.scss's .prism-login-tri/.prism-login-beam classes).
56
+ # Not tied to any brand — a host overriding #login_logo with their own
57
+ # image_tag/svg is the norm for a real app.
58
+ def self.login_mark(size: 64)
59
+ <<~SVG
60
+ <svg class="prism-login-mark" width="#{size}" height="#{size}" viewBox="0 0 100 100" aria-hidden="true">
61
+ <polygon class="prism-login-tri" points="24,72 50,20 76,72" fill="currentColor" fill-opacity="0.08" stroke="currentColor" stroke-width="3"/>
62
+ <line class="prism-login-beam" x1="2" y1="55" x2="38" y2="55" stroke="currentColor" stroke-width="4" stroke-linecap="round"/>
63
+ <line class="prism-login-beam" x1="62" y1="46" x2="99" y2="30" stroke="#e53935" stroke-width="3" stroke-linecap="round"/>
64
+ <line class="prism-login-beam" x1="62" y1="53" x2="99" y2="46" stroke="#fdd835" stroke-width="3" stroke-linecap="round"/>
65
+ <line class="prism-login-beam" x1="62" y1="60" x2="99" y2="62" stroke="#1e88e5" stroke-width="3" stroke-linecap="round"/>
66
+ <line class="prism-login-beam" x1="61" y1="67" x2="99" y2="78" stroke="#8e24aa" stroke-width="3" stroke-linecap="round"/>
67
+ </svg>
68
+ SVG
69
+ end
70
+ end
71
+
72
+ # Exposed as a view helper so hosts can call `prism_icon(:cart)` directly
73
+ # from Arbre blocks (app/admin/*.rb) or ERB views.
74
+ module IconHelper
75
+ def prism_icon(name, css_class: "prism-icon", size: 18)
76
+ svg = ActiveAdminPrism::Icons.svg(name, css_class: css_class, size: size)
77
+ svg&.html_safe # rubocop:disable Rails/OutputSafety
78
+ end
79
+ end
80
+ end
81
+
82
+ ActiveAdmin::ViewHelpers.include(ActiveAdminPrism::IconHelper) if defined?(ActiveAdmin::ViewHelpers)
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAdminPrism
4
+ # Resolves ActiveAdminPrism::Configuration#login_logo/#login_app_name/
5
+ # #login_tagline for app/views/active_admin/devise/sessions/new.html.erb.
6
+ #
7
+ # Mixed into ActiveAdmin::ViewHelpers (see the bottom of this file), which
8
+ # ActiveAdmin::Devise::Controller already adds as a controller `helper` —
9
+ # so these methods, and `render_in_context`/`active_admin_application`
10
+ # (both part of ActiveAdmin::ViewHelpers already), are directly callable
11
+ # from the plain-ERB devise views with no extra wiring.
12
+ module LoginHelper
13
+ def prism_login_logo_html
14
+ custom = render_in_context(self, ActiveAdminPrism.configuration.login_logo)
15
+ return custom.to_s.html_safe if custom.present? # rubocop:disable Rails/OutputSafety
16
+
17
+ ActiveAdminPrism::Icons.login_mark.html_safe # rubocop:disable Rails/OutputSafety
18
+ end
19
+
20
+ def prism_login_app_name
21
+ custom = render_in_context(self, ActiveAdminPrism.configuration.login_app_name)
22
+ custom.presence || active_admin_application.site_title(self)
23
+ end
24
+
25
+ def prism_login_tagline
26
+ render_in_context(self, ActiveAdminPrism.configuration.login_tagline)
27
+ end
28
+ end
29
+ end
30
+
31
+ ActiveAdmin::ViewHelpers.include(ActiveAdminPrism::LoginHelper) if defined?(ActiveAdmin::ViewHelpers)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAdminPrism
4
+ # A read-only toggle-switch visual for boolean values on index/show pages
5
+ # — the same look as the opt-in `f.input :x, as: :prism_toggle` form
6
+ # input, but for display rather than editing. ActiveAdmin's own default
7
+ # (status_tag "Yes"/"No") is untouched; this is an explicit opt-in helper
8
+ # a host calls from its own `column`/`row` block, e.g.:
9
+ #
10
+ # index do
11
+ # column :active do |product|
12
+ # prism_toggle_tag product.active
13
+ # end
14
+ # end
15
+ #
16
+ # show do
17
+ # attributes_table do
18
+ # row :active do |product|
19
+ # prism_toggle_tag product.active
20
+ # end
21
+ # end
22
+ # end
23
+ module ToggleTagHelper
24
+ def prism_toggle_tag(value, on_label: "Yes", off_label: "No")
25
+ label = value ? on_label : off_label
26
+
27
+ content_tag(:span, class: "prism-toggle-tag #{value ? 'on' : 'off'}",
28
+ role: "img", "aria-label": label, title: label) do
29
+ content_tag(:span, "", class: "prism-toggle-track", "aria-hidden": "true")
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ ActiveAdmin::ViewHelpers.include(ActiveAdminPrism::ToggleTagHelper) if defined?(ActiveAdmin::ViewHelpers)