phlex_kit 0.8.0 → 0.9.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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/phlex_kit/_tokens.css +11 -0
  3. data/app/components/phlex_kit/accordion/accordion.css +2 -2
  4. data/app/components/phlex_kit/accordion/accordion_content.rb +11 -2
  5. data/app/components/phlex_kit/accordion/accordion_controller.js +23 -4
  6. data/app/components/phlex_kit/alert/alert.css +3 -3
  7. data/app/components/phlex_kit/alert_dialog/alert_dialog.css +3 -3
  8. data/app/components/phlex_kit/alert_dialog/alert_dialog_content.rb +11 -4
  9. data/app/components/phlex_kit/alert_dialog/alert_dialog_controller.js +123 -6
  10. data/app/components/phlex_kit/aspect_ratio/aspect_ratio.rb +10 -4
  11. data/app/components/phlex_kit/attachment/attachment.css +1 -1
  12. data/app/components/phlex_kit/attachment/attachment_action.rb +8 -3
  13. data/app/components/phlex_kit/attachment/attachment_trigger.rb +3 -3
  14. data/app/components/phlex_kit/attachment/scroll_fade_controller.js +8 -0
  15. data/app/components/phlex_kit/avatar/avatar.css +16 -5
  16. data/app/components/phlex_kit/avatar/avatar_controller.js +10 -7
  17. data/app/components/phlex_kit/badge/badge.css +4 -4
  18. data/app/components/phlex_kit/breadcrumb/breadcrumb_ellipsis.rb +3 -1
  19. data/app/components/phlex_kit/bubble/bubble.css +3 -3
  20. data/app/components/phlex_kit/bubble/bubble.rb +2 -0
  21. data/app/components/phlex_kit/bubble/bubble_content.rb +5 -1
  22. data/app/components/phlex_kit/bubble/bubble_reactions.rb +4 -0
  23. data/app/components/phlex_kit/button/button.css +6 -6
  24. data/app/components/phlex_kit/button_group/button_group.css +2 -2
  25. data/app/components/phlex_kit/button_group/button_group_separator.rb +3 -1
  26. data/app/components/phlex_kit/calendar/calendar.css +2 -2
  27. data/app/components/phlex_kit/calendar/calendar.rb +10 -3
  28. data/app/components/phlex_kit/calendar/calendar_body.rb +12 -1
  29. data/app/components/phlex_kit/calendar/calendar_controller.js +151 -12
  30. data/app/components/phlex_kit/calendar/calendar_days.rb +17 -15
  31. data/app/components/phlex_kit/calendar/calendar_input_controller.js +6 -0
  32. data/app/components/phlex_kit/calendar/calendar_next.rb +5 -1
  33. data/app/components/phlex_kit/calendar/calendar_prev.rb +5 -1
  34. data/app/components/phlex_kit/calendar/calendar_weekdays.rb +1 -1
  35. data/app/components/phlex_kit/carousel/carousel.rb +11 -1
  36. data/app/components/phlex_kit/carousel/carousel_controller.js +28 -1
  37. data/app/components/phlex_kit/chart/chart_controller.js +37 -10
  38. data/app/components/phlex_kit/checkbox/checkbox.css +2 -1
  39. data/app/components/phlex_kit/clipboard/clipboard.css +1 -1
  40. data/app/components/phlex_kit/clipboard/clipboard_controller.js +7 -0
  41. data/app/components/phlex_kit/clipboard/clipboard_popover.rb +3 -1
  42. data/app/components/phlex_kit/codeblock/codeblock.rb +4 -1
  43. data/app/components/phlex_kit/collapsible/collapsible_content.rb +15 -2
  44. data/app/components/phlex_kit/collapsible/collapsible_controller.js +11 -2
  45. data/app/components/phlex_kit/collapsible/collapsible_trigger.rb +17 -2
  46. data/app/components/phlex_kit/combobox/combobox.css +10 -8
  47. data/app/components/phlex_kit/combobox/combobox_controller.js +67 -11
  48. data/app/components/phlex_kit/combobox/combobox_list_group.rb +13 -5
  49. data/app/components/phlex_kit/command/command.css +4 -4
  50. data/app/components/phlex_kit/command/command_controller.js +51 -10
  51. data/app/components/phlex_kit/command/command_dialog.rb +5 -6
  52. data/app/components/phlex_kit/command/command_dialog_controller.js +26 -8
  53. data/app/components/phlex_kit/command/command_input.rb +6 -2
  54. data/app/components/phlex_kit/command/command_item.rb +4 -1
  55. data/app/components/phlex_kit/context_menu/context_menu.css +6 -6
  56. data/app/components/phlex_kit/context_menu/context_menu_checkbox_item.rb +6 -2
  57. data/app/components/phlex_kit/context_menu/context_menu_controller.js +69 -2
  58. data/app/components/phlex_kit/context_menu/context_menu_radio_item.rb +6 -2
  59. data/app/components/phlex_kit/context_menu/context_menu_sub_trigger.rb +5 -2
  60. data/app/components/phlex_kit/data_table/data_table.css +1 -1
  61. data/app/components/phlex_kit/data_table/data_table_column_toggle.rb +11 -12
  62. data/app/components/phlex_kit/data_table/data_table_column_visibility_controller.js +4 -1
  63. data/app/components/phlex_kit/data_table/data_table_pagy_adapter.rb +2 -1
  64. data/app/components/phlex_kit/data_table/data_table_per_page_select.rb +19 -5
  65. data/app/components/phlex_kit/data_table/data_table_search_controller.js +13 -1
  66. data/app/components/phlex_kit/date_picker/date_picker.rb +50 -3
  67. data/app/components/phlex_kit/dialog/dialog.css +4 -4
  68. data/app/components/phlex_kit/dialog/dialog_controller.js +20 -4
  69. data/app/components/phlex_kit/drawer/drawer.css +3 -3
  70. data/app/components/phlex_kit/dropdown_menu/dropdown_menu.css +6 -5
  71. data/app/components/phlex_kit/dropdown_menu/dropdown_menu.rb +5 -1
  72. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_checkbox_item.rb +6 -2
  73. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_content.rb +3 -1
  74. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_controller.js +99 -45
  75. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_label.rb +3 -1
  76. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_radio_item.rb +6 -2
  77. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub_trigger.rb +5 -2
  78. data/app/components/phlex_kit/field/field.css +1 -1
  79. data/app/components/phlex_kit/field/field_error.rb +5 -6
  80. data/app/components/phlex_kit/form_field/form_field_controller.js +44 -12
  81. data/app/components/phlex_kit/form_field/form_field_error.rb +8 -1
  82. data/app/components/phlex_kit/hover_card/hover_card.css +1 -1
  83. data/app/components/phlex_kit/hover_card/hover_card_controller.js +29 -5
  84. data/app/components/phlex_kit/input/input.css +1 -1
  85. data/app/components/phlex_kit/input_group/input_group.css +9 -7
  86. data/app/components/phlex_kit/input_group/input_group_addon.rb +5 -1
  87. data/app/components/phlex_kit/input_otp/input_otp.css +1 -1
  88. data/app/components/phlex_kit/input_otp/input_otp_controller.js +43 -4
  89. data/app/components/phlex_kit/input_otp/input_otp_separator.rb +4 -2
  90. data/app/components/phlex_kit/item/item.css +1 -1
  91. data/app/components/phlex_kit/item/item_group.rb +4 -2
  92. data/app/components/phlex_kit/item/item_separator.rb +3 -1
  93. data/app/components/phlex_kit/marker/marker.css +1 -1
  94. data/app/components/phlex_kit/masked_input/masked_input.rb +1 -1
  95. data/app/components/phlex_kit/masked_input/masked_input_controller.js +34 -9
  96. data/app/components/phlex_kit/menubar/menubar.css +10 -9
  97. data/app/components/phlex_kit/menubar/menubar_content.rb +8 -2
  98. data/app/components/phlex_kit/menubar/menubar_controller.js +119 -12
  99. data/app/components/phlex_kit/menubar/menubar_menu.rb +4 -1
  100. data/app/components/phlex_kit/menubar/menubar_sub.rb +6 -1
  101. data/app/components/phlex_kit/menubar/menubar_sub_trigger.rb +7 -2
  102. data/app/components/phlex_kit/message_scroller/message_scroller.css +1 -1
  103. data/app/components/phlex_kit/message_scroller/message_scroller_controller.js +46 -2
  104. data/app/components/phlex_kit/native_select/native_select.css +1 -1
  105. data/app/components/phlex_kit/navigation_menu/navigation_menu.css +11 -3
  106. data/app/components/phlex_kit/navigation_menu/navigation_menu.rb +6 -1
  107. data/app/components/phlex_kit/navigation_menu/navigation_menu_trigger.rb +1 -1
  108. data/app/components/phlex_kit/pagination/pagination.css +2 -2
  109. data/app/components/phlex_kit/pagination/pagination_ellipsis.rb +3 -1
  110. data/app/components/phlex_kit/popover/popover.css +1 -1
  111. data/app/components/phlex_kit/resizable/resizable.css +3 -1
  112. data/app/components/phlex_kit/resizable/resizable_controller.js +78 -3
  113. data/app/components/phlex_kit/resizable/resizable_handle.rb +3 -1
  114. data/app/components/phlex_kit/resizable/resizable_panel.rb +3 -1
  115. data/app/components/phlex_kit/scroll_area/scroll_area.rb +4 -2
  116. data/app/components/phlex_kit/select/select.css +3 -3
  117. data/app/components/phlex_kit/select/select.rb +11 -7
  118. data/app/components/phlex_kit/select/select_controller.js +35 -4
  119. data/app/components/phlex_kit/select/select_item.rb +5 -2
  120. data/app/components/phlex_kit/sheet/sheet.css +3 -3
  121. data/app/components/phlex_kit/sheet/sheet_content_controller.js +36 -12
  122. data/app/components/phlex_kit/sidebar/sidebar.css +27 -15
  123. data/app/components/phlex_kit/sidebar/sidebar_controller.js +98 -1
  124. data/app/components/phlex_kit/sidebar/sidebar_menu_button.rb +24 -5
  125. data/app/components/phlex_kit/sidebar/sidebar_rail.rb +13 -4
  126. data/app/components/phlex_kit/sidebar/sidebar_trigger.rb +14 -4
  127. data/app/components/phlex_kit/slider/slider.css +3 -3
  128. data/app/components/phlex_kit/slider/slider.rb +3 -1
  129. data/app/components/phlex_kit/switch/switch.rb +6 -1
  130. data/app/components/phlex_kit/table/table.css +5 -3
  131. data/app/components/phlex_kit/tabs/tabs.css +4 -4
  132. data/app/components/phlex_kit/tabs/tabs_content.rb +6 -3
  133. data/app/components/phlex_kit/tabs/tabs_controller.js +3 -1
  134. data/app/components/phlex_kit/theme_toggle/theme_toggle.rb +11 -0
  135. data/app/components/phlex_kit/theme_toggle/theme_toggle_controller.js +49 -7
  136. data/app/components/phlex_kit/toast/toast.css +32 -14
  137. data/app/components/phlex_kit/toast/toast_controller.js +24 -4
  138. data/app/components/phlex_kit/toast/toast_region.rb +19 -5
  139. data/app/components/phlex_kit/toast/toaster_controller.js +20 -6
  140. data/app/components/phlex_kit/toggle/toggle.css +4 -4
  141. data/app/components/phlex_kit/toggle/toggle.rb +9 -3
  142. data/app/components/phlex_kit/tooltip/tooltip.css +28 -6
  143. data/app/components/phlex_kit/typography/heading.rb +9 -3
  144. data/app/components/phlex_kit/typography/typography.css +4 -3
  145. data/app/javascript/phlex_kit/controllers/index.js +0 -2
  146. data/lib/phlex_kit/version.rb +1 -1
  147. metadata +1 -2
  148. data/app/components/phlex_kit/select/select_item_controller.js +0 -14
@@ -32,6 +32,7 @@ module PhlexKit
32
32
  hotkey: %w[alt t],
33
33
  dir: :ltr,
34
34
  flash: nil,
35
+ id: "pk-toaster",
35
36
  **attrs
36
37
  )
37
38
  @position = POSITIONS.fetch(position.to_sym)
@@ -46,12 +47,16 @@ module PhlexKit
46
47
  @hotkey = hotkey
47
48
  @dir = dir
48
49
  @flash = flash
50
+ # Base id for the <ol> stack; the wrapper takes "#{id}-region". Defaults
51
+ # keep the historical "pk-toaster"/"pk-toaster-region" pair — pass a
52
+ # distinct id when rendering more than one region on a page.
53
+ @id = id
49
54
  @attrs = attrs
50
55
  end
51
56
 
52
57
  def view_template(&block)
53
58
  div(**mix(region_attrs, @attrs)) do
54
- ol(id: "pk-toaster", class: "pk-toast-list") do
59
+ ol(id: @id, class: "pk-toast-list") do
55
60
  render_flash if @flash
56
61
  yield(self) if block
57
62
  end
@@ -68,16 +73,23 @@ module PhlexKit
68
73
  @flash.each do |key, message|
69
74
  next if message.nil? || message.to_s.empty?
70
75
  variant = Toast.flash_variant(key)
71
- render ToastItem.new(variant: variant, id: "flash-#{key}") do
76
+ # Stamp the region's duration — without it the toast controller's
77
+ # 4000ms default wins and ToastRegion(duration:) is dead config.
78
+ render ToastItem.new(variant: variant, id: "flash-#{key}", duration: @duration) do
72
79
  render ToastIcon.new(variant: variant)
73
80
  render ToastTitle.new { message.to_s }
81
+ # The region-wide close button must reach flash toasts too — the CSS
82
+ # already reserves their right padding when close_button is on.
83
+ render ToastClose.new if @close_button
74
84
  end
75
85
  end
76
86
  end
77
87
 
78
88
  def skeleton(variant)
79
89
  template(data: { phlex_kit__toaster_target: "skeleton", variant: variant.to_s }) do
80
- render ToastItem.new(variant: variant) do
90
+ # Region duration baked into the skeleton; _spawn overrides it only
91
+ # when the caller passes a per-toast duration.
92
+ render ToastItem.new(variant: variant, duration: @duration) do
81
93
  render ToastIcon.new(variant: variant)
82
94
  div(class: "pk-toast-body") do
83
95
  render ToastTitle.new
@@ -94,10 +106,12 @@ module PhlexKit
94
106
 
95
107
  def region_attrs
96
108
  {
97
- id: "pk-toaster-region",
109
+ id: "#{@id}-region",
98
110
  role: "region",
99
111
  class: "pk-toast-region",
100
- aria: { label: "Notifications", live: "polite" },
112
+ # No aria-live here: each toast item is its own live region
113
+ # (role="status"/"alert"); a live wrapper would double-announce.
114
+ aria: { label: "Notifications" },
101
115
  data: {
102
116
  controller: "phlex-kit--toaster",
103
117
  turbo_permanent: "",
@@ -181,6 +181,9 @@ export default class extends Controller {
181
181
  _setExpanded(value) {
182
182
  if (this._expanded === value) return
183
183
  this._expanded = value
184
+ // Mirror the pause state onto the DOM so toasts spawned while the stack
185
+ // is already hovered can arm paused (the pause event predates them).
186
+ this._listEl?.toggleAttribute("data-pk-toasts-paused", value)
184
187
  document.dispatchEvent(new CustomEvent(value ? "phlex-kit:toast:pause" : "phlex-kit:toast:resume"))
185
188
  this._reflow()
186
189
  }
@@ -189,7 +192,10 @@ export default class extends Controller {
189
192
  if (!this._listEl) return
190
193
  const isBottom = this.positionValue.startsWith("bottom")
191
194
  const items = this.toastTargets
192
- const order = isBottom ? items.slice().reverse() : items.slice()
195
+ // Newest toast fronts the stack at BOTH edges (index 0 = front); only
196
+ // the offset direction flips with the position. Using DOM order at
197
+ // top-* put the oldest toast in front and stacked new ones behind it.
198
+ const order = items.slice().reverse()
193
199
  const heights = order.map(el => this._heights.get(el) || el.offsetHeight || 64)
194
200
  const gap = this.gapValue
195
201
  const peekOffset = 16
@@ -224,7 +230,10 @@ export default class extends Controller {
224
230
  el.style.transformOrigin = isBottom ? "center bottom" : "center top"
225
231
  el.style.top = isBottom ? "auto" : "0"
226
232
  el.style.bottom = isBottom ? "0" : "auto"
227
- el.style.transform = `translate3d(0, ${ty}px, 0) scale(${scale})`
233
+ // Compose the toast controller's swipe offset (custom props it sets
234
+ // during pointer-drag) into the stacking transform — the swipe must
235
+ // never overwrite this transform or non-front toasts snap out of place.
236
+ el.style.transform = `translate3d(var(--pk-toast-swipe-x, 0px), calc(${ty}px + var(--pk-toast-swipe-y, 0px)), 0) scale(${scale})`
228
237
  el.style.zIndex = String(1000 - i)
229
238
  el.style.pointerEvents = visible ? "auto" : "none"
230
239
  el.tabIndex = visible ? 0 : -1
@@ -237,9 +246,10 @@ export default class extends Controller {
237
246
 
238
247
  _enforceMax(items) {
239
248
  if (items.length <= this.maxValue) return
240
- const isBottom = this.positionValue.startsWith("bottom")
241
249
  const dropping = items.length - this.maxValue
242
- const candidates = isBottom ? items.slice(0, dropping) : items.slice(-dropping)
250
+ // Evict the OLDEST toasts (DOM order = append order) regardless of edge;
251
+ // slicing from the tail at top-* dropped the newest ones instead.
252
+ const candidates = items.slice(0, dropping)
243
253
  candidates.forEach(el => {
244
254
  if (el.dataset.state !== "closing") {
245
255
  el.dispatchEvent(new CustomEvent("phlex-kit:toast:force-dismiss", { bubbles: true }))
@@ -253,13 +263,17 @@ export default class extends Controller {
253
263
  const wantAlt = parts.includes("alt")
254
264
  const wantCtrl = parts.includes("ctrl")
255
265
  const wantMeta = parts.includes("meta")
266
+ const wantShift = parts.includes("shift")
256
267
  if (e.key.toLowerCase() !== key.toLowerCase()) return
257
268
  if (wantAlt !== e.altKey) return
258
269
  if (wantCtrl !== e.ctrlKey) return
259
270
  if (wantMeta !== e.metaKey) return
271
+ if (wantShift !== e.shiftKey) return
260
272
  e.preventDefault()
261
- const first = this._listEl.firstElementChild
262
- first?.focus()
273
+ // Newest toast is the LAST child (append order) and fronts the stack —
274
+ // the first child is the oldest, faded, possibly pointer-events:none one.
275
+ const newest = this._listEl.lastElementChild
276
+ newest?.focus()
263
277
  }
264
278
 
265
279
  _registerGlobalApi() {
@@ -25,7 +25,7 @@
25
25
  .pk-toggle svg { pointer-events: none; flex-shrink: 0; }
26
26
  .pk-toggle svg:not([class*="size-"]) { width: 1rem; height: 1rem; }
27
27
  .pk-toggle.sm svg:not([class*="size-"]) { width: .875rem; height: .875rem; }
28
- .pk-toggle:has(> [data-icon="inline-start"]) { padding-left: .5rem; }
29
- .pk-toggle:has(> [data-icon="inline-end"]) { padding-right: .5rem; }
30
- .pk-toggle.sm:has(> [data-icon="inline-start"]) { padding-left: .375rem; }
31
- .pk-toggle.sm:has(> [data-icon="inline-end"]) { padding-right: .375rem; }
28
+ .pk-toggle:has(> [data-icon="inline-start"]) { padding-inline-start: .5rem; }
29
+ .pk-toggle:has(> [data-icon="inline-end"]) { padding-inline-end: .5rem; }
30
+ .pk-toggle.sm:has(> [data-icon="inline-start"]) { padding-inline-start: .375rem; }
31
+ .pk-toggle.sm:has(> [data-icon="inline-end"]) { padding-inline-end: .375rem; }
@@ -7,7 +7,9 @@ module PhlexKit
7
7
  SIZES = { sm: "sm", default: nil, lg: "lg" }.freeze
8
8
 
9
9
  def self.modifier_classes(variant:, size:)
10
- [ VARIANTS.fetch(variant, nil), SIZES.fetch(size, nil) ].compact
10
+ # Plain fetch an unknown variant/size raises KeyError (kit-wide
11
+ # fail-loud rule); the nil-default form silently rendered unstyled.
12
+ [ VARIANTS.fetch(variant), SIZES.fetch(size) ].compact
11
13
  end
12
14
 
13
15
  def initialize(pressed: false, name: nil, value: "1", unpressed_value: nil,
@@ -52,8 +54,12 @@ module PhlexKit
52
54
  end
53
55
 
54
56
  def render_hidden_input
55
- input(type: "hidden", name: @name, value: @pressed ? @value : @unpressed_value.to_s,
56
- data: { phlex_kit__toggle_target: "input" })
57
+ # Disabled in lockstep with the button a disabled toggle must not
58
+ # submit its value (matches native disabled-control form semantics).
59
+ a = { type: "hidden", name: @name, value: @pressed ? @value : @unpressed_value.to_s,
60
+ data: { phlex_kit__toggle_target: "input" } }
61
+ a[:disabled] = true if @disabled
62
+ input(**a)
57
63
  end
58
64
  end
59
65
  end
@@ -34,9 +34,14 @@
34
34
  opacity: 0;
35
35
  visibility: hidden;
36
36
  pointer-events: none;
37
- transition: opacity .12s ease;
37
+ /* Zero delay here = hover-out hides immediately; the hover-in rule below
38
+ re-declares the delays for the open direction only. */
39
+ transition: opacity .12s ease 0s, visibility 0s linear 0s;
38
40
  }
39
- .pk-tooltip-content:has(kbd) { padding-right: .375rem; }
41
+ @media (prefers-reduced-motion: reduce) {
42
+ .pk-tooltip-content { transition-duration: 0s, 0s; }
43
+ }
44
+ .pk-tooltip-content:has(kbd) { padding-inline-end: .375rem; }
40
45
  /* The notch. The arrow is position:fixed so both anchors are acceptable
41
46
  (an absolute pseudo's containing block is the bubble, which doesn't
42
47
  contain the trigger — anchor() wouldn't resolve). Its center chases the
@@ -66,8 +71,25 @@
66
71
  position-area: inline-end; margin: 0 0 0 6px;
67
72
  position-try-fallbacks: flip-inline;
68
73
  }
69
- .pk-tooltip:hover .pk-tooltip-content,
70
- .pk-tooltip:focus-within .pk-tooltip-content { opacity: 1; visibility: visible; }
71
- /* Escape-dismissed (WCAG 1.4.13): suppressed until pointer/focus leaves. */
74
+ /* Hover reveal only on devices that actually hover (a touch tap must not
75
+ stick the bubble open), with an open delay so tooltips don't flash while
76
+ the cursor sweeps across dense UI. Visibility gets the same delay so the
77
+ bubble doesn't occupy hit-testing space during the wait. */
78
+ @media (hover: hover) {
79
+ .pk-tooltip:hover .pk-tooltip-content {
80
+ opacity: 1;
81
+ visibility: visible;
82
+ transition-delay: .4s, .4s;
83
+ }
84
+ }
85
+ /* Keyboard focus reveals instantly (:focus-visible, so pointer/touch clicks
86
+ that focus the trigger don't stick the bubble open). */
87
+ .pk-tooltip:has(:focus-visible) .pk-tooltip-content {
88
+ opacity: 1;
89
+ visibility: visible;
90
+ transition-delay: 0s, 0s;
91
+ }
92
+ /* Escape-dismissed (WCAG 1.4.13): suppressed until pointer/focus leaves.
93
+ transition: none so the hide is immediate regardless of hover-in delays. */
72
94
  .pk-tooltip[data-pk-dismissed]:hover .pk-tooltip-content,
73
- .pk-tooltip[data-pk-dismissed]:focus-within .pk-tooltip-content { opacity: 0; visibility: hidden; }
95
+ .pk-tooltip[data-pk-dismissed]:focus-within .pk-tooltip-content { opacity: 0; visibility: hidden; transition: none; }
@@ -1,14 +1,20 @@
1
1
  module PhlexKit
2
- # Heading, ported from ruby_ui's RubyUI::Heading. `level:` (1–4) picks the
2
+ # Heading, ported from ruby_ui's RubyUI::Heading. `level:` (1–6) picks the
3
3
  # element AND a default size; `as:` overrides the element; `size:` (1–9)
4
4
  # overrides the size on ruby_ui's scale (text-xs … text-5xl). Presentational;
5
5
  # attrs pass through via mix. Tailwind → vanilla `.pk-heading*` (typography.css).
6
6
  class Heading < BaseComponent
7
7
  SIZES = %w[1 2 3 4 5 6 7 8 9].freeze
8
- # ruby_ui's level → default size mapping (h1 is big, h4 smaller).
9
- LEVEL_SIZE = { "1" => "7", "2" => "6", "3" => "5", "4" => "4" }.freeze
8
+ # ruby_ui's level → default size mapping (h1 is big, h4 smaller); 5/6
9
+ # extend the scale downward HTML has h1–h6, so all six must map.
10
+ LEVEL_SIZE = { "1" => "7", "2" => "6", "3" => "5", "4" => "4", "5" => "3", "6" => "2" }.freeze
10
11
 
11
12
  def initialize(level: nil, as: nil, size: nil, **attrs)
13
+ # Fail loud at construction: level 7 previously exploded at render time
14
+ # with NoMethodError (no h7 element), and 5/6 fell through the size map.
15
+ unless level.nil? || LEVEL_SIZE.key?(level.to_s)
16
+ raise ArgumentError, "Invalid heading level: #{level.inspect} — valid: 1–6"
17
+ end
12
18
  @level = level
13
19
  @as = as
14
20
  @size = size
@@ -38,7 +38,8 @@
38
38
  font-size: .875rem;
39
39
  background: var(--pk-bg);
40
40
  border: 1px solid var(--pk-border);
41
- border-radius: 6px;
41
+ /* Tracks the radius token — 6px at the default --pk-radius (0.625rem). */
42
+ border-radius: calc(var(--pk-radius) - 4px);
42
43
  padding: .15rem .4rem;
43
44
  }
44
45
 
@@ -47,8 +48,8 @@
47
48
  .pk-inline-link:hover { text-decoration: underline; }
48
49
 
49
50
  /* Blockquote */
50
- .pk-blockquote { margin: 1.5rem 0; border-left: 2px solid var(--pk-border); padding-left: 1.5rem; font-style: italic; }
51
+ .pk-blockquote { margin: 1.5rem 0; border-inline-start: 2px solid var(--pk-border); padding-inline-start: 1.5rem; font-style: italic; }
51
52
 
52
53
  /* Prose list (shadcn typography's list style). */
53
- .pk-list { margin: 1.5rem 0; padding-left: 1.5rem; list-style: disc; }
54
+ .pk-list { margin: 1.5rem 0; padding-inline-start: 1.5rem; list-style: disc; }
54
55
  .pk-list > li { margin-top: .5rem; }
@@ -36,7 +36,6 @@ import PopoverController from "phlex_kit/controllers/popover_controller"
36
36
  import ResizableController from "phlex_kit/controllers/resizable_controller"
37
37
  import ScrollFadeController from "phlex_kit/controllers/scroll_fade_controller"
38
38
  import SelectController from "phlex_kit/controllers/select_controller"
39
- import SelectItemController from "phlex_kit/controllers/select_item_controller"
40
39
  import SheetController from "phlex_kit/controllers/sheet_controller"
41
40
  import SheetContentController from "phlex_kit/controllers/sheet_content_controller"
42
41
  import SidebarController from "phlex_kit/controllers/sidebar_controller"
@@ -78,7 +77,6 @@ export function registerPhlexKitControllers(application) {
78
77
  application.register("phlex-kit--resizable", ResizableController)
79
78
  application.register("phlex-kit--scroll-fade", ScrollFadeController)
80
79
  application.register("phlex-kit--select", SelectController)
81
- application.register("phlex-kit--select-item", SelectItemController)
82
80
  application.register("phlex-kit--sheet", SheetController)
83
81
  application.register("phlex-kit--sheet-content", SheetContentController)
84
82
  application.register("phlex-kit--sidebar", SidebarController)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PhlexKit
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlex_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Kennedy
@@ -486,7 +486,6 @@ files:
486
486
  - app/components/phlex_kit/select/select_group.rb
487
487
  - app/components/phlex_kit/select/select_input.rb
488
488
  - app/components/phlex_kit/select/select_item.rb
489
- - app/components/phlex_kit/select/select_item_controller.js
490
489
  - app/components/phlex_kit/select/select_label.rb
491
490
  - app/components/phlex_kit/select/select_separator.rb
492
491
  - app/components/phlex_kit/select/select_trigger.rb
@@ -1,14 +0,0 @@
1
- import { Controller } from "@hotwired/stimulus";
2
-
3
- // Ported verbatim from ruby_ui's phlex-kit--select-item controller. Flips its own
4
- // element's aria-selected when an item is chosen (the select controller invokes
5
- // this on every item via the outlet), which drives the checkmark in select.css.
6
- export default class extends Controller {
7
- handleSelectItem({ target }) {
8
- if (this.element.dataset.value == target.dataset.value) {
9
- this.element.setAttribute("aria-selected", true);
10
- } else {
11
- this.element.removeAttribute("aria-selected");
12
- }
13
- }
14
- }