From ca695305915b63f9c2fe3363682aacd1bf2b1e20 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Thu, 28 Aug 2025 22:36:43 +0200 Subject: [PATCH] ui/search: fix divider and text styles --- .../src/components/Search/Search.module.css | 24 ++++++++++++----- .../src/components/Search/Search.stories.tsx | 27 ++++++++++++++----- .../ui/src/components/Search/Search.tsx | 12 ++++++--- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/pkgs/clan-app/ui/src/components/Search/Search.module.css b/pkgs/clan-app/ui/src/components/Search/Search.module.css index d52dedebe..deff2bdfe 100644 --- a/pkgs/clan-app/ui/src/components/Search/Search.module.css +++ b/pkgs/clan-app/ui/src/components/Search/Search.module.css @@ -43,20 +43,32 @@ .searchItem { @apply flex flex-col justify-center overflow-hidden; - box-shadow: 0 1px 0 0 theme(colors.border.inv.2); - &[data-highlighted], - &:focus, - &:focus-visible, - &:hover { + &.hasDivider { + box-shadow: 0 1px 0 0 theme(colors.border.inv.2); + } + + /* Next element is hovered */ + &:has(+ &:hover) { + box-shadow: unset; + } + + &:not([aria-disabled="true"])[data-highlighted], + &:not([aria-disabled="true"]):focus, + &:not([aria-disabled="true"]):focus-visible, + &:not([aria-disabled="true"]):hover { @apply bg-inv-acc-2 rounded-md; box-shadow: unset; } - &:active { + &:not([aria-disabled="true"]):active { @apply bg-inv-acc-3 rounded-md; box-shadow: unset; } + + &[aria-disabled="true"] { + @apply cursor-not-allowed; + } } .searchContainer { diff --git a/pkgs/clan-app/ui/src/components/Search/Search.stories.tsx b/pkgs/clan-app/ui/src/components/Search/Search.stories.tsx index 27da6034a..c2ef728e9 100644 --- a/pkgs/clan-app/ui/src/components/Search/Search.stories.tsx +++ b/pkgs/clan-app/ui/src/components/Search/Search.stories.tsx @@ -55,8 +55,8 @@ function generateModules(count: number): Module[] { modules.push({ value: `lolcat/module-${i + 1}`, label: `Module ${i + 1}`, - description: `${greek[i % greek.length]}#${i + 1}`, - input: "lolcat", + description: `${greek[i % greek.length]}#${i + 1} this is a very long description to test text wrapping in the search component`, + input: "lolcat-flake-part-from-nixpkgs-via-nix-via-clan-flake", }); } @@ -78,7 +78,7 @@ export const Default: Story = { renderItem: (item: Module) => { return (
-
+
@@ -95,8 +95,12 @@ export const Default: Story = { inverted class="flex justify-between" > - {item.description} - by {item.input} + + {item.description} + + + by {item.input} +
@@ -105,7 +109,7 @@ export const Default: Story = { }, render: (args: SearchProps) => { return ( -
+
{...args} onChange={(module) => { @@ -145,11 +149,13 @@ type MachineOrTag = value: string; label: string; type: "machine"; + disabled?: boolean; } | { members: string[]; value: string; label: string; + disabled?: boolean; type: "tag"; }; @@ -193,7 +199,13 @@ export const Multiple: Story = { - + {item.label} @@ -226,6 +238,7 @@ export const Multiple: Story = {
{...args} + divider height="20rem" virtualizerOptions={{ estimateSize: () => 38, diff --git a/pkgs/clan-app/ui/src/components/Search/Search.tsx b/pkgs/clan-app/ui/src/components/Search/Search.tsx index 5dcb69fa9..8bbada0bb 100644 --- a/pkgs/clan-app/ui/src/components/Search/Search.tsx +++ b/pkgs/clan-app/ui/src/components/Search/Search.tsx @@ -11,17 +11,20 @@ import cx from "classnames"; export interface Option { value: string; label: string; + disabled?: boolean; } export interface SearchProps { onChange: (value: T | null) => void; options: T[]; - renderItem: (item: T) => JSX.Element; + renderItem: (item: T, opts: { disabled: boolean }) => JSX.Element; loading?: boolean; loadingComponent?: JSX.Element; headerClass?: string; height: string; // e.g. '14.5rem' + divider?: boolean; } + export function Search(props: SearchProps) { // Controlled input value, to allow resetting the input itself const [value, setValue] = createSignal(null); @@ -65,13 +68,14 @@ export function Search(props: SearchProps) { setInputValue(value ? value.label : ""); props.onChange(value); }} - class={styles.searchContainer} + class={cx(styles.searchContainer, props.divider && styles.hasDivider)} placement="bottom-start" options={props.options} optionValue="value" optionTextValue="label" optionLabel="label" placeholder="Search a service" + optionDisabled={"disabled"} sameWidth={true} open={true} gutter={7} @@ -181,7 +185,9 @@ export function Search(props: SearchProps) { transform: `translateY(${virtualRow.start}px)`, }} > - {props.renderItem(item.rawValue)} + {props.renderItem(item.rawValue, { + disabled: item.disabled, + })} ); }}