plutonium 0.36.0 → 0.37.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.
@@ -10,6 +10,11 @@ module Plutonium
10
10
  include Phlexi::Field::Common::Tokens
11
11
  include Plutonium::UI::Form::Options::InferredTypes
12
12
 
13
+ def textarea_tag(**attributes, &)
14
+ attributes[:data_controller] = tokens(attributes[:data_controller], "textarea-autogrow")
15
+ super
16
+ end
17
+
13
18
  def easymde_tag(**, &)
14
19
  create_component(Plutonium::UI::Form::Components::Easymde, :easymde, **, &)
15
20
  end
@@ -8,7 +8,7 @@ module Plutonium
8
8
  include Plutonium::UI::Component::Methods
9
9
 
10
10
  def view_template
11
- div(class: "flex space-x-1") do
11
+ div(class: "flex items-center space-x-1") do
12
12
  super
13
13
  render_add_button
14
14
  end
@@ -23,9 +23,9 @@ module Plutonium
23
23
 
24
24
  a(
25
25
  href: add_url,
26
- class: "bg-[var(--pu-surface-alt)] hover:bg-[var(--pu-border)] border border-[var(--pu-border)] rounded-[var(--pu-radius-md)] p-3 focus:ring-2 focus:ring-[var(--pu-border)] focus:outline-none text-[var(--pu-text)] transition-colors"
26
+ class: "bg-[var(--pu-surface-alt)] hover:bg-[var(--pu-border)] border border-[var(--pu-border)] rounded-[var(--pu-radius-md)] px-4 py-3 focus:ring-2 focus:ring-[var(--pu-border)] focus:outline-none text-[var(--pu-text-muted)] hover:text-[var(--pu-text)] transition-colors"
27
27
  ) do
28
- render Phlex::TablerIcons::Plus.new(class: "w-3 h-3")
28
+ render Phlex::TablerIcons::Plus.new(class: "w-6 h-6")
29
29
  end
30
30
  end
31
31
 
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- VERSION = "0.36.0"
2
+ VERSION = "0.37.0"
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.36.0",
3
+ "version": "0.37.0",
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",
@@ -36,11 +36,25 @@
36
36
 
37
37
  /* Main container - Updated to match form input theme */
38
38
  .ss-main {
39
- @apply flex flex-row relative select-none w-full min-h-8 p-2 cursor-pointer border border-gray-300 rounded-md shadow-sm font-medium text-sm bg-white text-gray-700 outline-none transition-colors duration-200 overflow-hidden focus:ring-2 focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:placeholder-gray-400;
39
+ @apply flex flex-row items-center relative select-none w-full px-4 py-3 cursor-pointer border font-medium text-base leading-normal outline-none transition-colors duration-200 overflow-hidden focus:ring-2;
40
+ background-color: var(--pu-input-bg);
41
+ border-color: var(--pu-input-border);
42
+ border-radius: var(--pu-radius-md);
43
+ color: var(--pu-text);
44
+ }
45
+ .ss-main:hover {
46
+ border-color: var(--pu-border-strong);
47
+ }
48
+ .ss-main:focus,
49
+ .ss-main[aria-expanded="true"] {
50
+ border-color: var(--pu-input-focus-ring);
51
+ box-shadow: 0 0 0 3px theme(colors.primary.500 / 15%);
40
52
  }
41
53
 
42
54
  .ss-main.ss-disabled {
43
- @apply cursor-not-allowed bg-gray-100 text-gray-500 dark:bg-gray-900 dark:text-gray-400;
55
+ @apply cursor-not-allowed;
56
+ background-color: var(--pu-surface-alt);
57
+ color: var(--pu-text-muted);
44
58
  }
45
59
 
46
60
  .ss-main.ss-disabled .ss-values .ss-value .ss-value-delete {
@@ -57,11 +71,12 @@
57
71
 
58
72
  /* Values container */
59
73
  .ss-main .ss-values {
60
- @apply inline-flex flex-wrap gap-1 flex-1;
74
+ @apply inline-flex flex-wrap gap-1 flex-1 items-center min-h-[1.5em];
61
75
  }
62
76
 
63
77
  .ss-main .ss-values .ss-placeholder {
64
- @apply flex px-0 py-0 my-auto leading-none items-center w-full text-gray-400 overflow-hidden text-ellipsis whitespace-nowrap dark:text-gray-400;
78
+ @apply flex px-0 py-0 my-auto items-center w-full overflow-hidden text-ellipsis whitespace-nowrap;
79
+ color: var(--pu-input-placeholder);
65
80
  }
66
81
 
67
82
  .ss-main .ss-values .ss-max {
@@ -111,7 +126,8 @@
111
126
  }
112
127
 
113
128
  .ss-main .ss-deselect svg path {
114
- @apply fill-none stroke-gray-700 dark:stroke-white;
129
+ @apply fill-none;
130
+ stroke: var(--pu-text-muted);
115
131
  stroke-width: 20;
116
132
  stroke-linecap: round;
117
133
  stroke-linejoin: round;
@@ -123,7 +139,8 @@
123
139
  }
124
140
 
125
141
  .ss-main .ss-arrow path {
126
- @apply fill-none stroke-gray-700 transition-transform duration-200 dark:stroke-white;
142
+ @apply fill-none transition-transform duration-200;
143
+ stroke: var(--pu-text-muted);
127
144
  stroke-width: 18;
128
145
  stroke-linecap: round;
129
146
  stroke-linejoin: round;
@@ -131,7 +148,10 @@
131
148
 
132
149
  /* Content container - Updated to match shadow and borders */
133
150
  .ss-content {
134
- @apply absolute flex h-auto flex-col w-auto max-h-72 border border-gray-300 bg-white shadow-sm transition-all duration-200 opacity-0 z-[10000] overflow-hidden dark:bg-gray-700 dark:border-gray-600;
151
+ @apply absolute flex h-auto flex-col w-auto max-h-72 border transition-all duration-200 opacity-0 z-[10000] overflow-hidden;
152
+ background-color: var(--pu-surface);
153
+ border-color: var(--pu-border);
154
+ box-shadow: var(--pu-shadow-md);
135
155
  transform: scaleY(0);
136
156
  transform-origin: top;
137
157
  }
@@ -162,14 +182,28 @@
162
182
  }
163
183
 
164
184
  .ss-content .ss-search input {
165
- @apply inline-flex flex-auto w-full min-w-0 p-2 m-0 border border-gray-300 rounded-md shadow-sm font-medium text-sm bg-white outline-none text-left placeholder:text-gray-400 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:placeholder-gray-400;
185
+ @apply inline-flex flex-auto w-full min-w-0 p-2 m-0 border font-medium text-sm outline-none text-left focus:ring-2;
186
+ background-color: var(--pu-input-bg);
187
+ border-color: var(--pu-input-border);
188
+ border-radius: var(--pu-radius-md);
189
+ color: var(--pu-text);
166
190
  box-sizing: border-box;
167
191
  font-size: inherit;
168
192
  line-height: inherit;
169
193
  }
194
+ .ss-content .ss-search input::placeholder {
195
+ color: var(--pu-input-placeholder);
196
+ }
197
+ .ss-content .ss-search input:focus {
198
+ border-color: var(--pu-input-focus-ring);
199
+ box-shadow: 0 0 0 3px theme(colors.primary.500 / 15%);
200
+ }
170
201
 
171
202
  .ss-content .ss-search .ss-addable {
172
- @apply inline-flex justify-center items-center cursor-pointer flex-none h-auto ml-2 border border-gray-300 rounded-md shadow-sm dark:border-gray-600;
203
+ @apply inline-flex justify-center items-center cursor-pointer flex-none h-auto ml-2 border;
204
+ border-color: var(--pu-border);
205
+ border-radius: var(--pu-radius-md);
206
+ box-shadow: var(--pu-shadow-sm);
173
207
  }
174
208
 
175
209
  .ss-content .ss-search .ss-addable svg {
@@ -177,7 +211,8 @@
177
211
  }
178
212
 
179
213
  .ss-content .ss-search .ss-addable svg path {
180
- @apply fill-none stroke-gray-700 dark:stroke-white;
214
+ @apply fill-none;
215
+ stroke: var(--pu-text);
181
216
  stroke-width: 18;
182
217
  stroke-linecap: round;
183
218
  stroke-linejoin: round;
@@ -193,7 +228,8 @@
193
228
  }
194
229
 
195
230
  .ss-content .ss-list .ss-searching {
196
- @apply text-gray-700 p-2 dark:text-white;
231
+ @apply p-2;
232
+ color: var(--pu-text);
197
233
  }
198
234
 
199
235
  .ss-content .ss-list .ss-optgroup.ss-close .ss-option {
@@ -202,11 +238,13 @@
202
238
 
203
239
  /* Option groups - Updated colors */
204
240
  .ss-content .ss-list .ss-optgroup .ss-optgroup-label {
205
- @apply flex flex-row items-center justify-between p-2 bg-gray-50 dark:bg-gray-600;
241
+ @apply flex flex-row items-center justify-between p-2;
242
+ background-color: var(--pu-surface-alt);
206
243
  }
207
244
 
208
245
  .ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-label-text {
209
- @apply flex-auto font-bold text-gray-700 dark:text-white;
246
+ @apply flex-auto font-bold;
247
+ color: var(--pu-text);
210
248
  }
211
249
 
212
250
  .ss-content .ss-list .ss-optgroup .ss-optgroup-label:has(.ss-arrow) {
@@ -256,7 +294,8 @@
256
294
  }
257
295
 
258
296
  .ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-closable .ss-arrow path {
259
- @apply fill-none stroke-gray-700 transition-transform duration-200 dark:stroke-white;
297
+ @apply fill-none transition-transform duration-200;
298
+ stroke: var(--pu-text);
260
299
  stroke-width: 18;
261
300
  stroke-linecap: round;
262
301
  stroke-linejoin: round;
@@ -268,7 +307,8 @@
268
307
 
269
308
  /* Options - Updated text colors to match theme */
270
309
  .ss-content .ss-list .ss-option {
271
- @apply block p-2 whitespace-normal text-gray-700 cursor-pointer select-none hover:bg-primary-500 hover:text-white dark:text-white dark:hover:bg-primary-500 dark:hover:text-white;
310
+ @apply block p-2 whitespace-normal cursor-pointer select-none hover:bg-primary-500 hover:text-white;
311
+ color: var(--pu-text);
272
312
  min-height: 0;
273
313
  }
274
314
 
@@ -282,11 +322,14 @@
282
322
  }
283
323
 
284
324
  .ss-content .ss-list .ss-option.ss-disabled {
285
- @apply cursor-not-allowed bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400;
325
+ @apply cursor-not-allowed;
326
+ background-color: var(--pu-surface-alt);
327
+ color: var(--pu-text-muted);
286
328
  }
287
329
 
288
330
  .ss-content .ss-list .ss-option.ss-disabled:hover {
289
- @apply text-gray-500 bg-gray-100 dark:text-gray-400 dark:bg-gray-800;
331
+ background-color: var(--pu-surface-alt);
332
+ color: var(--pu-text-muted);
290
333
  }
291
334
 
292
335
  .ss-content .ss-list .ss-option .ss-search-highlight {
@@ -295,19 +338,33 @@
295
338
 
296
339
  /* Additional state classes to match form validation states */
297
340
  .ss-main.ss-invalid {
298
- @apply bg-red-50 border-red-500 dark:border-red-500 text-red-900 dark:text-red-500 focus:ring-red-500 focus:border-red-500;
341
+ @apply border-danger-500 bg-danger-50/50 text-danger-900;
342
+ }
343
+ .ss-main.ss-invalid:focus,
344
+ .ss-main.ss-invalid[aria-expanded="true"] {
345
+ box-shadow: 0 0 0 3px theme(colors.danger.500 / 15%);
346
+ }
347
+ .dark .ss-main.ss-invalid {
348
+ @apply bg-danger-950/20 border-danger-500/70 text-danger-200;
299
349
  }
300
350
 
301
351
  .ss-main.ss-invalid .ss-values .ss-placeholder {
302
- @apply text-red-700 dark:text-red-500;
352
+ @apply text-danger-400;
303
353
  }
304
354
 
305
355
  .ss-main.ss-valid {
306
- @apply bg-green-50 border-green-500 dark:border-green-500 text-green-900 dark:text-green-400 focus:ring-green-500 focus:border-green-500;
356
+ @apply border-success-500 bg-success-50/50 text-success-900;
357
+ }
358
+ .ss-main.ss-valid:focus,
359
+ .ss-main.ss-valid[aria-expanded="true"] {
360
+ box-shadow: 0 0 0 3px theme(colors.success.500 / 15%);
361
+ }
362
+ .dark .ss-main.ss-valid {
363
+ @apply bg-success-950/20 border-success-500/70 text-success-200;
307
364
  }
308
365
 
309
366
  .ss-main.ss-valid .ss-values .ss-placeholder {
310
- @apply text-green-700 dark:text-green-500;
367
+ @apply text-success-400;
311
368
  }
312
369
 
313
370
  /* Modal-specific styles for SlimSelect dropdown */
@@ -22,6 +22,7 @@ import RemoteModalController from "./remote_modal_controller.js"
22
22
  import KeyValueStoreController from "./key_value_store_controller.js"
23
23
  import BulkActionsController from "./bulk_actions_controller.js"
24
24
  import FilterPanelController from "./filter_panel_controller.js"
25
+ import TextareaAutogrowController from "./textarea_autogrow_controller.js"
25
26
 
26
27
  export default function (application) {
27
28
  // Register controllers here
@@ -48,4 +49,5 @@ export default function (application) {
48
49
  application.register("key-value-store", KeyValueStoreController)
49
50
  application.register("bulk-actions", BulkActionsController)
50
51
  application.register("filter-panel", FilterPanelController)
52
+ application.register("textarea-autogrow", TextareaAutogrowController)
51
53
  }
@@ -0,0 +1,56 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static values = {
5
+ maxHeight: { type: Number, default: 0 } // 0 means use CSS max-height or 50vh
6
+ }
7
+
8
+ connect() {
9
+ this.resize()
10
+ this.element.addEventListener("input", this.resize)
11
+ window.addEventListener("resize", this.resize)
12
+ }
13
+
14
+ disconnect() {
15
+ this.element.removeEventListener("input", this.resize)
16
+ window.removeEventListener("resize", this.resize)
17
+ }
18
+
19
+ resize = () => {
20
+ const element = this.element
21
+ const maxHeight = this.#getMaxHeight()
22
+
23
+ // Reset to auto to get the natural scroll height
24
+ element.style.height = "auto"
25
+ element.style.overflow = "hidden"
26
+
27
+ const scrollHeight = element.scrollHeight
28
+
29
+ if (maxHeight > 0 && scrollHeight > maxHeight) {
30
+ element.style.height = `${maxHeight}px`
31
+ element.style.overflow = "auto"
32
+ } else {
33
+ element.style.height = `${scrollHeight}px`
34
+ }
35
+ }
36
+
37
+ #getMaxHeight() {
38
+ if (this.maxHeightValue > 0) {
39
+ return this.maxHeightValue
40
+ }
41
+
42
+ // Check for CSS max-height
43
+ const computedStyle = window.getComputedStyle(this.element)
44
+ const cssMaxHeight = computedStyle.maxHeight
45
+
46
+ if (cssMaxHeight && cssMaxHeight !== "none") {
47
+ const parsed = parseFloat(cssMaxHeight)
48
+ if (!isNaN(parsed) && parsed > 0) {
49
+ return parsed
50
+ }
51
+ }
52
+
53
+ // Default to 300px
54
+ return 300
55
+ }
56
+ }