plutonium 0.16.3 → 0.16.5
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.
- checksums.yaml +4 -4
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +19 -12
- data/app/assets/plutonium.js.map +2 -2
- data/app/assets/plutonium.min.js +14 -14
- data/app/assets/plutonium.min.js.map +3 -3
- data/app/views/plutonium/_resource_sidebar.html.erb +10 -8
- data/docs/guide/tutorial.md +0 -2
- data/docs/public/templates/pluton8.rb +7 -0
- data/lib/generators/pu/core/assets/templates/tailwind.config.js +7 -6
- data/lib/plutonium/ui/sidebar_menu.rb +149 -0
- data/lib/plutonium/version.rb +1 -1
- data/package-lock.json +2 -2
- data/package.json +1 -1
- data/src/js/controllers/resource_collapse_controller.js +21 -13
- data/tailwind.options.js +46 -0
- metadata +18 -2
@@ -1,16 +1,18 @@
|
|
1
1
|
<%= render Plutonium::UI::Layout::Sidebar.new do %>
|
2
2
|
<%=
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
next unless allowed_to? :index?, resource
|
3
|
+
render Plutonium::UI::SidebarMenu.new(
|
4
|
+
Phlexi::Menu::Builder.new do |m|
|
5
|
+
m.item "Dashboard",
|
6
|
+
url: root_path,
|
7
|
+
icon: Phlex::TablerIcons::Home
|
9
8
|
|
10
|
-
|
9
|
+
m.item "Resources", icon: Phlex::TablerIcons::GridDots do |n|
|
10
|
+
registered_resources.each do |resource|
|
11
|
+
n.item resource.model_name.human.pluralize,
|
12
|
+
url: resource_url_for(resource, parent: nil)
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
14
|
-
|
16
|
+
)
|
15
17
|
%>
|
16
18
|
<% end %>
|
data/docs/guide/tutorial.md
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
after_bundle do
|
2
|
+
# Run the plutonium install
|
3
|
+
rails_command "app:template LOCATION=https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb"
|
4
|
+
|
5
|
+
# Enliten!
|
6
|
+
rails_command "app:template LOCATION=https://raw.githubusercontent.com/thedumbtechguy/enlitenment/main/template.rb"
|
7
|
+
end
|
@@ -17,14 +17,15 @@ module.exports = {
|
|
17
17
|
throw Error(`unsupported plugin: ${plugin}: ${(typeof plugin)}`)
|
18
18
|
}
|
19
19
|
})),
|
20
|
-
theme: plutoniumTailwindConfig.
|
20
|
+
theme: plutoniumTailwindConfig.merge(
|
21
|
+
{
|
22
|
+
},
|
23
|
+
plutoniumTailwindConfig.theme
|
24
|
+
),
|
21
25
|
content: [
|
22
|
-
`${__dirname}/app/**/*.rb`,
|
23
|
-
`${__dirname}/app/views/**/*.html.erb`,
|
24
|
-
`${__dirname}/app/helpers/**/*.rb`,
|
26
|
+
`${__dirname}/app/**/*.{erb,haml,html,slim,rb}`,
|
25
27
|
`${__dirname}/app/assets/stylesheets/**/*.css`,
|
26
28
|
`${__dirname}/app/javascript/**/*.js`,
|
27
|
-
`${__dirname}/packages/**/app/**/*.rb`,
|
28
|
-
`${__dirname}/packages/**/app/views/**/*.html.erb`,
|
29
|
+
`${__dirname}/packages/**/app/**/*.{erb,haml,html,slim,rb}`,
|
29
30
|
].concat(plutoniumTailwindConfig.content),
|
30
31
|
}
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require "phlexi-menu"
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
# A sidebar navigation component that renders a max depth of 2 levels
|
6
|
+
# Provides collapsible menu sections and is compatible with turbo-permanent
|
7
|
+
class SidebarMenu < Phlexi::Menu::Component
|
8
|
+
include Plutonium::UI::Component::Behaviour
|
9
|
+
|
10
|
+
DEFAULT_MAX_DEPTH = 2
|
11
|
+
|
12
|
+
class Theme < Theme
|
13
|
+
def self.theme
|
14
|
+
super.merge({
|
15
|
+
# Base container styles
|
16
|
+
nav: "space-y-2 pb-6 mb-6",
|
17
|
+
items_container: "space-y-2",
|
18
|
+
|
19
|
+
# Item wrapper styles
|
20
|
+
item_wrapper: "w-full transition-colors duration-200 ease-in-out",
|
21
|
+
item_parent: nil,
|
22
|
+
|
23
|
+
# Link and button base styles
|
24
|
+
item_link: "flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group",
|
25
|
+
item_span: "flex items-center p-2 w-full text-base font-normal text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700",
|
26
|
+
|
27
|
+
# Label and content styles
|
28
|
+
item_label: ->(depth) { "flex-1 #{(depth > 0) ? "ml-9" : "ml-3"} text-left whitespace-nowrap" },
|
29
|
+
|
30
|
+
# Badge styles
|
31
|
+
leading_badge: "inline-flex justify-center items-center w-5 h-5 text-xs font-semibold rounded-full text-primary-800 bg-primary-100 dark:bg-primary-200 dark:text-primary-800",
|
32
|
+
trailing_badge: "inline-flex justify-center items-center px-2 ml-3 text-sm font-medium text-gray-800 bg-gray-200 rounded-full dark:bg-gray-700 dark:text-gray-300",
|
33
|
+
|
34
|
+
# Icon styles
|
35
|
+
icon_wrapper: "shrink-0 w-6 h-6 flex items-center justify-center",
|
36
|
+
icon: "text-gray-400 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white",
|
37
|
+
|
38
|
+
# Collapse icon styles
|
39
|
+
collapse_icon: "w-6 h-6 ml-auto transition-transform duration-200",
|
40
|
+
collapse_icon_expanded: "transform rotate-180",
|
41
|
+
|
42
|
+
# Submenu styles
|
43
|
+
sub_items_container: "hidden py-2 space-y-2",
|
44
|
+
|
45
|
+
# Due to how we use turbo frames, we can't set active states
|
46
|
+
active: nil
|
47
|
+
})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def render_items(items, depth = 0)
|
54
|
+
return if depth >= @max_depth || items.empty?
|
55
|
+
|
56
|
+
if depth.zero?
|
57
|
+
ul(class: themed(:items_container, depth)) do
|
58
|
+
items.each { |item| render_item_wrapper(item, depth) }
|
59
|
+
end
|
60
|
+
else
|
61
|
+
ul(class: themed(:sub_items_container, depth), data: {"resource-collapse-target": "menu"}) do
|
62
|
+
items.each { |item| render_item_wrapper(item, depth) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# def render_items(items, depth = 0)
|
68
|
+
# return if depth >= @max_depth
|
69
|
+
|
70
|
+
# if depth.zero?
|
71
|
+
# ul(class: themed(:items_container, depth)) do
|
72
|
+
# items.each do |item|
|
73
|
+
# render_item_wrapper(item, depth)
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
# else
|
77
|
+
# # Use collapsible rendering for nested levels
|
78
|
+
# ul(
|
79
|
+
# id: generate_menu_id(:root),
|
80
|
+
# class: themed(:sub_items_container, depth),
|
81
|
+
# data: {"resource-collapse-target": "menu"}
|
82
|
+
# ) do
|
83
|
+
# items.each do |item|
|
84
|
+
# render_item_wrapper(item, depth)
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
|
90
|
+
def render_item_wrapper(item, depth)
|
91
|
+
wrapper_attrs = {
|
92
|
+
class: tokens(themed(:item_wrapper, depth)),
|
93
|
+
data: {}
|
94
|
+
}
|
95
|
+
|
96
|
+
if nested?(item, depth)
|
97
|
+
wrapper_attrs[:data][:controller] = "resource-collapse"
|
98
|
+
wrapper_attrs[:data]["resource-collapse-active-class"] = themed(:collapse_icon_expanded)
|
99
|
+
end
|
100
|
+
|
101
|
+
li(**wrapper_attrs) do
|
102
|
+
render_item_content(item, depth)
|
103
|
+
render_items(item.items, depth + 1) if nested?(item, depth)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def render_item_content(item, depth)
|
108
|
+
if nested?(item, depth)
|
109
|
+
render_collapsible_button(item, depth)
|
110
|
+
else
|
111
|
+
render_item_link(item, depth)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def render_collapsible_button(item, depth)
|
116
|
+
button(
|
117
|
+
type: "button",
|
118
|
+
class: themed(:item_span, depth),
|
119
|
+
data: {
|
120
|
+
"resource-collapse-target": "trigger",
|
121
|
+
action: "resource-collapse#toggle"
|
122
|
+
},
|
123
|
+
aria: {
|
124
|
+
expanded: "false"
|
125
|
+
}
|
126
|
+
) do
|
127
|
+
render_item_interior(item, depth)
|
128
|
+
render_collapse_icon(depth)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def render_collapse_icon(depth)
|
133
|
+
svg(
|
134
|
+
class: themed(:collapse_icon, depth),
|
135
|
+
fill: "currentColor",
|
136
|
+
viewBox: "0 0 20 20",
|
137
|
+
xmlns: "http://www.w3.org/2000/svg",
|
138
|
+
aria: {inert: true}
|
139
|
+
) do |s|
|
140
|
+
s.path(
|
141
|
+
fill_rule: "evenodd",
|
142
|
+
d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z",
|
143
|
+
clip_rule: "evenodd"
|
144
|
+
)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/plutonium/version.rb
CHANGED
data/package-lock.json
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "@radioactive-labs/plutonium",
|
3
|
-
"version": "0.1
|
3
|
+
"version": "0.2.1",
|
4
4
|
"lockfileVersion": 3,
|
5
5
|
"requires": true,
|
6
6
|
"packages": {
|
7
7
|
"": {
|
8
8
|
"name": "@radioactive-labs/plutonium",
|
9
|
-
"version": "0.1
|
9
|
+
"version": "0.2.1",
|
10
10
|
"license": "MIT",
|
11
11
|
"dependencies": {
|
12
12
|
"@hotwired/stimulus": "^3.2.2",
|
data/package.json
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
import { Controller } from "@hotwired/stimulus"
|
2
|
-
import { Collapse } from 'flowbite';
|
3
|
-
|
4
2
|
|
5
3
|
// Connects to data-controller="resource-collapse"
|
6
4
|
export default class extends Controller {
|
@@ -9,22 +7,32 @@ export default class extends Controller {
|
|
9
7
|
connect() {
|
10
8
|
console.log(`resource-collapse connected: ${this.element}`)
|
11
9
|
|
12
|
-
|
13
|
-
|
10
|
+
// Default to false if the data attribute isn't set
|
11
|
+
if (!this.element.hasAttribute('data-visible')) {
|
12
|
+
this.element.setAttribute('data-visible', 'false')
|
13
|
+
}
|
14
14
|
|
15
|
-
|
16
|
-
this
|
15
|
+
// Set initial state
|
16
|
+
this.#updateState()
|
17
17
|
}
|
18
18
|
|
19
19
|
toggle() {
|
20
|
-
this.
|
21
|
-
|
22
|
-
|
23
|
-
show() {
|
24
|
-
this.collapse.show()
|
20
|
+
const isVisible = this.element.getAttribute('data-visible') === 'true'
|
21
|
+
this.element.setAttribute('data-visible', (!isVisible).toString())
|
22
|
+
this.#updateState()
|
25
23
|
}
|
26
24
|
|
27
|
-
|
28
|
-
this.
|
25
|
+
#updateState() {
|
26
|
+
const isVisible = this.element.getAttribute('data-visible') === 'true'
|
27
|
+
|
28
|
+
if (isVisible) {
|
29
|
+
this.menuTarget.classList.remove('hidden')
|
30
|
+
this.triggerTarget.setAttribute('aria-expanded', 'true')
|
31
|
+
this.dispatch('expand')
|
32
|
+
} else {
|
33
|
+
this.menuTarget.classList.add('hidden')
|
34
|
+
this.triggerTarget.setAttribute('aria-expanded', 'false')
|
35
|
+
this.dispatch('collapse')
|
36
|
+
}
|
29
37
|
}
|
30
38
|
}
|
data/tailwind.options.js
CHANGED
@@ -248,6 +248,52 @@ export const safelist = [
|
|
248
248
|
},
|
249
249
|
]
|
250
250
|
|
251
|
+
export const merge = function (...configs) {
|
252
|
+
function isObject(item) {
|
253
|
+
return item && typeof item === 'object' && !Array.isArray(item);
|
254
|
+
}
|
255
|
+
|
256
|
+
function mergeArrays(target, source) {
|
257
|
+
// Combine arrays and remove duplicates for simple values
|
258
|
+
if (target.every(item => typeof item === 'string')) {
|
259
|
+
return [...new Set([...target, ...source])];
|
260
|
+
}
|
261
|
+
// For arrays of objects or complex types, concatenate
|
262
|
+
return [...target, ...source];
|
263
|
+
}
|
264
|
+
|
265
|
+
function deepMerge(target, source) {
|
266
|
+
if (!isObject(target) || !isObject(source)) {
|
267
|
+
return source;
|
268
|
+
}
|
269
|
+
|
270
|
+
const output = { ...target };
|
271
|
+
|
272
|
+
Object.keys(source).forEach(key => {
|
273
|
+
const targetValue = output[key];
|
274
|
+
const sourceValue = source[key];
|
275
|
+
|
276
|
+
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
|
277
|
+
output[key] = mergeArrays(targetValue, sourceValue);
|
278
|
+
} else if (isObject(targetValue) && isObject(sourceValue)) {
|
279
|
+
// Handle function properties (like theme functions in Tailwind)
|
280
|
+
if (typeof targetValue === 'function' || typeof sourceValue === 'function') {
|
281
|
+
output[key] = sourceValue;
|
282
|
+
} else {
|
283
|
+
output[key] = deepMerge(targetValue, sourceValue);
|
284
|
+
}
|
285
|
+
} else {
|
286
|
+
output[key] = sourceValue;
|
287
|
+
}
|
288
|
+
});
|
289
|
+
|
290
|
+
return output;
|
291
|
+
}
|
292
|
+
|
293
|
+
// Reduce all configs into a single merged config
|
294
|
+
return configs.reduce((merged, config) => deepMerge(merged, config), {});
|
295
|
+
}
|
296
|
+
|
251
297
|
// // Object.keys(colors).forEach((color) => {
|
252
298
|
// // if (typeof colors[color] === 'object') {
|
253
299
|
// // Object.keys(colors[color]).forEach((shade) => {
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plutonium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.16.
|
4
|
+
version: 0.16.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Froelich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -254,6 +254,20 @@ dependencies:
|
|
254
254
|
- - ">="
|
255
255
|
- !ruby/object:Gem::Version
|
256
256
|
version: '0'
|
257
|
+
- !ruby/object:Gem::Dependency
|
258
|
+
name: phlexi-menu
|
259
|
+
requirement: !ruby/object:Gem::Requirement
|
260
|
+
requirements:
|
261
|
+
- - ">="
|
262
|
+
- !ruby/object:Gem::Version
|
263
|
+
version: '0'
|
264
|
+
type: :runtime
|
265
|
+
prerelease: false
|
266
|
+
version_requirements: !ruby/object:Gem::Requirement
|
267
|
+
requirements:
|
268
|
+
- - ">="
|
269
|
+
- !ruby/object:Gem::Version
|
270
|
+
version: '0'
|
257
271
|
- !ruby/object:Gem::Dependency
|
258
272
|
name: tailwind_merge
|
259
273
|
requirement: !ruby/object:Gem::Requirement
|
@@ -1080,6 +1094,7 @@ files:
|
|
1080
1094
|
- docs/public/plutonium.png
|
1081
1095
|
- docs/public/site.webmanifest
|
1082
1096
|
- docs/public/templates/base.rb
|
1097
|
+
- docs/public/templates/pluton8.rb
|
1083
1098
|
- docs/public/templates/plutonium.rb
|
1084
1099
|
- docs/public/tutorial/plutonium-association-panel.png
|
1085
1100
|
- docs/public/tutorial/plutonium-dashboard.png
|
@@ -1433,6 +1448,7 @@ files:
|
|
1433
1448
|
- lib/plutonium/ui/page/show.rb
|
1434
1449
|
- lib/plutonium/ui/page_header.rb
|
1435
1450
|
- lib/plutonium/ui/panel.rb
|
1451
|
+
- lib/plutonium/ui/sidebar_menu.rb
|
1436
1452
|
- lib/plutonium/ui/skeleton_table.rb
|
1437
1453
|
- lib/plutonium/ui/table/base.rb
|
1438
1454
|
- lib/plutonium/ui/table/components/pagy_info.rb
|