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
|
@@ -36,17 +36,7 @@ module PhlexKit
|
|
|
36
36
|
private
|
|
37
37
|
|
|
38
38
|
def check_icon
|
|
39
|
-
|
|
40
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
41
|
-
viewbox: "0 0 24 24",
|
|
42
|
-
fill: "none",
|
|
43
|
-
stroke: "currentColor",
|
|
44
|
-
"stroke-width": "2",
|
|
45
|
-
"stroke-linecap": "round",
|
|
46
|
-
"stroke-linejoin": "round",
|
|
47
|
-
class: "pk-select-item-check",
|
|
48
|
-
"aria-hidden": "true"
|
|
49
|
-
) { |s| s.path(d: "M20 6 9 17l-5-5") }
|
|
39
|
+
render Icon.new(:check, size: nil, class: "pk-select-item-check")
|
|
50
40
|
end
|
|
51
41
|
end
|
|
52
42
|
end
|
|
@@ -23,20 +23,7 @@ module PhlexKit
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
25
|
def icon
|
|
26
|
-
|
|
27
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
28
|
-
viewbox: "0 0 24 24",
|
|
29
|
-
fill: "none",
|
|
30
|
-
stroke: "currentColor",
|
|
31
|
-
"stroke-width": "2",
|
|
32
|
-
"stroke-linecap": "round",
|
|
33
|
-
"stroke-linejoin": "round",
|
|
34
|
-
class: "pk-select-trigger-icon",
|
|
35
|
-
"aria-hidden": "true"
|
|
36
|
-
) do |s|
|
|
37
|
-
s.path(d: "m7 15 5 5 5-5")
|
|
38
|
-
s.path(d: "m7 9 5-5 5 5")
|
|
39
|
-
end
|
|
26
|
+
render Icon.new(:chevrons_up_down, size: nil, class: "pk-select-trigger-icon")
|
|
40
27
|
end
|
|
41
28
|
end
|
|
42
29
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Co-located with sheet.rb. Theme tokens from the global stylesheet. */
|
|
2
2
|
.pk-sheet-trigger { display: inline-block; }
|
|
3
|
-
.pk-sheet-backdrop { position: fixed; inset: 0; z-index: 50; background: rgb(0 0 0 / .5); backdrop-filter: blur(
|
|
3
|
+
.pk-sheet-backdrop { position: fixed; inset: 0; z-index: 50; background: rgb(0 0 0 / .5); backdrop-filter: blur(8px); }
|
|
4
4
|
.pk-sheet-content {
|
|
5
5
|
position: fixed; z-index: 50; display: flex; flex-direction: column; gap: 1rem;
|
|
6
6
|
background: var(--pk-surface); color: var(--pk-text); padding: 1.5rem;
|
|
@@ -12,9 +12,7 @@ module PhlexKit
|
|
|
12
12
|
div(**mix({ class: ["pk-sheet-content", SIDES.fetch(@side)].join(" ") }, @attrs)) do
|
|
13
13
|
block&.call
|
|
14
14
|
button(type: "button", class: "pk-overlay-close", data: { action: "click->phlex-kit--sheet-content#close" }) do
|
|
15
|
-
|
|
16
|
-
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")
|
|
17
|
-
end
|
|
15
|
+
render Icon.new(:x, size: 15)
|
|
18
16
|
span(class: "pk-sr-only") { "Close" }
|
|
19
17
|
end
|
|
20
18
|
end
|
|
@@ -1,23 +1,59 @@
|
|
|
1
1
|
/* Co-located with sidebar.rb — UI::Sidebar (+ Wrapper / Header / Content / Footer
|
|
2
|
-
/ Group /
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/ Group / GroupLabel / GroupContent / GroupAction / Menu / MenuItem / MenuButton
|
|
3
|
+
/ MenuAction / MenuBadge / MenuSkeleton / MenuSub / MenuSubItem / MenuSubButton
|
|
4
|
+
/ Separator / Input / Inset). ruby_ui's structure at collapsible:none, extended
|
|
5
|
+
to shadcn/ui's static part set; Tailwind → vanilla CSS on the palette tokens.
|
|
6
|
+
The collapsible/offcanvas/icon modes (+ their trigger/rail/Stimulus/cookie/
|
|
7
|
+
Sheet) are deliberately not ported.
|
|
8
|
+
Theme tokens come from the global stylesheet. The sidebar reads the shared
|
|
9
|
+
palette by default; --pk-sidebar[-text|-accent|-border|-primary|-primary-ink]
|
|
10
|
+
are optional per-role overrides (shadcn sidebar-* parity), undefined by
|
|
11
|
+
default. shadcn's sidebar-ring / sidebar-accent-foreground have no consumption
|
|
12
|
+
site here and stay unmapped. */
|
|
6
13
|
.pk-sidebar-wrapper { display: flex; min-height: 100dvh; width: 100%; }
|
|
7
14
|
|
|
8
15
|
.pk-sidebar {
|
|
9
16
|
display: flex;
|
|
10
17
|
flex-direction: column;
|
|
11
|
-
width:
|
|
18
|
+
width: 16rem;
|
|
12
19
|
flex-shrink: 0;
|
|
13
|
-
background: var(--pk-surface);
|
|
14
|
-
border-right: 1px solid var(--pk-border);
|
|
20
|
+
background: var(--pk-sidebar, var(--pk-surface));
|
|
21
|
+
border-right: 1px solid var(--pk-sidebar-border, var(--pk-border));
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
.pk-sidebar-header { display: flex; flex-direction: column; gap: .5rem; padding: 1rem .75rem .25rem; }
|
|
18
25
|
.pk-sidebar-content { display: flex; flex-direction: column; gap: .25rem; flex: 1; min-height: 0; overflow: auto; padding: .5rem .75rem; }
|
|
19
26
|
.pk-sidebar-footer { display: flex; flex-direction: column; gap: .5rem; padding: .75rem; }
|
|
20
|
-
.pk-sidebar-group { display: flex; flex-direction: column; gap: .125rem; }
|
|
27
|
+
.pk-sidebar-group { position: relative; display: flex; flex-direction: column; gap: .125rem; }
|
|
28
|
+
.pk-sidebar-group-label {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
flex-shrink: 0;
|
|
32
|
+
height: 2rem;
|
|
33
|
+
padding: 0 .5rem;
|
|
34
|
+
border-radius: calc(var(--pk-radius) - 2px);
|
|
35
|
+
font-size: .75rem;
|
|
36
|
+
font-weight: 500;
|
|
37
|
+
color: color-mix(in oklab, var(--pk-sidebar-text, var(--pk-text)) 70%, transparent);
|
|
38
|
+
}
|
|
39
|
+
.pk-sidebar-group-content { width: 100%; font-size: .875rem; }
|
|
40
|
+
.pk-sidebar-group-action {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: .375rem;
|
|
43
|
+
right: .25rem;
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
aspect-ratio: 1;
|
|
48
|
+
width: 1.25rem;
|
|
49
|
+
padding: 0;
|
|
50
|
+
border: 0;
|
|
51
|
+
border-radius: calc(var(--pk-radius) - 2px);
|
|
52
|
+
background: transparent;
|
|
53
|
+
color: var(--pk-sidebar-text, var(--pk-text));
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
}
|
|
56
|
+
.pk-sidebar-group-action:hover { background: var(--pk-sidebar-accent, var(--pk-accent)); }
|
|
21
57
|
|
|
22
58
|
.pk-sidebar-menu { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .125rem; width: 100%; }
|
|
23
59
|
.pk-sidebar-menu-item { position: relative; }
|
|
@@ -30,13 +66,95 @@
|
|
|
30
66
|
border: 0;
|
|
31
67
|
border-radius: 8px;
|
|
32
68
|
background: transparent;
|
|
33
|
-
color: var(--pk-text);
|
|
69
|
+
color: var(--pk-sidebar-text, var(--pk-text));
|
|
34
70
|
font: inherit;
|
|
35
71
|
font-size: .9rem;
|
|
36
72
|
text-align: left;
|
|
73
|
+
text-decoration: none;
|
|
37
74
|
cursor: pointer;
|
|
38
75
|
}
|
|
39
|
-
.pk-sidebar-menu-button:hover { background: var(--pk-accent); text-decoration: none; }
|
|
40
|
-
|
|
76
|
+
.pk-sidebar-menu-button:hover { background: var(--pk-sidebar-accent, var(--pk-accent)); text-decoration: none; }
|
|
77
|
+
/* Active item: shadcn's stock "Default" menu look (accent tint). The create
|
|
78
|
+
page's "Solid" mode is the .menu-solid modifier on the sidebar root. */
|
|
79
|
+
.pk-sidebar-menu-button[data-active="true"] { background: var(--pk-sidebar-accent, var(--pk-accent)); color: var(--pk-sidebar-text, var(--pk-text)); font-weight: 500; }
|
|
80
|
+
.pk-sidebar.menu-solid .pk-sidebar-menu-button[data-active="true"] { background: var(--pk-sidebar-primary, var(--pk-brand)); color: var(--pk-sidebar-primary-ink, var(--pk-brand-ink)); font-weight: 600; }
|
|
81
|
+
|
|
82
|
+
/* Anchored to the top of the item (the button row) — the item may also
|
|
83
|
+
contain a submenu, so 50%-of-item would drift. */
|
|
84
|
+
.pk-sidebar-menu-action {
|
|
85
|
+
position: absolute;
|
|
86
|
+
top: .5rem;
|
|
87
|
+
right: .25rem;
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
aspect-ratio: 1;
|
|
92
|
+
width: 1.25rem;
|
|
93
|
+
padding: 0;
|
|
94
|
+
border: 0;
|
|
95
|
+
border-radius: calc(var(--pk-radius) - 2px);
|
|
96
|
+
background: transparent;
|
|
97
|
+
color: var(--pk-sidebar-text, var(--pk-text));
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
}
|
|
100
|
+
.pk-sidebar-menu-action:hover { background: var(--pk-sidebar-accent, var(--pk-accent)); }
|
|
101
|
+
.pk-sidebar-menu-badge {
|
|
102
|
+
pointer-events: none;
|
|
103
|
+
user-select: none;
|
|
104
|
+
position: absolute;
|
|
105
|
+
top: .5rem;
|
|
106
|
+
right: .25rem;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
height: 1.25rem;
|
|
111
|
+
min-width: 1.25rem;
|
|
112
|
+
padding: 0 .25rem;
|
|
113
|
+
border-radius: calc(var(--pk-radius) - 2px);
|
|
114
|
+
font-size: .75rem;
|
|
115
|
+
font-weight: 500;
|
|
116
|
+
font-variant-numeric: tabular-nums;
|
|
117
|
+
color: var(--pk-sidebar-text, var(--pk-text));
|
|
118
|
+
}
|
|
119
|
+
.pk-sidebar-menu-skeleton { display: flex; align-items: center; gap: .5rem; height: 2rem; padding: 0 .5rem; border-radius: calc(var(--pk-radius) - 2px); }
|
|
120
|
+
.pk-sidebar-menu-skeleton .pk-skeleton.icon { width: 1rem; height: 1rem; flex-shrink: 0; }
|
|
121
|
+
.pk-sidebar-menu-skeleton .pk-skeleton.text { height: 1rem; flex: 1; max-width: var(--pk-skeleton-width, 70%); }
|
|
122
|
+
|
|
123
|
+
.pk-sidebar-menu-sub {
|
|
124
|
+
list-style: none;
|
|
125
|
+
display: flex;
|
|
126
|
+
flex-direction: column;
|
|
127
|
+
gap: .25rem;
|
|
128
|
+
min-width: 0;
|
|
129
|
+
margin: 0 .875rem;
|
|
130
|
+
padding: .125rem .625rem;
|
|
131
|
+
border-left: 1px solid var(--pk-sidebar-border, var(--pk-border));
|
|
132
|
+
}
|
|
133
|
+
.pk-sidebar-menu-sub-item { position: relative; }
|
|
134
|
+
.pk-sidebar-menu-sub-button {
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
gap: .5rem;
|
|
138
|
+
height: 1.75rem;
|
|
139
|
+
min-width: 0;
|
|
140
|
+
width: 100%;
|
|
141
|
+
padding: 0 .5rem;
|
|
142
|
+
overflow: hidden;
|
|
143
|
+
border: 0;
|
|
144
|
+
border-radius: calc(var(--pk-radius) - 2px);
|
|
145
|
+
background: transparent;
|
|
146
|
+
color: var(--pk-sidebar-text, var(--pk-text));
|
|
147
|
+
font: inherit;
|
|
148
|
+
font-size: .875rem;
|
|
149
|
+
text-align: left;
|
|
150
|
+
text-decoration: none;
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
}
|
|
153
|
+
.pk-sidebar-menu-sub-button:hover { background: var(--pk-sidebar-accent, var(--pk-accent)); text-decoration: none; }
|
|
154
|
+
/* shadcn highlights active sub-items with the accent role, not primary. */
|
|
155
|
+
.pk-sidebar-menu-sub-button[data-active="true"] { background: var(--pk-sidebar-accent, var(--pk-accent)); font-weight: 600; }
|
|
156
|
+
|
|
157
|
+
.pk-separator.pk-sidebar-separator { width: auto; margin: 0 .5rem; background: var(--pk-sidebar-border, var(--pk-border)); }
|
|
158
|
+
.pk-input.pk-sidebar-input { height: 2rem; width: 100%; background: var(--pk-bg); box-shadow: none; }
|
|
41
159
|
|
|
42
160
|
.pk-sidebar-inset { flex: 1; min-width: 0; }
|
|
@@ -3,17 +3,30 @@ module PhlexKit
|
|
|
3
3
|
# (the NonCollapsibleSidebar path): a static flex-column rail. Deliberately
|
|
4
4
|
# WITHOUT ruby_ui's collapsible/offcanvas Stimulus controller + cookie state +
|
|
5
5
|
# mobile Sheet (the admin sidebar is static) — those can be added later if a
|
|
6
|
-
# collapsible chrome is wanted.
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# collapsible chrome is wanted. The static part set matches shadcn/ui's Sidebar:
|
|
7
|
+
# compose with SidebarWrapper (the page root) + SidebarHeader / SidebarContent /
|
|
8
|
+
# SidebarGroup (+ Label / Content / Action) / SidebarMenu (+ Item / Button /
|
|
9
|
+
# Action / Badge / Skeleton / Sub / SubItem / SubButton) / SidebarSeparator /
|
|
10
|
+
# SidebarInput / SidebarFooter, alongside a SidebarInset for the main content.
|
|
11
|
+
# Tailwind → vanilla `.pk-sidebar*` (sidebar.css).
|
|
10
12
|
class Sidebar < BaseComponent
|
|
11
|
-
|
|
13
|
+
# Active-item treatment (ui.shadcn.com/create's "Menu" option): :default is
|
|
14
|
+
# shadcn's stock accent tint; :solid fills with the primary/brand role.
|
|
15
|
+
MENUS = { default: nil, solid: "menu-solid" }.freeze
|
|
16
|
+
|
|
17
|
+
def initialize(menu: :default, **attrs)
|
|
18
|
+
@menu = menu.to_sym
|
|
12
19
|
@attrs = attrs
|
|
13
20
|
end
|
|
14
21
|
|
|
15
22
|
def view_template(&block)
|
|
16
|
-
div(**mix({ class:
|
|
23
|
+
div(**mix({ class: classes }, @attrs), &block)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def classes
|
|
29
|
+
[ "pk-sidebar", MENUS.fetch(@menu) ].compact.join(" ")
|
|
17
30
|
end
|
|
18
31
|
end
|
|
19
32
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Action button pinned to the top-right of a SidebarGroup (the group is the
|
|
3
|
+
# positioning context), ported from shadcn/ui's SidebarGroupAction.
|
|
4
|
+
# See sidebar.rb.
|
|
5
|
+
class SidebarGroupAction < BaseComponent
|
|
6
|
+
def initialize(**attrs)
|
|
7
|
+
@attrs = attrs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template(&block)
|
|
11
|
+
button(**mix({ class: "pk-sidebar-group-action", type: "button" }, @attrs), &block)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Content container within a SidebarGroup, ported from shadcn/ui's
|
|
3
|
+
# SidebarGroupContent. See sidebar.rb.
|
|
4
|
+
class SidebarGroupContent < BaseComponent
|
|
5
|
+
def initialize(**attrs)
|
|
6
|
+
@attrs = attrs
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def view_template(&block)
|
|
10
|
+
div(**mix({ class: "pk-sidebar-group-content" }, @attrs), &block)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Heading for a SidebarGroup, ported from shadcn/ui's SidebarGroupLabel.
|
|
3
|
+
# See sidebar.rb.
|
|
4
|
+
class SidebarGroupLabel < BaseComponent
|
|
5
|
+
def initialize(**attrs)
|
|
6
|
+
@attrs = attrs
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def view_template(&block)
|
|
10
|
+
div(**mix({ class: "pk-sidebar-group-label" }, @attrs), &block)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Search/filter input for the sidebar, ported from shadcn/ui's SidebarInput:
|
|
3
|
+
# the kit Input on the page background, sized to the sidebar's control height.
|
|
4
|
+
# See sidebar.rb.
|
|
5
|
+
class SidebarInput < BaseComponent
|
|
6
|
+
def initialize(**attrs)
|
|
7
|
+
@attrs = attrs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template
|
|
11
|
+
render Input.new(**mix({ class: "pk-sidebar-input" }, @attrs))
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Secondary action pinned to the right edge of a SidebarMenuItem (the item is
|
|
3
|
+
# position:relative), ported from shadcn/ui's SidebarMenuAction. See sidebar.rb.
|
|
4
|
+
class SidebarMenuAction < BaseComponent
|
|
5
|
+
def initialize(**attrs)
|
|
6
|
+
@attrs = attrs
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def view_template(&block)
|
|
10
|
+
button(**mix({ class: "pk-sidebar-menu-action", type: "button" }, @attrs), &block)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Non-interactive count/status pinned to the right edge of a SidebarMenuItem,
|
|
3
|
+
# ported from shadcn/ui's SidebarMenuBadge. See sidebar.rb.
|
|
4
|
+
class SidebarMenuBadge < BaseComponent
|
|
5
|
+
def initialize(**attrs)
|
|
6
|
+
@attrs = attrs
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def view_template(&block)
|
|
10
|
+
span(**mix({ class: "pk-sidebar-menu-badge" }, @attrs), &block)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Loading placeholder for a menu item, ported from shadcn/ui's
|
|
3
|
+
# SidebarMenuSkeleton: an optional icon shimmer plus a text shimmer whose
|
|
4
|
+
# width randomises 50–90% per render (their --skeleton-width trick).
|
|
5
|
+
# `show_icon:` mirrors shadcn's showIcon. See sidebar.rb.
|
|
6
|
+
class SidebarMenuSkeleton < BaseComponent
|
|
7
|
+
def initialize(show_icon: false, **attrs)
|
|
8
|
+
@show_icon = show_icon
|
|
9
|
+
@attrs = attrs
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template
|
|
13
|
+
div(**mix({ class: "pk-sidebar-menu-skeleton" }, @attrs)) do
|
|
14
|
+
render Skeleton.new(class: "icon") if @show_icon
|
|
15
|
+
render Skeleton.new(class: "text", style: "--pk-skeleton-width: #{rand(50..90)}%;")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Nested submenu list under a SidebarMenuItem, ported from shadcn/ui's
|
|
3
|
+
# SidebarMenuSub. See sidebar.rb.
|
|
4
|
+
class SidebarMenuSub < BaseComponent
|
|
5
|
+
def initialize(**attrs)
|
|
6
|
+
@attrs = attrs
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def view_template(&block)
|
|
10
|
+
ul(**mix({ class: "pk-sidebar-menu-sub" }, @attrs), &block)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# The interactive element of a SidebarMenuSubItem, ported from shadcn/ui's
|
|
3
|
+
# SidebarMenuSubButton. `as:` is :a (their default — pass `href:`) or :button;
|
|
4
|
+
# `active: true` highlights with the accent role (shadcn subs use accent, not
|
|
5
|
+
# primary). Kwarg rather than attr for the same mix-merge reason as
|
|
6
|
+
# SidebarMenuButton. See sidebar.rb.
|
|
7
|
+
class SidebarMenuSubButton < BaseComponent
|
|
8
|
+
def initialize(as: :a, active: false, **attrs)
|
|
9
|
+
@as = as
|
|
10
|
+
@active = active
|
|
11
|
+
@attrs = attrs
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def view_template(&block)
|
|
15
|
+
send(@as, **mix({ class: "pk-sidebar-menu-sub-button", "data-active": (@active ? "true" : "false") }, @attrs), &block)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# An item within a SidebarMenuSub, ported from shadcn/ui's SidebarMenuSubItem.
|
|
3
|
+
# See sidebar.rb.
|
|
4
|
+
class SidebarMenuSubItem < BaseComponent
|
|
5
|
+
def initialize(**attrs)
|
|
6
|
+
@attrs = attrs
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def view_template(&block)
|
|
10
|
+
li(**mix({ class: "pk-sidebar-menu-sub-item" }, @attrs), &block)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Divider between sidebar sections, ported from shadcn/ui's SidebarSeparator:
|
|
3
|
+
# the kit Separator inset to the sidebar's padding and tinted with the
|
|
4
|
+
# sidebar border role. See sidebar.rb.
|
|
5
|
+
class SidebarSeparator < BaseComponent
|
|
6
|
+
def initialize(**attrs)
|
|
7
|
+
@attrs = attrs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template
|
|
11
|
+
render Separator.new(**mix({ class: "pk-sidebar-separator" }, @attrs))
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -10,18 +10,13 @@ module PhlexKit
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def view_template
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
viewbox: "0 0 24 24",
|
|
16
|
-
fill: "none",
|
|
17
|
-
stroke: "currentColor",
|
|
18
|
-
"stroke-width": "2",
|
|
19
|
-
"stroke-linecap": "round",
|
|
20
|
-
"stroke-linejoin": "round",
|
|
13
|
+
# A visible loader, not decoration: role=status + label, aria-hidden off.
|
|
14
|
+
render Icon.new(:loader, size: nil, **mix({
|
|
21
15
|
class: [ "pk-spinner", SIZES.fetch(@size) ].compact.join(" "),
|
|
22
16
|
role: "status",
|
|
23
|
-
aria: { label: "Loading" }
|
|
24
|
-
|
|
17
|
+
aria: { label: "Loading" },
|
|
18
|
+
"aria-hidden": false
|
|
19
|
+
}, @attrs))
|
|
25
20
|
end
|
|
26
21
|
end
|
|
27
22
|
end
|
|
@@ -13,21 +13,7 @@ module PhlexKit
|
|
|
13
13
|
aria: { label: "Close toast" },
|
|
14
14
|
data: { slot: "close", action: "click->phlex-kit--toast#dismiss" }
|
|
15
15
|
}, @attrs)) do
|
|
16
|
-
|
|
17
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
18
|
-
width: "14",
|
|
19
|
-
height: "14",
|
|
20
|
-
viewbox: "0 0 24 24",
|
|
21
|
-
fill: "none",
|
|
22
|
-
stroke: "currentColor",
|
|
23
|
-
"stroke-width": "2",
|
|
24
|
-
"stroke-linecap": "round",
|
|
25
|
-
"stroke-linejoin": "round",
|
|
26
|
-
"aria-hidden": "true"
|
|
27
|
-
) do |s|
|
|
28
|
-
s.path(d: "M18 6 6 18")
|
|
29
|
-
s.path(d: "m6 6 12 12")
|
|
30
|
-
end
|
|
16
|
+
render Icon.new(:x, size: 14)
|
|
31
17
|
span(class: "pk-sr-only") { "Close" }
|
|
32
18
|
end
|
|
33
19
|
end
|
|
@@ -9,55 +9,31 @@ module PhlexKit
|
|
|
9
9
|
@attrs = attrs
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
# Status → canonical glyph. Error is circle_x (octagon-x has no equivalent
|
|
13
|
+
# outside Lucide, so the cross-library semantic name wins).
|
|
14
|
+
GLYPHS = {
|
|
15
|
+
success: :circle_check,
|
|
16
|
+
error: :circle_x,
|
|
17
|
+
warning: :triangle_alert,
|
|
18
|
+
info: :info,
|
|
19
|
+
loading: :loader
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
12
22
|
def view_template
|
|
13
23
|
return unless renderable?
|
|
14
24
|
span(**mix({ class: "pk-toast-icon", data: { slot: "icon" } }, @attrs)) do
|
|
15
|
-
|
|
16
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
17
|
-
width: "16",
|
|
18
|
-
height: "16",
|
|
19
|
-
viewbox: "0 0 24 24",
|
|
20
|
-
fill: "none",
|
|
21
|
-
stroke: "currentColor",
|
|
22
|
-
"stroke-width": "2",
|
|
23
|
-
"stroke-linecap": "round",
|
|
24
|
-
"stroke-linejoin": "round",
|
|
25
|
-
class: svg_classes,
|
|
26
|
-
"aria-hidden": "true"
|
|
27
|
-
) { |s| paths(s) }
|
|
25
|
+
render Icon.new(GLYPHS.fetch(@variant), size: 16, class: svg_classes)
|
|
28
26
|
end
|
|
29
27
|
end
|
|
30
28
|
|
|
31
29
|
private
|
|
32
30
|
|
|
33
31
|
def renderable?
|
|
34
|
-
|
|
32
|
+
GLYPHS.key?(@variant)
|
|
35
33
|
end
|
|
36
34
|
|
|
37
35
|
def svg_classes
|
|
38
36
|
[ "pk-toast-icon-svg", ("spin" if @variant == :loading) ].compact.join(" ")
|
|
39
37
|
end
|
|
40
|
-
|
|
41
|
-
def paths(s)
|
|
42
|
-
case @variant
|
|
43
|
-
when :success
|
|
44
|
-
s.circle(cx: "12", cy: "12", r: "10")
|
|
45
|
-
s.path(d: "m9 12 2 2 4-4")
|
|
46
|
-
when :error
|
|
47
|
-
s.path(d: "M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z")
|
|
48
|
-
s.path(d: "m15 9-6 6")
|
|
49
|
-
s.path(d: "m9 9 6 6")
|
|
50
|
-
when :warning
|
|
51
|
-
s.path(d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3")
|
|
52
|
-
s.path(d: "M12 9v4")
|
|
53
|
-
s.path(d: "M12 17h.01")
|
|
54
|
-
when :info
|
|
55
|
-
s.circle(cx: "12", cy: "12", r: "10")
|
|
56
|
-
s.path(d: "M12 16v-4")
|
|
57
|
-
s.path(d: "M12 8h.01")
|
|
58
|
-
when :loading
|
|
59
|
-
s.path(d: "M21 12a9 9 0 1 1-6.219-8.56")
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
38
|
end
|
|
63
39
|
end
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
tokens come from the global stylesheet. */
|
|
5
5
|
|
|
6
6
|
/* Heading */
|
|
7
|
-
.pk-heading { margin: 0 0 .6rem; font-weight:
|
|
7
|
+
.pk-heading { margin: 0 0 .6rem; font-weight: 600; line-height: 1.2; letter-spacing: -.025em; }
|
|
8
8
|
.pk-heading-1 { font-size: .75rem; }
|
|
9
9
|
.pk-heading-2 { font-size: .875rem; }
|
|
10
10
|
.pk-heading-3 { font-size: 1rem; }
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
/* Inline code */
|
|
36
36
|
.pk-inline-code {
|
|
37
|
-
font-family: ui-monospace, monospace;
|
|
37
|
+
font-family: var(--pk-font-mono, ui-monospace, monospace);
|
|
38
38
|
font-size: .875rem;
|
|
39
39
|
background: var(--pk-bg);
|
|
40
40
|
border: 1px solid var(--pk-border);
|
|
@@ -47,4 +47,8 @@
|
|
|
47
47
|
.pk-inline-link:hover { text-decoration: underline; }
|
|
48
48
|
|
|
49
49
|
/* Blockquote */
|
|
50
|
-
.pk-blockquote { margin:
|
|
50
|
+
.pk-blockquote { margin: 1.5rem 0; border-left: 2px solid var(--pk-border); padding-left: 1.5rem; font-style: italic; }
|
|
51
|
+
|
|
52
|
+
/* Prose list (shadcn typography's list style). */
|
|
53
|
+
.pk-list { margin: 1.5rem 0; padding-left: 1.5rem; list-style: disc; }
|
|
54
|
+
.pk-list > li { margin-top: .5rem; }
|
|
@@ -18,7 +18,23 @@ export default class extends Controller {
|
|
|
18
18
|
this.animationDurationValue = d
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
toggle() {
|
|
21
|
+
toggle() {
|
|
22
|
+
const next = !this.openValue
|
|
23
|
+
if (next) this.closeSiblings()
|
|
24
|
+
this.openValue = next
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// In a type="single" accordion (shadcn's default), opening one item closes
|
|
28
|
+
// the rest.
|
|
29
|
+
closeSiblings() {
|
|
30
|
+
const root = this.element.closest(".pk-accordion")
|
|
31
|
+
if (!root || root.dataset.type !== "single") return
|
|
32
|
+
root.querySelectorAll('[data-controller~="phlex-kit--accordion"]').forEach((el) => {
|
|
33
|
+
if (el === this.element) return
|
|
34
|
+
const ctrl = this.application.getControllerForElementAndIdentifier(el, "phlex-kit--accordion")
|
|
35
|
+
if (ctrl && ctrl.openValue) ctrl.openValue = false
|
|
36
|
+
})
|
|
37
|
+
}
|
|
22
38
|
openValueChanged(isOpen) { isOpen ? this.open() : this.close() }
|
|
23
39
|
|
|
24
40
|
open() {
|
|
@@ -34,6 +34,7 @@ import MenubarController from "phlex_kit/controllers/menubar_controller"
|
|
|
34
34
|
import MessageScrollerController from "phlex_kit/controllers/message_scroller_controller"
|
|
35
35
|
import PopoverController from "phlex_kit/controllers/popover_controller"
|
|
36
36
|
import ResizableController from "phlex_kit/controllers/resizable_controller"
|
|
37
|
+
import ScrollFadeController from "phlex_kit/controllers/scroll_fade_controller"
|
|
37
38
|
import SelectController from "phlex_kit/controllers/select_controller"
|
|
38
39
|
import SelectItemController from "phlex_kit/controllers/select_item_controller"
|
|
39
40
|
import SheetController from "phlex_kit/controllers/sheet_controller"
|
|
@@ -73,6 +74,7 @@ export function registerPhlexKitControllers(application) {
|
|
|
73
74
|
application.register("phlex-kit--message-scroller", MessageScrollerController)
|
|
74
75
|
application.register("phlex-kit--popover", PopoverController)
|
|
75
76
|
application.register("phlex-kit--resizable", ResizableController)
|
|
77
|
+
application.register("phlex-kit--scroll-fade", ScrollFadeController)
|
|
76
78
|
application.register("phlex-kit--select", SelectController)
|
|
77
79
|
application.register("phlex-kit--select-item", SelectItemController)
|
|
78
80
|
application.register("phlex-kit--sheet", SheetController)
|