Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List: Sortable list interactions update #1541

Merged
merged 15 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 49 additions & 14 deletions docs/app/views/examples/components/list/_preview.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -55,41 +55,76 @@ sample_products = [

<h3 class="<%= SageClassnames::TYPE::HEADING_6 %>">Default configuration</h3>
<%= md("
SageList is implemented most simply by passing the desired contents for the items through the `SageList` `items` property.
This assumes that the contents of the list are already formatted as desired.
`SageList` is implemented most simply by passing the desired contents for the items through the `SageList` `items` property.
This assumes that the contents of the list are already formatted as desired or you're outputting simple content values.
", use_sage_type: true) %>

<%= sage_component SageList, {
items: sample_products.map { | item | {
content: "Item #{item[:name]}",
id: "example-default-item-#{item[:id]}",
more_actions: { items: dropdown_items(item[:id]) },
}
},
} %>

<h3 class="<%= SageClassnames::TYPE::HEADING_6 %>">Sortable configuration</h3>
<%= md("
Sortable lists can be created with `SageList` by adding `sortable: true` and a `sortable_resource`.
You can also pass a `sortable_update_url` for sorting callbacks to items
that will be called after the user finishes sorting an item.
", use_sage_type: true) %>

<%= sage_component SageList, {
sortable: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sortable examples, thoughts on adding tag: "ol" to drive home the ordinal values?

I suppose we could also be more heavy-handed and change the default to an ol when setting sortable: true in the component, but that's beyond the scope of this work.

sortable_resource: "sample_products",
items: sample_products.map { | item | {
content: %(
<h4>Item #{item[:name]}</h4>
<p>Item #{item[:id]} specs</p>
).html_safe,
id: item[:id],
content: "Item #{item[:name]}",
id: "example-sortable-item-#{item[:id]}",
more_actions: { items: dropdown_items(item[:id]) },
sortable: true,
sortable_update_url: "#sortable-update-url?item=#{item[:id]}",
}
}
},
} %>

<h3 class="<%= SageClassnames::TYPE::HEADING_6 %>">Fully draggable row</h3>
<%= md("
By default only the drag handle is active for dragging/sorting a row.
However, `draggable_anywhere` can be set to `true` in order to allow the whole row to be draggable instead.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you need to change draggable_anywhere to "drag_handle_type can be set to row" here.

", use_sage_type: true) %>

<%= sage_component SageList, {
sortable: true,
sortable_resource: "sample_products_2",
items: sample_products.map { | item | {
content: "Item #{item[:name]}",
id: "example-fully-draggable-item-#{item[:id]}",
more_actions: { items: dropdown_items(item[:id]) },
sortable_update_url: "#sortable-update-url?item=#{item[:id]}",
}},
drag_handle_type: "row",
} %>

<h3 class="<%= SageClassnames::TYPE::HEADING_6 %>">Compositional approach</h3>
<%= md("
If you need more native content formatting you can instead opt to render items using a nested loop and the SageListItem component.
If you need more native content formatting you can instead opt to render items using a nested loop and the `SageListItem` component.
Note that this example also implements the [Reveal utility class](#{pages_helpers_path(:reveal)}).
", use_sage_type: true) %>

<%= sage_component SageList, { sortable_resource: "sample_products_2" } do %>
<%= sage_component SageList, {
sortable: true,
sortable_resource: "sample_products_3",
drag_handle_type: "row",
} do %>
<% sample_products.each do | item | %>
<%= sage_component SageListItem, {
id: item[:id],
id: "example-default-item-#{item[:id]}",
more_actions: { items: dropdown_items(item[:id]) },
sortable: true,
sortable_update_url: "#sortable-update-url?item={item[:id]}",
css_classes: SageClassnames::REVEAL_CONTAINER,
} do %>
<%= sage_component SageCardRow, { grid_template: "ete" } do %>
<img src="//source.unsplash.com/random/240x160?v=<%= item[:id] %>" width="100" alt="" />
<img src="https://source.unsplash.com/random/240x160?v=<%= item[:id] %>" width="64" alt="" />
<%= sage_component SageCardBlock, {} do %>
<h4><%= item[:name] %></h4>
<p>Item <%= item[:id] %> specs</p>
Expand Down
15 changes: 9 additions & 6 deletions docs/app/views/examples/components/list/_props.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@
<td><%= md('See schema for `SageListItem` below.') %></td>
<td><%= md('`[]`') %></td>
</tr>
<tr>
<td><%= md('`drag_handle_type`') %></td>
<td><%= md('
By default only the drag handle is active for dragging/sorting a row.
However, `drag_handle_type` can be set to `"row"` in order to allow the whole row to be draggable instead.
') %></td>
<td><%= md('`"default"` | `"row"` ') %></td>
<td><%= md('`"default"`') %></td>
</tr>
<tr>
<td><%= md('`sortable_resource`') %></td>
<td><%= md('Provide the resource name for a sortable list. Required only when `sortable` is activated for any child items') %></td>
<td><%= md('String') %></td>
<td><%= md('`nil`') %></td>
</tr>
<tr>
<td><%= md('`hide_first_border`') %></td>
<td><%= md('Removes top border from first list item within a SageList') %></td>
<td><%= md('Boolean') %></td>
<td><%= md('`false`') %></td>
</tr>
<tr>
<td colspan="4">
<%= md("**SageListItem**") %>
Expand Down
4 changes: 2 additions & 2 deletions docs/lib/sage_rails/app/sage_tokens/sage_schemas.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,15 @@ module SageSchemas
LIST_ITEM = {
id: [:optional, Integer, String],
more_actions: [:optional, NilClass, SageSchemas::DROPDOWN],
sortable: [:optional, NilClass, TrueClass],
sortable_update_url: [:optional, NilClass, String],
}

LIST = {
drag_handle_type: [:optional, NilClass, Set.new(["default", "row"])],
items: [:optional, [[SageSchemas::LIST_ITEM]]],
sortable: [:optional, NilClass, TrueClass],
sortable_resource: [:optional, NilClass, String],
tag: [:optional, NilClass, Set.new(["ul", "ol"])],
hide_first_border: [:optional, TrueClass, String],
}

PANEL_FIGURE = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<%
tag = component.tag.present? ? component.tag : "ul"
drag_handle_type = component.tag.present? ? component.tag : "default"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may have been an oversight or a misunderstanding from me. drag_handle_type accepts two options based on the code above default and row. So if component.tag is anything other than those two values, what would be the expectation here?

Is this tag: [:optional, NilClass, Set.new(["ul", "ol"])], the same component.tag values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow good catch. I'll touch this up for sure!

%>
<<%= tag %>
class="
sage-list
<%= component.generated_css_classes %>
<%= "sage-list--hide-first-border" if component.hide_first_border %>
<%= "sage-list--sortable" if component.sortable %>
<%= "sage-list--draggable-by-row" if component.drag_handle_type == "row" %>
"
<%= "data-js-list-sortable=#{component.sortable_resource}" if component.sortable_resource.present? %>
<%= component.generated_html_attributes.html_safe %>
Expand All @@ -15,6 +17,5 @@ tag = component.tag.present? ? component.tag : "ul"
<%= sage_component SageListItem, item_configs %>
<% end %>
<% end %>

<%= component.content %>
</<%= tag %>>
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,15 @@ end
<li
class="
sage-list__item
<%= "sage-list__item--sortable" if component.sortable %>
<%= component.generated_css_classes %>
"
<%= "data-js-list-sortable-update-url=#{component.sortable_update_url}" if component.sortable_update_url.present? %>
<%= "id=#{component.id}" if component.id %>
<%= component.generated_html_attributes.html_safe %>
>
<% if component.sortable %>
<div class="sage-list__item-sortable-handle">
<%= sage_component SageIcon, { icon: "handle" } %>
</div>
<% end %>
<div class="sage-list__item-sortable-handle">
<%= sage_component SageIcon, { icon: "handle-2-vertical" } %>
</div>

<div class="sage-list__item-content">
<%= component.content %>
Expand Down
64 changes: 46 additions & 18 deletions packages/sage-assets/lib/stylesheets/components/_list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,45 +13,73 @@
.sage-list__item {
display: flex;
align-items: center;
gap: sage-spacing(md);
padding-top: sage-spacing(sm);
padding-bottom: sage-spacing(sm);
gap: sage-spacing(sm);
padding: sage-spacing(xs) rem(12px);
list-style: none;
border-top: sage-border(default);
border-radius: sage-border(radius-medium);

&:first-child {
.sage-list--hide-first-border & {
border-top: 0;
}
&:hover {
background-color: sage-color(grey, 200);
}

.sage-list--draggable-by-row & {
cursor: grab;
}
}

.sage-list__item--sortable {
cursor: grab;
.sage-list__item--sortable-active {
background-color: sage-color(white);

&.sage-list__item--sortable-active:active,
&:active {
.sage-list--draggable-by-row & {
cursor: grabbing;
}
}

.sage-list__item--sortable-drag {
background-color: sage-color(white);
box-shadow: sage-shadow(lg);
// NOTE: !important is added and lint-ignored here so that it can override
// inline opacity that the SortableJS utility we're using adds during sorting.
opacity: 1 !important; /* stylelint-disable-line declaration-no-important */
}

.sage-list__item--sortable-ghost {
opacity: 0.5;
background-color: sage-color(grey, 200);
}

.sage-list__item-content {
flex: 1;

.sage-list__item--sortable-ghost & {
opacity: 0;
}
}

.sage-list__item-more-actions,
.sage-list__item-sortable-handle {
.sage-list__item-more-actions {
width: auto;

.sage-list__item--sortable-ghost & {
opacity: 0;
}
}

.sage-list__item-sortable-handle {
color: sage-color(charcoal, 100);
display: none;

.sage-list--sortable & {
display: initial;
width: auto;
}

&:hover {
cursor: grab;
}

.sage-list__item--sortable-active & {
cursor: grabbing;
}

.sage-list__item:hover & {
color: sage-color(charcoal, 500);
.sage-list__item--sortable-ghost & {
opacity: 0;
}
}
5 changes: 3 additions & 2 deletions packages/sage-assets/lib/stylesheets/components/_lists.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
.sage-list {
padding-left: 0;
list-style: none;

&:not(.sage-list--inline-compact):not(.sage-list--inline-fit-compact) {

// TODO: These styles interfered with the new List component and should be deprecated.
&:not(.sage-list--inline-compact):not(.sage-list--inline-fit-compact):not(.sage-list__item) {
margin-right: -1 * sage-spacing(sm);
margin-left: -1 * sage-spacing(sm);

Expand Down
Loading