phlex_kit 0.2.1 → 0.2.2
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/README.md +81 -6
- data/THIRD_PARTY_LICENSES +104 -0
- data/app/assets/stylesheets/phlex_kit/_tokens.css +72 -0
- data/app/assets/stylesheets/phlex_kit/phlex_kit.css +1 -1
- data/app/assets/stylesheets/phlex_kit/themes/claude.css +86 -0
- data/app/assets/stylesheets/phlex_kit/themes/neutral.css +95 -0
- data/app/assets/stylesheets/phlex_kit/themes/zinc.css +85 -0
- data/app/components/phlex_kit/accordion/accordion.css +13 -0
- data/app/components/phlex_kit/accordion/accordion.rb +11 -2
- data/app/components/phlex_kit/accordion/accordion_icon.rb +1 -3
- data/app/components/phlex_kit/accordion/accordion_item.rb +5 -2
- data/app/components/phlex_kit/alert_dialog/alert_dialog.css +45 -4
- data/app/components/phlex_kit/alert_dialog/alert_dialog_action.rb +3 -2
- data/app/components/phlex_kit/alert_dialog/alert_dialog_content.rb +6 -2
- data/app/components/phlex_kit/alert_dialog/alert_dialog_media.rb +14 -0
- data/app/components/phlex_kit/attachment/attachment.css +102 -2
- data/app/components/phlex_kit/attachment/attachment.rb +18 -6
- data/app/components/phlex_kit/attachment/attachment_action.rb +1 -6
- data/app/components/phlex_kit/attachment/attachment_group.rb +21 -0
- data/app/components/phlex_kit/attachment/attachment_media.rb +13 -4
- data/app/components/phlex_kit/attachment/attachment_trigger.rb +22 -0
- data/app/components/phlex_kit/breadcrumb/breadcrumb_ellipsis.rb +1 -5
- data/app/components/phlex_kit/breadcrumb/breadcrumb_separator.rb +1 -1
- data/app/components/phlex_kit/calendar/calendar_next.rb +1 -15
- data/app/components/phlex_kit/calendar/calendar_prev.rb +1 -15
- data/app/components/phlex_kit/card/card.css +50 -14
- data/app/components/phlex_kit/card/card.rb +6 -2
- data/app/components/phlex_kit/card/card_action.rb +14 -0
- data/app/components/phlex_kit/carousel/carousel_next.rb +1 -15
- data/app/components/phlex_kit/carousel/carousel_previous.rb +1 -15
- data/app/components/phlex_kit/codeblock/codeblock.css +1 -1
- data/app/components/phlex_kit/combobox/combobox_clear_button.rb +1 -15
- data/app/components/phlex_kit/combobox/combobox_input_trigger.rb +1 -12
- data/app/components/phlex_kit/combobox/combobox_item_indicator.rb +1 -15
- data/app/components/phlex_kit/combobox/combobox_search_input.rb +1 -14
- data/app/components/phlex_kit/combobox/combobox_trigger.rb +1 -14
- data/app/components/phlex_kit/command/command.css +2 -2
- data/app/components/phlex_kit/command/command_input.rb +1 -13
- data/app/components/phlex_kit/context_menu/context_menu_item.rb +1 -3
- data/app/components/phlex_kit/data_table/data_table_column_toggle.rb +1 -13
- data/app/components/phlex_kit/data_table/data_table_expand_toggle.rb +1 -12
- data/app/components/phlex_kit/data_table/data_table_sort_head.rb +2 -28
- data/app/components/phlex_kit/dialog/dialog.css +8 -2
- data/app/components/phlex_kit/dialog/dialog_content.rb +1 -3
- data/app/components/phlex_kit/drawer/drawer.css +1 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu.css +64 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_checkbox_item.rb +31 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_group.rb +11 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_item.rb +5 -2
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_radio_group.rb +10 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_radio_item.rb +31 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_shortcut.rb +10 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub.rb +11 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub_content.rb +12 -0
- data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub_trigger.rb +18 -0
- data/app/components/phlex_kit/icon/icon.rb +51 -0
- data/app/components/phlex_kit/marker/marker.css +25 -0
- data/app/components/phlex_kit/marker/marker.rb +18 -0
- data/app/components/phlex_kit/marker/marker_content.rb +7 -0
- data/app/components/phlex_kit/marker/marker_icon.rb +7 -0
- data/app/components/phlex_kit/native_select/native_select_icon.rb +1 -10
- data/app/components/phlex_kit/navigation_menu/navigation_menu_trigger.rb +1 -5
- data/app/components/phlex_kit/pagination/pagination_ellipsis.rb +1 -5
- data/app/components/phlex_kit/select/select_item.rb +1 -11
- data/app/components/phlex_kit/select/select_trigger.rb +1 -14
- data/app/components/phlex_kit/sheet/sheet.css +1 -1
- data/app/components/phlex_kit/sheet/sheet_content.rb +1 -3
- data/app/components/phlex_kit/sidebar/sidebar.css +129 -11
- data/app/components/phlex_kit/sidebar/sidebar.rb +19 -6
- data/app/components/phlex_kit/sidebar/sidebar_group_action.rb +14 -0
- data/app/components/phlex_kit/sidebar/sidebar_group_content.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_group_label.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_input.rb +14 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_action.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_badge.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_skeleton.rb +19 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_sub.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_sub_button.rb +18 -0
- data/app/components/phlex_kit/sidebar/sidebar_menu_sub_item.rb +13 -0
- data/app/components/phlex_kit/sidebar/sidebar_separator.rb +14 -0
- data/app/components/phlex_kit/spinner/spinner.rb +5 -10
- data/app/components/phlex_kit/toast/toast_close.rb +1 -15
- data/app/components/phlex_kit/toast/toast_icon.rb +12 -36
- data/app/components/phlex_kit/typography/heading.rb +1 -1
- data/app/components/phlex_kit/typography/typography.css +7 -3
- data/app/javascript/phlex_kit/controllers/accordion_controller.js +17 -1
- data/app/javascript/phlex_kit/controllers/index.js +2 -0
- data/app/javascript/phlex_kit/controllers/scroll_fade_controller.js +31 -0
- data/lib/phlex_kit/configuration.rb +7 -0
- data/lib/phlex_kit/icons/lucide.rb +88 -0
- data/lib/phlex_kit/icons/phosphor.rb +88 -0
- data/lib/phlex_kit/icons/remix.rb +88 -0
- data/lib/phlex_kit/icons/tabler.rb +88 -0
- data/lib/phlex_kit/icons.rb +40 -0
- data/lib/phlex_kit/version.rb +1 -1
- data/lib/phlex_kit.rb +1 -0
- metadata +45 -8
- data/app/components/phlex_kit/stars/stars.css +0 -4
- data/app/components/phlex_kit/stars/stars.rb +0 -19
|
@@ -7,21 +7,7 @@ module PhlexKit
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def view_template
|
|
10
|
-
|
|
11
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
12
|
-
width: "24",
|
|
13
|
-
height: "24",
|
|
14
|
-
viewbox: "0 0 24 24",
|
|
15
|
-
fill: "none",
|
|
16
|
-
stroke: "currentColor",
|
|
17
|
-
"stroke-width": "2",
|
|
18
|
-
"stroke-linecap": "round",
|
|
19
|
-
"stroke-linejoin": "round",
|
|
20
|
-
class: "pk-combobox-item-indicator",
|
|
21
|
-
"aria-hidden": "true"
|
|
22
|
-
}, @attrs)) do |s|
|
|
23
|
-
s.path(d: "M20 6 9 17l-5-5")
|
|
24
|
-
end
|
|
10
|
+
render Icon.new(:check, size: 24, **mix({ class: "pk-combobox-item-indicator" }, @attrs))
|
|
25
11
|
end
|
|
26
12
|
end
|
|
27
13
|
end
|
|
@@ -30,20 +30,7 @@ module PhlexKit
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
32
|
def icon
|
|
33
|
-
|
|
34
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
35
|
-
viewbox: "0 0 24 24",
|
|
36
|
-
fill: "none",
|
|
37
|
-
stroke: "currentColor",
|
|
38
|
-
"stroke-width": "2",
|
|
39
|
-
"stroke-linecap": "round",
|
|
40
|
-
"stroke-linejoin": "round",
|
|
41
|
-
class: "pk-combobox-search-icon",
|
|
42
|
-
"aria-hidden": "true"
|
|
43
|
-
) do |s|
|
|
44
|
-
s.circle(cx: "11", cy: "11", r: "8")
|
|
45
|
-
s.path(d: "m21 21-4.3-4.3")
|
|
46
|
-
end
|
|
33
|
+
render Icon.new(:search, size: nil, class: "pk-combobox-search-icon")
|
|
47
34
|
end
|
|
48
35
|
end
|
|
49
36
|
end
|
|
@@ -29,20 +29,7 @@ module PhlexKit
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
31
|
def icon
|
|
32
|
-
|
|
33
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
34
|
-
viewbox: "0 0 24 24",
|
|
35
|
-
fill: "none",
|
|
36
|
-
stroke: "currentColor",
|
|
37
|
-
"stroke-width": "2",
|
|
38
|
-
"stroke-linecap": "round",
|
|
39
|
-
"stroke-linejoin": "round",
|
|
40
|
-
class: "pk-combobox-trigger-icon",
|
|
41
|
-
"aria-hidden": "true"
|
|
42
|
-
) do |s|
|
|
43
|
-
s.path(d: "m7 15 5 5 5-5")
|
|
44
|
-
s.path(d: "m7 9 5-5 5 5")
|
|
45
|
-
end
|
|
32
|
+
render Icon.new(:chevrons_up_down, size: nil, class: "pk-combobox-trigger-icon")
|
|
46
33
|
end
|
|
47
34
|
end
|
|
48
35
|
end
|
|
@@ -38,19 +38,7 @@ module PhlexKit
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
40
|
def search_icon
|
|
41
|
-
|
|
42
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
43
|
-
viewbox: "0 0 20 20",
|
|
44
|
-
fill: "currentColor",
|
|
45
|
-
class: "pk-command-input-icon",
|
|
46
|
-
"aria-hidden": "true"
|
|
47
|
-
) do |s|
|
|
48
|
-
s.path(
|
|
49
|
-
fill_rule: "evenodd",
|
|
50
|
-
d: "M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z",
|
|
51
|
-
clip_rule: "evenodd"
|
|
52
|
-
)
|
|
53
|
-
end
|
|
41
|
+
render Icon.new(:search, size: nil, class: "pk-command-input-icon")
|
|
54
42
|
end
|
|
55
43
|
end
|
|
56
44
|
end
|
|
@@ -12,9 +12,7 @@ module PhlexKit
|
|
|
12
12
|
data: { action: "click->phlex-kit--context-menu#close", phlex_kit__context_menu_target: "menuItem", disabled: @disabled } }, @attrs)) do
|
|
13
13
|
if @checked
|
|
14
14
|
span(class: "pk-context-menu-check") do
|
|
15
|
-
|
|
16
|
-
s.path(fill_rule: "evenodd", clip_rule: "evenodd", fill: "currentColor", d: "M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z")
|
|
17
|
-
end
|
|
15
|
+
render Icon.new(:check, size: 15)
|
|
18
16
|
end
|
|
19
17
|
end
|
|
20
18
|
yield
|
|
@@ -43,19 +43,7 @@ module PhlexKit
|
|
|
43
43
|
private
|
|
44
44
|
|
|
45
45
|
def chevron_icon
|
|
46
|
-
|
|
47
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
48
|
-
width: "16",
|
|
49
|
-
height: "16",
|
|
50
|
-
viewbox: "0 0 24 24",
|
|
51
|
-
fill: "none",
|
|
52
|
-
stroke: "currentColor",
|
|
53
|
-
"stroke-width": "2",
|
|
54
|
-
"stroke-linecap": "round",
|
|
55
|
-
"stroke-linejoin": "round",
|
|
56
|
-
class: "pk-data-table-column-toggle-icon",
|
|
57
|
-
"aria-hidden": "true"
|
|
58
|
-
) { |s| s.polyline(points: "6 9 12 15 18 9") }
|
|
46
|
+
render Icon.new(:chevron_down, class: "pk-data-table-column-toggle-icon")
|
|
59
47
|
end
|
|
60
48
|
end
|
|
61
49
|
end
|
|
@@ -23,18 +23,7 @@ module PhlexKit
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
25
|
def icon
|
|
26
|
-
|
|
27
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
28
|
-
width: "16",
|
|
29
|
-
height: "16",
|
|
30
|
-
viewbox: "0 0 24 24",
|
|
31
|
-
fill: "none",
|
|
32
|
-
stroke: "currentColor",
|
|
33
|
-
"stroke-width": "2",
|
|
34
|
-
"stroke-linecap": "round",
|
|
35
|
-
"stroke-linejoin": "round",
|
|
36
|
-
"aria-hidden": "true"
|
|
37
|
-
) { |s| s.polyline(points: "9 18 15 12 9 6") }
|
|
26
|
+
render Icon.new(:chevron_right)
|
|
38
27
|
end
|
|
39
28
|
end
|
|
40
29
|
end
|
|
@@ -49,34 +49,8 @@ module PhlexKit
|
|
|
49
49
|
|
|
50
50
|
def sort_icon
|
|
51
51
|
icon_class = [ "pk-data-table-sort-icon", (current_direction ? nil : "unsorted") ].compact.join(" ")
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
sort_svg(icon_class) { |s| s.polyline(points: "18 15 12 9 6 15") }
|
|
55
|
-
when "desc"
|
|
56
|
-
sort_svg(icon_class) { |s| s.polyline(points: "6 9 12 15 18 9") }
|
|
57
|
-
else
|
|
58
|
-
sort_svg(icon_class) do |s|
|
|
59
|
-
s.polyline(points: "8 15 12 19 16 15")
|
|
60
|
-
s.polyline(points: "8 9 12 5 16 9")
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def sort_svg(icon_class, &)
|
|
66
|
-
svg(
|
|
67
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
68
|
-
width: "12",
|
|
69
|
-
height: "12",
|
|
70
|
-
viewbox: "0 0 24 24",
|
|
71
|
-
fill: "none",
|
|
72
|
-
stroke: "currentColor",
|
|
73
|
-
"stroke-width": "2",
|
|
74
|
-
"stroke-linecap": "round",
|
|
75
|
-
"stroke-linejoin": "round",
|
|
76
|
-
class: icon_class,
|
|
77
|
-
"aria-hidden": "true",
|
|
78
|
-
&
|
|
79
|
-
)
|
|
52
|
+
glyph = { "asc" => :chevron_up, "desc" => :chevron_down }.fetch(current_direction, :chevrons_up_down)
|
|
53
|
+
render Icon.new(glyph, size: 12, class: icon_class)
|
|
80
54
|
end
|
|
81
55
|
end
|
|
82
56
|
end
|
|
@@ -8,14 +8,20 @@
|
|
|
8
8
|
padding: 1.5rem; box-shadow: 0 10px 30px rgb(0 0 0 / .35);
|
|
9
9
|
}
|
|
10
10
|
.pk-dialog[open] { display: flex; flex-direction: column; }
|
|
11
|
-
.pk-dialog::backdrop { background: rgb(0 0 0 / .5); backdrop-filter: blur(
|
|
11
|
+
.pk-dialog::backdrop { background: rgb(0 0 0 / .5); backdrop-filter: blur(8px); }
|
|
12
12
|
.pk-dialog.xs { max-width: 24rem; }
|
|
13
13
|
.pk-dialog.sm { max-width: 28rem; }
|
|
14
14
|
.pk-dialog.lg { max-width: 42rem; }
|
|
15
15
|
.pk-dialog.xl { max-width: 56rem; }
|
|
16
16
|
.pk-dialog.full { max-width: 100%; }
|
|
17
17
|
.pk-dialog-header { display: flex; flex-direction: column; gap: .375rem; text-align: center; }
|
|
18
|
-
.pk-dialog-footer {
|
|
18
|
+
.pk-dialog-footer {
|
|
19
|
+
display: flex; flex-direction: column-reverse; gap: .5rem;
|
|
20
|
+
margin: 0 calc(-1 * var(--pk-dialog-spacing)) calc(-1 * var(--pk-dialog-spacing));
|
|
21
|
+
padding: var(--pk-dialog-spacing);
|
|
22
|
+
border-top: 1px solid var(--pk-border);
|
|
23
|
+
background: color-mix(in oklab, var(--pk-surface-2) 50%, transparent);
|
|
24
|
+
}
|
|
19
25
|
.pk-dialog-title { font-size: 1.125rem; font-weight: 600; line-height: 1; letter-spacing: -.01em; }
|
|
20
26
|
.pk-dialog-description { font-size: .875rem; color: var(--pk-muted); }
|
|
21
27
|
.pk-dialog-middle { padding-block: 1rem; }
|
|
@@ -10,9 +10,7 @@ module PhlexKit
|
|
|
10
10
|
dialog(**mix({ class: cls, data: { phlex_kit__dialog_target: "dialog", action: "click->phlex-kit--dialog#backdropClick" } }, @attrs)) do
|
|
11
11
|
yield
|
|
12
12
|
button(type: "button", class: "pk-overlay-close", data: { action: "click->phlex-kit--dialog#dismiss" }) do
|
|
13
|
-
|
|
14
|
-
s.path(fill_rule: "evenodd", clip_rule: "evenodd", fill: "currentColor", d: "M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z")
|
|
15
|
-
end
|
|
13
|
+
render Icon.new(:x, size: 15)
|
|
16
14
|
span(class: "pk-sr-only") { "Close" }
|
|
17
15
|
end
|
|
18
16
|
end
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
.pk-dropdown-menu-item {
|
|
22
22
|
display: flex;
|
|
23
23
|
align-items: center;
|
|
24
|
+
gap: .5rem;
|
|
24
25
|
width: 100%;
|
|
25
26
|
cursor: pointer;
|
|
26
27
|
user-select: none;
|
|
@@ -36,3 +37,66 @@
|
|
|
36
37
|
|
|
37
38
|
.pk-dropdown-menu-label { margin: 0; padding: .375rem .5rem; font-size: .875rem; font-weight: 600; }
|
|
38
39
|
.pk-dropdown-menu-separator { height: 1px; margin: .25rem -.25rem; background: var(--pk-border); }
|
|
40
|
+
|
|
41
|
+
.pk-dropdown-menu-item > svg { flex: none; width: 1rem; height: 1rem; color: var(--pk-muted); }
|
|
42
|
+
.pk-dropdown-menu-group { display: contents; }
|
|
43
|
+
|
|
44
|
+
/* Destructive rows (irreversible actions). */
|
|
45
|
+
.pk-dropdown-menu-item.destructive { color: var(--pk-red); }
|
|
46
|
+
.pk-dropdown-menu-item.destructive > svg { color: var(--pk-red); }
|
|
47
|
+
.pk-dropdown-menu-item.destructive:hover,
|
|
48
|
+
.pk-dropdown-menu-item.destructive:focus {
|
|
49
|
+
background: color-mix(in oklab, var(--pk-red) 12%, transparent);
|
|
50
|
+
color: var(--pk-red);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Keyboard hint, pushed to the row's end. */
|
|
54
|
+
.pk-dropdown-menu-shortcut {
|
|
55
|
+
margin-left: auto;
|
|
56
|
+
font-size: .75rem;
|
|
57
|
+
letter-spacing: .1em;
|
|
58
|
+
color: var(--pk-muted);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Checkbox / radio rows: real (hidden) inputs; indicator shows via :has. */
|
|
62
|
+
.pk-dropdown-menu-item-input {
|
|
63
|
+
position: absolute;
|
|
64
|
+
width: 1px;
|
|
65
|
+
height: 1px;
|
|
66
|
+
opacity: 0;
|
|
67
|
+
pointer-events: none;
|
|
68
|
+
}
|
|
69
|
+
.pk-dropdown-menu-checkbox-item,
|
|
70
|
+
.pk-dropdown-menu-radio-item { position: relative; }
|
|
71
|
+
.pk-dropdown-menu-item-indicator {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
flex: none;
|
|
76
|
+
width: 1rem;
|
|
77
|
+
height: 1rem;
|
|
78
|
+
visibility: hidden;
|
|
79
|
+
}
|
|
80
|
+
.pk-dropdown-menu-item-indicator svg { width: .875rem; height: .875rem; }
|
|
81
|
+
.pk-dropdown-menu-radio-item .pk-dropdown-menu-item-indicator svg { width: .5rem; height: .5rem; }
|
|
82
|
+
.pk-dropdown-menu-item:has(.pk-dropdown-menu-item-input:checked) .pk-dropdown-menu-item-indicator { visibility: visible; }
|
|
83
|
+
.pk-dropdown-menu-item:has(.pk-dropdown-menu-item-input:focus-visible) {
|
|
84
|
+
box-shadow: 0 0 0 2px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Submenu: trigger row + side panel, revealed on hover/focus — pure CSS. */
|
|
88
|
+
.pk-dropdown-menu-sub { position: relative; }
|
|
89
|
+
.pk-dropdown-menu-sub-chevron { margin-left: auto; width: 1rem; height: 1rem; }
|
|
90
|
+
.pk-dropdown-menu-sub-content {
|
|
91
|
+
position: absolute;
|
|
92
|
+
top: -0.25rem;
|
|
93
|
+
left: 100%;
|
|
94
|
+
z-index: 60;
|
|
95
|
+
min-width: 10rem;
|
|
96
|
+
padding-left: .25rem; /* hover bridge over the gap */
|
|
97
|
+
display: none;
|
|
98
|
+
}
|
|
99
|
+
.pk-dropdown-menu-sub:hover > .pk-dropdown-menu-sub-content,
|
|
100
|
+
.pk-dropdown-menu-sub:focus-within > .pk-dropdown-menu-sub-content { display: block; }
|
|
101
|
+
.pk-dropdown-menu-sub:hover > .pk-dropdown-menu-sub-trigger,
|
|
102
|
+
.pk-dropdown-menu-sub:focus-within > .pk-dropdown-menu-sub-trigger { background: var(--pk-accent); }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Toggleable menu row, ported from shadcn/ui's DropdownMenuCheckboxItem: a
|
|
3
|
+
# <label> around a real (hidden) checkbox — clicking toggles it natively and
|
|
4
|
+
# the ✓ shows via CSS, without closing the menu (matching Radix). Pass
|
|
5
|
+
# `name:`/`value:` to submit the state with a form. See dropdown_menu.rb.
|
|
6
|
+
class DropdownMenuCheckboxItem < BaseComponent
|
|
7
|
+
def initialize(checked: false, name: nil, value: "1", **attrs)
|
|
8
|
+
@checked = checked
|
|
9
|
+
@name = name
|
|
10
|
+
@value = value
|
|
11
|
+
@attrs = attrs
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def view_template(&block)
|
|
15
|
+
label(**mix({
|
|
16
|
+
class: "pk-dropdown-menu-item pk-dropdown-menu-checkbox-item",
|
|
17
|
+
role: "menuitemcheckbox"
|
|
18
|
+
}, @attrs)) do
|
|
19
|
+
input(type: :checkbox, class: "pk-dropdown-menu-item-input", name: @name, value: @value, checked: @checked)
|
|
20
|
+
span(class: "pk-dropdown-menu-item-indicator", aria: { hidden: "true" }) { check_icon }
|
|
21
|
+
yield if block
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def check_icon
|
|
28
|
+
render Icon.new(:check, size: nil)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Grouping wrapper for related items (with an optional leading
|
|
3
|
+
# DropdownMenuLabel). Ported from shadcn/ui's DropdownMenuGroup.
|
|
4
|
+
# See dropdown_menu.rb.
|
|
5
|
+
class DropdownMenuGroup < BaseComponent
|
|
6
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
7
|
+
def view_template(&)
|
|
8
|
+
div(**mix({ class: "pk-dropdown-menu-group", role: "group" }, @attrs), &)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -2,9 +2,12 @@ module PhlexKit
|
|
|
2
2
|
# A menu row in a PhlexKit::DropdownMenuContent. `as: :a` (default, pass `href:`) or
|
|
3
3
|
# `:div` (e.g. to wrap a button_to). Closes the menu on click. See dropdown_menu.rb.
|
|
4
4
|
class DropdownMenuItem < BaseComponent
|
|
5
|
-
|
|
5
|
+
VARIANTS = { default: nil, destructive: "destructive" }.freeze
|
|
6
|
+
|
|
7
|
+
def initialize(as: :a, href: "#", variant: :default, **attrs)
|
|
6
8
|
@as = as.to_sym
|
|
7
9
|
@href = href
|
|
10
|
+
@variant = variant.to_sym
|
|
8
11
|
@attrs = attrs
|
|
9
12
|
end
|
|
10
13
|
|
|
@@ -12,7 +15,7 @@ module PhlexKit
|
|
|
12
15
|
base = {
|
|
13
16
|
role: "menuitem",
|
|
14
17
|
tabindex: "-1",
|
|
15
|
-
class: "pk-dropdown-menu-item",
|
|
18
|
+
class: [ "pk-dropdown-menu-item", VARIANTS.fetch(@variant) ].compact.join(" "),
|
|
16
19
|
data: { phlex_kit__dropdown_menu_target: "menuItem", action: "click->phlex-kit--dropdown-menu#close" }
|
|
17
20
|
}
|
|
18
21
|
base[:href] = @href unless @as == :div
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Exclusive-choice group of DropdownMenuRadioItems (share the same `name:`).
|
|
3
|
+
# Ported from shadcn/ui's DropdownMenuRadioGroup. See dropdown_menu.rb.
|
|
4
|
+
class DropdownMenuRadioGroup < BaseComponent
|
|
5
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
6
|
+
def view_template(&)
|
|
7
|
+
div(**mix({ class: "pk-dropdown-menu-group", role: "radiogroup" }, @attrs), &)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# One choice in a DropdownMenuRadioGroup — a <label> around a real (hidden)
|
|
3
|
+
# radio; the ● shows via CSS and picking doesn't close the menu (matching
|
|
4
|
+
# Radix). See dropdown_menu.rb.
|
|
5
|
+
class DropdownMenuRadioItem < BaseComponent
|
|
6
|
+
def initialize(name:, value:, checked: false, **attrs)
|
|
7
|
+
@name = name
|
|
8
|
+
@value = value
|
|
9
|
+
@checked = checked
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&block)
|
|
14
|
+
label(**mix({
|
|
15
|
+
class: "pk-dropdown-menu-item pk-dropdown-menu-radio-item",
|
|
16
|
+
role: "menuitemradio"
|
|
17
|
+
}, @attrs)) do
|
|
18
|
+
input(type: :radio, class: "pk-dropdown-menu-item-input", name: @name, value: @value, checked: @checked)
|
|
19
|
+
span(class: "pk-dropdown-menu-item-indicator", aria: { hidden: "true" }) do
|
|
20
|
+
# Deliberately NOT a PhlexKit::Icon: a filled selection dot is
|
|
21
|
+
# geometry, not icon-library vocabulary — it stays identical across
|
|
22
|
+
# icon_library settings (shadcn fills its Circle icon via CSS too).
|
|
23
|
+
svg(xmlns: "http://www.w3.org/2000/svg", viewbox: "0 0 24 24", fill: "currentColor") do |s|
|
|
24
|
+
s.circle(cx: "12", cy: "12", r: "5")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
yield if block
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Right-aligned keyboard hint in a menu row. Ported from shadcn/ui's
|
|
3
|
+
# DropdownMenuShortcut. See dropdown_menu.rb.
|
|
4
|
+
class DropdownMenuShortcut < BaseComponent
|
|
5
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
6
|
+
def view_template(&)
|
|
7
|
+
span(**mix({ class: "pk-dropdown-menu-shortcut" }, @attrs), &)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Nested submenu wrapper: SubTrigger row + SubContent panel, revealed on
|
|
3
|
+
# hover/focus with pure CSS (no extra controller). Ported from shadcn/ui's
|
|
4
|
+
# DropdownMenuSub. See dropdown_menu.rb.
|
|
5
|
+
class DropdownMenuSub < BaseComponent
|
|
6
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
7
|
+
def view_template(&)
|
|
8
|
+
div(**mix({ class: "pk-dropdown-menu-sub" }, @attrs), &)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# The nested panel of a DropdownMenuSub — opens beside the trigger.
|
|
3
|
+
# See dropdown_menu.rb.
|
|
4
|
+
class DropdownMenuSubContent < BaseComponent
|
|
5
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
6
|
+
def view_template(&)
|
|
7
|
+
div(**mix({ class: "pk-dropdown-menu-sub-content", role: "menu" }, @attrs)) do
|
|
8
|
+
div(class: "pk-dropdown-menu-viewport", &)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# The row that opens a DropdownMenuSub (trailing ▸ chevron). See
|
|
3
|
+
# dropdown_menu.rb.
|
|
4
|
+
class DropdownMenuSubTrigger < BaseComponent
|
|
5
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
6
|
+
def view_template(&block)
|
|
7
|
+
div(**mix({
|
|
8
|
+
class: "pk-dropdown-menu-item pk-dropdown-menu-sub-trigger",
|
|
9
|
+
role: "menuitem",
|
|
10
|
+
tabindex: "0",
|
|
11
|
+
aria: { haspopup: "menu" }
|
|
12
|
+
}, @attrs)) do
|
|
13
|
+
block&.call
|
|
14
|
+
render Icon.new(:chevron_right, size: nil, class: "pk-dropdown-menu-sub-chevron")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# A glyph from the configured icon library (ui.shadcn.com/create's "Icon
|
|
3
|
+
# Library" option): :lucide (default), :tabler, :phosphor, or :remix — set
|
|
4
|
+
# kit-wide via PhlexKit.config.icon_library or per-instance with `library:`.
|
|
5
|
+
# Names are PhlexKit's canonical semantic set (PhlexKit::Icons.names);
|
|
6
|
+
# unknown names/libraries fail loud (KeyError).
|
|
7
|
+
#
|
|
8
|
+
# `size:` emits width/height attributes (default 16, shadcn's size-4); pass
|
|
9
|
+
# `size: nil` to defer sizing to CSS (existing `.pk-x svg { … }` rules).
|
|
10
|
+
# Stroke libraries (lucide/tabler) render stroke="currentColor"; fill
|
|
11
|
+
# libraries (phosphor/remix) render fill="currentColor" — both follow text
|
|
12
|
+
# color. Attrs pass through via mix.
|
|
13
|
+
class Icon < BaseComponent
|
|
14
|
+
def initialize(name, library: nil, size: 16, **attrs)
|
|
15
|
+
@name = name
|
|
16
|
+
@library = library
|
|
17
|
+
@size = size
|
|
18
|
+
@attrs = attrs
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def view_template
|
|
22
|
+
icon = Icons.fetch(@name, library: @library || PhlexKit.config.icon_library)
|
|
23
|
+
svg(**mix(base_attrs(icon), @attrs)) do |s|
|
|
24
|
+
icon[:elements].each { |tag, attrs| s.public_send(tag, **attrs) }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def base_attrs(icon)
|
|
31
|
+
base = {
|
|
32
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
33
|
+
viewbox: icon[:view_box],
|
|
34
|
+
class: "pk-icon",
|
|
35
|
+
"aria-hidden": "true"
|
|
36
|
+
}
|
|
37
|
+
base[:width] = base[:height] = @size.to_s if @size
|
|
38
|
+
if icon[:mode] == :stroke
|
|
39
|
+
base.merge(
|
|
40
|
+
fill: "none",
|
|
41
|
+
stroke: "currentColor",
|
|
42
|
+
"stroke-width": icon[:stroke_width].to_s,
|
|
43
|
+
"stroke-linecap": "round",
|
|
44
|
+
"stroke-linejoin": "round"
|
|
45
|
+
)
|
|
46
|
+
else
|
|
47
|
+
base.merge(fill: "currentColor")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* Co-located with marker.rb — UI::Marker, shadcn parity: muted inline status,
|
|
2
|
+
bordered system-note row, or labeled separator in a conversation. */
|
|
3
|
+
.pk-marker {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: .5rem;
|
|
7
|
+
min-height: 1.25rem;
|
|
8
|
+
color: var(--pk-muted);
|
|
9
|
+
font-size: .875rem;
|
|
10
|
+
}
|
|
11
|
+
.pk-marker-icon { display: inline-flex; flex: none; }
|
|
12
|
+
.pk-marker-icon svg { width: 1rem; height: 1rem; }
|
|
13
|
+
.pk-marker-content { min-width: 0; }
|
|
14
|
+
.pk-marker.border {
|
|
15
|
+
border: 1px solid var(--pk-border);
|
|
16
|
+
border-radius: var(--pk-radius);
|
|
17
|
+
padding: .5rem .625rem;
|
|
18
|
+
}
|
|
19
|
+
.pk-marker.separator { justify-content: center; }
|
|
20
|
+
.pk-marker.separator::before, .pk-marker.separator::after {
|
|
21
|
+
content: "";
|
|
22
|
+
flex: 1;
|
|
23
|
+
height: 1px;
|
|
24
|
+
background: var(--pk-border);
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Conversation annotation, ported from shadcn/ui's Marker (an AI-chat-era
|
|
3
|
+
# addition): an inline status line, bordered system-note row, or labeled
|
|
4
|
+
# separator. Compose Marker(variant:) > [MarkerIcon +] MarkerContent.
|
|
5
|
+
# `.pk-marker*` (marker.css).
|
|
6
|
+
class Marker < BaseComponent
|
|
7
|
+
VARIANTS = { default: nil, border: "border", separator: "separator" }.freeze
|
|
8
|
+
|
|
9
|
+
def initialize(variant: :default, **attrs)
|
|
10
|
+
@variant = variant.to_sym
|
|
11
|
+
@attrs = attrs
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def view_template(&)
|
|
15
|
+
div(**mix({ class: [ "pk-marker", VARIANTS.fetch(@variant) ].compact.join(" ") }, @attrs), &)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -17,16 +17,7 @@ module PhlexKit
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
19
|
def chevron
|
|
20
|
-
|
|
21
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
22
|
-
viewbox: "0 0 24 24",
|
|
23
|
-
fill: "none",
|
|
24
|
-
stroke: "currentColor",
|
|
25
|
-
"stroke-width": "2",
|
|
26
|
-
"stroke-linecap": "round",
|
|
27
|
-
"stroke-linejoin": "round",
|
|
28
|
-
"aria-hidden": "true"
|
|
29
|
-
) { |s| s.path(d: "m6 9 6 6 6-6") }
|
|
20
|
+
render Icon.new(:chevron_down, size: nil)
|
|
30
21
|
end
|
|
31
22
|
end
|
|
32
23
|
end
|
|
@@ -17,11 +17,7 @@ module PhlexKit
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
19
|
def chevron
|
|
20
|
-
|
|
21
|
-
stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round",
|
|
22
|
-
"stroke-linejoin": "round", class: "pk-navigation-menu-chevron", "aria-hidden": "true") do |s|
|
|
23
|
-
s.path(d: "m6 9 6 6 6-6")
|
|
24
|
-
end
|
|
20
|
+
render Icon.new(:chevron_down, size: nil, class: "pk-navigation-menu-chevron")
|
|
25
21
|
end
|
|
26
22
|
end
|
|
27
23
|
end
|
|
@@ -4,11 +4,7 @@ module PhlexKit
|
|
|
4
4
|
def view_template
|
|
5
5
|
li do
|
|
6
6
|
span(**mix({ class: "pk-pagination-ellipsis", aria: { hidden: true } }, @attrs)) do
|
|
7
|
-
|
|
8
|
-
s.circle(cx: "12", cy: "12", r: "1")
|
|
9
|
-
s.circle(cx: "19", cy: "12", r: "1")
|
|
10
|
-
s.circle(cx: "5", cy: "12", r: "1")
|
|
11
|
-
end
|
|
7
|
+
render Icon.new(:ellipsis, size: nil)
|
|
12
8
|
span(class: "pk-sr-only") { "More pages" }
|
|
13
9
|
end
|
|
14
10
|
end
|