Merge pull request #898 from basecamp/more-navigable-lists

More navigable lists
This commit is contained in:
Andy Smith
2025-08-13 12:53:28 -05:00
committed by GitHub
15 changed files with 76 additions and 29 deletions
+8 -7
View File
@@ -65,7 +65,7 @@
&:focus-visible {
.input--file:has(&) {
outline: 0.15rem solid var(--color-selected-dark);
}
}
}
}
@@ -177,12 +177,6 @@
}
}
.switch__input:disabled + & {
--switch-color: var(--color-ink-medium) !important;
cursor: not-allowed;
}
.switch__input:checked + & {
--switch-color: var(--color-link);
@@ -190,6 +184,13 @@
transform: translateX(1.2em);
}
}
.switch__input:disabled + & {
--switch-color: var(--color-ink-medium);
cursor: not-allowed;
opacity: 0.5;
}
}
/* Containers that act like (and contain) inputs */
+30 -3
View File
@@ -1,6 +1,7 @@
@layer components {
.settings {
--settings-spacer: var(--block-space);
--settings-item-padding-inline: 0.5ch;
display: grid;
gap: calc(var(--settings-spacer) * 2);
@@ -55,14 +56,21 @@
.settings__user-list {
flex: 1 1 auto;
inline-size: 100%;
list-style: none;
margin: calc(var(--settings-spacer) / -4) 0;
margin: calc(var(--settings-spacer) / -4) calc(-1 * var(--settings-item-padding-inline));
padding: 0;
overflow: auto;
li {
border-radius: 0.5em;
/* Add padding if it's not already on a link within */
&:not(:has(a:first-child)) { padding-inline-end: var(--settings-item-padding-inline); }
&:not(:has(a:last-child)) { padding-inline-end: var(--settings-item-padding-inline); }
}
a {
padding-block: calc(var(--settings-spacer) / 4);
padding: calc(var(--settings-spacer) / 4) var(--settings-item-padding-inline);
@media(any-hover: hover) {
&:hover {
@@ -70,5 +78,24 @@
}
}
}
/* Only add a BG color when you can actually navigate */
.settings__user-filter:focus-within & {
[aria-selected] {
background: var(--color-selected);
}
}
}
.settings__user-list-tips {
border-top: 1px solid var(--color-ink-light);
color: var(--color-ink-dark);
font-size: var(--text-small);
padding-block-start: var(--settings-spacer);
.settings__user-filter--bg & {
margin-inline: calc(-1 * var(--settings-spacer));
padding-inline: var(--settings-spacer);
}
}
}
+4 -1
View File
@@ -1,7 +1,10 @@
module AccessesHelper
def access_menu_tag(collection, **options, &)
tag.menu class: [ options[:class], { "toggler--toggled": collection.all_access? } ], data: {
controller: "filter toggle-class",
controller: "filter toggle-class navigable-list",
action: "keydown->navigable-list#navigate filter:changed->navigable-list#reset",
navigable_list_focus_on_selection_value: true,
navigable_list_actionable_items_value: true,
toggle_class_toggle_class: "toggler--toggled" }, &
end
+1 -1
View File
@@ -20,7 +20,7 @@ module AvatarsHelper
end
def avatar_preview_tag(user, hidden_for_screen_reader: false, **options)
tag.span class: "avatar",
tag.span class: class_names("avatar", options.delete(:class)),
aria: { hidden: hidden_for_screen_reader, label: user.name },
tabindex: hidden_for_screen_reader ? -1 : nil do
avatar_image_tag(user, **options)
@@ -106,9 +106,13 @@ export default class extends Controller {
#toggleCurrentItem(event) {
if (this.actionableItemsValue && this.currentItem && this.#visibleItems.length) {
const toggleable = this.currentItem.querySelector("input[type=checkbox]")
const isDisabled = toggleable.hasAttribute("disabled")
if (toggleable) {
toggleable.checked = !toggleable.checked
toggleable.dispatchEvent(new Event('change', { bubbles: true }))
if (!isDisabled) {
toggleable.checked = !toggleable.checked
toggleable.dispatchEvent(new Event('change', { bubbles: true }))
}
event.preventDefault()
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
<li class="flex align-center gap-half" data-filter-target="item">
<li class="flex align-center gap-half" data-filter-target="item" data-navigable-list-target="item">
<%= link_to user, class: "txt-ink flex gap-half align-center min-width" do %>
<%= avatar_preview_tag user, hidden_for_screen_reader: true %>
<strong class="overflow-ellipsis"><%= user.name %></strong>
+11 -2
View File
@@ -1,4 +1,9 @@
<div class="settings__panel settings__panel--users panel shadow center" data-controller="filter">
<%= tag.div class: "settings__panel settings__panel--users panel shadow center", data: {
controller: "filter navigable-list",
action: "keydown->navigable-list#navigate filter:changed->navigable-list#reset",
navigable_list_focus_on_selection_value: true,
navigable_list_actionable_items_value: true
} do %>
<h2 class="divider txt-large">People on the account</h2>
<%#= render "accounts/invite" # Scheduled work in https://3.basecamp.com/2914079/buckets/37331921/todos/8746162337 %>
@@ -8,5 +13,9 @@
<ul class="settings__user-list" data-filter-target="list">
<%= render partial: "accounts/user", collection: users %>
</ul>
<footer class="settings__user-list-tips">
Press <kbd class="kbd">↑</kbd><kbd class="kbd">↓</kbd> to move, <kbd class="kbd">enter</kbd> to visit profile, <kbd class="kbd">SHIFT+ENTER</kbd> to toggle.
</footer>
</div>
</div>
<% end %>
+5 -5
View File
@@ -21,13 +21,9 @@
<h3 class="card__title overflow-line-clamp">
<%= card.title %>
</h3>
<%= link_to collection_card_path(card.collection, card), draggable: false, class: "card__link", title: card_title_tag(card), data: { action: "dialog#close", turbo_frame: "_top" } do %>
<span class="for-screen-reader"><%= card.title %></span>
<% end %>
</div>
<%= render "cards/stagings/stages", card: card if card.doing? %>
<%= render "cards/display/preview/stages", card: card if card.doing? %>
</div>
</div>
@@ -36,6 +32,10 @@
<%= render "cards/display/common/background", card: card %>
</footer>
<%= link_to collection_card_path(card.collection, card), draggable: false, class: "card__link", title: card_title_tag(card), data: { action: "dialog#close", turbo_frame: "_top" } do %>
<span class="for-screen-reader"><%= card.title %></span>
<% end %>
<% if card.entropic? %>
<%= render "cards/display/preview/bubble", card: card %>
<% end %>
@@ -1,6 +1,6 @@
<div class="display-contents" id="<%= dom_id(card, :assignees) %>">
<% card.assignees.each do |assignee| %>
<%= avatar_tag assignee %>
<%= avatar_tag assignee, tabindex: (local_assigns.key?(:preview) && local_assigns[:preview]) ? -1 : 0 %>
<% end %>
<div class="position-relative" data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside" <%= "hidden" if card.closed? %>>
@@ -1,7 +1,7 @@
<div class="card__meta" id="<%= dom_id(card, :meta) %>">
<div class="card__meta-grid card__meta-grid--author">
<%= avatar_tag card.creator, class: "card__meta-author" %>
<%= avatar_tag card.creator, class: "card__meta-author", tabindex: (local_assigns.key?(:preview) && local_assigns[:preview]) ? -1 : 0 %>
<span class="card__meta-text card__meta-text--added overflow-ellipsis">
Added <%= local_datetime_tag(card.created_at, style: :daysago) %>
@@ -1 +1 @@
<%= render "cards/display/common/assignees", card: card %>
<%= render "cards/display/common/assignees", card: card, preview: true %>
@@ -1,3 +1,3 @@
<%= render "cards/display/common/meta", card: card do %>
<%= render "cards/display/common/meta", card: card, preview: true do %>
<%= render "cards/display/preview/assignees", card: card %>
<% end %>
@@ -1,3 +1,3 @@
<%= render "cards/display/common/people", card: card do%>
<%= render "cards/display/preview/assignees", card: card %>
<%= render "cards/display/preview/assignees", card: card, preview: true %>
<% end %>
+1 -2
View File
@@ -1,5 +1,4 @@
<li class="flex align-center gap-half" data-filter-target="item">
<li class="flex align-center gap-half" data-filter-target="item" data-navigable-list-target="item">
<%= link_to user, class: "txt-ink flex gap-half align-center min-width" do %>
<%= avatar_preview_tag user, hidden_for_screen_reader: true %>
<strong class="overflow-ellipsis"><%= user.name %></strong>
@@ -24,4 +24,8 @@
<%= access_toggles_for selected_users, selected: true %>
<%= access_toggles_for unselected_users, selected: false %>
</ul>
<footer class="settings__user-list-tips">
Press <kbd class="kbd">↑</kbd><kbd class="kbd">↓</kbd> to move, <kbd class="kbd">enter</kbd> to visit profile, <kbd class="kbd">SHIFT+ENTER</kbd> to toggle.
</footer>
<% end %>