senren-ui 0.2.0 → 0.3.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/CHANGELOG.md +111 -0
- data/CONTRIBUTING.md +1 -0
- data/README.md +28 -0
- data/docs/visual_style.md +80 -0
- data/lib/generators/senren/install/install_generator.rb +3 -0
- data/lib/generators/senren/install/templates/base_component.rb.tt +97 -7
- data/lib/generators/senren/install/templates/conventions.md.tt +41 -0
- data/lib/generators/senren/install/templates/senren_themes.css.tt +339 -0
- data/lib/senren/rails/agent_rules_writer.rb +3 -0
- data/lib/senren/rails/version.rb +1 -1
- data/templates/components/accordion/accordion_component.html.erb +3 -3
- data/templates/components/alert_dialog/alert_dialog_component.html.erb +6 -3
- data/templates/components/alert_dialog/alert_dialog_component.rb +4 -0
- data/templates/components/api_key_field/api_key_field_component.html.erb +1 -1
- data/templates/components/breadcrumb/breadcrumb_component.html.erb +8 -6
- data/templates/components/button/button_component.html.erb +1 -1
- data/templates/components/button/button_component.rb +27 -1
- data/templates/components/calendar/calendar_component.html.erb +2 -2
- data/templates/components/card/card_component.html.erb +4 -6
- data/templates/components/carousel/carousel_component.html.erb +1 -1
- data/templates/components/cart/cart_component.html.erb +7 -1
- data/templates/components/clipboard/clipboard_component.html.erb +3 -3
- data/templates/components/collapsible/collapsible_component.html.erb +3 -3
- data/templates/components/combobox/combobox_component.html.erb +3 -3
- data/templates/components/command/command_component.html.erb +1 -1
- data/templates/components/context_menu/context_menu_component.html.erb +6 -3
- data/templates/components/date_picker/date_picker_component.html.erb +3 -3
- data/templates/components/dialog/dialog_component.html.erb +7 -3
- data/templates/components/dialog/dialog_component.rb +11 -0
- data/templates/components/dropdown_menu/dropdown_menu_component.html.erb +6 -3
- data/templates/components/dropdown_menu/dropdown_menu_component.rb +57 -5
- data/templates/components/hover_card/hover_card_component.html.erb +6 -3
- data/templates/components/invite_member_dialog/invite_member_dialog_component.html.erb +3 -2
- data/templates/components/link/link_component.html.erb +1 -1
- data/templates/components/native_select/native_select_component.html.erb +2 -2
- data/templates/components/pagination/pagination_component.html.erb +2 -2
- data/templates/components/popover/popover_component.html.erb +6 -3
- data/templates/components/product_card/product_card_component.html.erb +2 -2
- data/templates/components/progress/progress_component.html.erb +2 -2
- data/templates/components/search_input/search_input_component.html.erb +2 -2
- data/templates/components/sheet/sheet_component.html.erb +13 -4
- data/templates/components/sheet/sheet_component.rb +11 -0
- data/templates/components/shortcut_key/shortcut_key_component.html.erb +2 -2
- data/templates/components/sidebar/sidebar_component.html.erb +2 -2
- data/templates/components/switch/switch_component.html.erb +1 -1
- data/templates/components/table/table_component.html.erb +2 -2
- data/templates/components/tabs/tabs_component.html.erb +3 -3
- data/templates/components/tooltip/tooltip_component.html.erb +5 -3
- data/templates/components/top_nav/top_nav_component.html.erb +2 -2
- data/templates/controllers/cart_controller.js +3 -1
- metadata +2 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
<input type="date" id="<%= id %>" name="<%= name %>" value="<%= value %>" placeholder="<%= placeholder %>" data-senren--date-picker-target="input" class="h-10 w-full max-w-56 cursor-pointer rounded-(--senren-radius) border bg-[hsl(var(--senren-background))] px-3 text-sm text-[hsl(var(--senren-foreground))] outline-none transition-colors focus:ring-2 focus:ring-[hsl(var(--senren-ring))] [&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-60 [&::-webkit-calendar-picker-indicator]:hover:opacity-100 <%= self.class::VARIANTS[variant] %>">
|
|
1
|
+
<%= tag.div(**root_attrs("flex w-full items-center gap-2", data: { controller: "senren--date-picker" })) do %>
|
|
2
|
+
<input type="date" autocomplete="off" id="<%= id %>" name="<%= name %>" value="<%= value %>" placeholder="<%= placeholder %>" data-senren--date-picker-target="input" class="h-10 w-full max-w-56 cursor-pointer rounded-(--senren-radius) border bg-[hsl(var(--senren-background))] px-3 text-sm text-[hsl(var(--senren-foreground))] outline-none transition-colors focus:ring-2 focus:ring-[hsl(var(--senren-ring))] [&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-60 [&::-webkit-calendar-picker-indicator]:hover:opacity-100 <%= self.class::VARIANTS[variant] %>">
|
|
3
3
|
<button type="button" data-action="click->senren--date-picker#today" class="inline-flex h-10 cursor-pointer items-center rounded-(--senren-radius) border border-[hsl(var(--senren-border))] px-3 text-sm font-medium hover:bg-[hsl(var(--senren-accent))]">Today</button>
|
|
4
4
|
<button type="button" data-action="click->senren--date-picker#clear" class="inline-flex h-10 cursor-pointer items-center rounded-(--senren-radius) px-3 text-sm text-[hsl(var(--senren-muted-foreground))] hover:bg-[hsl(var(--senren-accent))]">Clear</button>
|
|
5
|
-
|
|
5
|
+
<% end %>
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
<%# Root goes through root_attrs so a caller's class: and data: reach it.
|
|
2
|
+
Hand-writing these attributes dropped both -- which is why a Stimulus
|
|
3
|
+
value could not be set from the server. %>
|
|
4
|
+
<%= tag.div(**wrapper_attrs(id: dom_id, data: { controller: "senren--dialog", "senren--dialog-open-value": open })) do %>
|
|
2
5
|
<% if trigger? %>
|
|
3
6
|
<span data-action="click->senren--dialog#open" data-senren--dialog-target="trigger" aria-controls="<%= dom_id %>-panel">
|
|
4
7
|
<%= trigger %>
|
|
@@ -9,13 +12,14 @@
|
|
|
9
12
|
<% end %>
|
|
10
13
|
|
|
11
14
|
<div data-senren--dialog-target="overlay" hidden
|
|
15
|
+
data-action="click->senren--dialog#close"
|
|
12
16
|
class="fixed inset-0 z-40 bg-black/50 backdrop-blur-sm"></div>
|
|
13
17
|
|
|
14
18
|
<div data-senren--dialog-target="panel" hidden
|
|
15
19
|
role="dialog" aria-modal="true" aria-labelledby="<%= dom_id %>-title"
|
|
16
20
|
id="<%= dom_id %>-panel"
|
|
17
21
|
tabindex="-1"
|
|
18
|
-
class="fixed left-1/2 top-1/2 z-50 w-full max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] text-[hsl(var(--senren-popover-foreground))] p-6 shadow-lg focus:outline-none">
|
|
22
|
+
class="<%= panel_class("fixed left-1/2 top-1/2 z-50 w-full max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] text-[hsl(var(--senren-popover-foreground))] p-6 shadow-lg focus:outline-none") %>">
|
|
19
23
|
<% if title? %>
|
|
20
24
|
<h2 id="<%= dom_id %>-title" class="text-lg font-semibold leading-none tracking-tight"><%= title %></h2>
|
|
21
25
|
<% end %>
|
|
@@ -35,4 +39,4 @@
|
|
|
35
39
|
<span aria-hidden="true">×</span>
|
|
36
40
|
</button>
|
|
37
41
|
</div>
|
|
38
|
-
|
|
42
|
+
<% end %>
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Senren
|
|
4
|
+
# Two ancestor traps to know about, because neither is visible in this file
|
|
5
|
+
# and both look like a z-index bug from the outside:
|
|
6
|
+
#
|
|
7
|
+
# * A `z-index` on any positioned ancestor creates a stacking context, and
|
|
8
|
+
# a fixed overlay cannot escape one. The panel then competes only inside
|
|
9
|
+
# that context, so a sticky header with a lower z-index still covers it.
|
|
10
|
+
# `position: relative` alone is fine; it is the z-index that traps.
|
|
11
|
+
# * `transform`, `filter`, `backdrop-filter`, `will-change` and an opacity
|
|
12
|
+
# below 1 create a stacking context too, with the same effect.
|
|
13
|
+
#
|
|
14
|
+
# Symptom in both cases: raising the panel's z-index changes nothing.
|
|
4
15
|
class DialogComponent < BaseComponent
|
|
5
16
|
renders_one :trigger
|
|
6
17
|
renders_one :title
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
<%# Root goes through root_attrs so a caller's class: and data: reach it.
|
|
2
|
+
Hand-writing these attributes dropped both -- which is why a Stimulus
|
|
3
|
+
value could not be set from the server. %>
|
|
4
|
+
<%= tag.div(**wrapper_attrs("relative inline-block", data: { controller: "senren--dropdown-menu", state: "closed" })) do %>
|
|
2
5
|
<div class="inline-flex cursor-pointer" data-state="closed" aria-haspopup="menu" aria-expanded="false" data-senren--dropdown-menu-target="trigger" data-action="click->senren--dropdown-menu#toggle keydown->senren--dropdown-menu#onTriggerKey">
|
|
3
6
|
<%= trigger %>
|
|
4
7
|
</div>
|
|
5
8
|
|
|
6
9
|
<div data-senren--dropdown-menu-target="menu" role="menu" hidden
|
|
7
|
-
class="absolute right-0 z-50 mt-2 w-56 rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] text-[hsl(var(--senren-popover-foreground))] p-1 shadow-md">
|
|
10
|
+
class="<%= panel_class("absolute right-0 z-50 mt-2 w-56 rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] text-[hsl(var(--senren-popover-foreground))] p-1 shadow-md") %>">
|
|
8
11
|
<% items.each do |it| %>
|
|
9
12
|
<%= it %>
|
|
10
13
|
<% end %>
|
|
11
14
|
</div>
|
|
12
|
-
|
|
15
|
+
<% end %>
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Senren
|
|
4
|
+
# A trigger and an absolutely positioned menu.
|
|
5
|
+
#
|
|
6
|
+
# The menu is taller than the trigger and escapes it, so an ancestor with
|
|
7
|
+
# `overflow-hidden` will clip it -- usually a card, table wrapper, or list
|
|
8
|
+
# container that has the class only to make its border radius clip children.
|
|
9
|
+
# Symptom: the menu opens but is cut off at the container's edge. Remove the
|
|
10
|
+
# overflow, or round the first and last rows instead of the wrapper.
|
|
4
11
|
class DropdownMenuComponent < BaseComponent
|
|
5
12
|
renders_one :trigger
|
|
6
13
|
renders_many :items, 'ItemTag'
|
|
@@ -17,6 +24,10 @@ module Senren
|
|
|
17
24
|
SIZES = { md: '' }.freeze
|
|
18
25
|
ITEM_ACTION = 'click->senren--dropdown-menu#close keydown->senren--dropdown-menu#onItemKey'
|
|
19
26
|
|
|
27
|
+
BASE_CLASSES = 'block w-full text-left px-3 py-2 text-sm rounded-sm ' \
|
|
28
|
+
'hover:bg-[hsl(var(--senren-accent))] focus:bg-[hsl(var(--senren-accent))] ' \
|
|
29
|
+
'outline-none cursor-pointer'
|
|
30
|
+
|
|
20
31
|
def initialize(href: nil, method: nil, destructive: false, class_name: nil, **)
|
|
21
32
|
super(variant: :default, size: :md, class_name: class_name, **)
|
|
22
33
|
@href = href
|
|
@@ -25,15 +36,56 @@ module Senren
|
|
|
25
36
|
end
|
|
26
37
|
|
|
27
38
|
def call
|
|
28
|
-
klass = 'block w-full text-left px-3 py-2 text-sm rounded-sm hover:bg-[hsl(var(--senren-accent))] focus:bg-[hsl(var(--senren-accent))] outline-none cursor-pointer'
|
|
29
|
-
klass += " #{class_name}" if class_name.present?
|
|
30
|
-
klass += ' text-[hsl(var(--senren-destructive))]' if @destructive
|
|
31
39
|
if @href
|
|
32
|
-
link_to(content, safe_url(@href), role: 'menuitem',
|
|
40
|
+
link_to(content, safe_url(@href), role: 'menuitem', class: item_classes, **item_attrs)
|
|
33
41
|
else
|
|
34
|
-
tag.button(content, type: 'button', role: 'menuitem', class:
|
|
42
|
+
tag.button(content, type: 'button', role: 'menuitem', class: item_classes, **item_attrs)
|
|
35
43
|
end
|
|
36
44
|
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# `class:` is merged, not substituted.
|
|
49
|
+
#
|
|
50
|
+
# It used to reach `call` only through **html_attrs, which the tag helper
|
|
51
|
+
# splatted over the computed class -- so `class: "font-bold"` produced an
|
|
52
|
+
# item wearing nothing but font-bold. Losing the hover style is cosmetic;
|
|
53
|
+
# losing `focus:bg-` is not, because it is the only indication a keyboard
|
|
54
|
+
# user has of where they are while arrowing through the menu.
|
|
55
|
+
#
|
|
56
|
+
# Same defect `data:` had, and the fix for `data:` was not extended here.
|
|
57
|
+
def item_classes
|
|
58
|
+
[
|
|
59
|
+
BASE_CLASSES,
|
|
60
|
+
('text-[hsl(var(--senren-destructive))]' if @destructive),
|
|
61
|
+
class_name.presence,
|
|
62
|
+
caller_class.presence
|
|
63
|
+
].compact.join(' ')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Two defects lived in the old one-line version of `call`.
|
|
67
|
+
#
|
|
68
|
+
# It wrote `data: { action: ITEM_ACTION }` and then splatted html_attrs
|
|
69
|
+
# after it, so any caller passing `data:` replaced the whole hash and
|
|
70
|
+
# silently lost close-on-click and keyboard handling. This is the same
|
|
71
|
+
# defect `root_attrs` had for `data:`, fixed there and never here --
|
|
72
|
+
# merging is the fix in both places.
|
|
73
|
+
#
|
|
74
|
+
# And `method:` was passed to link_to, which was a rails-ujs option.
|
|
75
|
+
# Rails 7 dropped rails-ujs, so it rendered a `method` attribute on an
|
|
76
|
+
# `<a>` that does nothing at all: a documented parameter that had been
|
|
77
|
+
# inert since the library targeted Rails 7.1. Turbo reads
|
|
78
|
+
# data-turbo-method -- and only on a link, which is why `method:` without
|
|
79
|
+
# `href:` emits nothing rather than an attribute a <button> cannot act on.
|
|
80
|
+
#
|
|
81
|
+
# A caller's own action is appended rather than dropped: an item that
|
|
82
|
+
# tracks a click still has to close its menu.
|
|
83
|
+
def item_attrs
|
|
84
|
+
data = merge_data(caller_data, { action: ITEM_ACTION })
|
|
85
|
+
data = data.merge(turbo_method: @method) if @method && @href
|
|
86
|
+
|
|
87
|
+
html_attrs_without_class_and_data.merge(data: data)
|
|
88
|
+
end
|
|
37
89
|
end
|
|
38
90
|
end
|
|
39
91
|
end
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
<%# Root goes through root_attrs so a caller's class: and data: reach it.
|
|
2
|
+
Hand-writing these attributes dropped both -- which is why a Stimulus
|
|
3
|
+
value could not be set from the server. %>
|
|
4
|
+
<%= tag.span(**wrapper_attrs("relative inline-block", data: { controller: "senren--hover-card" })) do %>
|
|
2
5
|
<span tabindex="0"
|
|
3
6
|
data-action="mouseenter->senren--hover-card#show mouseleave->senren--hover-card#hide focus->senren--hover-card#show blur->senren--hover-card#hide">
|
|
4
7
|
<%= trigger %>
|
|
5
8
|
</span>
|
|
6
9
|
<span hidden data-senren--hover-card-target="panel"
|
|
7
|
-
class="absolute z-40 mt-2 w-64 rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] text-[hsl(var(--senren-popover-foreground))] p-4 shadow-md">
|
|
10
|
+
class="<%= panel_class("absolute z-40 mt-2 w-64 rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] text-[hsl(var(--senren-popover-foreground))] p-4 shadow-md") %>">
|
|
8
11
|
<%= content_panel || content %>
|
|
9
12
|
</span>
|
|
10
|
-
|
|
13
|
+
<% end %>
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
</button>
|
|
8
8
|
<% end %>
|
|
9
9
|
|
|
10
|
-
<div hidden data-senren--invite-member-dialog-target="overlay"
|
|
10
|
+
<div hidden data-senren--invite-member-dialog-target="overlay"
|
|
11
|
+
data-action="click->senren--invite-member-dialog#close" class="fixed inset-0 z-40 bg-[hsl(var(--senren-foreground)/0.42)] backdrop-blur-sm"></div>
|
|
11
12
|
<div hidden id="<%= dom_id %>" data-senren--invite-member-dialog-target="panel" role="dialog" aria-modal="true" aria-labelledby="<%= dom_id %>-title" tabindex="-1" class="fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] p-6 text-[hsl(var(--senren-popover-foreground))] shadow-lg">
|
|
12
13
|
<h2 id="<%= dom_id %>-title" class="font-display text-lg font-semibold"><%= title %></h2>
|
|
13
14
|
<% if description.present? %>
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
<%= content %>
|
|
20
21
|
<% else %>
|
|
21
22
|
<label class="block text-sm font-medium text-[hsl(var(--senren-foreground))]" for="<%= dom_id %>-email">Email</label>
|
|
22
|
-
<input id="<%= dom_id %>-email" name="<%= email_name %>" type="email" placeholder="teammate@company.com" class="h-10 w-full rounded-(--senren-radius) border border-[hsl(var(--senren-input))] bg-[hsl(var(--senren-background))] px-3 text-sm outline-none focus:ring-2 focus:ring-[hsl(var(--senren-ring))]">
|
|
23
|
+
<input id="<%= dom_id %>-email" name="<%= email_name %>" type="email" autocomplete="email" placeholder="teammate@company.com" class="h-10 w-full rounded-(--senren-radius) border border-[hsl(var(--senren-input))] bg-[hsl(var(--senren-background))] px-3 text-sm outline-none focus:ring-2 focus:ring-[hsl(var(--senren-ring))]">
|
|
23
24
|
<label class="block text-sm font-medium text-[hsl(var(--senren-foreground))]" for="<%= dom_id %>-role">Role</label>
|
|
24
25
|
<select id="<%= dom_id %>-role" name="<%= role_name %>" class="h-10 w-full rounded-(--senren-radius) border border-[hsl(var(--senren-input))] bg-[hsl(var(--senren-background))] px-3 text-sm outline-none focus:ring-2 focus:ring-[hsl(var(--senren-ring))]">
|
|
25
26
|
<% roles.each do |role| %><option><%= role %></option><% end %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= tag.a content, **root_attrs("inline-flex items-center gap-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--senren-ring))] rounded-sm",
|
|
1
|
+
<%= tag.a content, href: safe_url(href), **root_attrs("inline-flex items-center gap-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--senren-ring))] rounded-sm", **external_attrs) %>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<% end %>
|
|
7
7
|
<% options.each do |opt| %>
|
|
8
8
|
<% value, label = Array === opt ? opt : [opt, opt] %>
|
|
9
|
-
<option value="<%= value %>"
|
|
9
|
+
<option value="<%= value %>" <% if selected.to_s == value.to_s %>selected<% end %>><%= label %></option>
|
|
10
10
|
<% end %>
|
|
11
11
|
<% end %>
|
|
12
12
|
<% else %>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<% end %>
|
|
19
19
|
<% options.each do |opt| %>
|
|
20
20
|
<% value, label = Array === opt ? opt : [opt, opt] %>
|
|
21
|
-
<option value="<%= value %>"
|
|
21
|
+
<option value="<%= value %>" <% if selected.to_s == value.to_s %>selected<% end %>><%= label %></option>
|
|
22
22
|
<% end %>
|
|
23
23
|
<% end %>
|
|
24
24
|
<svg aria-hidden="true" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[hsl(var(--senren-muted-foreground))] transition-transform duration-150 group-focus-within:rotate-180">
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<%= tag.nav(**root_attrs("flex flex-wrap items-center justify-center gap-1", aria: { label: label })) do %>
|
|
2
2
|
<% prev_page = current_page - 1 %>
|
|
3
3
|
<%= link_to "Previous", page_url(prev_page), class: "rounded-md border border-[hsl(var(--senren-border))] px-3 py-2 text-sm #{current_page == 1 ? "pointer-events-none opacity-50" : "hover:bg-[hsl(var(--senren-accent))]"}", aria: { disabled: current_page == 1 } %>
|
|
4
4
|
|
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
|
|
9
9
|
<% next_page = current_page + 1 %>
|
|
10
10
|
<%= link_to "Next", page_url(next_page), class: "rounded-md border border-[hsl(var(--senren-border))] px-3 py-2 text-sm #{current_page == total_pages ? "pointer-events-none opacity-50" : "hover:bg-[hsl(var(--senren-accent))]"}", aria: { disabled: current_page == total_pages } %>
|
|
11
|
-
|
|
11
|
+
<% end %>
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
<%# Root goes through root_attrs so a caller's class: and data: reach it.
|
|
2
|
+
Hand-writing these attributes dropped both -- which is why a Stimulus
|
|
3
|
+
value could not be set from the server. %>
|
|
4
|
+
<%= tag.div(**wrapper_attrs("relative inline-block", data: { controller: "senren--popover" })) do %>
|
|
2
5
|
<div data-senren--popover-target="trigger" data-action="click->senren--popover#toggle">
|
|
3
6
|
<%= trigger %>
|
|
4
7
|
</div>
|
|
5
8
|
<div data-senren--popover-target="panel" hidden role="dialog"
|
|
6
|
-
class="absolute z-40 mt-2 min-w-[12rem] rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] text-[hsl(var(--senren-popover-foreground))] p-4 shadow-md">
|
|
9
|
+
class="<%= panel_class("absolute z-40 mt-2 min-w-[12rem] rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-popover))] text-[hsl(var(--senren-popover-foreground))] p-4 shadow-md") %>">
|
|
7
10
|
<%= content_panel || content %>
|
|
8
11
|
</div>
|
|
9
|
-
|
|
12
|
+
<% end %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%= tag.article(**root_attrs("flex flex-col overflow-hidden rounded-(--senren-radius) border bg-[hsl(var(--senren-card))] shadow-sm", id: dom_id)) do %>
|
|
2
2
|
<div class="relative aspect-square w-full bg-[hsl(var(--senren-muted)/0.4)]">
|
|
3
3
|
<% if safe_image_url %>
|
|
4
|
-
|
|
4
|
+
<%= image_tag safe_image_url, alt: title, loading: "lazy", class: "h-full w-full object-cover" %>
|
|
5
5
|
<% else %>
|
|
6
6
|
<div class="flex h-full w-full items-center justify-center text-sm text-[hsl(var(--senren-muted-foreground))]" aria-hidden="true">
|
|
7
7
|
No image
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<%# A plain form: the server owns the cart, and Turbo handles the response. %>
|
|
30
30
|
<%= form_with url: safe_action_url, method: method, class: "mt-1" do %>
|
|
31
31
|
<button type="submit"
|
|
32
|
-
|
|
32
|
+
<% unless available? %>disabled<% end %>
|
|
33
33
|
class="inline-flex h-10 w-full cursor-pointer items-center justify-center rounded-(--senren-radius) bg-[hsl(var(--senren-primary))] px-4 text-sm font-medium text-[hsl(var(--senren-primary-foreground))] transition-colors hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--senren-ring))] disabled:cursor-not-allowed disabled:opacity-50">
|
|
34
34
|
<%= button_label %>
|
|
35
35
|
</button>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<%= tag.div(**root_attrs("w-full")) do %>
|
|
2
2
|
<% if label.present? %>
|
|
3
3
|
<div class="mb-2 flex items-center justify-between text-sm">
|
|
4
4
|
<span class="font-medium text-[hsl(var(--senren-foreground))]"><%= label %></span>
|
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
<div role="progressbar" aria-valuemin="0" aria-valuemax="<%= max.to_i %>" aria-valuenow="<%= value.to_i %>" class="h-2.5 w-full overflow-hidden rounded-full bg-[hsl(var(--senren-muted))]">
|
|
9
9
|
<div class="h-full rounded-full transition-[width] duration-300 ease-out <%= indicator_class %>" style="width: <%= percent %>%"></div>
|
|
10
10
|
</div>
|
|
11
|
-
|
|
11
|
+
<% end %>
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
<% else %>
|
|
5
5
|
<label class="sr-only" for="<%= name %>"><%= label %></label>
|
|
6
6
|
<span aria-hidden="true" class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--senren-muted-foreground))]">
|
|
7
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
|
|
7
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
|
|
8
8
|
<circle cx="11" cy="11" r="7"></circle>
|
|
9
9
|
<path d="m20 20-3.5-3.5"></path>
|
|
10
10
|
</svg>
|
|
11
11
|
</span>
|
|
12
|
-
<input id="<%= name %>" name="<%= name %>" type="search" value="<%= value %>" placeholder="<%= placeholder %>" aria-label="<%= label %>" class="h-10 w-full rounded-(--senren-radius) border border-[hsl(var(--senren-input))] bg-[hsl(var(--senren-background))] px-9 text-sm text-[hsl(var(--senren-foreground))] outline-none placeholder:text-[hsl(var(--senren-muted-foreground))] focus:ring-2 focus:ring-[hsl(var(--senren-ring))]">
|
|
12
|
+
<input id="<%= name %>" name="<%= name %>" type="search" autocomplete="off" value="<%= value %>" placeholder="<%= placeholder %>" aria-label="<%= label %>" class="h-10 w-full rounded-(--senren-radius) border border-[hsl(var(--senren-input))] bg-[hsl(var(--senren-background))] px-9 text-sm text-[hsl(var(--senren-foreground))] outline-none placeholder:text-[hsl(var(--senren-muted-foreground))] focus:ring-2 focus:ring-[hsl(var(--senren-ring))]">
|
|
13
13
|
<% end %>
|
|
14
14
|
<% end %>
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
<%# Root goes through root_attrs so a caller's class: and data: reach it.
|
|
2
|
+
Hand-writing these attributes dropped both -- which is why a Stimulus
|
|
3
|
+
value could not be set from the server. %>
|
|
4
|
+
<%= tag.div(**wrapper_attrs(id: dom_id, data: { controller: "senren--sheet" })) do %>
|
|
2
5
|
<% if trigger? %>
|
|
3
6
|
<span data-action="click->senren--sheet#open" data-senren--sheet-target="trigger">
|
|
4
7
|
<%= trigger %>
|
|
@@ -8,11 +11,12 @@
|
|
|
8
11
|
<% end %>
|
|
9
12
|
|
|
10
13
|
<div data-senren--sheet-target="overlay" hidden
|
|
14
|
+
data-action="click->senren--sheet#close"
|
|
11
15
|
class="fixed inset-0 z-40 bg-black/50 backdrop-blur-sm"></div>
|
|
12
16
|
|
|
13
17
|
<div data-senren--sheet-target="panel" hidden role="dialog" aria-modal="true"
|
|
14
18
|
data-open="false"
|
|
15
|
-
class="fixed z-50 bg-[hsl(var(--senren-background))] text-[hsl(var(--senren-foreground))] shadow-lg transition-transform duration-200
|
|
19
|
+
class="<%= panel_class("fixed z-50 bg-[hsl(var(--senren-background))] text-[hsl(var(--senren-foreground))] shadow-lg transition-transform duration-200 #{ self.class::VARIANTS[variant] }") %>">
|
|
16
20
|
<div class="flex h-full flex-col p-6">
|
|
17
21
|
<% if title? %>
|
|
18
22
|
<h2 class="text-lg font-semibold"><%= title %></h2>
|
|
@@ -20,7 +24,12 @@
|
|
|
20
24
|
<% if description? %>
|
|
21
25
|
<p class="text-sm text-[hsl(var(--senren-muted-foreground))]"><%= description %></p>
|
|
22
26
|
<% end %>
|
|
23
|
-
|
|
27
|
+
<%# -mx-2 px-2 gives the scroll container room for focus rings.
|
|
28
|
+
overflow-auto clips whatever draws outside a child's border box,
|
|
29
|
+
and a ring does exactly that -- a focused input sitting flush
|
|
30
|
+
against the edge had its ring shaved off on both sides. The
|
|
31
|
+
negative margin cancels the padding, so nothing moves. %>
|
|
32
|
+
<div class="-mx-2 mt-4 flex-1 overflow-auto px-2">
|
|
24
33
|
<%= body || content %>
|
|
25
34
|
</div>
|
|
26
35
|
<% if footer? %>
|
|
@@ -34,4 +43,4 @@
|
|
|
34
43
|
<span aria-hidden="true">×</span>
|
|
35
44
|
</button>
|
|
36
45
|
</div>
|
|
37
|
-
|
|
46
|
+
<% end %>
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Senren
|
|
4
|
+
# Two ancestor traps to know about, because neither is visible in this file
|
|
5
|
+
# and both look like a z-index bug from the outside:
|
|
6
|
+
#
|
|
7
|
+
# * A `z-index` on any positioned ancestor creates a stacking context, and
|
|
8
|
+
# a fixed overlay cannot escape one. The panel then competes only inside
|
|
9
|
+
# that context, so a sticky header with a lower z-index still covers it.
|
|
10
|
+
# `position: relative` alone is fine; it is the z-index that traps.
|
|
11
|
+
# * `transform`, `filter`, `backdrop-filter`, `will-change` and an opacity
|
|
12
|
+
# below 1 create a stacking context too, with the same effect.
|
|
13
|
+
#
|
|
14
|
+
# Symptom in both cases: raising the panel's z-index changes nothing.
|
|
4
15
|
class SheetComponent < BaseComponent
|
|
5
16
|
renders_one :trigger
|
|
6
17
|
renders_one :title
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<%= tag.span(**root_attrs("inline-flex items-center gap-1 text-xs text-[hsl(var(--senren-muted-foreground))]")) do %>
|
|
2
2
|
<% (keys.presence || [content]).compact.each_with_index do |key, index| %>
|
|
3
3
|
<% if index.positive? %><span aria-hidden="true">+</span><% end %>
|
|
4
4
|
<kbd class="rounded border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-muted))] px-1.5 py-0.5 font-mono text-[11px] text-[hsl(var(--senren-foreground))] shadow-sm"><%= key %></kbd>
|
|
5
5
|
<% end %>
|
|
6
|
-
|
|
6
|
+
<% end %>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<%= tag.aside(**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" })) do %>
|
|
2
2
|
<div class="mb-4 flex items-center justify-between gap-2 px-2">
|
|
3
3
|
<div data-senren--sidebar-target="brand" class="truncate font-display text-sm font-semibold tracking-tight"><%= brand %></div>
|
|
4
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 %>">
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
<% if content? %>
|
|
22
22
|
<div data-senren--sidebar-target="footer" class="mt-auto pt-4 text-xs text-[hsl(var(--senren-muted-foreground))]"><%= content %></div>
|
|
23
23
|
<% end %>
|
|
24
|
-
|
|
24
|
+
<% end %>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<%= tag.label(**root_attrs("inline-flex items-center gap-3 cursor-pointer")) do %>
|
|
2
2
|
<span class="relative inline-flex h-6 w-11 items-center rounded-full bg-[hsl(var(--senren-muted))] has-[:checked]:bg-[hsl(var(--senren-primary))] transition-colors">
|
|
3
|
-
<input type="checkbox" role="switch" id="<%= id %>" name="<%= name %>" value="<%= value %>"
|
|
3
|
+
<input type="checkbox" role="switch" id="<%= id %>" name="<%= name %>" value="<%= value %>" <% if checked %>checked<% end %> class="peer sr-only">
|
|
4
4
|
<span aria-hidden="true" class="absolute left-0.5 top-0.5 inline-block h-5 w-5 transform rounded-full bg-[hsl(var(--senren-background))] transition-transform peer-checked:translate-x-5"></span>
|
|
5
5
|
</span>
|
|
6
6
|
<% if label %>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<%= tag.div(**root_attrs("w-full overflow-hidden rounded-(--senren-radius) border border-[hsl(var(--senren-border))]")) do %>
|
|
2
2
|
<table class="w-full border-collapse text-left text-sm <%= self.class::VARIANTS[variant] %>">
|
|
3
3
|
<% if caption.present? %>
|
|
4
4
|
<caption class="sr-only"><%= caption %></caption>
|
|
@@ -23,4 +23,4 @@
|
|
|
23
23
|
<% end %>
|
|
24
24
|
</tbody>
|
|
25
25
|
</table>
|
|
26
|
-
|
|
26
|
+
<% end %>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<%= tag.div(**root_attrs("w-full", data: { controller: "senren--tabs" })) do %>
|
|
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) %>
|
|
@@ -10,9 +10,9 @@
|
|
|
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] %>" data-state="<%= selected ? "active" : "inactive" %>"
|
|
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" %>" <% unless selected %>hidden<% end %> 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 %>
|
|
17
17
|
</div>
|
|
18
|
-
|
|
18
|
+
<% end %>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
<%# Root goes through root_attrs so a caller's class: and data: reach it.
|
|
2
|
+
Hand-writing these attributes dropped both. %>
|
|
3
|
+
<%= tag.span(**wrapper_attrs("relative inline-block", data: { controller: "senren--tooltip" })) do %>
|
|
2
4
|
<span tabindex="0"
|
|
3
5
|
aria-describedby="<%= id %>"
|
|
4
6
|
data-action="mouseenter->senren--tooltip#show mouseleave->senren--tooltip#hide focus->senren--tooltip#show blur->senren--tooltip#hide">
|
|
5
7
|
<%= content %>
|
|
6
8
|
</span>
|
|
7
9
|
<span id="<%= id %>" role="tooltip" hidden data-senren--tooltip-target="bubble"
|
|
8
|
-
class="absolute z-50 mt-1 whitespace-nowrap rounded-md bg-[hsl(var(--senren-foreground))] text-[hsl(var(--senren-background))] px-2 py-1 text-xs shadow"><%= text %></span>
|
|
9
|
-
|
|
10
|
+
class="<%= panel_class("absolute z-50 mt-1 whitespace-nowrap rounded-md bg-[hsl(var(--senren-foreground))] text-[hsl(var(--senren-background))] px-2 py-1 text-xs shadow") %>"><%= text %></span>
|
|
11
|
+
<% end %>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<%= tag.header(**root_attrs("sticky top-0 z-30 border-b border-[hsl(var(--senren-border))] backdrop-blur-md")) do %>
|
|
2
2
|
<div class="mx-auto flex h-14 w-full max-w-[1400px] items-center gap-6 px-4 lg:px-8">
|
|
3
3
|
<div class="min-w-0 flex-none font-display text-sm font-semibold text-[hsl(var(--senren-foreground))]">
|
|
4
4
|
<% if brand? %><%= brand %><% else %>Senren<% end %>
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
<div class="ml-auto flex items-center gap-2"><%= actions %></div>
|
|
19
19
|
<% end %>
|
|
20
20
|
</div>
|
|
21
|
-
|
|
21
|
+
<% end %>
|
|
@@ -27,9 +27,11 @@ export default class extends Controller {
|
|
|
27
27
|
if (!line) return
|
|
28
28
|
|
|
29
29
|
const id = line.dataset.lineId
|
|
30
|
+
// Read before the node goes away.
|
|
31
|
+
const removeUrl = line.dataset.removeUrl || null
|
|
30
32
|
line.remove()
|
|
31
33
|
this._recalculate()
|
|
32
|
-
this.dispatch("removed", { detail: { id, subtotalCents: this.subtotalCentsValue } })
|
|
34
|
+
this.dispatch("removed", { detail: { id, removeUrl, subtotalCents: this.subtotalCentsValue } })
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
// The value is the state; this renders it. Called on initialization too, so
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: senren-ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- vutt
|
|
@@ -69,6 +69,7 @@ files:
|
|
|
69
69
|
- lib/generators/senren/install/templates/conventions.md.tt
|
|
70
70
|
- lib/generators/senren/install/templates/installed_components.yml.tt
|
|
71
71
|
- lib/generators/senren/install/templates/senren.css.tt
|
|
72
|
+
- lib/generators/senren/install/templates/senren_themes.css.tt
|
|
72
73
|
- lib/senren-ui.rb
|
|
73
74
|
- lib/senren/rails.rb
|
|
74
75
|
- lib/senren/rails/agent_rules_writer.rb
|