senren-ui 0.1.0 → 0.1.1
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/CHANGELOG.md +27 -1
- data/CONTRIBUTING.md +4 -0
- data/README.md +3 -0
- data/lib/senren/rails/registry.rb +4 -4
- data/lib/senren/rails/version.rb +1 -1
- data/templates/components/sidebar/sidebar_component.html.erb +15 -5
- data/templates/components/tabs/tabs_component.html.erb +2 -2
- data/templates/controllers/sidebar_controller.js +46 -2
- data/templates/controllers/tabs_controller.js +4 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8bcfbb253e663c59e6628841c25bf5584fb665385243e791d318bb65d8500dfb
|
|
4
|
+
data.tar.gz: f091c8049f283fca041ca8c1c716feda255008365239d9728f1b6ef2c38c378a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c42f55ffa59c1d82416c98f336b3927ecb72255bc32d530c2fa8f55ce60fa3cd2d6331159a30efabb78ac60b838853bdcfe17368248873d6a39a48d819be1118
|
|
7
|
+
data.tar.gz: abb2a2e92a1138842d5a06820de2f39006b8519d3ad010d2300154c5b6ac9986eb23e80e077e23e1ef386530944b1956ee1c15e1d774aa26828b5d256cfa75f3
|
data/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,8 @@ bug fixes only.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.1.1] — 2026-05-02
|
|
13
|
+
|
|
12
14
|
### Added
|
|
13
15
|
|
|
14
16
|
- Initial gem skeleton, engine, and version constant.
|
|
@@ -26,8 +28,32 @@ bug fixes only.
|
|
|
26
28
|
- Centralized `.senren/skill.md` system with preserved user-region.
|
|
27
29
|
- `public/llms.txt` and `public/llms-full.txt` generation.
|
|
28
30
|
- `apps/todolist` Rails app dogfooding the gem via local path.
|
|
31
|
+
- Bun-based JS tooling for Stimulus templates:
|
|
32
|
+
- `bun run controllers:syntax`
|
|
33
|
+
- `bun run controllers:lint`
|
|
34
|
+
- `bun run controllers:lint:fix`
|
|
35
|
+
- `bun run controllers:check`
|
|
36
|
+
- Biome lint configuration (`biome.json`) scoped to
|
|
37
|
+
`templates/controllers/**/*.js`.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- `SidebarComponent` template + Stimulus controller now support robust
|
|
42
|
+
compact/expanded syncing:
|
|
43
|
+
- hides brand/footer in compact mode
|
|
44
|
+
- shows link initials in compact mode and full labels in expanded mode
|
|
45
|
+
- uses a hamburger icon toggle with `aria-expanded`
|
|
46
|
+
- applies smoother width/label transition behavior
|
|
47
|
+
- `TabsComponent` template + Stimulus controller now use
|
|
48
|
+
`data-state="active|inactive"` for tab and panel state, so header active
|
|
49
|
+
styling updates correctly after client-side tab switches.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- Docs-site feedback issues now resolved at gem template level (not app-only):
|
|
54
|
+
sidebar compact truncation UX and tabs header active-state mismatch.
|
|
29
55
|
|
|
30
|
-
## [0.1.0] —
|
|
56
|
+
## [0.1.0] — 2026-04-27
|
|
31
57
|
|
|
32
58
|
First tagged release once the Unreleased entries are validated end-to-end
|
|
33
59
|
in `apps/todolist` per `plans/011_release_checklist.md`.
|
data/CONTRIBUTING.md
CHANGED
|
@@ -19,7 +19,9 @@ this file before opening a PR.
|
|
|
19
19
|
git clone <repo>
|
|
20
20
|
cd senren-rails
|
|
21
21
|
bundle install
|
|
22
|
+
bun install
|
|
22
23
|
bundle exec rake test
|
|
24
|
+
bun run controllers:check
|
|
23
25
|
```
|
|
24
26
|
|
|
25
27
|
To exercise the gem against a real Rails app, use the bundled workspace:
|
|
@@ -61,3 +63,5 @@ bin/rails server
|
|
|
61
63
|
- One logical change per commit.
|
|
62
64
|
- Mention the affected plan and history files in the commit body.
|
|
63
65
|
- Run `bundle exec rake test` before pushing.
|
|
66
|
+
- Run `bun run controllers:check` before pushing if you touched
|
|
67
|
+
`templates/controllers/*.js`.
|
data/README.md
CHANGED
|
@@ -118,7 +118,10 @@ See `registry/components.yml` for the canonical list. v0.1 ships:
|
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
120
|
bundle install
|
|
121
|
+
bun install
|
|
121
122
|
bundle exec rake test # gem tests
|
|
123
|
+
bun run controllers:check # lint + syntax check for templates/controllers/*.js
|
|
124
|
+
bun run controllers:lint:fix # auto-fix lint issues for controllers
|
|
122
125
|
bundle exec rake test:system # Stimulus/system tests
|
|
123
126
|
```
|
|
124
127
|
|
data/lib/senren/rails/version.rb
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
<aside <%= tag.attributes(**root_attrs("flex min-h-80 flex-col rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-card))] p-3 text-[hsl(var(--senren-card-foreground))]
|
|
1
|
+
<aside <%= tag.attributes(**root_attrs("flex min-h-80 flex-col overflow-hidden rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-card))] p-3 text-[hsl(var(--senren-card-foreground))] transition-[width] duration-300 ease-in-out", data: { controller: "senren--sidebar" })) %>>
|
|
2
2
|
<div class="mb-4 flex items-center justify-between gap-2 px-2">
|
|
3
|
-
<div class="truncate font-display text-sm font-semibold tracking-tight"><%= brand %></div>
|
|
4
|
-
<button type="button" class="cursor-pointer
|
|
3
|
+
<div data-senren--sidebar-target="brand" class="truncate font-display text-sm font-semibold tracking-tight"><%= brand %></div>
|
|
4
|
+
<button type="button" data-senren--sidebar-target="toggleButton" class="inline-flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-[hsl(var(--senren-muted-foreground))] transition-colors hover:bg-[hsl(var(--senren-accent))] hover:text-[hsl(var(--senren-accent-foreground))]" data-action="click->senren--sidebar#toggle" aria-label="Toggle sidebar" aria-expanded="<%= variant != :compact %>">
|
|
5
|
+
<span class="sr-only">Toggle sidebar</span>
|
|
6
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
7
|
+
<path d="M3 6h18" />
|
|
8
|
+
<path d="M3 12h18" />
|
|
9
|
+
<path d="M3 18h18" />
|
|
10
|
+
</svg>
|
|
11
|
+
</button>
|
|
5
12
|
</div>
|
|
6
13
|
<nav aria-label="<%= label %>" class="space-y-1">
|
|
7
14
|
<% items.each do |item| %>
|
|
8
|
-
<%= link_to item[:
|
|
15
|
+
<%= link_to item[:href], title: item[:label], data: { "senren--sidebar-target": "link" }, class: "flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-all duration-200 #{item[:active] ? "bg-[hsl(var(--senren-primary))] text-[hsl(var(--senren-primary-foreground))]" : "text-[hsl(var(--senren-muted-foreground))] hover:bg-[hsl(var(--senren-accent))] hover:text-[hsl(var(--senren-accent-foreground))]"}" do %>
|
|
16
|
+
<span data-senren--sidebar-target="linkInitial" class="inline-flex h-4 w-0 items-center justify-center overflow-hidden text-xs font-semibold uppercase tracking-wide opacity-0 transition-all duration-200"><%= item[:label].to_s.first&.upcase || "•" %></span>
|
|
17
|
+
<span data-senren--sidebar-target="linkLabel" class="max-w-40 truncate opacity-100 transition-all duration-200"><%= item[:label] %></span>
|
|
18
|
+
<% end %>
|
|
9
19
|
<% end %>
|
|
10
20
|
</nav>
|
|
11
21
|
<% if content? %>
|
|
12
|
-
<div class="mt-auto pt-4 text-xs text-[hsl(var(--senren-muted-foreground))]"><%= content %></div>
|
|
22
|
+
<div data-senren--sidebar-target="footer" class="mt-auto pt-4 text-xs text-[hsl(var(--senren-muted-foreground))]"><%= content %></div>
|
|
13
23
|
<% end %>
|
|
14
24
|
</aside>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div role="tablist" aria-label="<%= label %>" class="<%= self.class::VARIANTS[variant] %> flex flex-wrap items-center gap-1">
|
|
3
3
|
<% items.each do |item| %>
|
|
4
4
|
<% selected = active_item?(item) %>
|
|
5
|
-
<button type="button" role="tab" id="<%= item[:id] %>-tab" aria-selected="<%= selected %>" aria-controls="<%= item[:id] %>-panel" tabindex="<%= selected ? 0 : -1 %>" data-senren--tabs-target="tab" data-panel-id="<%= item[:id] %>" data-action="click->senren--tabs#select keydown->senren--tabs#onKey" class="cursor-pointer rounded-[calc(var(--senren-radius)-2px)] px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--senren-ring))]
|
|
5
|
+
<button type="button" role="tab" id="<%= item[:id] %>-tab" aria-selected="<%= selected %>" aria-controls="<%= item[:id] %>-panel" tabindex="<%= selected ? 0 : -1 %>" data-senren--tabs-target="tab" data-panel-id="<%= item[:id] %>" data-state="<%= selected ? "active" : "inactive" %>" data-action="click->senren--tabs#select keydown->senren--tabs#onKey" class="cursor-pointer rounded-[calc(var(--senren-radius)-2px)] px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--senren-ring))] text-[hsl(var(--senren-muted-foreground))] data-[state=active]:bg-[hsl(var(--senren-background))] data-[state=active]:text-[hsl(var(--senren-foreground))] data-[state=active]:shadow-sm data-[state=inactive]:hover:text-[hsl(var(--senren-foreground))]">
|
|
6
6
|
<%= item[:label] %>
|
|
7
7
|
</button>
|
|
8
8
|
<% end %>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<div class="mt-4">
|
|
11
11
|
<% items.each do |item| %>
|
|
12
12
|
<% selected = active_item?(item) %>
|
|
13
|
-
<section id="<%= item[:id] %>-panel" role="tabpanel" aria-labelledby="<%= item[:id] %>-tab" data-senren--tabs-target="panel" data-panel-id="<%= item[:id] %>" <%= "hidden" unless selected %> class="rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-card))] p-4 text-sm text-[hsl(var(--senren-card-foreground))]">
|
|
13
|
+
<section id="<%= item[:id] %>-panel" role="tabpanel" aria-labelledby="<%= item[:id] %>-tab" data-senren--tabs-target="panel" data-panel-id="<%= item[:id] %>" data-state="<%= selected ? "active" : "inactive" %>" <%= "hidden" unless selected %> class="rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-card))] p-4 text-sm text-[hsl(var(--senren-card-foreground))]">
|
|
14
14
|
<%= item[:content].presence || content %>
|
|
15
15
|
</section>
|
|
16
16
|
<% end %>
|
|
@@ -3,8 +3,52 @@ import { Controller } from "@hotwired/stimulus"
|
|
|
3
3
|
// senren--sidebar
|
|
4
4
|
// Local UI: compact/expanded visual state only.
|
|
5
5
|
export default class extends Controller {
|
|
6
|
+
static targets = ["brand", "footer", "toggleButton", "link", "linkLabel", "linkInitial"]
|
|
7
|
+
|
|
8
|
+
connect() {
|
|
9
|
+
this.syncState()
|
|
10
|
+
}
|
|
11
|
+
|
|
6
12
|
toggle() {
|
|
7
|
-
this.element.classList.
|
|
8
|
-
this.element.classList.toggle("w-
|
|
13
|
+
const isCompact = this.element.classList.contains("w-20")
|
|
14
|
+
this.element.classList.toggle("w-20", !isCompact)
|
|
15
|
+
this.element.classList.toggle("w-64", isCompact)
|
|
16
|
+
this.syncState()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
syncState() {
|
|
20
|
+
const isCompact = this.element.classList.contains("w-20")
|
|
21
|
+
|
|
22
|
+
if (this.hasBrandTarget) {
|
|
23
|
+
this.brandTarget.classList.toggle("hidden", isCompact)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (this.hasFooterTarget) {
|
|
27
|
+
this.footerTarget.classList.toggle("hidden", isCompact)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.linkTargets.forEach((link) => {
|
|
31
|
+
link.classList.toggle("justify-center", isCompact)
|
|
32
|
+
link.classList.toggle("px-2", isCompact)
|
|
33
|
+
link.classList.toggle("px-3", !isCompact)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
this.linkLabelTargets.forEach((label) => {
|
|
37
|
+
label.classList.toggle("max-w-0", isCompact)
|
|
38
|
+
label.classList.toggle("opacity-0", isCompact)
|
|
39
|
+
label.classList.toggle("max-w-40", !isCompact)
|
|
40
|
+
label.classList.toggle("opacity-100", !isCompact)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
this.linkInitialTargets.forEach((initial) => {
|
|
44
|
+
initial.classList.toggle("w-4", isCompact)
|
|
45
|
+
initial.classList.toggle("opacity-100", isCompact)
|
|
46
|
+
initial.classList.toggle("w-0", !isCompact)
|
|
47
|
+
initial.classList.toggle("opacity-0", !isCompact)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
if (this.hasToggleButtonTarget) {
|
|
51
|
+
this.toggleButtonTarget.setAttribute("aria-expanded", String(!isCompact))
|
|
52
|
+
}
|
|
9
53
|
}
|
|
10
54
|
}
|
|
@@ -26,9 +26,12 @@ export default class extends Controller {
|
|
|
26
26
|
const selected = target === tab
|
|
27
27
|
target.setAttribute("aria-selected", selected ? "true" : "false")
|
|
28
28
|
target.tabIndex = selected ? 0 : -1
|
|
29
|
+
target.dataset.state = selected ? "active" : "inactive"
|
|
29
30
|
})
|
|
30
31
|
this.panelTargets.forEach((panel) => {
|
|
31
|
-
|
|
32
|
+
const selected = panel.dataset.panelId === panelId
|
|
33
|
+
panel.hidden = !selected
|
|
34
|
+
panel.dataset.state = selected ? "active" : "inactive"
|
|
32
35
|
})
|
|
33
36
|
}
|
|
34
37
|
|