From e731a0ecb2c155e38a9a410014b00474529e6327 Mon Sep 17 00:00:00 2001 From: Aaron Reisman Date: Wed, 17 Jun 2026 17:01:32 +0700 Subject: [PATCH 01/14] Refine component slot and story consistency --- .../propel/src/components/button/index.tsx | 14 +++----- .../propel/src/components/dropdown/index.tsx | 34 +++++++++---------- .../src/components/icon-button/index.tsx | 6 ++-- .../propel/src/components/nav-item/index.tsx | 33 +++++++++--------- .../nav-item/nav-item-header.stories.tsx | 15 ++++---- .../components/nav-item/nav-item.stories.tsx | 20 +++++------ packages/propel/src/components/pill/index.tsx | 30 +++++++--------- .../src/components/pill/pill.stories.tsx | 12 +++---- .../components/progress/progress.stories.tsx | 8 ++--- .../src/components/search/search.stories.tsx | 9 ++--- packages/propel/src/internal/node-slot.ts | 15 -------- packages/propel/src/internal/node-slot.tsx | 13 +++++++ 12 files changed, 92 insertions(+), 117 deletions(-) delete mode 100644 packages/propel/src/internal/node-slot.ts create mode 100644 packages/propel/src/internal/node-slot.tsx diff --git a/packages/propel/src/components/button/index.tsx b/packages/propel/src/components/button/index.tsx index f64ca01..acd4912 100644 --- a/packages/propel/src/components/button/index.tsx +++ b/packages/propel/src/components/button/index.tsx @@ -3,7 +3,7 @@ import { LoaderCircle } from "lucide-react"; import * as React from "react"; import { getLoadingButtonProps } from "../../internal/loading-button"; -import { nodeSlotClass } from "../../internal/node-slot"; +import { NodeSlot } from "../../internal/node-slot"; // Magnitudes follow the Figma "Buttons" Size scale. Figma ships S/Base/L/XL; those // map to sm/md/lg/xl by their px heights (20/24/28/32). Per Figma: @@ -49,7 +49,7 @@ export const buttonVariants = cva( // any larger line-height (e.g. `leading-snug`) inflates the line box and // makes the top/bottom padding read as uneven. Link overrides below opt // back into a taller line-height since inline link text may wrap. - // Each magnitude also sets `--node-size`, the size every leading/trailing node + // Each magnitude also sets `--node-size`, the size every inline-start/end node // (icon, avatar, ...) renders at. Per Figma's per-size icon values: 14px up to // Base, 16px from L up. The slots and the loading spinner read this variable, so // the button owns its node sizing in one place. @@ -223,16 +223,10 @@ export function Button({ {loading ? ( ) : inlineStartNode ? ( - - {inlineStartNode} - + {inlineStartNode} ) : null} {children} - {!loading && inlineEndNode ? ( - - {inlineEndNode} - - ) : null} + {!loading && inlineEndNode ? {inlineEndNode} : null} ); } diff --git a/packages/propel/src/components/dropdown/index.tsx b/packages/propel/src/components/dropdown/index.tsx index ad46ce4..7647d9d 100644 --- a/packages/propel/src/components/dropdown/index.tsx +++ b/packages/propel/src/components/dropdown/index.tsx @@ -4,7 +4,7 @@ import { Check, ChevronRight, Search } from "lucide-react"; import * as React from "react"; import { useControllableState } from "../../hooks/use-controllable-state/index"; -import { nodeSlotClass } from "../../internal/node-slot"; +import { NodeSlot } from "../../internal/node-slot"; import { OverlayPanel, type OverlayPanelWidth } from "../../internal/overlay-panel"; import { CheckboxVisual } from "../checkbox/index"; @@ -144,7 +144,7 @@ const dropdownItemVariants = cva( variant: { // Single-line rows: fixed 34px, content vertically centered. default: "h-[34px] items-center", - // Two-line rows: 6px top padding (Figma) with the leading icon top-aligned + // Two-line rows: 6px top padding (Figma) with the inline-start icon top-aligned // (align-start) so it sits with the first line; the row grows for the // description and keeps a matching 6px bottom. "with-description": "min-h-[34px] items-start py-1.5", @@ -183,7 +183,7 @@ export type DropdownItemProps = Omit< description?: React.ReactNode; /** * Muted text shown inline after the label (e.g. a language's English name). Sits between the - * label and the trailing node, on the same line as the label. + * label and the inline-end node, on the same line as the label. */ secondaryText?: React.ReactNode; /** @@ -192,8 +192,8 @@ export type DropdownItemProps = Omit< */ inlineEndNode?: React.ReactNode; /** - * Single-select selected state: keeps the row's own icon and marks the selection with a trailing - * checkmark (the row's icon is never replaced). Distinct from the multi-select + * Single-select selected state: keeps the row's own icon and marks the selection with an + * inline-end checkmark (the row's icon is never replaced). Distinct from the multi-select * `DropdownCheckboxItem`, which shows a `Checkbox` on every row. */ selected?: boolean; @@ -209,7 +209,7 @@ export type DropdownItemProps = Omit< * A selectable menu row: an optional `inlineStartNode` (icon or full-size control) + label (+ * optional description / inline secondary text / `inlineEndNode`) — all of it content, laid out by * `variant`. Closes the menu when clicked (Base UI default). Pass `selected` for the single-select - * trailing-checkmark pattern. + * inline-end checkmark pattern. */ export function DropdownItem({ variant, @@ -227,7 +227,7 @@ export function DropdownItem({ {/* The node slots size icons to `--node-size` (16px) and let full-size controls (Checkbox/Avatar/swatch) size themselves; the row's text color cascades. */} - {inlineStartNode != null ? {inlineStartNode} : null} + {inlineStartNode != null ? {inlineStartNode} : null} {label ?? children} @@ -243,8 +243,8 @@ export function DropdownItem({ ) : null} - {inlineEndNode != null ? {inlineEndNode} : null} - {/* Selection is marked with a trailing check — the row's own node is kept. */} + {inlineEndNode != null ? {inlineEndNode} : null} + {/* Selection is marked with an inline-end check — the row's own node is kept. */} {selected ? (