phlex_kit 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/phlex_kit/_tokens.css +16 -0
- data/app/assets/stylesheets/phlex_kit/phlex_kit.css +1 -0
- data/app/components/phlex_kit/alert/alert.css +60 -22
- data/app/components/phlex_kit/alert/alert.rb +11 -7
- data/app/components/phlex_kit/alert/alert_action.rb +14 -0
- data/app/components/phlex_kit/alert/alert_title.rb +3 -2
- data/app/components/phlex_kit/alert_dialog/alert_dialog.css +5 -2
- data/app/components/phlex_kit/avatar/avatar.css +80 -9
- data/app/components/phlex_kit/avatar/avatar.rb +6 -4
- data/app/components/phlex_kit/avatar/avatar_badge.rb +15 -0
- data/app/components/phlex_kit/avatar/avatar_group_count.rb +14 -0
- data/app/components/phlex_kit/badge/badge.css +61 -39
- data/app/components/phlex_kit/badge/badge.rb +24 -7
- data/app/components/phlex_kit/breadcrumb/breadcrumb.css +2 -2
- data/app/components/phlex_kit/bubble/bubble.css +49 -8
- data/app/components/phlex_kit/button/button.css +71 -26
- data/app/components/phlex_kit/button/button.rb +20 -13
- data/app/components/phlex_kit/button_group/button_group.css +59 -8
- data/app/components/phlex_kit/button_group/button_group.rb +11 -5
- data/app/components/phlex_kit/button_group/button_group_separator.rb +14 -0
- data/app/components/phlex_kit/button_group/button_group_text.rb +14 -0
- data/app/components/phlex_kit/calendar/calendar.css +66 -15
- data/app/components/phlex_kit/calendar/calendar.rb +87 -19
- data/app/components/phlex_kit/calendar/calendar_controller.js +226 -75
- data/app/components/phlex_kit/calendar/calendar_days.rb +7 -6
- data/app/components/phlex_kit/calendar/calendar_weekdays.rb +3 -1
- data/app/components/phlex_kit/checkbox/checkbox.css +52 -7
- data/app/components/phlex_kit/collapsible/collapsible.css +10 -1
- data/app/components/phlex_kit/collapsible/collapsible_controller.js +9 -2
- data/app/components/phlex_kit/combobox/combobox.css +37 -3
- data/app/components/phlex_kit/combobox/combobox.rb +3 -1
- data/app/components/phlex_kit/combobox/combobox_controller.js +9 -1
- data/app/components/phlex_kit/combobox/combobox_input_trigger.rb +7 -2
- data/app/components/phlex_kit/command/command.css +58 -22
- data/app/components/phlex_kit/command/command_controller.js +3 -1
- data/app/components/phlex_kit/command/command_input.rb +29 -25
- data/app/components/phlex_kit/command/command_separator.rb +18 -0
- data/app/components/phlex_kit/command/command_shortcut.rb +13 -0
- data/app/components/phlex_kit/context_menu/context_menu.css +78 -6
- data/app/components/phlex_kit/context_menu/context_menu_checkbox_item.rb +27 -0
- data/app/components/phlex_kit/context_menu/context_menu_group.rb +10 -0
- data/app/components/phlex_kit/context_menu/context_menu_item.rb +13 -3
- data/app/components/phlex_kit/context_menu/context_menu_radio_group.rb +10 -0
- data/app/components/phlex_kit/context_menu/context_menu_radio_item.rb +30 -0
- data/app/components/phlex_kit/context_menu/context_menu_shortcut.rb +11 -0
- data/app/components/phlex_kit/context_menu/context_menu_sub.rb +11 -0
- data/app/components/phlex_kit/context_menu/context_menu_sub_content.rb +12 -0
- data/app/components/phlex_kit/context_menu/context_menu_sub_trigger.rb +17 -0
- data/app/components/phlex_kit/dialog/dialog.css +34 -16
- data/app/components/phlex_kit/dialog/dialog_close.rb +13 -0
- data/app/components/phlex_kit/dialog/dialog_content.rb +13 -5
- data/app/components/phlex_kit/drawer/drawer.css +56 -17
- data/app/components/phlex_kit/drawer/drawer_content.rb +8 -4
- data/app/components/phlex_kit/empty/empty.css +13 -8
- data/app/components/phlex_kit/field/field.css +152 -0
- data/app/components/phlex_kit/field/field.rb +35 -0
- data/app/components/phlex_kit/field/field_content.rb +11 -0
- data/app/components/phlex_kit/field/field_description.rb +10 -0
- data/app/components/phlex_kit/field/field_error.rb +29 -0
- data/app/components/phlex_kit/field/field_group.rb +12 -0
- data/app/components/phlex_kit/field/field_label.rb +12 -0
- data/app/components/phlex_kit/field/field_legend.rb +17 -0
- data/app/components/phlex_kit/field/field_separator.rb +15 -0
- data/app/components/phlex_kit/field/field_set.rb +10 -0
- data/app/components/phlex_kit/field/field_title.rb +11 -0
- data/app/components/phlex_kit/hover_card/hover_card.css +12 -4
- data/app/components/phlex_kit/hover_card/hover_card_content.rb +11 -2
- data/app/components/phlex_kit/input/input.css +45 -11
- data/app/components/phlex_kit/input_group/input_group.css +88 -12
- data/app/components/phlex_kit/input_group/input_group_addon.rb +11 -4
- data/app/components/phlex_kit/input_group/input_group_button.rb +18 -0
- data/app/components/phlex_kit/input_otp/input_otp.css +29 -5
- data/app/components/phlex_kit/item/item.css +56 -8
- data/app/components/phlex_kit/item/item.rb +13 -3
- data/app/components/phlex_kit/item/item_footer.rb +8 -0
- data/app/components/phlex_kit/item/item_header.rb +8 -0
- data/app/components/phlex_kit/item/item_media.rb +13 -3
- data/app/components/phlex_kit/item/item_separator.rb +10 -0
- data/app/components/phlex_kit/kbd/kbd.css +10 -0
- data/app/components/phlex_kit/marker/marker.css +29 -9
- data/app/components/phlex_kit/marker/marker.rb +13 -3
- data/app/components/phlex_kit/menubar/menubar.css +88 -17
- data/app/components/phlex_kit/menubar/menubar_checkbox_item.rb +26 -0
- data/app/components/phlex_kit/menubar/menubar_group.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_item.rb +9 -4
- data/app/components/phlex_kit/menubar/menubar_label.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_radio_group.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_radio_item.rb +29 -0
- data/app/components/phlex_kit/menubar/menubar_shortcut.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_sub.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_sub_content.rb +11 -0
- data/app/components/phlex_kit/menubar/menubar_sub_trigger.rb +17 -0
- data/app/components/phlex_kit/message/message.css +8 -1
- data/app/components/phlex_kit/message_scroller/message_scroller.css +18 -0
- data/app/components/phlex_kit/native_select/native_select.css +25 -5
- data/app/components/phlex_kit/navigation_menu/navigation_menu.css +9 -6
- data/app/components/phlex_kit/pagination/pagination.css +10 -3
- data/app/components/phlex_kit/pagination/pagination_link.rb +21 -0
- data/app/components/phlex_kit/pagination/pagination_next.rb +20 -0
- data/app/components/phlex_kit/pagination/pagination_previous.rb +21 -0
- data/app/components/phlex_kit/popover/popover.css +15 -4
- data/app/components/phlex_kit/popover/popover_content.rb +11 -2
- data/app/components/phlex_kit/popover/popover_description.rb +10 -0
- data/app/components/phlex_kit/popover/popover_header.rb +10 -0
- data/app/components/phlex_kit/popover/popover_title.rb +10 -0
- data/app/components/phlex_kit/progress/progress.css +5 -5
- data/app/components/phlex_kit/radio_button/radio_button.css +46 -2
- data/app/components/phlex_kit/radio_group/radio_group.css +2 -2
- data/app/components/phlex_kit/resizable/resizable.css +17 -0
- data/app/components/phlex_kit/resizable/resizable_handle.rb +7 -3
- data/app/components/phlex_kit/select/select.css +46 -10
- data/app/components/phlex_kit/select/select_separator.rb +10 -0
- data/app/components/phlex_kit/select/select_trigger.rb +5 -2
- data/app/components/phlex_kit/sheet/sheet.css +18 -9
- data/app/components/phlex_kit/sheet/sheet_close.rb +10 -0
- data/app/components/phlex_kit/sheet/sheet_content.rb +8 -4
- data/app/components/phlex_kit/sidebar/sidebar.css +76 -9
- data/app/components/phlex_kit/sidebar/sidebar_controller.js +22 -7
- data/app/components/phlex_kit/sidebar/sidebar_menu_button.rb +8 -3
- data/app/components/phlex_kit/sidebar/sidebar_rail.rb +21 -0
- data/app/components/phlex_kit/sidebar/sidebar_wrapper.rb +25 -8
- data/app/components/phlex_kit/skeleton/skeleton.css +1 -1
- data/app/components/phlex_kit/slider/slider.css +33 -12
- data/app/components/phlex_kit/switch/switch.css +50 -13
- data/app/components/phlex_kit/switch/switch.rb +5 -2
- data/app/components/phlex_kit/table/table.css +8 -4
- data/app/components/phlex_kit/tabs/tabs.css +72 -8
- data/app/components/phlex_kit/tabs/tabs.rb +7 -2
- data/app/components/phlex_kit/tabs/tabs_list.rb +11 -2
- data/app/components/phlex_kit/textarea/textarea.css +33 -13
- data/app/components/phlex_kit/toast/toast.css +1 -1
- data/app/components/phlex_kit/toggle/toggle.css +25 -10
- data/app/components/phlex_kit/toggle_group/toggle_group.css +7 -6
- data/app/components/phlex_kit/tooltip/tooltip.css +37 -10
- data/app/components/phlex_kit/tooltip/tooltip_content.rb +9 -4
- data/lib/phlex_kit/version.rb +1 -1
- metadata +50 -1
|
@@ -7,30 +7,47 @@ module PhlexKit
|
|
|
7
7
|
# :offcanvas — the rail hides/reveals via a PhlexKit::SidebarTrigger. On
|
|
8
8
|
# desktop it slides out of the layout; below 768px it overlays
|
|
9
9
|
# as a fixed drawer behind a click-to-close scrim, closed by
|
|
10
|
-
# Escape and before Turbo caches the page.
|
|
11
|
-
#
|
|
10
|
+
# Escape and before Turbo caches the page.
|
|
11
|
+
# :icon — like :offcanvas, but on desktop the rail collapses to a
|
|
12
|
+
# 3rem icon strip instead of leaving: labels hide, menu
|
|
13
|
+
# buttons become icon squares (add `tooltip:` to
|
|
14
|
+
# SidebarMenuButton for hover labels), groups/badges/subs
|
|
15
|
+
# disappear. Mobile behaves like :offcanvas.
|
|
16
|
+
#
|
|
17
|
+
# State is DOM-only: `data-collapsed` (desktop) / `data-open` (mobile
|
|
18
|
+
# drawer). ⌘B/Ctrl+B toggles. The desktop state persists in a
|
|
19
|
+
# `pk_sidebar_state` cookie ("collapsed"/"expanded") — pass
|
|
20
|
+
# `default_collapsed: cookies[:pk_sidebar_state] == "collapsed"` from the
|
|
21
|
+
# host layout to render the collapsed rail server-side with no flash.
|
|
22
|
+
# Add a PhlexKit::SidebarRail inside the Sidebar for the edge grab-strip.
|
|
12
23
|
# See sidebar.rb.
|
|
13
24
|
class SidebarWrapper < BaseComponent
|
|
14
|
-
COLLAPSIBLE = { none: nil, offcanvas: "collapsible-offcanvas" }.freeze
|
|
25
|
+
COLLAPSIBLE = { none: nil, offcanvas: "collapsible-offcanvas", icon: "collapsible-icon" }.freeze
|
|
15
26
|
|
|
16
|
-
def initialize(collapsible: :none, **attrs)
|
|
27
|
+
def initialize(collapsible: :none, default_collapsed: false, **attrs)
|
|
17
28
|
@collapsible = collapsible.to_sym
|
|
29
|
+
@default_collapsed = default_collapsed
|
|
18
30
|
@attrs = attrs
|
|
19
31
|
end
|
|
20
32
|
|
|
21
33
|
def view_template(&block)
|
|
22
|
-
unless
|
|
34
|
+
unless collapsible?
|
|
23
35
|
return div(**mix({ class: classes }, @attrs), &block)
|
|
24
36
|
end
|
|
25
37
|
|
|
26
|
-
|
|
38
|
+
base = {
|
|
27
39
|
class: classes,
|
|
28
40
|
data: {
|
|
29
41
|
controller: "phlex-kit--sidebar",
|
|
30
42
|
action: "keydown.esc@window->phlex-kit--sidebar#closeMobile " \
|
|
43
|
+
"keydown.meta+b@window->phlex-kit--sidebar#toggle:prevent " \
|
|
44
|
+
"keydown.ctrl+b@window->phlex-kit--sidebar#toggle:prevent " \
|
|
31
45
|
"turbo:before-cache@window->phlex-kit--sidebar#closeMobile"
|
|
32
46
|
}
|
|
33
|
-
}
|
|
47
|
+
}
|
|
48
|
+
base["data-collapsed"] = "" if @default_collapsed
|
|
49
|
+
|
|
50
|
+
div(**mix(base, @attrs)) do
|
|
34
51
|
yield if block
|
|
35
52
|
button(type: :button, class: "pk-sidebar-scrim", aria_label: "Close sidebar",
|
|
36
53
|
tabindex: "-1", data: { action: "click->phlex-kit--sidebar#closeMobile" })
|
|
@@ -39,7 +56,7 @@ module PhlexKit
|
|
|
39
56
|
|
|
40
57
|
private
|
|
41
58
|
|
|
42
|
-
def
|
|
59
|
+
def collapsible? = @collapsible != :none
|
|
43
60
|
|
|
44
61
|
def classes
|
|
45
62
|
[ "pk-sidebar-wrapper", COLLAPSIBLE.fetch(@collapsible) ].compact.join(" ")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Co-located with skeleton.rb. Theme tokens come from the global stylesheet. */
|
|
2
2
|
.pk-skeleton {
|
|
3
3
|
border-radius: calc(var(--pk-radius) - 2px);
|
|
4
|
-
background:
|
|
4
|
+
background: var(--pk-surface-2);
|
|
5
5
|
animation: pk-pulse 1.5s cubic-bezier(.4, 0, .6, 1) infinite;
|
|
6
6
|
}
|
|
7
7
|
@keyframes pk-pulse { 50% { opacity: .5; } }
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
.pk-slider:focus-visible { outline: none; }
|
|
14
14
|
.pk-slider::-webkit-slider-runnable-track {
|
|
15
|
-
height: .
|
|
15
|
+
height: .25rem;
|
|
16
16
|
border-radius: 9999px;
|
|
17
17
|
background: linear-gradient(to right,
|
|
18
18
|
var(--pk-brand) var(--pk-slider-progress, 50%),
|
|
@@ -20,33 +20,54 @@
|
|
|
20
20
|
}
|
|
21
21
|
.pk-slider::-webkit-slider-thumb {
|
|
22
22
|
-webkit-appearance: none;
|
|
23
|
-
width:
|
|
24
|
-
height:
|
|
25
|
-
margin-top: -0.
|
|
23
|
+
width: .75rem;
|
|
24
|
+
height: .75rem;
|
|
25
|
+
margin-top: -0.25rem;
|
|
26
26
|
border-radius: 9999px;
|
|
27
|
-
border: 1px solid var(--pk-
|
|
28
|
-
background:
|
|
27
|
+
border: 1px solid var(--pk-ring);
|
|
28
|
+
background: #fff;
|
|
29
29
|
box-shadow: 0 1px 2px rgb(0 0 0 / .25);
|
|
30
30
|
transition: box-shadow .15s ease;
|
|
31
31
|
}
|
|
32
|
+
.pk-slider:hover::-webkit-slider-thumb,
|
|
33
|
+
.pk-slider:active::-webkit-slider-thumb {
|
|
34
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
35
|
+
}
|
|
32
36
|
.pk-slider:focus-visible::-webkit-slider-thumb {
|
|
33
37
|
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
34
38
|
}
|
|
35
39
|
.pk-slider::-moz-range-track {
|
|
36
|
-
height: .
|
|
40
|
+
height: .25rem;
|
|
37
41
|
border-radius: 9999px;
|
|
38
42
|
background: var(--pk-surface-2);
|
|
39
43
|
}
|
|
40
44
|
.pk-slider::-moz-range-progress {
|
|
41
|
-
height: .
|
|
45
|
+
height: .25rem;
|
|
42
46
|
border-radius: 9999px;
|
|
43
47
|
background: var(--pk-brand);
|
|
44
48
|
}
|
|
45
49
|
.pk-slider::-moz-range-thumb {
|
|
46
|
-
width:
|
|
47
|
-
height:
|
|
50
|
+
width: .75rem;
|
|
51
|
+
height: .75rem;
|
|
48
52
|
border-radius: 9999px;
|
|
49
|
-
border: 1px solid var(--pk-
|
|
50
|
-
background:
|
|
53
|
+
border: 1px solid var(--pk-ring);
|
|
54
|
+
background: #fff;
|
|
55
|
+
}
|
|
56
|
+
.pk-slider:hover::-moz-range-thumb,
|
|
57
|
+
.pk-slider:active::-moz-range-thumb {
|
|
58
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
51
59
|
}
|
|
52
60
|
.pk-slider:disabled { opacity: .5; cursor: not-allowed; }
|
|
61
|
+
/* Vertical: modern writing-mode flip (min-h-40 like theirs). */
|
|
62
|
+
.pk-slider.vertical {
|
|
63
|
+
writing-mode: vertical-lr;
|
|
64
|
+
direction: rtl;
|
|
65
|
+
width: 1rem;
|
|
66
|
+
height: auto;
|
|
67
|
+
min-height: 10rem;
|
|
68
|
+
}
|
|
69
|
+
.pk-slider.vertical::-webkit-slider-runnable-track {
|
|
70
|
+
background: linear-gradient(to top,
|
|
71
|
+
var(--pk-brand) var(--pk-slider-progress, 50%),
|
|
72
|
+
var(--pk-surface-2) var(--pk-slider-progress, 50%));
|
|
73
|
+
}
|
|
@@ -1,28 +1,65 @@
|
|
|
1
|
-
/* Co-located with switch.rb.
|
|
1
|
+
/* Co-located with switch.rb. Matched to shadcn/ui's current switch (nova):
|
|
2
|
+
32×18.4px track (sm 24×14) with a size-4 thumb sliding calc(100%-2px);
|
|
3
|
+
unchecked fills with the input tone (stronger in dark), checked with the
|
|
4
|
+
brand; the thumb is the page background in light, foreground in dark
|
|
5
|
+
(brand-ink when checked). Pure CSS (:has(:checked)), no Stimulus. */
|
|
2
6
|
.pk-switch {
|
|
7
|
+
position: relative;
|
|
3
8
|
display: inline-flex;
|
|
4
|
-
height:
|
|
5
|
-
width:
|
|
9
|
+
height: 18.4px;
|
|
10
|
+
width: 32px;
|
|
6
11
|
flex: none;
|
|
7
12
|
cursor: pointer;
|
|
8
13
|
align-items: center;
|
|
9
|
-
border:
|
|
14
|
+
border: 1px solid transparent;
|
|
10
15
|
border-radius: 9999px;
|
|
11
|
-
background: var(--pk-
|
|
12
|
-
transition: background-color .15s ease;
|
|
16
|
+
background: color-mix(in oklab, var(--pk-input) 80%, transparent);
|
|
17
|
+
transition: background-color .15s ease, box-shadow .15s ease;
|
|
13
18
|
}
|
|
19
|
+
.pk-switch.sm { height: 14px; width: 24px; }
|
|
14
20
|
.pk-switch:has(.pk-switch-input:checked) { background: var(--pk-brand); }
|
|
15
21
|
.pk-switch:has(.pk-switch-input:disabled) { cursor: not-allowed; opacity: .5; }
|
|
16
|
-
.pk-switch-input {
|
|
22
|
+
.pk-switch:has(.pk-switch-input:focus-visible) {
|
|
23
|
+
border-color: var(--pk-ring);
|
|
24
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
25
|
+
}
|
|
26
|
+
.pk-switch:has(.pk-switch-input[aria-invalid="true"]) {
|
|
27
|
+
border-color: var(--pk-red);
|
|
28
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 40%, transparent);
|
|
29
|
+
}
|
|
30
|
+
.pk-switch-input {
|
|
31
|
+
position: absolute;
|
|
32
|
+
width: 1px;
|
|
33
|
+
height: 1px;
|
|
34
|
+
opacity: 0;
|
|
35
|
+
pointer-events: none;
|
|
36
|
+
}
|
|
17
37
|
.pk-switch-thumb {
|
|
18
38
|
pointer-events: none;
|
|
19
39
|
display: block;
|
|
20
|
-
height:
|
|
21
|
-
width:
|
|
40
|
+
height: 1rem;
|
|
41
|
+
width: 1rem;
|
|
22
42
|
border-radius: 9999px;
|
|
23
|
-
background: var(--pk-
|
|
24
|
-
|
|
25
|
-
transition: transform .15s ease;
|
|
43
|
+
background: var(--pk-text);
|
|
44
|
+
transition: transform .15s ease, background-color .15s ease;
|
|
26
45
|
transform: translateX(0);
|
|
27
46
|
}
|
|
28
|
-
.pk-switch
|
|
47
|
+
.pk-switch.sm .pk-switch-thumb { height: .75rem; width: .75rem; }
|
|
48
|
+
.pk-switch:has(.pk-switch-input:checked) .pk-switch-thumb {
|
|
49
|
+
transform: translateX(calc(100% - 2px));
|
|
50
|
+
background: var(--pk-brand-ink);
|
|
51
|
+
}
|
|
52
|
+
:root[data-theme="light"] .pk-switch { background: var(--pk-input); }
|
|
53
|
+
:root[data-theme="light"] .pk-switch:has(.pk-switch-input:checked) { background: var(--pk-brand); }
|
|
54
|
+
:root[data-theme="light"] .pk-switch-thumb { background: var(--pk-bg) !important; }
|
|
55
|
+
:root[data-theme="light"] .pk-switch:has(.pk-switch-input[aria-invalid="true"]) {
|
|
56
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
57
|
+
}
|
|
58
|
+
@media (prefers-color-scheme: light) {
|
|
59
|
+
:root[data-theme="system"] .pk-switch { background: var(--pk-input); }
|
|
60
|
+
:root[data-theme="system"] .pk-switch:has(.pk-switch-input:checked) { background: var(--pk-brand); }
|
|
61
|
+
:root[data-theme="system"] .pk-switch-thumb { background: var(--pk-bg) !important; }
|
|
62
|
+
:root[data-theme="system"] .pk-switch:has(.pk-switch-input[aria-invalid="true"]) {
|
|
63
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -3,15 +3,18 @@ module PhlexKit
|
|
|
3
3
|
# ruby_ui's RubyUI::Switch — pure CSS (`:has(:checked)`), no Stimulus. Emits an
|
|
4
4
|
# optional hidden field so an unchecked box still posts a value (Rails idiom).
|
|
5
5
|
class Switch < BaseComponent
|
|
6
|
-
|
|
6
|
+
SIZES = { md: nil, sm: "sm" }.freeze
|
|
7
|
+
|
|
8
|
+
def initialize(include_hidden: true, checked_value: "1", unchecked_value: "0", size: :md, **attrs)
|
|
7
9
|
@include_hidden = include_hidden
|
|
8
10
|
@checked_value = checked_value
|
|
9
11
|
@unchecked_value = unchecked_value
|
|
12
|
+
@size = size.to_sym
|
|
10
13
|
@attrs = attrs
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def view_template
|
|
14
|
-
label(**mix({ role: "switch", class: "pk-switch" }, {})) do
|
|
17
|
+
label(**mix({ role: "switch", class: [ "pk-switch", SIZES.fetch(@size) ].compact.join(" ") }, {})) do
|
|
15
18
|
input(type: "hidden", name: @attrs[:name], value: @unchecked_value) if @include_hidden
|
|
16
19
|
input(**mix({ class: "pk-switch-input" }, @attrs).merge(type: "checkbox", value: @checked_value))
|
|
17
20
|
span(class: "pk-switch-thumb")
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
.pk-table-header .pk-table-row { border-bottom: 1px solid var(--pk-border); }
|
|
9
9
|
.pk-table-body .pk-table-row:last-child { border-bottom: 0; }
|
|
10
|
-
.pk-table-footer { border-top: 1px solid var(--pk-border); font-weight: 500; }
|
|
10
|
+
.pk-table-footer { border-top: 1px solid var(--pk-border); font-weight: 500; background: color-mix(in oklab, var(--pk-surface-2) 50%, transparent); }
|
|
11
11
|
|
|
12
12
|
.pk-table-row { border-bottom: 1px solid var(--pk-border); transition: background-color .12s ease; }
|
|
13
|
-
.pk-table-row:hover { background: color-mix(in oklab, var(--pk-surface-2)
|
|
13
|
+
.pk-table-row:hover { background: color-mix(in oklab, var(--pk-surface-2) 50%, transparent); }
|
|
14
|
+
.pk-table-row[data-state="selected"] { background: var(--pk-surface-2); }
|
|
14
15
|
|
|
15
16
|
.pk-table-head {
|
|
16
17
|
height: 2.5rem;
|
|
@@ -18,7 +19,10 @@
|
|
|
18
19
|
text-align: left;
|
|
19
20
|
vertical-align: middle;
|
|
20
21
|
font-weight: 500;
|
|
21
|
-
|
|
22
|
+
white-space: nowrap;
|
|
23
|
+
color: var(--pk-text);
|
|
22
24
|
}
|
|
23
|
-
.pk-table-
|
|
25
|
+
.pk-table-head:has([role="checkbox"]),
|
|
26
|
+
.pk-table-cell:has([role="checkbox"]) { padding-right: 0; }
|
|
27
|
+
.pk-table-cell { padding: .5rem; vertical-align: middle; white-space: nowrap; }
|
|
24
28
|
.pk-table-caption { margin-top: 1rem; font-size: .875rem; color: var(--pk-muted); }
|
|
@@ -1,13 +1,77 @@
|
|
|
1
|
-
/* Co-located with tabs.rb.
|
|
1
|
+
/* Co-located with tabs.rb. Matched to nova: h-8 list with 3px padding,
|
|
2
|
+
rounded-md px-1.5 triggers with data-icon insets and svg-4 glyphs, active
|
|
3
|
+
fill (translucent input in dark) with a soft shadow; the `line` variant is
|
|
4
|
+
transparent with an animated underline; `orientation: :vertical` stacks
|
|
5
|
+
the list (underline moves to the right edge). Tokens global. */
|
|
6
|
+
.pk-tabs { display: flex; flex-direction: column; gap: .5rem; }
|
|
7
|
+
.pk-tabs.vertical { flex-direction: row; align-items: flex-start; }
|
|
2
8
|
.pk-tabs-list {
|
|
3
|
-
display: inline-flex; height:
|
|
4
|
-
border-radius: var(--pk-radius); background: var(--pk-surface-2); padding:
|
|
9
|
+
display: inline-flex; width: fit-content; height: 2rem; align-items: center; justify-content: center;
|
|
10
|
+
border-radius: var(--pk-radius); background: var(--pk-surface-2); padding: 3px; color: var(--pk-muted);
|
|
5
11
|
}
|
|
12
|
+
.pk-tabs.vertical .pk-tabs-list { height: fit-content; flex-direction: column; align-items: stretch; }
|
|
6
13
|
.pk-tabs-trigger {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
position: relative;
|
|
15
|
+
display: inline-flex; align-items: center; justify-content: center; gap: .375rem;
|
|
16
|
+
white-space: nowrap; flex: 1;
|
|
17
|
+
border: 1px solid transparent;
|
|
18
|
+
border-radius: calc(var(--pk-radius) - 2px); padding: 2px .375rem; font-size: .875rem; font-weight: 500;
|
|
19
|
+
background: none; cursor: pointer; color: inherit; transition: all .15s ease;
|
|
20
|
+
}
|
|
21
|
+
.pk-tabs.vertical .pk-tabs-trigger { width: 100%; justify-content: flex-start; }
|
|
22
|
+
.pk-tabs-trigger:hover { color: var(--pk-text); }
|
|
23
|
+
.pk-tabs-trigger svg { pointer-events: none; flex-shrink: 0; }
|
|
24
|
+
.pk-tabs-trigger svg:not([class*="size-"]) { width: 1rem; height: 1rem; }
|
|
25
|
+
.pk-tabs-trigger:has(> [data-icon="inline-start"]) { padding-left: .25rem; }
|
|
26
|
+
.pk-tabs-trigger:has(> [data-icon="inline-end"]) { padding-right: .25rem; }
|
|
27
|
+
.pk-tabs-trigger:focus-visible {
|
|
28
|
+
outline: none;
|
|
29
|
+
border-color: var(--pk-ring);
|
|
30
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
31
|
+
}
|
|
32
|
+
.pk-tabs-trigger[data-state="active"] {
|
|
33
|
+
background: color-mix(in oklab, var(--pk-input) 30%, transparent);
|
|
34
|
+
border-color: var(--pk-input);
|
|
35
|
+
color: var(--pk-text);
|
|
36
|
+
box-shadow: 0 1px 2px rgb(0 0 0 / .1);
|
|
10
37
|
}
|
|
11
|
-
.pk-tabs-trigger[data-state="active"] { background: var(--pk-bg); color: var(--pk-text); box-shadow: 0 1px 2px rgb(0 0 0 / .1); }
|
|
12
38
|
.pk-tabs-trigger:disabled { pointer-events: none; opacity: .5; }
|
|
13
|
-
|
|
39
|
+
|
|
40
|
+
/* line variant: transparent list, underline on the active trigger. */
|
|
41
|
+
.pk-tabs-list.line { background: transparent; border-radius: 0; gap: .25rem; padding: 0; }
|
|
42
|
+
.pk-tabs-list.line .pk-tabs-trigger[data-state="active"] {
|
|
43
|
+
background: transparent;
|
|
44
|
+
border-color: transparent;
|
|
45
|
+
box-shadow: none;
|
|
46
|
+
}
|
|
47
|
+
.pk-tabs-trigger::after {
|
|
48
|
+
content: "";
|
|
49
|
+
position: absolute;
|
|
50
|
+
inset-inline: 0;
|
|
51
|
+
bottom: -5px;
|
|
52
|
+
height: 2px;
|
|
53
|
+
background: var(--pk-text);
|
|
54
|
+
opacity: 0;
|
|
55
|
+
transition: opacity .15s ease;
|
|
56
|
+
}
|
|
57
|
+
.pk-tabs.vertical .pk-tabs-trigger::after {
|
|
58
|
+
inset-inline: auto;
|
|
59
|
+
inset-block: 0;
|
|
60
|
+
right: -.25rem;
|
|
61
|
+
width: 2px;
|
|
62
|
+
height: auto;
|
|
63
|
+
}
|
|
64
|
+
.pk-tabs-list.line .pk-tabs-trigger[data-state="active"]::after { opacity: 1; }
|
|
65
|
+
|
|
66
|
+
.pk-tabs-content { flex: 1; font-size: .875rem; }
|
|
67
|
+
|
|
68
|
+
:root[data-theme="light"] .pk-tabs-trigger[data-state="active"] {
|
|
69
|
+
background: var(--pk-bg);
|
|
70
|
+
border-color: transparent;
|
|
71
|
+
}
|
|
72
|
+
@media (prefers-color-scheme: light) {
|
|
73
|
+
:root[data-theme="system"] .pk-tabs-trigger[data-state="active"] {
|
|
74
|
+
background: var(--pk-bg);
|
|
75
|
+
border-color: transparent;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -2,12 +2,17 @@ module PhlexKit
|
|
|
2
2
|
# Tabbed panels. Ported from ruby_ui's RubyUI::Tabs. Compose Tabs(default:) >
|
|
3
3
|
# (TabsList > TabsTrigger(value:)) + TabsContent(value:). phlex-kit--tabs.
|
|
4
4
|
class Tabs < BaseComponent
|
|
5
|
-
|
|
5
|
+
ORIENTATIONS = { horizontal: nil, vertical: "vertical" }.freeze
|
|
6
|
+
|
|
7
|
+
def initialize(default: nil, orientation: :horizontal, **attrs)
|
|
6
8
|
@default = default
|
|
9
|
+
@orientation = orientation.to_sym
|
|
7
10
|
@attrs = attrs
|
|
8
11
|
end
|
|
12
|
+
|
|
9
13
|
def view_template(&)
|
|
10
|
-
|
|
14
|
+
classes = [ "pk-tabs", ORIENTATIONS.fetch(@orientation) ].compact.join(" ")
|
|
15
|
+
div(**mix({ class: classes, data: { controller: "phlex-kit--tabs", phlex_kit__tabs_active_value: @default } }, @attrs), &)
|
|
11
16
|
end
|
|
12
17
|
end
|
|
13
18
|
end
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
2
|
class TabsList < BaseComponent
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
VARIANTS = { default: nil, line: "line" }.freeze
|
|
4
|
+
|
|
5
|
+
def initialize(variant: :default, **attrs)
|
|
6
|
+
@variant = variant.to_sym
|
|
7
|
+
@attrs = attrs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template(&)
|
|
11
|
+
classes = [ "pk-tabs-list", VARIANTS.fetch(@variant) ].compact.join(" ")
|
|
12
|
+
div(**mix({ class: classes, role: "tablist" }, @attrs), &)
|
|
13
|
+
end
|
|
5
14
|
end
|
|
6
15
|
end
|
|
@@ -1,27 +1,47 @@
|
|
|
1
|
-
/* Co-located with textarea.rb —
|
|
2
|
-
.pk-input (input.css):
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
stylesheet. */
|
|
1
|
+
/* Co-located with textarea.rb — PhlexKit::Textarea. Same nova treatment as
|
|
2
|
+
.pk-input (input.css): rounded-lg, translucent dark fill / transparent
|
|
3
|
+
light, ring focus, invalid + disabled states, text-base on small screens
|
|
4
|
+
(iOS zoom guard) dropping to text-sm from md. Height stays with `rows`. */
|
|
6
5
|
.pk-textarea {
|
|
7
6
|
width: 100%;
|
|
8
|
-
border-radius:
|
|
7
|
+
border-radius: var(--pk-radius);
|
|
9
8
|
border: 1px solid var(--pk-input);
|
|
10
|
-
background: var(--pk-
|
|
9
|
+
background: color-mix(in oklab, var(--pk-input) 30%, transparent);
|
|
11
10
|
color: var(--pk-text);
|
|
12
|
-
padding: .5rem .
|
|
11
|
+
padding: .5rem .625rem;
|
|
13
12
|
font: inherit;
|
|
14
|
-
font-size:
|
|
15
|
-
|
|
13
|
+
font-size: 1rem;
|
|
14
|
+
transition: color .15s ease, border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
|
|
15
|
+
}
|
|
16
|
+
@media (min-width: 768px) {
|
|
17
|
+
.pk-textarea { font-size: .875rem; }
|
|
16
18
|
}
|
|
17
19
|
.pk-textarea::placeholder { color: var(--pk-muted); }
|
|
18
20
|
.pk-textarea:focus-visible {
|
|
19
21
|
outline: none;
|
|
20
|
-
border-color: var(--pk-
|
|
22
|
+
border-color: var(--pk-ring);
|
|
21
23
|
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
22
24
|
}
|
|
23
|
-
.pk-textarea:disabled {
|
|
25
|
+
.pk-textarea:disabled {
|
|
26
|
+
cursor: not-allowed;
|
|
27
|
+
opacity: .5;
|
|
28
|
+
background: color-mix(in oklab, var(--pk-input) 80%, transparent);
|
|
29
|
+
}
|
|
24
30
|
.pk-textarea[aria-invalid="true"] {
|
|
31
|
+
border-color: color-mix(in oklab, var(--pk-red) 50%, transparent);
|
|
32
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 40%, transparent);
|
|
33
|
+
}
|
|
34
|
+
:root[data-theme="light"] .pk-textarea { background: transparent; }
|
|
35
|
+
:root[data-theme="light"] .pk-textarea:disabled { background: color-mix(in oklab, var(--pk-input) 50%, transparent); }
|
|
36
|
+
:root[data-theme="light"] .pk-textarea[aria-invalid="true"] {
|
|
25
37
|
border-color: var(--pk-red);
|
|
26
|
-
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red)
|
|
38
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
39
|
+
}
|
|
40
|
+
@media (prefers-color-scheme: light) {
|
|
41
|
+
:root[data-theme="system"] .pk-textarea { background: transparent; }
|
|
42
|
+
:root[data-theme="system"] .pk-textarea:disabled { background: color-mix(in oklab, var(--pk-input) 50%, transparent); }
|
|
43
|
+
:root[data-theme="system"] .pk-textarea[aria-invalid="true"] {
|
|
44
|
+
border-color: var(--pk-red);
|
|
45
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
46
|
+
}
|
|
27
47
|
}
|
|
@@ -1,16 +1,31 @@
|
|
|
1
|
-
/* Co-located with toggle.rb.
|
|
1
|
+
/* Co-located with toggle.rb. Matched to nova: h-8/h-7/h-9 sizes with min-w,
|
|
2
|
+
rounded-lg (sm capped), muted hover + on-state fills, gap-1, data-icon
|
|
3
|
+
insets, svg-4 (3.5 at sm). Theme tokens from the global stylesheet. */
|
|
2
4
|
.pk-contents { display: contents; }
|
|
3
5
|
.pk-toggle {
|
|
4
|
-
display: inline-flex; align-items: center; justify-content: center; gap: .
|
|
5
|
-
height:
|
|
6
|
-
border-radius:
|
|
6
|
+
display: inline-flex; align-items: center; justify-content: center; gap: .25rem;
|
|
7
|
+
height: 2rem; min-width: 2rem; padding-inline: .625rem;
|
|
8
|
+
border-radius: var(--pk-radius); border: 1px solid transparent; background: transparent;
|
|
7
9
|
font-size: .875rem; font-weight: 500; white-space: nowrap; color: var(--pk-text);
|
|
8
10
|
cursor: pointer; outline: none; transition: color .15s ease, background-color .15s ease, box-shadow .15s ease;
|
|
9
11
|
}
|
|
10
|
-
.pk-toggle:hover { background: var(--pk-
|
|
12
|
+
.pk-toggle:hover { background: var(--pk-surface-2); color: var(--pk-text); }
|
|
11
13
|
.pk-toggle:disabled { pointer-events: none; opacity: .5; }
|
|
12
|
-
.pk-toggle[data-state="on"] { background: var(--pk-surface-2); color: var(--pk-text); }
|
|
13
|
-
.pk-toggle
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
.pk-toggle[data-state="on"], .pk-toggle[aria-pressed="true"] { background: var(--pk-surface-2); color: var(--pk-text); }
|
|
15
|
+
.pk-toggle:focus-visible {
|
|
16
|
+
border-color: var(--pk-ring);
|
|
17
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
18
|
+
}
|
|
19
|
+
.pk-toggle.outline { border-color: var(--pk-input); }
|
|
20
|
+
.pk-toggle.sm {
|
|
21
|
+
height: 1.75rem; min-width: 1.75rem; font-size: .8rem;
|
|
22
|
+
border-radius: min(calc(var(--pk-radius) - 2px), 12px);
|
|
23
|
+
}
|
|
24
|
+
.pk-toggle.lg { height: 2.25rem; min-width: 2.25rem; }
|
|
25
|
+
.pk-toggle svg { pointer-events: none; flex-shrink: 0; }
|
|
26
|
+
.pk-toggle svg:not([class*="size-"]) { width: 1rem; height: 1rem; }
|
|
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; }
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/* Co-located with toggle_group.rb. Theme tokens from the global stylesheet. */
|
|
2
|
-
.pk-toggle-group { display: inline-flex; width: fit-content; align-items: center; border-radius:
|
|
2
|
+
.pk-toggle-group { display: inline-flex; width: fit-content; align-items: center; border-radius: var(--pk-radius); }
|
|
3
3
|
.pk-toggle-group.vertical { flex-direction: column; align-items: stretch; }
|
|
4
4
|
.pk-toggle-group.spaced { gap: .25rem; }
|
|
5
|
-
/* Joined (default, unspaced): square the inner corners. */
|
|
5
|
+
/* Joined (default, unspaced): square the inner corners, tighten padding. */
|
|
6
|
+
.pk-toggle-group:not(.spaced) .pk-toggle-group-item { padding-inline: .5rem; }
|
|
6
7
|
.pk-toggle-group:not(.spaced) .pk-toggle-group-item { border-radius: 0; }
|
|
7
|
-
.pk-toggle-group:not(.spaced):not(.vertical) .pk-toggle-group-item:first-of-type { border-top-left-radius:
|
|
8
|
-
.pk-toggle-group:not(.spaced):not(.vertical) .pk-toggle-group-item:last-of-type { border-top-right-radius:
|
|
9
|
-
.pk-toggle-group.vertical:not(.spaced) .pk-toggle-group-item:first-of-type { border-top-left-radius:
|
|
10
|
-
.pk-toggle-group.vertical:not(.spaced) .pk-toggle-group-item:last-of-type { border-bottom-left-radius:
|
|
8
|
+
.pk-toggle-group:not(.spaced):not(.vertical) .pk-toggle-group-item:first-of-type { border-top-left-radius: var(--pk-radius); border-bottom-left-radius: var(--pk-radius); }
|
|
9
|
+
.pk-toggle-group:not(.spaced):not(.vertical) .pk-toggle-group-item:last-of-type { border-top-right-radius: var(--pk-radius); border-bottom-right-radius: var(--pk-radius); }
|
|
10
|
+
.pk-toggle-group.vertical:not(.spaced) .pk-toggle-group-item:first-of-type { border-top-left-radius: var(--pk-radius); border-top-right-radius: var(--pk-radius); }
|
|
11
|
+
.pk-toggle-group.vertical:not(.spaced) .pk-toggle-group-item:last-of-type { border-bottom-left-radius: var(--pk-radius); border-bottom-right-radius: var(--pk-radius); }
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* Co-located with tooltip.rb — UI::Tooltip (+ Trigger / Content). CSS-only:
|
|
2
|
-
the bubble is absolutely positioned
|
|
3
|
-
|
|
4
|
-
|
|
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). */
|
|
5
6
|
.pk-tooltip { position: relative; display: inline-flex; }
|
|
6
7
|
.pk-tooltip-trigger { display: inline-flex; }
|
|
7
8
|
.pk-tooltip-content {
|
|
@@ -10,19 +11,45 @@
|
|
|
10
11
|
left: 50%;
|
|
11
12
|
transform: translateX(-50%);
|
|
12
13
|
z-index: 50;
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: .375rem;
|
|
13
17
|
width: max-content;
|
|
14
|
-
max-width: min(
|
|
15
|
-
padding: .
|
|
18
|
+
max-width: min(20rem, calc(100vw - 2rem));
|
|
19
|
+
padding: .375rem .75rem;
|
|
16
20
|
border-radius: calc(var(--pk-radius) - 2px);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
font-size: .8rem;
|
|
21
|
-
box-shadow: 0 4px 16px rgba(0, 0, 0, .4);
|
|
21
|
+
background: var(--pk-text);
|
|
22
|
+
color: var(--pk-bg);
|
|
23
|
+
font-size: .75rem;
|
|
22
24
|
opacity: 0;
|
|
23
25
|
visibility: hidden;
|
|
24
26
|
pointer-events: none;
|
|
25
27
|
transition: opacity .12s ease;
|
|
26
28
|
}
|
|
29
|
+
.pk-tooltip-content:has(kbd) { padding-right: .375rem; }
|
|
30
|
+
.pk-tooltip-content::after {
|
|
31
|
+
content: "";
|
|
32
|
+
position: absolute;
|
|
33
|
+
z-index: -1;
|
|
34
|
+
width: .625rem;
|
|
35
|
+
height: .625rem;
|
|
36
|
+
border-radius: 2px;
|
|
37
|
+
background: inherit;
|
|
38
|
+
top: 100%;
|
|
39
|
+
left: 50%;
|
|
40
|
+
transform: translate(-50%, calc(-50% - 2px)) rotate(45deg);
|
|
41
|
+
}
|
|
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); }
|
|
44
|
+
.pk-tooltip-content.left {
|
|
45
|
+
bottom: auto; left: auto; right: calc(100% + 6px); top: 50%;
|
|
46
|
+
transform: translateY(-50%);
|
|
47
|
+
}
|
|
48
|
+
.pk-tooltip-content.left::after { top: 50%; left: 100%; transform: translate(calc(-50% - 2px), -50%) rotate(45deg); }
|
|
49
|
+
.pk-tooltip-content.right {
|
|
50
|
+
bottom: auto; right: auto; left: calc(100% + 6px); top: 50%;
|
|
51
|
+
transform: translateY(-50%);
|
|
52
|
+
}
|
|
53
|
+
.pk-tooltip-content.right::after { top: 50%; left: 0; transform: translate(calc(-50% + 2px), -50%) rotate(45deg); }
|
|
27
54
|
.pk-tooltip:hover .pk-tooltip-content,
|
|
28
55
|
.pk-tooltip:focus-within .pk-tooltip-content { opacity: 1; visibility: visible; }
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
|
-
# The tooltip bubble
|
|
2
|
+
# The tooltip bubble. `side:` picks the edge it opens from (:top default,
|
|
3
|
+
# :bottom/:left/:right — shadcn's side prop). See tooltip.rb.
|
|
3
4
|
class TooltipContent < BaseComponent
|
|
4
|
-
|
|
5
|
+
SIDES = { top: nil, bottom: "bottom", left: "left", right: "right" }.freeze
|
|
6
|
+
|
|
7
|
+
def initialize(side: :top, **attrs)
|
|
8
|
+
@side = side.to_sym
|
|
5
9
|
@attrs = attrs
|
|
6
10
|
end
|
|
7
11
|
|
|
8
|
-
def view_template(&
|
|
9
|
-
|
|
12
|
+
def view_template(&)
|
|
13
|
+
classes = [ "pk-tooltip-content", SIDES.fetch(@side) ].compact.join(" ")
|
|
14
|
+
div(**mix({ class: classes, role: "tooltip" }, @attrs), &)
|
|
10
15
|
end
|
|
11
16
|
end
|
|
12
17
|
end
|
data/lib/phlex_kit/version.rb
CHANGED