Skip to content
Merged
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
80 changes: 15 additions & 65 deletions app/components/lessons/sidebar_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,72 +1,22 @@
<%# Off-canvas drawer — below xl %>
<div
class="relative z-40 xl:hidden lesson-sidebar-drawer hidden"
role="dialog"
aria-modal="true"
aria-label="Course contents"
data-controller="visibility"
data-visibility-target="content"
data-visibility-visible-value="false">

<div
class="fixed inset-0 bg-gray-900/80 dark:bg-black/70 hidden"
data-visibility-target="content"
data-action="click->visibility#off"
aria-hidden="true"
data-transition-enter="transition-opacity ease-linear duration-200"
data-transition-enter-start="opacity-0"
data-transition-enter-end="opacity-100"
data-transition-leave="transition-opacity ease-linear duration-200"
data-transition-leave-start="opacity-100"
data-transition-leave-end="opacity-0"></div>

<div class="fixed inset-0 flex">
<div
class="relative mr-16 flex w-full max-w-xs flex-1 hidden"
data-visibility-target="content"
data-transition-enter="transition ease-in-out duration-200 transform"
data-transition-enter-start="-translate-x-full"
data-transition-enter-end="translate-x-0"
data-transition-leave="transition ease-in-out duration-200 transform"
data-transition-leave-start="translate-x-0"
data-transition-leave-end="-translate-x-full">

<div
class="absolute left-full top-0 flex w-16 justify-center pt-5 hidden"
data-visibility-target="content"
data-transition-enter="ease-in-out duration-200"
data-transition-enter-start="opacity-0"
data-transition-enter-end="opacity-100"
data-transition-leave="ease-in-out duration-200"
data-transition-leave-start="opacity-100"
data-transition-leave-end="opacity-0">

<button type="button" class="-m-2.5 p-2.5" data-action="click->visibility#off">
<span class="sr-only">Close course contents</span>
<%= inline_svg_tag 'icons/x-mark.svg', class: 'h-6 w-6 text-white', aria: true, title: 'Close course contents' %>
</button>
</div>

<div class="flex grow flex-col gap-y-4 overflow-y-auto scrollbar-thin bg-white dark:bg-gray-900 px-4 pb-6 pt-5">
<div class="flex flex-col gap-2">
<%= render Ui::BackLinkComponent.new(path: path_course_path(course.path, course), name: 'Back to course') %>

<%= link_to course.title, path_course_path(course.path, course), class: 'text-lg font-semibold text-gray-800 dark:text-gray-200 no-underline hover:underline' %>
<%= render Overlays::DrawerComponent.new(hook_class: 'lesson-sidebar-drawer', breakpoint: :xl, close_label: 'Close course contents', aria_label: 'Course contents') do %>
<div class="flex grow flex-col gap-y-4 overflow-y-auto scrollbar-thin bg-white dark:bg-gray-900 px-4 pb-6 pt-5">
<div class="flex flex-col gap-2">
<%= render Ui::BackLinkComponent.new(path: path_course_path(course.path, course), name: 'Back to course') %>

<% if progress_percentage %>
<%= render Lessons::Sidebar::ProgressBarComponent.new(percentage: progress_percentage) %>
<% end %>
</div>
<%= link_to course.title, path_course_path(course.path, course), class: 'text-lg font-semibold text-gray-800 dark:text-gray-200 no-underline hover:underline' %>

<nav aria-label="Course contents">
<% sections.each do |section| %>
<%= render Lessons::Sidebar::SectionComponent.new(section:, current_lesson:, current_user:) %>
<% end %>
</nav>
</div>
<% if progress_percentage %>
<%= render Lessons::Sidebar::ProgressBarComponent.new(percentage: progress_percentage) %>
<% end %>
</div>

<nav aria-label="Course contents">
<% sections.each do |section| %>
<%= render Lessons::Sidebar::SectionComponent.new(section:, current_lesson:, current_user:) %>
<% end %>
</nav>
</div>
</div>
<% end %>

<%# Static desktop sidebar — xl+ %>
<aside class="hidden xl:block xl:h-full" data-test-id="lesson-sidebar">
Expand Down
52 changes: 52 additions & 0 deletions app/components/overlays/drawer_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<div
class="relative z-50 hidden <%= breakpoint_class %> <%= hook_class %>"
role="dialog"
aria-modal="true"
aria-label="<%= aria_label %>"
data-controller="visibility"
data-visibility-target="content"
data-visibility-visible-value="false">

<div
class="fixed inset-0 bg-gray-900/80 dark:bg-black/70 hidden"
data-visibility-target="content"
data-action="click->visibility#off"
aria-hidden="true"
data-transition-enter="transition-opacity ease-linear duration-200"
data-transition-enter-start="opacity-0"
data-transition-enter-end="opacity-100"
data-transition-leave="transition-opacity ease-linear duration-200"
data-transition-leave-start="opacity-100"
data-transition-leave-end="opacity-0">
</div>

<div class="fixed inset-0 flex">
<div
class="relative mr-16 flex w-full max-w-xs flex-1 hidden"
data-visibility-target="content"
data-transition-enter="transition ease-in-out duration-200 transform"
data-transition-enter-start="-translate-x-full"
data-transition-enter-end="translate-x-0"
data-transition-leave="transition ease-in-out duration-200 transform"
data-transition-leave-start="translate-x-0"
data-transition-leave-end="-translate-x-full">

<div
class="absolute left-full top-0 flex w-16 justify-center pt-5 hidden"
data-visibility-target="content"
data-transition-enter="ease-in-out duration-200"
data-transition-enter-start="opacity-0"
data-transition-enter-end="opacity-100"
data-transition-leave="ease-in-out duration-200"
data-transition-leave-start="opacity-100"
data-transition-leave-end="opacity-0">
<button type="button" class="-m-2.5 p-2.5" data-action="click->visibility#off">
Comment thread
KevinMulhern marked this conversation as resolved.
<span class="sr-only"><%= close_label %></span>
<%= inline_svg_tag 'icons/x-mark.svg', class: 'h-6 w-6 text-white', aria: true, title: close_label %>
</button>
</div>

<%= content %>
</div>
</div>
</div>
22 changes: 22 additions & 0 deletions app/components/overlays/drawer_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Overlays::DrawerComponent < ApplicationComponent
BREAKPOINT_CLASSES = { lg: 'lg:hidden', xl: 'xl:hidden' }.freeze

def initialize(hook_class:, aria_label:, breakpoint: :lg, close_label: 'Close sidebar')
unless BREAKPOINT_CLASSES.key?(breakpoint)
raise ArgumentError, "Unsupported breakpoint: #{breakpoint.inspect}. Must be one of #{BREAKPOINT_CLASSES.keys}"
end

@hook_class = hook_class
@breakpoint = breakpoint
@close_label = close_label
@aria_label = aria_label
end

private

attr_reader :hook_class, :breakpoint, :close_label, :aria_label

def breakpoint_class
BREAKPOINT_CLASSES.fetch(breakpoint)
end
end
86 changes: 17 additions & 69 deletions app/views/layouts/admin/_sidebar_nav.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,78 +20,26 @@ nav_links = [
]
%>

<!-- Off-canvas menu for mobile, show/hide based on off-canvas menu state. -->
<div
class="relative z-50 lg:hidden off-canvas-menu"
role="dialog"
aria-modal="true"
data-controller="visibility"
data-visibility-target="content"
data-visibility-visible-value="false">

<div
class="fixed inset-0 bg-gray-900/80 hidden"
data-visibility-target="content"
data-action="click->visibility#off"
aria-hidden="true"
data-transition-enter="transition-opacity ease-linear duration-200"
data-transition-enter-start="opacity-0"
data-transition-enter-end="opacity-100"
data-transition-leave="transition-opacity ease-linear duration-200"
data-transition-leave-start="opacity-100"
data-transition-leave-end="opacity-0">
</div>

<div class="fixed inset-0 flex">

<div
class="relative mr-16 flex w-full max-w-xs flex-1 hidden"
data-visibility-target="content"
data-transition-enter="transition ease-in-out duration-200 transform"
data-transition-enter-start="-translate-x-full"
data-transition-enter-end="translate-x-0"
data-transition-leave="transition ease-in-out duration-200 transform"
data-transition-leave-start="translate-x-0"
data-transition-leave-end="-translate-x-full">

<div
class="absolute left-full top-0 flex w-16 justify-center pt-5"
data-visibility-target="content"
data-transition-enter="ease-in-out duration-200"
data-transition-enter-start="opacity-0"
data-transition-enter-end="opacity-100"
data-transition-leave="ease-in-out duration-200"
data-transition-leave-start="opacity-100"
data-transition-leave-end="opacity-0">

<button type="button" class="-m-2.5 p-2.5" data-action="click->visibility#off">
<span class="sr-only">Close sidebar</span>
<%= inline_svg_tag 'icons/x-mark.svg', class: 'h-6 w-6 text-white', aria: true, title: 'Close menu' %>
</button>
</div>

<!-- Sidebar component -->
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-white dark:bg-gray-800 px-6 pb-4">
<div class="flex h-16 shrink-0 items-center">
<%= render LogoComponent.new(classes: 'block h-10 w-auto', path: admin_root_path) %>
</div>
<%= render Overlays::DrawerComponent.new(hook_class: 'off-canvas-menu', close_label: 'Close sidebar', aria_label: 'Admin navigation') do %>
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-white dark:bg-gray-800 px-6 pb-4">
<div class="flex h-16 shrink-0 items-center">
<%= render LogoComponent.new(classes: 'block h-10 w-auto', path: admin_root_path) %>
</div>

<nav class="flex flex-1 flex-col">
<ul role="list" class="flex flex-1 flex-col gap-y-7">
<%= render 'layouts/admin/nav_links', nav_links: %>
<nav class="flex flex-1 flex-col">
<ul role="list" class="flex flex-1 flex-col gap-y-7">
<%= render 'layouts/admin/nav_links', nav_links: %>

<li class="mt-auto">
<%= link_to '#', class: 'group -mx-2 flex gap-x-3 rounded-md p-2 text-sm font-semibold leading-6 text-gray-700 hover:bg-gray-200 hover:text-gray-800 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-200' do %>
<%= inline_svg_tag 'icons/gear.svg', class: 'h-5 w-5 shrink-0 text-gray-500 group-hover:text-gray-800 dark:text-gray-400 dark:group-hover:text-gray-200' %>
Settings
<% end %>
</li>
</ul>
</nav>
</div>
</div>
<li class="mt-auto">
<%= link_to '#', class: 'group -mx-2 flex gap-x-3 rounded-md p-2 text-sm font-semibold leading-6 text-gray-700 hover:bg-gray-200 hover:text-gray-800 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-200' do %>
<%= inline_svg_tag 'icons/gear.svg', class: 'h-5 w-5 shrink-0 text-gray-500 group-hover:text-gray-800 dark:text-gray-400 dark:group-hover:text-gray-200' %>
Settings
<% end %>
</li>
</ul>
</nav>
</div>
</div>
<% end %>

<!-- Static sidebar for desktop -->
<div class="hidden lg:fixed lg:inset-y-0 lg:z-30 lg:flex lg:w-72 lg:flex-col">
Expand Down
120 changes: 35 additions & 85 deletions app/views/shared/_off_canvas_menu.html.erb
Original file line number Diff line number Diff line change
@@ -1,90 +1,40 @@
<div
data-controller="visibility"
data-visibility-target="content"
data-visibility-visible-value="false"
class="fixed inset-0 flex z-40 hidden lg:hidden off-canvas-menu"
role="dialog"
aria-modal="true">

<div
data-visibility-target="content"
data-action="click->visibility#off"
class="fixed inset-0 bg-gray-600 bg-opacity-75 dark:bg-black dark:bg-opacity-60 hidden"
aria-hidden="true"
data-transition-enter="transition-opacity ease-linear duration-200"
data-transition-enter-start="transform opacity-0"
data-transition-enter-end="transform opacity-100"
data-transition-leave="transition-opacity ease-linear duration-200"
data-transition-leave-start="transform opacity-100"
data-transition-leave-end="transform opacity-0">
</div>

<div
data-visibility-target="content"
class="relative flex-1 flex flex-col max-w-xs w-full bg-white focus:outline-hidden hidden dark:bg-gray-800"
data-transition-enter="transition ease-in-out duration-200 transform"
data-transition-enter-start="-translate-x-full"
data-transition-enter-end="translate-x-0"
data-transition-leave="transition ease-in-out duration-200 transform"
data-transition-leave-start="translate-x-0"
data-transition-leave-end="-translate-x-full">

<div
data-visibility-target="content"
class="absolute top-2 right-2 -mr-12 pt-3 hidden"
data-transition-enter="transition ease-in-out duration-200"
data-transition-enter-start="transform opacity-0"
data-transition-enter-end="transform opacity-100"
data-transition-leave="transition ease-in duration-200"
data-transition-leave-start="transform opacity-100"
data-transition-leave-end="transform opacity-0">
<button type="button" data-action="click->visibility#off" class="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-hidden focus:ring-2 focus:ring-inset focus:ring-white">
<span class="sr-only">Close sidebar</span>
<%= inline_svg_tag 'icons/x-mark.svg', class: 'h-6 w-6 text-white', aria: true, title: 'Close menu' %>
</button>
<%= render Overlays::DrawerComponent.new(hook_class: 'off-canvas-menu', close_label: 'Close sidebar', aria_label: 'Main navigation') do %>
<div class="flex grow flex-col pt-3 pb-4 overflow-y-auto bg-white dark:bg-gray-800">
<div class="shrink-0 flex items-center px-4">
<%= render LogoComponent.new(classes: 'h-12 w-auto') %>
</div>

<div class="flex-1 h-0 pt-3 pb-4 overflow-y-auto">
<div class="shrink-0 flex items-center px-4">
<%= render LogoComponent.new(classes: 'h-12 w-auto') %>
<nav aria-label="Sidebar" class="mt-3">
<div class="px-2 space-y-1">
<% if user_signed_in? %>
<%= render Nav::ItemComponent.new(path: dashboard_path, text: 'Dashboard', test_id: 'nav-dashboard', icon_path: 'icons/home.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: paths_url, text: 'All Paths', test_id: 'nav-all-paths', icon_path: 'icons/map.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: notifications_path, text: 'Notifications', test_id: 'nav-notifications', icon_path: 'icons/bell.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: support_us_path, text: 'Support Us', test_id: 'nav-support-us', icon_path: 'icons/heart-outline.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: ODIN_CHAT_URL, text: 'Community', test_id: 'nav-community', icon_path: 'icons/speech-bubbles.svg', options: { mobile: true }) %>
<% else %>
<%= render Nav::ItemComponent.new(path: root_path, text: 'Home', test_id: 'nav-home', icon_path: 'icons/home.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: paths_url, text: 'All Paths', test_id: 'nav-all-paths', icon_path: 'icons/map.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: about_path, text: 'About', test_id: 'nav-about', icon_path: 'icons/information.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: support_us_path, text: 'Support Us', test_id: 'nav-support-us', icon_path: 'icons/heart-outline.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: ODIN_CHAT_URL, text: 'Community', test_id: 'nav-community', icon_path: 'icons/speech-bubbles.svg', options: { mobile: true }) %>
<% end %>
</div>
<nav aria-label="Sidebar" class="mt-3">
<div class="px-2 space-y-1">
<% if user_signed_in? %>
<%= render Nav::ItemComponent.new(path: dashboard_path, text: 'Dashboard', test_id: 'nav-dashboard', icon_path: 'icons/home.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: paths_url, text: 'All Paths', test_id: 'nav-all-paths', icon_path: 'icons/map.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: notifications_path, text: 'Notifications', test_id: 'nav-notifications', icon_path: 'icons/bell.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: support_us_path, text: 'Support Us', test_id: 'nav-support-us', icon_path: 'icons/heart-outline.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: ODIN_CHAT_URL, text: 'Community', test_id: 'nav-community', icon_path: 'icons/speech-bubbles.svg', options: { mobile: true }) %>
<% else %>
<%= render Nav::ItemComponent.new(path: root_path, text: 'Home', test_id: 'nav-home', icon_path: 'icons/home.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: paths_url, text: 'All Paths', test_id: 'nav-all-paths', icon_path: 'icons/map.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: about_path, text: 'About', test_id: 'nav-about', icon_path: 'icons/information.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: support_us_path, text: 'Support Us', test_id: 'nav-support-us', icon_path: 'icons/heart-outline.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: ODIN_CHAT_URL, text: 'Community', test_id: 'nav-community', icon_path: 'icons/speech-bubbles.svg', options: { mobile: true }) %>
<hr class="border-t border-gray-200 my-4" aria-hidden="true">
<div class="px-2 space-y-1">
<% if user_signed_in? %>
<%= render Nav::ItemComponent.new(path: edit_users_profile_path, text: 'Settings', test_id: 'nav-settings', icon_path: 'icons/gear.svg', options: { mobile: true }) %>
<%= turbo_frame_tag 'theme_switcher_mobile' do %>
<%= render Theme::SwitcherComponent.new(current_theme:, type: :mobile) %>
<% end %>
</div>
<hr class="border-t border-gray-200 my-4" aria-hidden="true">
<div class="px-2 space-y-1">
<% if user_signed_in? %>
<%= render Nav::ItemComponent.new(path: edit_users_profile_path, text: 'Settings', test_id: 'nav-settings', icon_path: 'icons/gear.svg', options: { mobile: true }) %>
<%= turbo_frame_tag 'theme_switcher_mobile' do %>
<%= render Theme::SwitcherComponent.new(current_theme:, type: :mobile) %>
<% end %>
<%= render Nav::ItemComponent.new(path: sign_out_path, text: 'Sign out', test_id: 'nav-sign-out', icon_path: 'icons/sign-out.svg', options: { method: :delete, mobile: true }) %>
<% else %>
<%= render Nav::ItemComponent.new(path: sign_up_path, text: 'Get started', test_id: 'nav-sign-up', icon_path: 'icons/rocket.svg', options: { mobile: true }) %>
<%= turbo_frame_tag 'theme_switcher_mobile' do %>
<%= render Theme::SwitcherComponent.new(current_theme:, type: :mobile) %>
<% end %>
<%= render Nav::ItemComponent.new(path: sign_in_path, text: 'Sign in', test_id: 'nav-sign-in', icon_path: 'icons/sign-in.svg', options: { mobile: true }) %>
<%= render Nav::ItemComponent.new(path: sign_out_path, text: 'Sign out', test_id: 'nav-sign-out', icon_path: 'icons/sign-out.svg', options: { method: :delete, mobile: true }) %>
<% else %>
<%= render Nav::ItemComponent.new(path: sign_up_path, text: 'Get started', test_id: 'nav-sign-up', icon_path: 'icons/rocket.svg', options: { mobile: true }) %>
<%= turbo_frame_tag 'theme_switcher_mobile' do %>
<%= render Theme::SwitcherComponent.new(current_theme:, type: :mobile) %>
<% end %>
</div>
</nav>
</div>
</div>

<div class="shrink-0 w-14" aria-hidden="true">
<!-- Force sidebar to shrink to fit close icon -->
<%= render Nav::ItemComponent.new(path: sign_in_path, text: 'Sign in', test_id: 'nav-sign-in', icon_path: 'icons/sign-in.svg', options: { mobile: true }) %>
<% end %>
</div>
</nav>
</div>
</div>
<% end %>
Loading
Loading