phlex_kit 0.3.0 → 0.4.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/README.md +15 -2
- data/app/components/phlex_kit/sidebar/sidebar.css +57 -0
- data/app/components/phlex_kit/sidebar/sidebar.rb +5 -5
- data/app/components/phlex_kit/sidebar/sidebar_controller.js +24 -0
- data/app/components/phlex_kit/sidebar/sidebar_trigger.rb +23 -0
- data/app/components/phlex_kit/sidebar/sidebar_wrapper.rb +37 -2
- data/app/javascript/phlex_kit/controllers/index.js +2 -0
- data/lib/phlex_kit/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dabd58e18372455c16ef319dc2f6270469ea2bbacfa42f2418f1e82f127d57cb
|
|
4
|
+
data.tar.gz: 04b6f68484015c542bfb65d24322c9342ec029d4d3d322f80dca0f571bbdcba0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d7ffa228298fa3583da83586982aa28404e1c87979b5c839123c6f493da0e6ce8502b9df6a40b58ba73cd11276a06c3e2c96a081bd82057b903dfd5f9d4dddab
|
|
7
|
+
data.tar.gz: fee2a8edf8f4ad0da4ab8ee42da82316ee0a6674f689f9f1c6025b7efd097c4a557e788dbe725683c5ecdbe49635735cab086a798c39d104550bc17dbb017b70
|
data/README.md
CHANGED
|
@@ -9,7 +9,11 @@ instead of Tailwind**. The full [ruby_ui](https://ruby-ui.com) catalog plus
|
|
|
9
9
|
shadcn's own additions — 70 component families, 38 Stimulus controllers —
|
|
10
10
|
with the default theme lifted verbatim from shadcn/ui's current token system.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
<picture>
|
|
13
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/create-page-dark.png">
|
|
14
|
+
<source media="(prefers-color-scheme: light)" srcset="docs/images/create-page-light.png">
|
|
15
|
+
<img alt="The docs site's /create page — shadcn/ui's theme-builder demo rebuilt entirely from PhlexKit components" src="docs/images/create-page-light.png">
|
|
16
|
+
</picture>
|
|
13
17
|
|
|
14
18
|
*Every pixel above is PhlexKit: [shadcn's /create demo](https://ui.shadcn.com/create)
|
|
15
19
|
rebuilt in the dummy app from kit components — cards, charts, sliders,
|
|
@@ -147,7 +151,11 @@ stylesheet_link_tag "phlex_kit/themes/neutral"
|
|
|
147
151
|
|
|
148
152
|
The three knobs from ui.shadcn.com/create map directly: **Radius** →
|
|
149
153
|
`--pk-radius`; **Icon Library** → `PhlexKit.config.icon_library` (see Icons
|
|
150
|
-
below); **Menu** → `PhlexKit::Sidebar.new(menu: :default | :solid)`.
|
|
154
|
+
below); **Menu** → `PhlexKit::Sidebar.new(menu: :default | :solid)`. For an
|
|
155
|
+
off-canvas sidebar, `PhlexKit::SidebarWrapper.new(collapsible: :offcanvas)` +
|
|
156
|
+
a `PhlexKit::SidebarTrigger` anywhere inside it: desktop slides the rail out
|
|
157
|
+
of the layout, below 768px it overlays as a drawer behind a scrim (Escape /
|
|
158
|
+
scrim-click closes; state is DOM-only so Turbo caches restore closed).
|
|
151
159
|
|
|
152
160
|
## Icons
|
|
153
161
|
|
|
@@ -195,6 +203,11 @@ render PhlexKit::Chart.new(options: {
|
|
|
195
203
|
No `window.Chart`? The controller dispatches `phlex-kit--chart:connect` with
|
|
196
204
|
`{ canvas, options }` so you can drive any other library.
|
|
197
205
|
|
|
206
|
+
For the full walkthrough — pinning Chart.js through **importmap** (and the
|
|
207
|
+
gotcha that makes the obvious `bin/importmap pin chart.js` fail), the UMD
|
|
208
|
+
alternative, and end-to-end verification — see
|
|
209
|
+
[docs/06-CHARTS.md](docs/06-CHARTS.md).
|
|
210
|
+
|
|
198
211
|
## phlex-reactive (optional)
|
|
199
212
|
|
|
200
213
|
For components that own server-state behavior, add phlex-reactive and include
|
|
@@ -158,3 +158,60 @@
|
|
|
158
158
|
.pk-input.pk-sidebar-input { height: 2rem; width: 100%; background: var(--pk-bg); box-shadow: none; }
|
|
159
159
|
|
|
160
160
|
.pk-sidebar-inset { flex: 1; min-width: 0; }
|
|
161
|
+
|
|
162
|
+
/* --- Offcanvas mode (SidebarWrapper collapsible: :offcanvas) ---------------
|
|
163
|
+
Desktop: the rail slides out of the layout (data-collapsed on the wrapper).
|
|
164
|
+
Mobile (<768px): the rail is a fixed overlay drawer behind a scrim
|
|
165
|
+
(data-open). The trigger only shows inside an offcanvas wrapper. The 767px
|
|
166
|
+
cutoff matches sidebar_controller.js. */
|
|
167
|
+
.pk-sidebar-wrapper.collapsible-offcanvas .pk-sidebar {
|
|
168
|
+
transition: margin-left .2s ease, transform .2s ease;
|
|
169
|
+
}
|
|
170
|
+
.pk-sidebar-wrapper.collapsible-offcanvas[data-collapsed] .pk-sidebar { margin-left: -16rem; }
|
|
171
|
+
|
|
172
|
+
.pk-sidebar-trigger {
|
|
173
|
+
display: none;
|
|
174
|
+
align-items: center;
|
|
175
|
+
justify-content: center;
|
|
176
|
+
width: 2rem;
|
|
177
|
+
height: 2rem;
|
|
178
|
+
border: 0;
|
|
179
|
+
border-radius: calc(var(--pk-radius) - 2px);
|
|
180
|
+
background: transparent;
|
|
181
|
+
color: inherit;
|
|
182
|
+
padding: 0;
|
|
183
|
+
cursor: pointer;
|
|
184
|
+
}
|
|
185
|
+
.pk-sidebar-trigger:hover { background: var(--pk-accent); }
|
|
186
|
+
.pk-sidebar-trigger svg { width: 1rem; height: 1rem; }
|
|
187
|
+
.collapsible-offcanvas .pk-sidebar-trigger { display: inline-flex; }
|
|
188
|
+
|
|
189
|
+
.pk-sidebar-scrim { display: none; }
|
|
190
|
+
|
|
191
|
+
@media (max-width: 767px) {
|
|
192
|
+
.pk-sidebar-wrapper.collapsible-offcanvas .pk-sidebar {
|
|
193
|
+
position: fixed;
|
|
194
|
+
inset-block: 0;
|
|
195
|
+
left: 0;
|
|
196
|
+
z-index: 50;
|
|
197
|
+
height: 100dvh;
|
|
198
|
+
margin-left: 0;
|
|
199
|
+
transform: translateX(-100%);
|
|
200
|
+
box-shadow: 0 8px 30px rgb(0 0 0 / .35);
|
|
201
|
+
}
|
|
202
|
+
.pk-sidebar-wrapper.collapsible-offcanvas[data-open] .pk-sidebar { transform: none; }
|
|
203
|
+
.pk-sidebar-wrapper.collapsible-offcanvas[data-open] .pk-sidebar-scrim {
|
|
204
|
+
display: block;
|
|
205
|
+
position: fixed;
|
|
206
|
+
inset: 0;
|
|
207
|
+
z-index: 49;
|
|
208
|
+
background: rgb(0 0 0 / .5);
|
|
209
|
+
border: 0;
|
|
210
|
+
padding: 0;
|
|
211
|
+
cursor: default;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@media (prefers-reduced-motion: reduce) {
|
|
216
|
+
.pk-sidebar-wrapper.collapsible-offcanvas .pk-sidebar { transition: none; }
|
|
217
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
|
-
# Sidebar shell, ported from ruby_ui's RubyUI::Sidebar
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
2
|
+
# Sidebar shell, ported from ruby_ui's RubyUI::Sidebar: a flex-column rail.
|
|
3
|
+
# Static by default; SidebarWrapper(collapsible: :offcanvas) + SidebarTrigger
|
|
4
|
+
# give it shadcn's offcanvas behavior (desktop slide-away, mobile overlay
|
|
5
|
+
# drawer with scrim — see sidebar_wrapper.rb / sidebar_controller.js).
|
|
6
|
+
# The part set matches shadcn/ui's Sidebar:
|
|
7
7
|
# compose with SidebarWrapper (the page root) + SidebarHeader / SidebarContent /
|
|
8
8
|
# SidebarGroup (+ Label / Content / Action) / SidebarMenu (+ Item / Button /
|
|
9
9
|
# Action / Badge / Skeleton / Sub / SubItem / SubButton) / SidebarSeparator /
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="phlex-kit--sidebar" — set by SidebarWrapper when
|
|
4
|
+
// collapsible: :offcanvas. One attribute per surface, both DOM-only so Turbo's
|
|
5
|
+
// page cache never restores an open drawer (the wrapper also closes on
|
|
6
|
+
// turbo:before-cache): `data-open` shows the mobile overlay drawer,
|
|
7
|
+
// `data-collapsed` slides the desktop rail out of the layout. The 767px cutoff
|
|
8
|
+
// matches sidebar.css's offcanvas media query.
|
|
9
|
+
export default class extends Controller {
|
|
10
|
+
static MOBILE = "(max-width: 767px)"
|
|
11
|
+
|
|
12
|
+
toggle() {
|
|
13
|
+
if (this.mobile) this.element.toggleAttribute("data-open")
|
|
14
|
+
else this.element.toggleAttribute("data-collapsed")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
closeMobile() {
|
|
18
|
+
this.element.removeAttribute("data-open")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get mobile() {
|
|
22
|
+
return window.matchMedia(this.constructor.MOBILE).matches
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# The hamburger that toggles an offcanvas sidebar, ported from shadcn/ui's
|
|
3
|
+
# SidebarTrigger. Place it anywhere inside a
|
|
4
|
+
# SidebarWrapper(collapsible: :offcanvas) — typically at the top of the
|
|
5
|
+
# SidebarInset. Renders the :menu icon unless a block supplies its own
|
|
6
|
+
# content. Hidden by CSS unless the wrapper is offcanvas. See sidebar.rb.
|
|
7
|
+
class SidebarTrigger < BaseComponent
|
|
8
|
+
def initialize(**attrs)
|
|
9
|
+
@attrs = attrs
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template(&block)
|
|
13
|
+
button(**mix({
|
|
14
|
+
type: :button,
|
|
15
|
+
class: "pk-sidebar-trigger",
|
|
16
|
+
aria_label: "Toggle sidebar",
|
|
17
|
+
data: { action: "click->phlex-kit--sidebar#toggle" }
|
|
18
|
+
}, @attrs)) do
|
|
19
|
+
block ? yield : render(Icon.new(:menu, size: nil))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -1,13 +1,48 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
2
|
# The page-level flex row that holds a PhlexKit::Sidebar + a PhlexKit::SidebarInset.
|
|
3
|
+
#
|
|
4
|
+
# `collapsible:` (shadcn/ui's Sidebar `collapsible` prop, held here because the
|
|
5
|
+
# wrapper is the Stimulus scope shared by rail, trigger and scrim):
|
|
6
|
+
# :none — static rail (default).
|
|
7
|
+
# :offcanvas — the rail hides/reveals via a PhlexKit::SidebarTrigger. On
|
|
8
|
+
# desktop it slides out of the layout; below 768px it overlays
|
|
9
|
+
# as a fixed drawer behind a click-to-close scrim, closed by
|
|
10
|
+
# Escape and before Turbo caches the page. State is DOM-only:
|
|
11
|
+
# `data-collapsed` (desktop) / `data-open` (mobile drawer).
|
|
3
12
|
# See sidebar.rb.
|
|
4
13
|
class SidebarWrapper < BaseComponent
|
|
5
|
-
|
|
14
|
+
COLLAPSIBLE = { none: nil, offcanvas: "collapsible-offcanvas" }.freeze
|
|
15
|
+
|
|
16
|
+
def initialize(collapsible: :none, **attrs)
|
|
17
|
+
@collapsible = collapsible.to_sym
|
|
6
18
|
@attrs = attrs
|
|
7
19
|
end
|
|
8
20
|
|
|
9
21
|
def view_template(&block)
|
|
10
|
-
|
|
22
|
+
unless offcanvas?
|
|
23
|
+
return div(**mix({ class: classes }, @attrs), &block)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
div(**mix({
|
|
27
|
+
class: classes,
|
|
28
|
+
data: {
|
|
29
|
+
controller: "phlex-kit--sidebar",
|
|
30
|
+
action: "keydown.esc@window->phlex-kit--sidebar#closeMobile " \
|
|
31
|
+
"turbo:before-cache@window->phlex-kit--sidebar#closeMobile"
|
|
32
|
+
}
|
|
33
|
+
}, @attrs)) do
|
|
34
|
+
yield if block
|
|
35
|
+
button(type: :button, class: "pk-sidebar-scrim", aria_label: "Close sidebar",
|
|
36
|
+
tabindex: "-1", data: { action: "click->phlex-kit--sidebar#closeMobile" })
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def offcanvas? = @collapsible == :offcanvas
|
|
43
|
+
|
|
44
|
+
def classes
|
|
45
|
+
[ "pk-sidebar-wrapper", COLLAPSIBLE.fetch(@collapsible) ].compact.join(" ")
|
|
11
46
|
end
|
|
12
47
|
end
|
|
13
48
|
end
|
|
@@ -39,6 +39,7 @@ import SelectController from "phlex_kit/controllers/select_controller"
|
|
|
39
39
|
import SelectItemController from "phlex_kit/controllers/select_item_controller"
|
|
40
40
|
import SheetController from "phlex_kit/controllers/sheet_controller"
|
|
41
41
|
import SheetContentController from "phlex_kit/controllers/sheet_content_controller"
|
|
42
|
+
import SidebarController from "phlex_kit/controllers/sidebar_controller"
|
|
42
43
|
import SliderController from "phlex_kit/controllers/slider_controller"
|
|
43
44
|
import TabsController from "phlex_kit/controllers/tabs_controller"
|
|
44
45
|
import ThemeToggleController from "phlex_kit/controllers/theme_toggle_controller"
|
|
@@ -79,6 +80,7 @@ export function registerPhlexKitControllers(application) {
|
|
|
79
80
|
application.register("phlex-kit--select-item", SelectItemController)
|
|
80
81
|
application.register("phlex-kit--sheet", SheetController)
|
|
81
82
|
application.register("phlex-kit--sheet-content", SheetContentController)
|
|
83
|
+
application.register("phlex-kit--sidebar", SidebarController)
|
|
82
84
|
application.register("phlex-kit--slider", SliderController)
|
|
83
85
|
application.register("phlex-kit--tabs", TabsController)
|
|
84
86
|
application.register("phlex-kit--theme-toggle", ThemeToggleController)
|
data/lib/phlex_kit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phlex_kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matt Kennedy
|
|
@@ -462,6 +462,7 @@ files:
|
|
|
462
462
|
- app/components/phlex_kit/sidebar/sidebar.css
|
|
463
463
|
- app/components/phlex_kit/sidebar/sidebar.rb
|
|
464
464
|
- app/components/phlex_kit/sidebar/sidebar_content.rb
|
|
465
|
+
- app/components/phlex_kit/sidebar/sidebar_controller.js
|
|
465
466
|
- app/components/phlex_kit/sidebar/sidebar_footer.rb
|
|
466
467
|
- app/components/phlex_kit/sidebar/sidebar_group.rb
|
|
467
468
|
- app/components/phlex_kit/sidebar/sidebar_group_action.rb
|
|
@@ -480,6 +481,7 @@ files:
|
|
|
480
481
|
- app/components/phlex_kit/sidebar/sidebar_menu_sub_button.rb
|
|
481
482
|
- app/components/phlex_kit/sidebar/sidebar_menu_sub_item.rb
|
|
482
483
|
- app/components/phlex_kit/sidebar/sidebar_separator.rb
|
|
484
|
+
- app/components/phlex_kit/sidebar/sidebar_trigger.rb
|
|
483
485
|
- app/components/phlex_kit/sidebar/sidebar_wrapper.rb
|
|
484
486
|
- app/components/phlex_kit/skeleton/skeleton.css
|
|
485
487
|
- app/components/phlex_kit/skeleton/skeleton.rb
|