From f563d47e8a3454ca651761d5cd464918b77d0fe3 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Mon, 20 Jul 2026 02:59:22 +0400 Subject: [PATCH] fix(icons): standardize dropdown/select/pagination/expander chevrons app-select (the shared select used across every admin form) rendered the browser's native dropdown indicator - inconsistent across Chrome/Firefox/Safari and outside the icon system entirely. Hid it (appearance: none) and added a consistent chevronDown via app-icon. app-pagination used literal HTML entities (« / ») for prev/next instead of icons. Replaced with chevronLeft/chevronRight. Every native
/ expander (7 call sites across admin product form, page editor, and the builder's widget advanced-settings panel) relied on the browser's default disclosure triangle, which again varies per browser and shares no visual relationship with the rest of the icon system. Added one global CSS rule (details > summary) that hides the native marker and draws the same Lucide chevron path used everywhere else, animated on open/close - covers all 7 without touching each template. Co-Authored-By: Claude Sonnet 5 --- .../ui/pagination/pagination.component.html | 4 +- .../ui/pagination/pagination.component.ts | 3 +- .../shared/ui/select/select.component.html | 37 +++++++++-------- .../shared/ui/select/select.component.scss | 16 ++++++++ src/app/shared/ui/select/select.component.ts | 2 + src/styles.scss | 40 +++++++++++++++++++ 6 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/app/shared/ui/pagination/pagination.component.html b/src/app/shared/ui/pagination/pagination.component.html index af7d941..d8f2a3c 100644 --- a/src/app/shared/ui/pagination/pagination.component.html +++ b/src/app/shared/ui/pagination/pagination.component.html @@ -6,7 +6,7 @@ [attr.aria-label]="previousLabel()" (click)="goTo(currentPage() - 1)" > - « + @for (page of pages(); track $index) { @@ -32,6 +32,6 @@ [attr.aria-label]="nextLabel()" (click)="goTo(currentPage() + 1)" > - » + diff --git a/src/app/shared/ui/pagination/pagination.component.ts b/src/app/shared/ui/pagination/pagination.component.ts index 97b9485..fa40b08 100644 --- a/src/app/shared/ui/pagination/pagination.component.ts +++ b/src/app/shared/ui/pagination/pagination.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core'; import { ButtonComponent } from '../button/button.component'; +import { IconComponent } from '../icon/icon.component'; const SIBLING_COUNT = 1; const ELLIPSIS = '…' as const; @@ -7,7 +8,7 @@ const ELLIPSIS = '…' as const; @Component({ selector: 'app-pagination', standalone: true, - imports: [ButtonComponent], + imports: [ButtonComponent, IconComponent], templateUrl: './pagination.component.html', styleUrl: './pagination.component.scss', changeDetection: ChangeDetectionStrategy.OnPush diff --git a/src/app/shared/ui/select/select.component.html b/src/app/shared/ui/select/select.component.html index c87e1b0..e852836 100644 --- a/src/app/shared/ui/select/select.component.html +++ b/src/app/shared/ui/select/select.component.html @@ -1,20 +1,23 @@ - +
+ + +
@if (activeDescription(); as description) { {{ description }} } diff --git a/src/app/shared/ui/select/select.component.scss b/src/app/shared/ui/select/select.component.scss index 451d9c2..30bfc13 100644 --- a/src/app/shared/ui/select/select.component.scss +++ b/src/app/shared/ui/select/select.component.scss @@ -7,8 +7,24 @@ width: 100%; } +.app-select-wrap { + position: relative; + width: 100%; +} + +.app-select-arrow { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + color: var(--text-secondary, #6b7280); + pointer-events: none; +} + .app-select { width: 100%; + appearance: none; + padding-right: 36px; border: 1px solid var(--border-color, #e4e4e7); border-radius: var(--radius-md, 6px); background: var(--bg-primary, #fff); diff --git a/src/app/shared/ui/select/select.component.ts b/src/app/shared/ui/select/select.component.ts index 672bff0..737ad92 100644 --- a/src/app/shared/ui/select/select.component.ts +++ b/src/app/shared/ui/select/select.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, forwardRef, inject, input } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { FormFieldContext } from '../form-field/form-field.component'; +import { IconComponent } from '../icon/icon.component'; export type SelectSize = 'sm' | 'md' | 'lg'; @@ -13,6 +14,7 @@ export interface SelectOption { @Component({ selector: 'app-select', standalone: true, + imports: [IconComponent], templateUrl: './select.component.html', styleUrl: './select.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/styles.scss b/src/styles.scss index 204299c..31fa29e 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -149,6 +149,46 @@ a, button, input, textarea, select { animation: app-icon-spin 1s linear infinite; } +/* Consistent, animated expander chevron for every native
/ + disclosure in the app, replacing the browser's default triangle marker + (which renders differently per browser). Matches the Lucide ChevronDown + path used everywhere else via app-icon, so expanders use the same icon + family as the rest of the UI without touching every call site. */ +details > summary { + list-style: none; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + + &::-webkit-details-marker { + display: none; + } + + &::after { + content: ''; + flex-shrink: 0; + width: 16px; + height: 16px; + background-color: currentColor; + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + transition: transform 0.2s ease; + } +} + +details[open] > summary::after { + transform: rotate(180deg); +} + +@media (prefers-reduced-motion: reduce) { + details > summary::after { + transition: none; + } +} + /* Respect OS-level reduced-motion preference globally: neutralizes hover transforms, card-entrance/skeleton-shimmer animations, and smooth-scroll everywhere in one place rather than requiring every component to opt in