plutonium 0.16.3 → 0.16.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,5 @@
1
1
  # Building a Blog with Plutonium
2
2
 
3
- # Building a Blog with Plutonium
4
-
5
3
  > **Quick Links:**
6
4
  > - 🔍 [Tutorial Demo](https://github.com/radioactive-labs/plutonium-app)
7
5
  > - 📂 [Tutorial Source Code](https://github.com/radioactive-labs/plutonium-app)
@@ -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.theme,
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,169 @@
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: "flex-1 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
55
+ # return if items.empty?
56
+
57
+ # if depth.zero?
58
+ # ul(class: themed(:items_container, depth)) do
59
+ # items.each do |item|
60
+ # render_item_wrapper(item, depth)
61
+ # end
62
+ # end
63
+ # else
64
+ # # Use collapsible rendering for nested levels
65
+ # ul(
66
+ # id: generate_menu_id(items.first&.options&.dig(:parent)),
67
+ # class: themed(:sub_items_container, depth),
68
+ # data: { "resource-collapse-target": "menu" }
69
+ # ) do
70
+ # items.each do |item|
71
+ # render_item_wrapper(item, depth)
72
+ # end
73
+ # end
74
+ # end
75
+ # end
76
+
77
+ def render_item_wrapper(item, depth)
78
+ wrapper_attrs = {
79
+ class: tokens(themed(:item_wrapper, depth)),
80
+ data: {}
81
+ }
82
+
83
+ if nested?(item, depth)
84
+ wrapper_attrs[:data][:controller] = "resource-collapse"
85
+ wrapper_attrs[:data]["resource-collapse-active-class"] = themed(:collapse_icon_expanded)
86
+ wrapper_attrs[:id] = generate_item_id(item)
87
+
88
+ # # Store parent reference for nested items
89
+ # item.items.each { |child| child.options[:parent] = item }
90
+ end
91
+
92
+ li(**wrapper_attrs) do
93
+ render_item_content(item, depth)
94
+ render_items(item.items, depth + 1) if nested?(item, depth)
95
+ end
96
+ end
97
+
98
+ def render_item_content(item, depth)
99
+ if nested?(item, depth)
100
+ render_collapsible_button(item, depth)
101
+ else
102
+ render_item_link(item, depth)
103
+ end
104
+ end
105
+
106
+ # def render_item_link(item, depth)
107
+ # if item.url
108
+ # a(href: item.url, class: themed(:item_link, depth)) do
109
+ # render_item_interior(item, depth)
110
+ # end
111
+ # else
112
+ # span(class: themed(:item_span, depth)) do
113
+ # render_item_interior(item, depth)
114
+ # end
115
+ # end
116
+ # end
117
+
118
+ def render_collapsible_button(item, depth)
119
+ button(
120
+ type: "button",
121
+ class: themed(:item_span, depth),
122
+ data: {
123
+ "resource-collapse-target": "trigger",
124
+ "action": "resource-collapse#toggle"
125
+ },
126
+ aria: {
127
+ controls: generate_menu_id(item),
128
+ expanded: "false"
129
+ }
130
+ ) do
131
+ render_item_interior(item, depth)
132
+ render_collapse_icon(depth)
133
+ end
134
+ end
135
+
136
+ def render_collapse_icon(depth)
137
+ svg(
138
+ class: themed(:collapse_icon, depth),
139
+ fill: "currentColor",
140
+ viewBox: "0 0 20 20",
141
+ xmlns: "http://www.w3.org/2000/svg",
142
+ aria: { hidden: true }
143
+ ) do |s|
144
+ s.path(
145
+ fill_rule: "evenodd",
146
+ 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",
147
+ clip_rule: "evenodd"
148
+ )
149
+ end
150
+ end
151
+
152
+ private
153
+
154
+ def generate_item_id(item)
155
+ "sidebar-menu-item-#{item.label.to_s.parameterize}"
156
+ end
157
+
158
+ def generate_menu_id(item)
159
+ # return nil unless item
160
+ "#{generate_item_id(item)}-menu"
161
+ end
162
+
163
+ # # Override to disable active state for turbo-permanent compatibility
164
+ # def active_class(item, depth = 0)
165
+ # nil
166
+ # end
167
+ end
168
+ end
169
+ end
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- VERSION = "0.16.3"
2
+ VERSION = "0.16.4"
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-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@radioactive-labs/plutonium",
3
- "version": "0.1.16",
3
+ "version": "0.2.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@radioactive-labs/plutonium",
9
- "version": "0.1.16",
9
+ "version": "0.2.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@hotwired/stimulus": "^3.2.2",
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radioactive-labs/plutonium",
3
- "version": "0.1.17",
3
+ "version": "0.2.0",
4
4
  "description": "Core assets for the Plutonium gem",
5
5
  "type": "module",
6
6
  "main": "src/js/core.js",
@@ -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
- this.collapse = new Collapse(this.menuTarget, this.triggerTarget);
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
- disconnect() {
16
- this.collapse = null
15
+ // Set initial state
16
+ this.#updateState()
17
17
  }
18
18
 
19
19
  toggle() {
20
- this.collapse.toggle()
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
- hide() {
28
- this.collapse.hide()
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.3
4
+ version: 0.16.4
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-03 00:00:00.000000000 Z
11
+ date: 2024-12-13 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