Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions docs/app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ application.register("ruby-ui--accordion", RubyUi__AccordionController)
import RubyUi__AlertDialogController from "./ruby_ui/alert_dialog_controller"
application.register("ruby-ui--alert-dialog", RubyUi__AlertDialogController)

import RubyUi__AvatarController from "./ruby_ui/avatar_controller"
application.register("ruby-ui--avatar", RubyUi__AvatarController)

import RubyUi__CalendarController from "./ruby_ui/calendar_controller"
application.register("ruby-ui--calendar", RubyUi__CalendarController)

Expand Down
29 changes: 29 additions & 0 deletions docs/app/javascript/controllers/ruby_ui/avatar_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
static targets = ["image", "fallback"];

connect() {
if (!this.hasImageTarget) return;

if (this.imageTarget.complete && this.imageTarget.naturalWidth > 0) {
this.showImage();
} else {
this.showFallback();
}
}

showImage() {
this.imageTargets.forEach((image) => image.classList.remove("hidden"));
this.fallbackTargets.forEach((fallback) =>
fallback.classList.add("hidden"),
);
}

showFallback() {
this.imageTargets.forEach((image) => image.classList.add("hidden"));
this.fallbackTargets.forEach((fallback) =>
fallback.classList.remove("hidden"),
);
}
}
3 changes: 3 additions & 0 deletions gem/lib/ruby_ui/avatar/avatar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ def view_template(&)

def default_attrs
{
data: {
controller: "ruby-ui--avatar"
},
class: ["relative flex shrink-0 overflow-hidden rounded-full", @size_classes]
}
end
Expand Down
33 changes: 33 additions & 0 deletions gem/lib/ruby_ui/avatar/avatar_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
static targets = ["image", "fallback"];

connect() {
if (!this.hasImageTarget) {
return;
}

if (this.imageTarget.complete && this.imageTarget.naturalWidth > 0) {
this.showImage();
} else {
// Image not yet loaded (or failed): hide it so the fallback shows.
// Image visibility is restored by the load/error handlers.
this.showFallback();
}
}

showImage() {
this.imageTargets.forEach((image) => image.classList.remove("hidden"));
this.fallbackTargets.forEach((fallback) =>
fallback.classList.add("hidden"),
);
}

showFallback() {
this.imageTargets.forEach((image) => image.classList.add("hidden"));
this.fallbackTargets.forEach((fallback) =>
fallback.classList.remove("hidden"),
);
}
}
3 changes: 3 additions & 0 deletions gem/lib/ruby_ui/avatar/avatar_fallback.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ def view_template(&)

def default_attrs
{
data: {
ruby_ui__avatar_target: "fallback"
},
class: "flex h-full w-full items-center justify-center rounded-full bg-muted"
}
end
Expand Down
4 changes: 4 additions & 0 deletions gem/lib/ruby_ui/avatar/avatar_image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ def view_template
def default_attrs
{
loading: "lazy",
data: {
ruby_ui__avatar_target: "image",
action: "load->ruby-ui--avatar#showImage error->ruby-ui--avatar#showFallback"
},
class: "aspect-square h-full w-full",
alt: @alt,
src: @src
Expand Down
6 changes: 6 additions & 0 deletions gem/test/ruby_ui/avatar_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@ def test_render_with_all_items
end

assert_match(/joeldrapper/, output)
assert_match(/data-controller="ruby-ui--avatar"/, output)
assert_match(/data-ruby-ui--avatar-target="image"/, output)
assert_match(/load->ruby-ui--avatar#showImage error->ruby-ui--avatar#showFallback/, output)
assert_match(/data-ruby-ui--avatar-target="fallback"/, output)
assert_match(/class="aspect-square h-full w-full"/, output)
refute_match(/class="[^"]*\bhidden\b[^"]*aspect-square/, output)
end
end