mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-07 22:45:20 +02:00
Pre-launch design sync with Figma spec (#2154)
* Add lookbook + viewcomponent, organize design system file * Build menu component * Button updates * More button fixes * Replace all menus with new ViewComponent * Checkpoint: fix tests, all buttons and menus converted * Split into Link and Button components for clarity * Button cleanup * Simplify custom confirmation configuration in views * Finalize button, link component API * Add toggle field to custom form builder + Component * Basic tabs component * Custom tabs, convert all menu / tab instances in app * Gem updates * Centralized icon helper * Update all icon usage to central helper * Lint fixes * Centralize all disclosure instances * Dialog replacements * Consolidation of all dialog styles * Test fixes * Fix app layout issues, move to component with slots * Layout simplification * Flakey test fix * Fix dashboard mobile issues * Finalize homepage * Lint fixes * Fix shadows and borders in dark mode * Fix tests * Remove stale class * Fix filled icon logic * Move transparent? to public interface
This commit is contained in:
parent
1aafed5f8b
commit
90a9546f32
291 changed files with 4143 additions and 3104 deletions
|
@ -8,9 +8,8 @@
|
|||
border-color: color-mix(in srgb, <%= category.color %> 30%, white);
|
||||
color: <%= category.color %>;">
|
||||
<% if category.lucide_icon.present? %>
|
||||
<%= lucide_icon category.lucide_icon, class: "w-4 h-4 shrink-0" %>
|
||||
<%= icon category.lucide_icon, size: "sm", color: "current" %>
|
||||
<% end %>
|
||||
<%= category.name %>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -3,24 +3,22 @@
|
|||
<div id="<%= dom_id(category) %>" class="flex justify-between items-center px-4 pb-4 <%= "pt-4" unless category.subcategory? %> <%= "pb-4" unless category.subcategories.any? %> bg-container">
|
||||
<div class="flex w-full items-center gap-2.5">
|
||||
<% if category.subcategory? %>
|
||||
<%= lucide_icon "corner-down-right", class: "shrink-0 w-5 h-5 text-subdued ml-2" %>
|
||||
<span style="color: <%= category.color %>">
|
||||
<%= icon "corner-down-right", size: "sm", color: "current", class: "ml-2" %>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%= render partial: "categories/badge", locals: { category: category } %>
|
||||
</div>
|
||||
|
||||
<div class="justify-self-end">
|
||||
<%= contextual_menu do %>
|
||||
<%= contextual_menu_modal_action_item t(".edit"), edit_category_path(category) %>
|
||||
<%= render MenuComponent.new do |menu| %>
|
||||
<% menu.with_item(variant: "link", text: t(".edit"), icon: "pencil", href: edit_category_path(category), data: { turbo_frame: :modal }) %>
|
||||
|
||||
<% if category.transactions.any? %>
|
||||
<%= link_to new_category_deletion_path(category),
|
||||
class: "flex items-center w-full rounded-lg text-red-600 hover:bg-red-50 py-2 px-3 gap-2",
|
||||
data: { turbo_frame: :modal } do %>
|
||||
<%= lucide_icon "trash-2", class: "shrink-0 w-5 h-5" %>
|
||||
<span class="text-sm"><%= t(".delete") %></span>
|
||||
<% end %>
|
||||
<% menu.with_item(variant: "link", text: t(".delete"), icon: "trash-2", href: new_category_deletion_path(category), destructive: true, data: { turbo_frame: :modal }) %>
|
||||
<% else %>
|
||||
<%= contextual_menu_destructive_item t(".delete"), category_path(category), turbo_confirm: nil %>
|
||||
<% menu.with_item(variant: "button", text: t(".delete"), icon: "trash-2", href: category_path(category), method: :delete) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
data-category-target="avatar"
|
||||
class="w-14 h-14 flex items-center justify-center rounded-full"
|
||||
style="background-color: color-mix(in oklab, <%= category.color %> 10%, transparent); color: <%= category.color %>">
|
||||
<%= lucide_icon(category.lucide_icon, class: "w-8 h-8") %>
|
||||
<%= icon(category.lucide_icon, size: "2xl", color: "current") %>
|
||||
</span>
|
||||
|
|
|
@ -2,59 +2,60 @@
|
|||
|
||||
<div data-controller="category" data-category-preset-colors-value="<%= Category::COLORS %>">
|
||||
<%= styled_form_with model: category, class: "space-y-4" do |f| %>
|
||||
<section class="space-y-4">
|
||||
<div class="w-fit m-auto">
|
||||
<section class="space-y-4 ">
|
||||
<div class="w-fit mx-auto relative">
|
||||
<%= render partial: "color_avatar", locals: { category: category } %>
|
||||
|
||||
<details data-category-target="details">
|
||||
<summary class="cursor-pointer absolute -bottom-2 -right-2 flex justify-center items-center bg-gray-50 border-2 w-7 h-7 border-white rounded-full text-gray-500">
|
||||
<%= icon("pen", size: "sm") %>
|
||||
</summary>
|
||||
|
||||
<div class="fixed ml-8 mt-2 z-50 bg-container p-4 border border-alpha-black-25 rounded-2xl shadow-xs h-fit">
|
||||
<div class="flex gap-2 flex-col mb-4" data-category-target="selection" style="<%= "display:none;" if @category.subcategory? %>">
|
||||
<div data-category-target="pickerSection"></div>
|
||||
<h4 class="text-gray-500 text-sm">Color</h4>
|
||||
<div class="flex gap-2 items-center" data-category-target="colorsSection">
|
||||
<% Category::COLORS.each do |color| %>
|
||||
<label class="relative">
|
||||
<%= f.radio_button :color, color, class: "sr-only peer", data: { action: "change->category#handleColorChange" } %>
|
||||
<div class="w-6 h-6 rounded-full cursor-pointer peer-checked:ring-2 peer-checked:ring-offset-2 peer-checked:ring-blue-500" style="background-color: <%= color %>"></div>
|
||||
</label>
|
||||
<% end %>
|
||||
<label class="relative">
|
||||
<%= f.radio_button :color, "custom-color", class: "sr-only peer", data: { category_target: "colorPickerRadioBtn"} %>
|
||||
<div class="w-6 h-6 rounded-full cursor-pointer peer-checked:ring-2 peer-checked:ring-offset-2 peer-checked:ring-blue-500" data-category-target="pickerBtn" style="background: conic-gradient(red,orange,yellow,lime,green,teal,cyan,blue,indigo,purple,magenta,pink,red)"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center hidden flex-col" data-category-target="paletteSection">
|
||||
<div class="flex gap-2 items-center w-full">
|
||||
<div class="w-6 h-6 p-4 rounded-full cursor-pointer" style="background-color: <%= category.color %>" data-category-target="colorPreview"></div>
|
||||
<%= f.text_field :color , data: { category_target: "colorInput"}, inline: true %>
|
||||
<%= icon "palette", size: "2xl", data: { action: "click->category#toggleSections" } %>
|
||||
</div>
|
||||
<div data-category-target="validationMessage" class="hidden self-start flex gap-1 items-center text-xs text-destructive ">
|
||||
<span>Poor contrast, choose darker color or</span>
|
||||
<button type="button" class="underline cursor-pointer" data-action="category#autoAdjust">auto-adjust.</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 justify-center flex-col w-87">
|
||||
<h4 class="text-gray-500 text-sm">Icon</h4>
|
||||
<div class="flex flex-wrap gap-0.5">
|
||||
<% Category.icon_codes.each do |icon| %>
|
||||
<label class="relative">
|
||||
<%= f.radio_button :lucide_icon, icon, class: "sr-only peer", data: { action: "change->category#handleIconChange change->category#handleIconColorChange", category_target:"icon" } %>
|
||||
<div class="w-7 h-7 flex m-0.5 items-center justify-center rounded-full cursor-pointer hover:bg-gray-100 peer-checked:bg-gray-100 border-1 border-transparent">
|
||||
<%= icon(icon, size: "sm", color: "current") %>
|
||||
</div>
|
||||
</label>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<details data-category-target="details">
|
||||
<summary class="cursor-pointer absolute top-23 left-58.5 flex justify-center items-center bg-gray-50 border-2 w-7 h-7 border-white rounded-full text-gray-500">
|
||||
<%= icon("pen", size: "sm") %>
|
||||
</summary>
|
||||
|
||||
<div class=" absolute z-50 bg-container p-4 border border-alpha-black-25 rounded-2xl shadow-xs h-fit left-66 top-24">
|
||||
<div class="flex gap-2 flex-col mb-4" data-category-target="selection" style="<%= "display:none;" if @category.subcategory? %>">
|
||||
<div data-category-target="pickerSection"></div>
|
||||
<h4 class="text-gray-500 text-sm">Color</h4>
|
||||
<div class="flex gap-2 items-center" data-category-target="colorsSection">
|
||||
<% Category::COLORS.each do |color| %>
|
||||
<label class="relative">
|
||||
<%= f.radio_button :color, color, class: "sr-only peer", data: { action: "change->category#handleColorChange" } %>
|
||||
<div class="w-6 h-6 rounded-full cursor-pointer peer-checked:ring-2 peer-checked:ring-offset-2 peer-checked:ring-blue-500" style="background-color: <%= color %>"></div>
|
||||
</label>
|
||||
<% end %>
|
||||
<label class="relative">
|
||||
<%= f.radio_button :color, "custom-color", class: "sr-only peer", data: { category_target: "colorPickerRadioBtn"} %>
|
||||
<div class="w-6 h-6 rounded-full cursor-pointer peer-checked:ring-2 peer-checked:ring-offset-2 peer-checked:ring-blue-500" data-category-target="pickerBtn" style="background: conic-gradient(red,orange,yellow,lime,green,teal,cyan,blue,indigo,purple,magenta,pink,red)"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center hidden flex-col" data-category-target="paletteSection">
|
||||
<div class="flex gap-2 items-center w-full">
|
||||
<div class="w-6 h-6 p-4 rounded-full cursor-pointer" style="background-color: <%= category.color %>" data-category-target="colorPreview"></div>
|
||||
<%= f.text_field :color , data: { category_target: "colorInput"}, inline: true %>
|
||||
<%= lucide_icon "palette", class: "w-8 h-8 cursor-pointer hover:bg-gray-100 p-1", data: { action: "click->category#toggleSections" } %>
|
||||
</div>
|
||||
<div data-category-target="validationMessage" class="hidden self-start flex gap-1 items-center text-xs text-destructive ">
|
||||
<span>Poor contrast, choose darker color or</span>
|
||||
<button type="button" class="underline cursor-pointer" data-action="category#autoAdjust">auto-adjust.</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 justify-center flex-col w-87">
|
||||
<h4 class="text-gray-500 text-sm">Icon</h4>
|
||||
<div class="flex flex-wrap gap-0.5">
|
||||
<% Category.icon_codes.each do |icon| %>
|
||||
<label class="relative">
|
||||
<%= f.radio_button :lucide_icon, icon, class: "sr-only peer", data: { action: "change->category#handleIconChange change->category#handleIconColorChange", category_target:"icon" } %>
|
||||
<div class="w-7 h-7 flex m-0.5 items-center justify-center rounded-full cursor-pointer hover:bg-gray-100 peer-checked:bg-gray-100 border-1 border-transparent">
|
||||
<%= lucide_icon icon, class: "w-6 h-6 p-1" %>
|
||||
</div>
|
||||
</label>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<% if category.errors.any? %>
|
||||
<%= render "shared/form_errors", model: category %>
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
<%# locals: (transaction:) %>
|
||||
|
||||
<div class="relative" data-controller="menu" id="<%= dom_id(transaction, :category_menu) %>">
|
||||
<button data-menu-target="button" class="flex cursor-pointer">
|
||||
<%= render partial: "categories/badge", locals: { category: transaction.category } %>
|
||||
</button>
|
||||
<div data-menu-target="content" class="absolute z-10 hidden w-screen mt-2 max-w-min cursor-default">
|
||||
<div class="w-80 text-sm font-semibold leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
|
||||
<%= turbo_frame_tag "category_dropdown", src: category_dropdown_path(category_id: transaction.category_id, transaction_id: transaction.id), loading: :lazy do %>
|
||||
<div class="p-6 flex items-center justify-center">
|
||||
<p class="text-sm text-secondary animate-pulse"><%= t(".loading") %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%= render MenuComponent.new(variant: "button") do |menu| %>
|
||||
<% menu.with_button do %>
|
||||
<% render partial: "categories/badge", locals: { category: transaction.category } %>
|
||||
<% end %>
|
||||
|
||||
<% menu.with_custom_content do %>
|
||||
<%= turbo_frame_tag "category_dropdown", src: category_dropdown_path(category_id: transaction.category_id, transaction_id: transaction.id), loading: :lazy do %>
|
||||
<div class="p-6 flex items-center justify-center">
|
||||
<p class="text-sm text-secondary animate-pulse"><%= t(".loading") %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
<%= modal_form_wrapper title: t(".edit"), overflow_visible: true do %>
|
||||
<%= render "form", category: @category, categories: @categories %>
|
||||
<%= render DialogComponent.new do |dialog| %>
|
||||
<% dialog.with_header(title: t(".edit")) %>
|
||||
<% dialog.with_body do %>
|
||||
<%= render "form", category: @category, categories: @categories %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -2,18 +2,23 @@
|
|||
<h1 class="text-primary text-xl font-medium"><%= t(".categories") %></h1>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<%= contextual_menu do %>
|
||||
<%= contextual_menu_destructive_item "Delete all", destroy_all_categories_path, turbo_confirm: {
|
||||
title: "Delete all categories?",
|
||||
body: "All of your transactions will become uncategorized and this cannot be undone.",
|
||||
accept: "Delete all categories",
|
||||
} %>
|
||||
<%= render MenuComponent.new do |menu| %>
|
||||
<% menu.with_item(
|
||||
variant: "button",
|
||||
text: "Delete all",
|
||||
href: destroy_all_categories_path,
|
||||
method: :delete,
|
||||
icon: "trash-2",
|
||||
confirm: CustomConfirm.for_resource_deletion("All categories", high_severity: true)) %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to new_category_path, class: "btn btn--primary flex items-center gap-1 justify-center", data: { turbo_frame: :modal } do %>
|
||||
<%= lucide_icon "plus", class: "w-5 h-5" %>
|
||||
<p><%= t(".new") %></p>
|
||||
<% end %>
|
||||
<%= render LinkComponent.new(
|
||||
text: t(".new"),
|
||||
variant: "primary",
|
||||
icon: "plus",
|
||||
href: new_category_path,
|
||||
frame: :modal
|
||||
) %>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
@ -33,12 +38,18 @@
|
|||
<div class="text-center flex flex-col items-center max-w-[500px]">
|
||||
<p class="text-sm text-secondary mb-4"><%= t(".empty") %></p>
|
||||
<div class="flex items-center gap-2">
|
||||
<%= button_to t(".bootstrap"), bootstrap_categories_path, class: "btn btn--primary" %>
|
||||
<%= render ButtonComponent.new(
|
||||
text: t(".bootstrap"),
|
||||
href: bootstrap_categories_path,
|
||||
) %>
|
||||
|
||||
<%= link_to new_category_path, class: "btn btn--outline flex items-center gap-1", data: { turbo_frame: "modal" } do %>
|
||||
<%= lucide_icon("plus", class: "w-5 h-5") %>
|
||||
<span><%= t(".new") %></span>
|
||||
<% end %>
|
||||
<%= render LinkComponent.new(
|
||||
text: t(".new"),
|
||||
variant: "outline",
|
||||
icon: "plus",
|
||||
href: new_category_path,
|
||||
frame: :modal
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
<%= modal_form_wrapper title: t(".new_category"), overflow_visible: true do %>
|
||||
<%= render "form", category: @category, categories: @categories %>
|
||||
<%= render DialogComponent.new do |dialog| %>
|
||||
<% dialog.with_header(title: t(".new_category")) %>
|
||||
<% dialog.with_body do %>
|
||||
<%= render "form", category: @category, categories: @categories %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue