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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c45b2de6be65de34864a76e477a59011047aedbcd60622748301fba338ab61bf
4
- data.tar.gz: 91d2611e8b3d7aaecaae5a123065efaac66e53ae67a8c60d8b4264bba8cbb34c
3
+ metadata.gz: dabd58e18372455c16ef319dc2f6270469ea2bbacfa42f2418f1e82f127d57cb
4
+ data.tar.gz: 04b6f68484015c542bfb65d24322c9342ec029d4d3d322f80dca0f571bbdcba0
5
5
  SHA512:
6
- metadata.gz: 2528e616b1d5d55ee66f8089cb016207e505601e2be03cca6e9f706f4c68c29622414e9fcd96717354222e142c570806dc85290842eff2570e3d2120f7cc97ce
7
- data.tar.gz: 9c218687c79045dc1cf070b40b37f4706fb023c878a142a86c5c2f05cd88f99516a81f00d5cb382d97aae2c9b48dc8c7e0a680f4b43a09146a5a8bcddde3668f
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
- ![The docs site's /create page — shadcn/ui's theme-builder demo rebuilt entirely from PhlexKit components](docs/images/create-page.png)
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 at `collapsible: :none`
3
- # (the NonCollapsibleSidebar path): a static flex-column rail. Deliberately
4
- # WITHOUT ruby_ui's collapsible/offcanvas Stimulus controller + cookie state +
5
- # mobile Sheet (the admin sidebar is static) those can be added later if a
6
- # collapsible chrome is wanted. The static part set matches shadcn/ui's Sidebar:
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 scrimsee 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
- def initialize(**attrs)
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
- div(**mix({ class: "pk-sidebar-wrapper" }, @attrs), &block)
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PhlexKit
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
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.3.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