diff --git a/.claude/rules/emcn-components.md b/.claude/rules/emcn-components.md index f4a88a40314..d2809e3fd02 100644 --- a/.claude/rules/emcn-components.md +++ b/.claude/rules/emcn-components.md @@ -32,7 +32,7 @@ The menu surface intentionally diverges from the pill: `dropdown-menu.tsx` items - **`ChipDatePicker`** — chip-styled date field. - **`ChipTimePicker`** — minute-granular time sibling of `ChipDatePicker`, a `ChipInput` that leniently parses typed input (`9:47`, `947`, `2:05pm`, `14:30`), commits on Enter/blur, and re-renders the canonical `9:47 AM` label. - **`DropdownMenu`** — the canonical context/action menu (Radix-backed). Not a chip, but the standard menu for command/action lists; reach for it instead of a hand-rolled popover. Its surface intentionally diverges from the chip pill (`text-small`, `gap-2`) — keep them distinct. For a pill that opens a value picker, use `ChipDropdown`/`ChipSelect` instead. -- **`OverflowText`** — the canonical single-line overflow treatment for read-only human labels and titles. It owns `min-w-0`, fade-only clipping (never an ellipsis), the conditional 18px edge mask, and the full-value floating tooltip; consumers pass only layout/typography through `className`. `overflowTextClipClass` and `overflowTextFadeClass` are the complete base/faded treatments for the rare component that must own measurement itself; never pair either with `truncate`, `text-ellipsis`, or hover-time mask removal. Use `DropdownMenuItemLabel` for a menu label beside icons, checks, or actions. A non-editable `Combobox` passes the full visual value through `overlayLabel`; the combobox owns the visual overlay's fade and keeps its one accessible tooltip on the interactive layer. Keep ordinary `truncate` only for editable values, code/log/path content, dense or virtualized grids, and rich composite content that cannot supply a plain tooltip label. Multiline copy uses an intentional `line-clamp-*` treatment instead. +- **`OverflowText`** — the canonical single-line overflow treatment for read-only human labels and titles. It owns `min-w-0`, fade-only clipping (never an ellipsis), the conditional 16px edge mask, and the full-value floating tooltip; consumers pass only layout/typography through `className`. `overflowTextClipClass` and `overflowTextFadeClass` are the complete base/faded treatments for the rare component that must own measurement itself; never pair either with `truncate`, `text-ellipsis`, or hover-time mask removal. Use `DropdownMenuItemLabel` for a menu label beside icons, checks, or actions. A non-editable `Combobox` passes the full visual value through `overlayLabel`; the combobox owns the visual overlay's fade and keeps its one accessible tooltip on the interactive layer. Keep ordinary `truncate` only for editable values, code/log/path content, dense or virtualized grids, and rich composite content that cannot supply a plain tooltip label. Multiline copy uses an intentional `line-clamp-*` treatment instead. ## Modal keyboard defaults diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx index 6424128e163..370ad014091 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx @@ -562,7 +562,7 @@ function BreadcrumbLocationPopover({ border className={cn( POPOVER_ANIMATION_CLASSES, - 'bg-[var(--bg)] p-1.5 text-[var(--text-body)] shadow-sm' + 'bg-[var(--bg)] p-1.5 text-[var(--text-body)] shadow-sm dark:[--surface-hover:var(--surface-4)]' )} onMouseEnter={openPopover} onMouseLeave={scheduleClose} diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/views-menu/views-menu.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/views-menu/views-menu.tsx index a0527fd593e..4ad8b6fe290 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/views-menu/views-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/views-menu/views-menu.tsx @@ -8,13 +8,13 @@ import { chipContentLabelClass, chipVariants, cn, + dropdownMenuRowClass, OverflowText, POPOVER_ANIMATION_CLASSES, Popover, PopoverAnchor, PopoverContent, PopoverItem, - PopoverSection, Tooltip, } from '@sim/emcn' import { Check, Pencil, Pin, Plus, Trash } from '@sim/emcn/icons' @@ -27,7 +27,7 @@ export const ALL_ROWS_VIEW_LABEL = 'All' /** Matches the breadcrumb location popover's hover-intent grace period. */ const POPOVER_CLOSE_DELAY_MS = 120 -/** Rendered width of one action button (`p-1` + `size-3` glyph) plus its `gap-0.5`. +/** Rendered width of one 20px action button plus its `gap-0.5`. * The row reserves `actionCount` of these, so keep it in step with the button * classes below — the overlay is absolutely positioned and can't size the spacer. */ const VIEW_ACTION_SLOT_PX = 22 @@ -131,21 +131,18 @@ export const ViewsMenu = memo(function ViewsMenu({ side='bottom' align='start' sideOffset={6} - minWidth={240} - maxWidth={320} + minWidth={220} + maxWidth={280} maxHeight={420} border className={cn( POPOVER_ANIMATION_CLASSES, - 'bg-[var(--bg)] p-1.5 text-[var(--text-body)] shadow-sm' + 'bg-[var(--bg)] p-1 text-[var(--text-body)] shadow-sm dark:bg-[var(--bg)]' )} onMouseEnter={openPopover} onMouseLeave={scheduleClose} onFocusCapture={cancelScheduledClose} > - - Views -
{!hasDefaultView && ( {canEdit && ( <> -
- runAndClose(onNewView)} - className='h-7 items-center gap-1.5 px-1.5 py-0 text-xs' - > +
+ runAndClose(onNewView)} className={dropdownMenuRowClass}> - + @@ -268,10 +262,10 @@ function ViewRow({ label, isActive, onSelect, defaultState, actions }: ViewRowPr - {isActive && } + {isActive && } {actionCount > 0 && ( @@ -283,7 +277,7 @@ function ViewRow({ label, isActive, onSelect, defaultState, actions }: ViewRowPr )} {actionCount > 0 && ( -
+
{actions?.map((action) => { // Disabled via aria-disabled, not the `disabled` attribute: the button // must keep receiving hover and focus events so the tooltip can explain @@ -311,7 +305,7 @@ function ViewRow({ label, isActive, onSelect, defaultState, actions }: ViewRowPr : 'group-focus-within/view:opacity-100 group-hover/view:opacity-100' )} > - + ) return action.disabledReason ? ( @@ -344,7 +338,7 @@ function ViewRow({ label, isActive, onSelect, defaultState, actions }: ViewRowPr defaultState.onSetDefault?.() }} > - + )}
diff --git a/packages/emcn/src/components/overflow-text/overflow-text.test.tsx b/packages/emcn/src/components/overflow-text/overflow-text.test.tsx index 294797f9356..0461dcd63ab 100644 --- a/packages/emcn/src/components/overflow-text/overflow-text.test.tsx +++ b/packages/emcn/src/components/overflow-text/overflow-text.test.tsx @@ -100,6 +100,7 @@ describe('OverflowText', () => { expect(label.classList.contains('text-sm')).toBe(true) expect(label.className).toContain('-webkit-mask-image:linear-gradient') expect(label.className).toContain('mask-image:linear-gradient') + expect(label.className).toContain('100%_-_16px') act(() => { label.dispatchEvent( diff --git a/packages/emcn/src/components/overflow-text/overflow-text.tsx b/packages/emcn/src/components/overflow-text/overflow-text.tsx index cf9b6dc7b7e..c87d5d105e5 100644 --- a/packages/emcn/src/components/overflow-text/overflow-text.tsx +++ b/packages/emcn/src/components/overflow-text/overflow-text.tsx @@ -12,7 +12,7 @@ import { /** Complete fade-only clipping treatment for measured special cases. */ export const overflowTextFadeClass = - 'overflow-hidden text-clip whitespace-nowrap [-webkit-mask-image:linear-gradient(to_right,black_calc(100%_-_18px),transparent)] [mask-image:linear-gradient(to_right,black_calc(100%_-_18px),transparent)]' + 'overflow-hidden text-clip whitespace-nowrap [-webkit-mask-image:linear-gradient(to_right,black_calc(100%_-_16px),transparent)] [mask-image:linear-gradient(to_right,black_calc(100%_-_16px),transparent)]' /** Fade-free clipping for externally measured labels and rich-content overflow exceptions. */ export const overflowTextClipClass = 'block min-w-0 overflow-hidden text-clip whitespace-nowrap'