phlex_kit 0.5.1 → 0.6.1

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -8
  3. data/app/components/phlex_kit/accordion/accordion.css +2 -0
  4. data/app/components/phlex_kit/accordion/accordion.rb +1 -1
  5. data/app/components/phlex_kit/accordion/accordion_content.rb +3 -1
  6. data/app/components/phlex_kit/accordion/accordion_controller.js +26 -2
  7. data/app/components/phlex_kit/accordion/accordion_default_trigger.rb +20 -4
  8. data/app/components/phlex_kit/accordion/accordion_trigger.rb +18 -2
  9. data/app/components/phlex_kit/alert/alert.rb +1 -1
  10. data/app/components/phlex_kit/alert_dialog/alert_dialog_cancel.rb +1 -1
  11. data/app/components/phlex_kit/alert_dialog/alert_dialog_content.rb +1 -1
  12. data/app/components/phlex_kit/attachment/attachment.rb +2 -2
  13. data/app/components/phlex_kit/attachment/attachment_media.rb +1 -1
  14. data/app/components/phlex_kit/avatar/avatar.rb +1 -1
  15. data/app/components/phlex_kit/badge/badge.rb +1 -1
  16. data/app/components/phlex_kit/button/button.rb +1 -1
  17. data/app/components/phlex_kit/button_group/button_group.rb +1 -1
  18. data/app/components/phlex_kit/calendar/calendar_controller.js +12 -8
  19. data/app/components/phlex_kit/card/card.rb +1 -1
  20. data/app/components/phlex_kit/carousel/carousel.rb +1 -1
  21. data/app/components/phlex_kit/clipboard/clipboard_popover.rb +1 -1
  22. data/app/components/phlex_kit/combobox/combobox.css +21 -12
  23. data/app/components/phlex_kit/combobox/combobox.rb +19 -3
  24. data/app/components/phlex_kit/combobox/combobox_badge_trigger.rb +7 -2
  25. data/app/components/phlex_kit/combobox/combobox_checkbox.rb +3 -0
  26. data/app/components/phlex_kit/combobox/combobox_controller.js +85 -20
  27. data/app/components/phlex_kit/combobox/combobox_input_trigger.rb +11 -2
  28. data/app/components/phlex_kit/combobox/combobox_item.rb +4 -1
  29. data/app/components/phlex_kit/combobox/combobox_list.rb +9 -2
  30. data/app/components/phlex_kit/combobox/combobox_popover.rb +8 -7
  31. data/app/components/phlex_kit/combobox/combobox_radio.rb +3 -0
  32. data/app/components/phlex_kit/combobox/combobox_search_input.rb +8 -3
  33. data/app/components/phlex_kit/command/command.rb +17 -2
  34. data/app/components/phlex_kit/command/command_controller.js +93 -3
  35. data/app/components/phlex_kit/command/command_dialog_content.rb +16 -4
  36. data/app/components/phlex_kit/command/command_input.rb +7 -2
  37. data/app/components/phlex_kit/command/command_item.rb +12 -3
  38. data/app/components/phlex_kit/command/command_list.rb +9 -2
  39. data/app/components/phlex_kit/context_menu/context_menu.css +25 -13
  40. data/app/components/phlex_kit/context_menu/context_menu_content.rb +1 -1
  41. data/app/components/phlex_kit/context_menu/context_menu_controller.js +71 -12
  42. data/app/components/phlex_kit/context_menu/context_menu_item.rb +7 -3
  43. data/app/components/phlex_kit/context_menu/context_menu_label.rb +1 -1
  44. data/app/components/phlex_kit/context_menu/context_menu_trigger.rb +3 -1
  45. data/app/components/phlex_kit/data_table/data_table_column_visibility_controller.js +1 -1
  46. data/app/components/phlex_kit/data_table/data_table_controller.js +12 -1
  47. data/app/components/phlex_kit/data_table/data_table_pagination.rb +12 -10
  48. data/app/components/phlex_kit/data_table/data_table_selection_summary.rb +1 -0
  49. data/app/components/phlex_kit/data_table/data_table_sort_head.rb +5 -1
  50. data/app/components/phlex_kit/dialog/dialog_content.rb +12 -4
  51. data/app/components/phlex_kit/dialog/dialog_controller.js +13 -0
  52. data/app/components/phlex_kit/drawer/drawer_content.rb +2 -2
  53. data/app/components/phlex_kit/dropdown_menu/dropdown_menu.css +36 -15
  54. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_content.rb +4 -3
  55. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_controller.js +8 -11
  56. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_item.rb +1 -1
  57. data/app/components/phlex_kit/empty/empty_media.rb +1 -1
  58. data/app/components/phlex_kit/field/field.rb +1 -1
  59. data/app/components/phlex_kit/field/field_legend.rb +1 -1
  60. data/app/components/phlex_kit/hover_card/hover_card.css +27 -8
  61. data/app/components/phlex_kit/hover_card/hover_card.rb +2 -1
  62. data/app/components/phlex_kit/hover_card/hover_card_content.rb +2 -2
  63. data/app/components/phlex_kit/hover_card/hover_card_controller.js +19 -5
  64. data/app/components/phlex_kit/input_group/input_group_addon.rb +1 -1
  65. data/app/components/phlex_kit/input_otp/input_otp.rb +4 -1
  66. data/app/components/phlex_kit/input_otp/input_otp_controller.js +12 -1
  67. data/app/components/phlex_kit/item/item.rb +1 -1
  68. data/app/components/phlex_kit/item/item_media.rb +1 -1
  69. data/app/components/phlex_kit/link/link.rb +1 -1
  70. data/app/components/phlex_kit/marker/marker.rb +1 -1
  71. data/app/components/phlex_kit/menubar/menubar.css +36 -14
  72. data/app/components/phlex_kit/menubar/menubar.rb +1 -1
  73. data/app/components/phlex_kit/menubar/menubar_checkbox_item.rb +5 -2
  74. data/app/components/phlex_kit/menubar/menubar_content.rb +1 -1
  75. data/app/components/phlex_kit/menubar/menubar_controller.js +100 -12
  76. data/app/components/phlex_kit/menubar/menubar_item.rb +8 -5
  77. data/app/components/phlex_kit/menubar/menubar_radio_item.rb +5 -2
  78. data/app/components/phlex_kit/menubar/menubar_trigger.rb +1 -0
  79. data/app/components/phlex_kit/native_select/native_select.rb +1 -1
  80. data/app/components/phlex_kit/navigation_menu/navigation_menu.css +24 -7
  81. data/app/components/phlex_kit/navigation_menu/navigation_menu_content.rb +3 -1
  82. data/app/components/phlex_kit/popover/popover.css +24 -9
  83. data/app/components/phlex_kit/popover/popover.rb +2 -1
  84. data/app/components/phlex_kit/popover/popover_content.rb +6 -4
  85. data/app/components/phlex_kit/popover/popover_controller.js +12 -8
  86. data/app/components/phlex_kit/popover/popover_trigger.rb +1 -1
  87. data/app/components/phlex_kit/resizable/resizable_panel.rb +4 -2
  88. data/app/components/phlex_kit/select/select.css +24 -12
  89. data/app/components/phlex_kit/select/select_content.rb +6 -4
  90. data/app/components/phlex_kit/select/select_controller.js +21 -17
  91. data/app/components/phlex_kit/select/select_trigger.rb +1 -1
  92. data/app/components/phlex_kit/sheet/sheet_content.rb +2 -2
  93. data/app/components/phlex_kit/sheet/sheet_content_controller.js +68 -2
  94. data/app/components/phlex_kit/sheet/sheet_controller.js +28 -3
  95. data/app/components/phlex_kit/sidebar/sidebar.rb +1 -1
  96. data/app/components/phlex_kit/sidebar/sidebar_wrapper.rb +1 -1
  97. data/app/components/phlex_kit/spinner/spinner.rb +1 -1
  98. data/app/components/phlex_kit/switch/switch.rb +7 -4
  99. data/app/components/phlex_kit/tabs/tabs.rb +1 -1
  100. data/app/components/phlex_kit/tabs/tabs_content.rb +4 -1
  101. data/app/components/phlex_kit/tabs/tabs_controller.js +60 -7
  102. data/app/components/phlex_kit/tabs/tabs_list.rb +2 -2
  103. data/app/components/phlex_kit/tabs/tabs_trigger.rb +14 -2
  104. data/app/components/phlex_kit/theme_toggle/theme_toggle.rb +11 -3
  105. data/app/components/phlex_kit/toast/toast_controller.js +17 -0
  106. data/app/components/phlex_kit/toast/toast_icon.rb +1 -1
  107. data/app/components/phlex_kit/toast/toast_item.rb +1 -1
  108. data/app/components/phlex_kit/toggle/toggle.rb +2 -2
  109. data/app/components/phlex_kit/toggle_group/toggle_group.rb +2 -2
  110. data/app/components/phlex_kit/toggle_group/toggle_group_item.rb +1 -1
  111. data/app/components/phlex_kit/tooltip/tooltip.css +40 -22
  112. data/app/components/phlex_kit/tooltip/tooltip.rb +3 -1
  113. data/app/components/phlex_kit/tooltip/tooltip_content.rb +1 -1
  114. data/app/components/phlex_kit/tooltip/tooltip_controller.js +32 -0
  115. data/app/components/phlex_kit/tooltip/tooltip_trigger.rb +8 -3
  116. data/app/components/phlex_kit/typography/text.rb +1 -1
  117. data/app/javascript/phlex_kit/controllers/index.js +2 -0
  118. data/lib/generators/phlex_kit/component/component_generator.rb +28 -0
  119. data/lib/generators/phlex_kit/install/templates/phlex_kit.rb +3 -0
  120. data/lib/phlex_kit/base_component.rb +12 -0
  121. data/lib/phlex_kit/engine.rb +11 -1
  122. data/lib/phlex_kit/icons/lucide.rb +1 -1
  123. data/lib/phlex_kit/icons/phosphor.rb +1 -1
  124. data/lib/phlex_kit/icons/remix.rb +1 -1
  125. data/lib/phlex_kit/icons/tabler.rb +1 -1
  126. data/lib/phlex_kit/version.rb +1 -1
  127. data/lib/phlex_kit.rb +5 -0
  128. metadata +2 -1
@@ -39,7 +39,7 @@ module PhlexKit
39
39
 
40
40
  def selected_values
41
41
  case @type
42
- when :single then @value.nil? ? [] : [@value.to_s]
42
+ when :single then @value.nil? ? [] : [ @value.to_s ]
43
43
  when :multiple then Array(@value).map(&:to_s)
44
44
  end
45
45
  end
@@ -55,7 +55,7 @@ module PhlexKit
55
55
 
56
56
  def group_default_attrs
57
57
  { role: (@type == :single) ? "radiogroup" : "group",
58
- class: ["pk-toggle-group", (@orientation == :vertical ? "vertical" : nil), (@spacing > 0 ? "spaced" : nil)].compact.join(" "),
58
+ class: [ "pk-toggle-group", (@orientation == :vertical ? "vertical" : nil), (@spacing > 0 ? "spaced" : nil) ].compact.join(" "),
59
59
  data: { controller: "phlex-kit--toggle-group",
60
60
  phlex_kit__toggle_group_type_value: @type.to_s,
61
61
  phlex_kit__toggle_group_name_value: @name.to_s,
@@ -18,7 +18,7 @@ module PhlexKit
18
18
 
19
19
  def item_default_attrs
20
20
  a = { type: "button",
21
- class: (["pk-toggle"] + self.class.modifier_classes(variant: @variant, size: @size) + ["pk-toggle-group-item"]).join(" "),
21
+ class: ([ "pk-toggle" ] + self.class.modifier_classes(variant: @variant, size: @size) + [ "pk-toggle-group-item" ]).join(" "),
22
22
  data: { state: @pressed ? "on" : "off", value: @item_value,
23
23
  phlex_kit__toggle_group_target: "item",
24
24
  action: "click->phlex-kit--toggle-group#select keydown->phlex-kit--toggle-group#navigate" } }
@@ -1,15 +1,25 @@
1
1
  /* Co-located with tooltip.rb — UI::Tooltip (+ Trigger / Content). CSS-only:
2
- the bubble is absolutely positioned and revealed on the wrapper's :hover /
3
- :focus-within (no JS, no @floating-ui). Matched to nova: INVERTED surface
4
- (foreground bg, background text) with a rotated-square arrow, rounded-md,
5
- px-3 py-1.5 text-xs, gap for inline kbd chips (kbd.css tints them). */
6
- .pk-tooltip { position: relative; display: inline-flex; }
7
- .pk-tooltip-trigger { display: inline-flex; }
2
+ the bubble is revealed on the wrapper's :hover / :focus-within and placed
3
+ with CSS anchor positioning (Baseline 2026, no JS, no @floating-ui): the
4
+ fixed panel escapes overflow-clipping ancestors and position-try-fallbacks
5
+ flips it to the opposite side at the viewport edge. Reveal stays opacity-
6
+ based (a native [popover] would need JS hover handlers), so it needs
7
+ z-index rather than the top layer. The ::after arrow pins itself to
8
+ whichever bubble edge faces the trigger (anchor(center) clamped into the
9
+ bubble's box), so it follows flip fallbacks automatically — one rule for
10
+ all four sides. Matched to nova: INVERTED surface (foreground bg,
11
+ background text) with a rotated-square arrow, rounded-md, px-3 py-1.5
12
+ text-xs, gap for inline kbd chips (kbd.css tints them). */
13
+ .pk-tooltip { display: inline-flex; anchor-scope: --pk-tooltip, --pk-tooltip-bubble; }
14
+ .pk-tooltip-trigger { display: inline-flex; anchor-name: --pk-tooltip; }
8
15
  .pk-tooltip-content {
9
- position: absolute;
10
- bottom: calc(100% + 6px);
11
- left: 50%;
12
- transform: translateX(-50%);
16
+ position: fixed;
17
+ position-anchor: --pk-tooltip;
18
+ anchor-name: --pk-tooltip-bubble;
19
+ inset: auto;
20
+ position-area: block-start;
21
+ margin: 0 0 6px;
22
+ position-try-fallbacks: flip-block;
13
23
  z-index: 50;
14
24
  display: inline-flex;
15
25
  align-items: center;
@@ -27,29 +37,37 @@
27
37
  transition: opacity .12s ease;
28
38
  }
29
39
  .pk-tooltip-content:has(kbd) { padding-right: .375rem; }
40
+ /* The notch. The arrow is position:fixed so both anchors are acceptable
41
+ (an absolute pseudo's containing block is the bubble, which doesn't
42
+ contain the trigger — anchor() wouldn't resolve). Its center chases the
43
+ trigger's center but is clamped 2px inside the bubble's box, so wherever
44
+ the bubble actually renders (declared side OR a flip fallback) the arrow
45
+ sits on the edge facing the trigger. */
30
46
  .pk-tooltip-content::after {
31
47
  content: "";
32
- position: absolute;
48
+ position: fixed;
49
+ /* default anchor = the trigger, so anchor() values are scroll-compensated */
50
+ position-anchor: --pk-tooltip;
33
51
  z-index: -1;
34
52
  width: .625rem;
35
53
  height: .625rem;
36
54
  border-radius: 2px;
37
55
  background: inherit;
38
- top: 100%;
39
- left: 50%;
40
- transform: translate(-50%, calc(-50% - 2px)) rotate(45deg);
56
+ top: clamp(calc(anchor(--pk-tooltip-bubble top) + 2px), anchor(--pk-tooltip center), calc(anchor(--pk-tooltip-bubble bottom) - 2px));
57
+ left: clamp(calc(anchor(--pk-tooltip-bubble left) + 2px), anchor(--pk-tooltip center), calc(anchor(--pk-tooltip-bubble right) - 2px));
58
+ transform: translate(-50%, -50%) rotate(45deg);
41
59
  }
42
- .pk-tooltip-content.bottom { bottom: auto; top: calc(100% + 6px); }
43
- .pk-tooltip-content.bottom::after { top: 0; transform: translate(-50%, calc(-50% + 2px)) rotate(45deg); }
60
+ .pk-tooltip-content.bottom { position-area: block-end; margin: 6px 0 0; }
44
61
  .pk-tooltip-content.left {
45
- bottom: auto; left: auto; right: calc(100% + 6px); top: 50%;
46
- transform: translateY(-50%);
62
+ position-area: inline-start; margin: 0 6px 0 0;
63
+ position-try-fallbacks: flip-inline;
47
64
  }
48
- .pk-tooltip-content.left::after { top: 50%; left: 100%; transform: translate(calc(-50% - 2px), -50%) rotate(45deg); }
49
65
  .pk-tooltip-content.right {
50
- bottom: auto; right: auto; left: calc(100% + 6px); top: 50%;
51
- transform: translateY(-50%);
66
+ position-area: inline-end; margin: 0 0 0 6px;
67
+ position-try-fallbacks: flip-inline;
52
68
  }
53
- .pk-tooltip-content.right::after { top: 50%; left: 0; transform: translate(calc(-50% + 2px), -50%) rotate(45deg); }
54
69
  .pk-tooltip:hover .pk-tooltip-content,
55
70
  .pk-tooltip:focus-within .pk-tooltip-content { opacity: 1; visibility: visible; }
71
+ /* Escape-dismissed (WCAG 1.4.13): suppressed until pointer/focus leaves. */
72
+ .pk-tooltip[data-pk-dismissed]:hover .pk-tooltip-content,
73
+ .pk-tooltip[data-pk-dismissed]:focus-within .pk-tooltip-content { opacity: 0; visibility: hidden; }
@@ -2,6 +2,8 @@ module PhlexKit
2
2
  # Hover/focus tooltip. ruby_ui's version uses @floating-ui + a cloned template;
3
3
  # we keep the Trigger/Content structure but reveal + position the bubble with
4
4
  # pure CSS (no JS, no npm dep) — same call as the Select floating-ui strip.
5
+ # The tiny phlex-kit--tooltip controller only wires aria-describedby and
6
+ # Escape-dismiss (WCAG 1.4.13); reveal stays CSS.
5
7
  # Bubble sits above the trigger. Compose TooltipTrigger + TooltipContent.
6
8
  class Tooltip < BaseComponent
7
9
  def initialize(**attrs)
@@ -9,7 +11,7 @@ module PhlexKit
9
11
  end
10
12
 
11
13
  def view_template(&block)
12
- div(**mix({ class: "pk-tooltip" }, @attrs), &block)
14
+ div(**mix({ class: "pk-tooltip", data: { controller: "phlex-kit--tooltip" } }, @attrs), &block)
13
15
  end
14
16
  end
15
17
  end
@@ -10,7 +10,7 @@ module PhlexKit
10
10
  end
11
11
 
12
12
  def view_template(&)
13
- classes = [ "pk-tooltip-content", SIDES.fetch(@side) ].compact.join(" ")
13
+ classes = [ "pk-tooltip-content", fetch_option(SIDES, @side, :side) ].compact.join(" ")
14
14
  div(**mix({ class: classes, role: "tooltip" }, @attrs), &)
15
15
  end
16
16
  end
@@ -0,0 +1,32 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ // The tooltip stays CSS-revealed (:hover/:focus-within, tooltip.css) — this
4
+ // controller only adds what CSS can't: aria-describedby wiring from trigger
5
+ // to bubble, and WCAG 1.4.13 Escape-dismiss (data-pk-dismissed suppresses the
6
+ // bubble until the pointer/focus leaves the wrapper).
7
+ // Connects to data-controller="phlex-kit--tooltip"
8
+ export default class extends Controller {
9
+ connect() {
10
+ const content = this.element.querySelector(".pk-tooltip-content")
11
+ const trigger = this.element.querySelector(".pk-tooltip-trigger")
12
+ if (content && trigger) {
13
+ if (!content.id) content.id = `pk-tooltip-${Math.random().toString(36).slice(2, 8)}`
14
+ if (!trigger.hasAttribute("aria-describedby")) trigger.setAttribute("aria-describedby", content.id)
15
+ }
16
+
17
+ this._onKeydown = (e) => {
18
+ if (e.key !== "Escape") return
19
+ if (this.element.matches(":hover, :focus-within")) this.element.dataset.pkDismissed = ""
20
+ }
21
+ this._reset = () => delete this.element.dataset.pkDismissed
22
+ window.addEventListener("keydown", this._onKeydown)
23
+ this.element.addEventListener("pointerleave", this._reset)
24
+ this.element.addEventListener("focusout", this._reset)
25
+ }
26
+
27
+ disconnect() {
28
+ window.removeEventListener("keydown", this._onKeydown)
29
+ this.element.removeEventListener("pointerleave", this._reset)
30
+ this.element.removeEventListener("focusout", this._reset)
31
+ }
32
+ }
@@ -1,13 +1,18 @@
1
1
  module PhlexKit
2
2
  # The element a PhlexKit::Tooltip is anchored to. `tabindex: 0` so keyboard users can
3
- # focus it (the bubble shows on `:focus-within`). See tooltip.rb.
3
+ # focus it (the bubble shows on `:focus-within`) pass `focusable: false` when
4
+ # the trigger wraps something already focusable (a button, a link) so it
5
+ # doesn't add a redundant tab stop. See tooltip.rb.
4
6
  class TooltipTrigger < BaseComponent
5
- def initialize(**attrs)
7
+ def initialize(focusable: true, **attrs)
8
+ @focusable = focusable
6
9
  @attrs = attrs
7
10
  end
8
11
 
9
12
  def view_template(&block)
10
- span(**mix({ class: "pk-tooltip-trigger", tabindex: 0 }, @attrs), &block)
13
+ defaults = { class: "pk-tooltip-trigger" }
14
+ defaults[:tabindex] = 0 if @focusable
15
+ span(**mix(defaults, @attrs), &block)
11
16
  end
12
17
  end
13
18
  end
@@ -42,7 +42,7 @@ module PhlexKit
42
42
  private
43
43
 
44
44
  def classes
45
- [ "pk-text", "pk-text-#{SIZES.fetch(@size)}", WEIGHTS.fetch(@weight) ].compact.join(" ")
45
+ [ "pk-text", "pk-text-#{fetch_option(SIZES, @size, :size)}", fetch_option(WEIGHTS, @weight, :weight) ].compact.join(" ")
46
46
  end
47
47
  end
48
48
  end
@@ -47,6 +47,7 @@ import ToastController from "phlex_kit/controllers/toast_controller"
47
47
  import ToasterController from "phlex_kit/controllers/toaster_controller"
48
48
  import ToggleController from "phlex_kit/controllers/toggle_controller"
49
49
  import ToggleGroupController from "phlex_kit/controllers/toggle_group_controller"
50
+ import TooltipController from "phlex_kit/controllers/tooltip_controller"
50
51
 
51
52
  export function registerPhlexKitControllers(application) {
52
53
  application.register("phlex-kit--accordion", AccordionController)
@@ -88,4 +89,5 @@ export function registerPhlexKitControllers(application) {
88
89
  application.register("phlex-kit--toaster", ToasterController)
89
90
  application.register("phlex-kit--toggle", ToggleController)
90
91
  application.register("phlex-kit--toggle-group", ToggleGroupController)
92
+ application.register("phlex-kit--tooltip", TooltipController)
91
93
  }
@@ -24,6 +24,9 @@ module PhlexKit
24
24
  directory file_name, "app/components/phlex_kit/#{file_name}"
25
25
  end
26
26
 
27
+ # Belt-and-braces: the gem manifest's relative import already resolves to
28
+ # the host copy once app/components is ahead on the asset path, but hosts
29
+ # that skipped/trimmed the manifest still need an explicit import.
27
30
  def wire_import
28
31
  css = "app/assets/stylesheets/application.css"
29
32
  line = %(@import url("phlex_kit/#{file_name}/#{file_name}.css");\n)
@@ -67,6 +70,31 @@ module PhlexKit
67
70
 
68
71
  def confirm
69
72
  say_status :phlex_kit, "ejected phlex_kit/#{file_name} — your copy now shadows the gem (Ruby, CSS, JS)", :green
73
+ foreign_behavior_owners.each do |owner|
74
+ say_status :note,
75
+ "#{file_name}'s behavior lives in the #{owner} component's controller " \
76
+ "(still gem-owned) — `rails g phlex_kit:component #{owner}` to own it too",
77
+ :yellow
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ # Folders (other than the ejected one) whose Stimulus controllers this
84
+ # component's .rb files reference — e.g. date_picker rides calendar's,
85
+ # drawer rides sheet's. Editing the ejected copy won't change that
86
+ # behavior, so tell the user instead of overstating ownership.
87
+ def foreign_behavior_owners
88
+ root = self.class.source_root
89
+ identifiers = Dir.glob(File.join(root, file_name, "*.rb"))
90
+ .flat_map { |f| File.read(f).scan(/phlex-kit--([a-z0-9-]+)/) }
91
+ .flatten.uniq
92
+ identifiers.filter_map { |id|
93
+ controller = Dir.glob(File.join(root, "*", "#{id.tr("-", "_")}_controller.js")).first
94
+ next unless controller
95
+ owner = File.basename(File.dirname(controller))
96
+ owner == file_name ? nil : owner
97
+ }.uniq.sort
70
98
  end
71
99
  end
72
100
  end
@@ -5,6 +5,9 @@ PhlexKit.configure do |c|
5
5
  # set true to require it, false to force it off.
6
6
  c.reactive = :auto
7
7
 
8
+ # Default icon set for PhlexKit::Icon (:lucide, :phosphor, :tabler, :remix).
9
+ # c.icon_library = :lucide
10
+
8
11
  # Alias `UI` to `PhlexKit` so you can write `UI::Button` instead of
9
12
  # `PhlexKit::Button`. Off by default to avoid claiming a generic constant.
10
13
  c.define_ui_alias = false
@@ -18,5 +18,17 @@ module PhlexKit
18
18
  def on(*, **)
19
19
  {}
20
20
  end
21
+
22
+ private
23
+
24
+ # Fail-loud option lookup with an actionable message. Still a KeyError —
25
+ # the same class a bare VARIANTS.fetch raised — but names the component,
26
+ # the option, and the valid values instead of "key not found: :prmary".
27
+ def fetch_option(map, key, name)
28
+ map.fetch(key) do
29
+ raise KeyError, "#{self.class.name}: unknown #{name} #{key.inspect} — " \
30
+ "valid: #{map.keys.map(&:inspect).join(", ")}"
31
+ end
32
+ end
21
33
  end
22
34
  end
@@ -49,7 +49,17 @@ module PhlexKit
49
49
 
50
50
  # (5) Optional convenience alias so revue-style `UI::Button` keeps working.
51
51
  config.after_initialize do
52
- Object.const_set(:UI, PhlexKit) if PhlexKit.config.define_ui_alias && !Object.const_defined?(:UI)
52
+ if PhlexKit.config.define_ui_alias
53
+ if Object.const_defined?(:UI)
54
+ # Fail loud-ish: silently skipping reads as "alias mysteriously absent".
55
+ Rails.logger&.warn(
56
+ "PhlexKit: define_ui_alias is enabled but ::UI is already defined " \
57
+ "(#{Object.const_get(:UI).inspect}) — alias skipped."
58
+ )
59
+ else
60
+ Object.const_set(:UI, PhlexKit)
61
+ end
62
+ end
53
63
  end
54
64
  end
55
65
  end
@@ -83,7 +83,7 @@ module PhlexKit
83
83
  mic: [ [ :path, { d: "M12 19v3" } ], [ :path, { d: "M19 10v2a7 7 0 0 1-14 0v-2" } ], [ :rect, { rx: "3", x: "9", y: "2", width: "6", height: "13" } ] ],
84
84
  play: [ [ :path, { d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" } ] ],
85
85
  pause: [ [ :rect, { rx: "1", x: "14", y: "3", width: "5", height: "18" } ], [ :rect, { rx: "1", x: "5", y: "3", width: "5", height: "18" } ] ],
86
- grip: [ [ :circle, { cx: "9", cy: "12", r: "1" } ], [ :circle, { cx: "9", cy: "5", r: "1" } ], [ :circle, { cx: "9", cy: "19", r: "1" } ], [ :circle, { cx: "15", cy: "12", r: "1" } ], [ :circle, { cx: "15", cy: "5", r: "1" } ], [ :circle, { cx: "15", cy: "19", r: "1" } ] ],
86
+ grip: [ [ :circle, { cx: "9", cy: "12", r: "1" } ], [ :circle, { cx: "9", cy: "5", r: "1" } ], [ :circle, { cx: "9", cy: "19", r: "1" } ], [ :circle, { cx: "15", cy: "12", r: "1" } ], [ :circle, { cx: "15", cy: "5", r: "1" } ], [ :circle, { cx: "15", cy: "19", r: "1" } ] ]
87
87
  }.freeze
88
88
  end
89
89
  end
@@ -83,7 +83,7 @@ module PhlexKit
83
83
  mic: [ [ :path, { d: "M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V240a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z" } ] ],
84
84
  play: [ [ :path, { d: "M232.4,114.49,88.32,26.35a16,16,0,0,0-16.2-.3A15.86,15.86,0,0,0,64,39.87V216.13A15.94,15.94,0,0,0,80,232a16.07,16.07,0,0,0,8.36-2.35L232.4,141.51a15.81,15.81,0,0,0,0-27ZM80,215.94V40l143.83,88Z" } ] ],
85
85
  pause: [ [ :path, { d: "M200,32H160a16,16,0,0,0-16,16V208a16,16,0,0,0,16,16h40a16,16,0,0,0,16-16V48A16,16,0,0,0,200,32Zm0,176H160V48h40ZM96,32H56A16,16,0,0,0,40,48V208a16,16,0,0,0,16,16H96a16,16,0,0,0,16-16V48A16,16,0,0,0,96,32Zm0,176H56V48H96Z" } ] ],
86
- grip: [ [ :path, { d: "M104,60A12,12,0,1,1,92,48,12,12,0,0,1,104,60Zm60,12a12,12,0,1,0-12-12A12,12,0,0,0,164,72ZM92,116a12,12,0,1,0,12,12A12,12,0,0,0,92,116Zm72,0a12,12,0,1,0,12,12A12,12,0,0,0,164,116ZM92,184a12,12,0,1,0,12,12A12,12,0,0,0,92,184Zm72,0a12,12,0,1,0,12,12A12,12,0,0,0,164,184Z" } ] ],
86
+ grip: [ [ :path, { d: "M104,60A12,12,0,1,1,92,48,12,12,0,0,1,104,60Zm60,12a12,12,0,1,0-12-12A12,12,0,0,0,164,72ZM92,116a12,12,0,1,0,12,12A12,12,0,0,0,92,116Zm72,0a12,12,0,1,0,12,12A12,12,0,0,0,164,116ZM92,184a12,12,0,1,0,12,12A12,12,0,0,0,92,184Zm72,0a12,12,0,1,0,12,12A12,12,0,0,0,164,184Z" } ] ]
87
87
  }.freeze
88
88
  end
89
89
  end
@@ -83,7 +83,7 @@ module PhlexKit
83
83
  mic: [ [ :path, { d: "M11.9998 3C10.3429 3 8.99976 4.34315 8.99976 6V10C8.99976 11.6569 10.3429 13 11.9998 13C13.6566 13 14.9998 11.6569 14.9998 10V6C14.9998 4.34315 13.6566 3 11.9998 3ZM11.9998 1C14.7612 1 16.9998 3.23858 16.9998 6V10C16.9998 12.7614 14.7612 15 11.9998 15C9.23833 15 6.99976 12.7614 6.99976 10V6C6.99976 3.23858 9.23833 1 11.9998 1ZM3.05469 11H5.07065C5.55588 14.3923 8.47329 17 11.9998 17C15.5262 17 18.4436 14.3923 18.9289 11H20.9448C20.4837 15.1716 17.1714 18.4839 12.9998 18.9451V23H10.9998V18.9451C6.82814 18.4839 3.51584 15.1716 3.05469 11Z" } ] ],
84
84
  play: [ [ :path, { d: "M16.3944 12.0001L10 7.7371V16.263L16.3944 12.0001ZM19.376 12.4161L8.77735 19.4818C8.54759 19.635 8.23715 19.5729 8.08397 19.3432C8.02922 19.261 8 19.1645 8 19.0658V4.93433C8 4.65818 8.22386 4.43433 8.5 4.43433C8.59871 4.43433 8.69522 4.46355 8.77735 4.5183L19.376 11.584C19.6057 11.7372 19.6678 12.0477 19.5146 12.2774C19.478 12.3323 19.4309 12.3795 19.376 12.4161Z" } ] ],
85
85
  pause: [ [ :path, { d: "M6 5H8V19H6V5ZM16 5H18V19H16V5Z" } ] ],
86
- grip: [ [ :path, { d: "M8.5 7C9.32843 7 10 6.32843 10 5.5C10 4.67157 9.32843 4 8.5 4C7.67157 4 7 4.67157 7 5.5C7 6.32843 7.67157 7 8.5 7ZM8.5 13.5C9.32843 13.5 10 12.8284 10 12C10 11.1716 9.32843 10.5 8.5 10.5C7.67157 10.5 7 11.1716 7 12C7 12.8284 7.67157 13.5 8.5 13.5ZM10 18.5C10 19.3284 9.32843 20 8.5 20C7.67157 20 7 19.3284 7 18.5C7 17.6716 7.67157 17 8.5 17C9.32843 17 10 17.6716 10 18.5ZM15.5 7C16.3284 7 17 6.32843 17 5.5C17 4.67157 16.3284 4 15.5 4C14.6716 4 14 4.67157 14 5.5C14 6.32843 14.6716 7 15.5 7ZM17 12C17 12.8284 16.3284 13.5 15.5 13.5C14.6716 13.5 14 12.8284 14 12C14 11.1716 14.6716 10.5 15.5 10.5C16.3284 10.5 17 11.1716 17 12ZM15.5 20C16.3284 20 17 19.3284 17 18.5C17 17.6716 16.3284 17 15.5 17C14.6716 17 14 17.6716 14 18.5C14 19.3284 14.6716 20 15.5 20Z" } ] ],
86
+ grip: [ [ :path, { d: "M8.5 7C9.32843 7 10 6.32843 10 5.5C10 4.67157 9.32843 4 8.5 4C7.67157 4 7 4.67157 7 5.5C7 6.32843 7.67157 7 8.5 7ZM8.5 13.5C9.32843 13.5 10 12.8284 10 12C10 11.1716 9.32843 10.5 8.5 10.5C7.67157 10.5 7 11.1716 7 12C7 12.8284 7.67157 13.5 8.5 13.5ZM10 18.5C10 19.3284 9.32843 20 8.5 20C7.67157 20 7 19.3284 7 18.5C7 17.6716 7.67157 17 8.5 17C9.32843 17 10 17.6716 10 18.5ZM15.5 7C16.3284 7 17 6.32843 17 5.5C17 4.67157 16.3284 4 15.5 4C14.6716 4 14 4.67157 14 5.5C14 6.32843 14.6716 7 15.5 7ZM17 12C17 12.8284 16.3284 13.5 15.5 13.5C14.6716 13.5 14 12.8284 14 12C14 11.1716 14.6716 10.5 15.5 10.5C16.3284 10.5 17 11.1716 17 12ZM15.5 20C16.3284 20 17 19.3284 17 18.5C17 17.6716 16.3284 17 15.5 17C14.6716 17 14 17.6716 14 18.5C14 19.3284 14.6716 20 15.5 20Z" } ] ]
87
87
  }.freeze
88
88
  end
89
89
  end
@@ -83,7 +83,7 @@ module PhlexKit
83
83
  mic: [ [ :path, { d: "M9 5a3 3 0 0 1 3 -3a3 3 0 0 1 3 3v5a3 3 0 0 1 -3 3a3 3 0 0 1 -3 -3l0 -5" } ], [ :path, { d: "M5 10a7 7 0 0 0 14 0" } ], [ :path, { d: "M8 21l8 0" } ], [ :path, { d: "M12 17l0 4" } ] ],
84
84
  play: [ [ :path, { d: "M7 4v16l13 -8l-13 -8" } ] ],
85
85
  pause: [ [ :path, { d: "M6 6a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1l0 -12" } ], [ :path, { d: "M14 6a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1l0 -12" } ] ],
86
- grip: [ [ :path, { d: "M8 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M8 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M8 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M14 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M14 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M14 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ] ],
86
+ grip: [ [ :path, { d: "M8 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M8 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M8 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M14 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M14 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ], [ :path, { d: "M14 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" } ] ]
87
87
  }.freeze
88
88
  end
89
89
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PhlexKit
4
- VERSION = "0.5.1"
4
+ VERSION = "0.6.1"
5
5
  end
data/lib/phlex_kit.rb CHANGED
@@ -3,6 +3,11 @@
3
3
  require "json" # Carousel serializes its options value standalone (no Rails to_json)
4
4
  require "securerandom" # DatePicker generates a default input id
5
5
  require "cgi" # DataTable sort/pagination build query strings standalone
6
+ # phlex-rails references ActiveSupport::SafeBuffer at require time; in a Rails
7
+ # app it's already loaded, but a bare `require "phlex_kit"` (script/console)
8
+ # crashes with "uninitialized constant Phlex::ActiveSupport" without these.
9
+ require "active_support"
10
+ require "active_support/core_ext/string/output_safety"
6
11
  require "phlex"
7
12
  require "phlex-rails"
8
13
 
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.5.1
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Kennedy
@@ -582,6 +582,7 @@ files:
582
582
  - app/components/phlex_kit/tooltip/tooltip.css
583
583
  - app/components/phlex_kit/tooltip/tooltip.rb
584
584
  - app/components/phlex_kit/tooltip/tooltip_content.rb
585
+ - app/components/phlex_kit/tooltip/tooltip_controller.js
585
586
  - app/components/phlex_kit/tooltip/tooltip_trigger.rb
586
587
  - app/components/phlex_kit/typography/blockquote.rb
587
588
  - app/components/phlex_kit/typography/heading.rb