From 09f522f0aa698be60c954e58bb7eee0e460c4439 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 4 Dec 2025 20:32:08 -0600 Subject: Reapply "feat(desktop): terminal pane (#5081)" This reverts commit f9dcd979364acc5172fd0044c1c8b04dcaec9229. --- packages/ui/src/components/icon.tsx | 7 ++- packages/ui/src/components/select.css | 2 + packages/ui/src/components/select.tsx | 56 ++++++++++++-------- packages/ui/src/components/tabs.css | 96 ++++++++++++++++++++++++++++++---- packages/ui/src/components/tabs.tsx | 17 +++--- packages/ui/src/components/tooltip.css | 7 ++- 6 files changed, 141 insertions(+), 44 deletions(-) (limited to 'packages/ui/src') diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 306d79649..9e4f00a0d 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -153,10 +153,10 @@ const newIcons = { stop: ``, enter: ``, "layout-left": ``, - "layout-left-partial": ``, + "layout-left-partial": ``, "layout-left-full": ``, "layout-right": ``, - "layout-right-partial": ``, + "layout-right-partial": ``, "layout-right-full": ``, "speech-bubble": ``, "align-right": ``, @@ -167,6 +167,9 @@ const newIcons = { "bubble-5": ``, github: ``, discord: ``, + "layout-bottom": ``, + "layout-bottom-partial": ``, + "layout-bottom-full": ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/select.css b/packages/ui/src/components/select.css index 421215a78..96ddf174c 100644 --- a/packages/ui/src/components/select.css +++ b/packages/ui/src/components/select.css @@ -20,6 +20,7 @@ [data-component="select-content"] { min-width: 4rem; + max-width: 23rem; overflow: hidden; border-radius: var(--radius-md); border-width: 1px; @@ -39,6 +40,7 @@ } [data-slot="select-select-content-list"] { + min-height: 2rem; overflow-y: auto; max-height: 12rem; white-space: nowrap; diff --git a/packages/ui/src/components/select.tsx b/packages/ui/src/components/select.tsx index 464900ef9..9ba1f177b 100644 --- a/packages/ui/src/components/select.tsx +++ b/packages/ui/src/components/select.tsx @@ -1,10 +1,10 @@ import { Select as Kobalte } from "@kobalte/core/select" -import { createMemo, type ComponentProps } from "solid-js" +import { createMemo, splitProps, type ComponentProps } from "solid-js" import { pipe, groupBy, entries, map } from "remeda" import { Button, ButtonProps } from "./button" import { Icon } from "./icon" -export interface SelectProps { +export type SelectProps = Omit>, "value" | "onSelect"> & { placeholder?: string options: T[] current?: T @@ -17,10 +17,21 @@ export interface SelectProps { } export function Select(props: SelectProps & ButtonProps) { + const [local, others] = splitProps(props, [ + "class", + "classList", + "placeholder", + "options", + "current", + "value", + "label", + "groupBy", + "onSelect", + ]) const grouped = createMemo(() => { const result = pipe( - props.options, - groupBy((x) => (props.groupBy ? props.groupBy(x) : "")), + local.options, + groupBy((x) => (local.groupBy ? local.groupBy(x) : "")), // mapValues((x) => x.sort((a, b) => a.title.localeCompare(b.title))), entries(), map(([k, v]) => ({ category: k, options: v })), @@ -29,28 +40,30 @@ export function Select(props: SelectProps & ButtonProps) { }) return ( + // @ts-ignore + {...others} data-component="select" - value={props.current} + value={local.current} options={grouped()} - optionValue={(x) => (props.value ? props.value(x) : (x as string))} - optionTextValue={(x) => (props.label ? props.label(x) : (x as string))} + optionValue={(x) => (local.value ? local.value(x) : (x as string))} + optionTextValue={(x) => (local.label ? local.label(x) : (x as string))} optionGroupChildren="options" - placeholder={props.placeholder} - sectionComponent={(props) => ( - {props.section.rawValue.category} + placeholder={local.placeholder} + sectionComponent={(local) => ( + {local.section.rawValue.category} )} itemComponent={(itemProps) => ( - {props.label ? props.label(itemProps.item.rawValue) : (itemProps.item.rawValue as string)} + {local.label ? local.label(itemProps.item.rawValue) : (itemProps.item.rawValue as string)} @@ -58,24 +71,25 @@ export function Select(props: SelectProps & ButtonProps) { )} onChange={(v) => { - props.onSelect?.(v ?? undefined) + local.onSelect?.(v ?? undefined) }} > data-slot="select-select-trigger-value"> {(state) => { - const selected = state.selectedOption() ?? props.current - if (!selected) return props.placeholder || "" - if (props.label) return props.label(selected) + const selected = state.selectedOption() ?? local.current + if (!selected) return local.placeholder || "" + if (local.label) return local.label(selected) return selected as string }} @@ -86,8 +100,8 @@ export function Select(props: SelectProps & ButtonProps) { diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css index d03e57320..d60edc5c5 100644 --- a/packages/ui/src/components/tabs.css +++ b/packages/ui/src/components/tabs.css @@ -6,7 +6,7 @@ background-color: var(--background-stronger); overflow: clip; - [data-slot="tabs-tabs-list"] { + [data-slot="tabs-list"] { height: 48px; width: 100%; position: relative; @@ -36,7 +36,7 @@ } } - [data-slot="tabs-tabs-trigger-wrapper"] { + [data-slot="tabs-trigger-wrapper"] { position: relative; height: 100%; display: flex; @@ -58,14 +58,14 @@ border-right: 1px solid var(--border-weak-base); background-color: var(--background-base); - [data-slot="tabs-tabs-trigger"] { + [data-slot="tabs-trigger"] { display: flex; align-items: center; justify-content: center; padding: 14px 24px; } - [data-slot="tabs-tabs-trigger-close-button"] { + [data-slot="tabs-trigger-close-button"] { display: flex; align-items: center; justify-content: center; @@ -84,12 +84,12 @@ box-shadow: 0 0 0 2px var(--border-focus); } &:has([data-hidden]) { - [data-slot="tabs-tabs-trigger-close-button"] { + [data-slot="tabs-trigger-close-button"] { opacity: 0; } &:hover { - [data-slot="tabs-tabs-trigger-close-button"] { + [data-slot="tabs-trigger-close-button"] { opacity: 1; } } @@ -98,23 +98,23 @@ color: var(--text-strong); background-color: transparent; border-bottom-color: transparent; - [data-slot="tabs-tabs-trigger-close-button"] { + [data-slot="tabs-trigger-close-button"] { opacity: 1; } } &:hover:not(:disabled):not([data-selected]) { color: var(--text-strong); } - &:has([data-slot="tabs-tabs-trigger-close-button"]) { + &:has([data-slot="tabs-trigger-close-button"]) { padding-right: 12px; - [data-slot="tabs-tabs-trigger"] { + [data-slot="tabs-trigger"] { padding-right: 0; } } } - [data-slot="tabs-tabs-content"] { + [data-slot="tabs-content"] { overflow-y: auto; flex: 1; @@ -129,4 +129,80 @@ outline: none; } } + + &[data-variant="alt"] { + [data-slot="tabs-list"] { + padding-left: 24px; + padding-right: 24px; + gap: 12px; + border-bottom: 1px solid var(--border-weak-base); + background-color: transparent; + + &::after { + border: none; + background-color: transparent; + } + &:empty::after { + display: none; + } + } + + [data-slot="tabs-trigger-wrapper"] { + border: none; + color: var(--text-base); + background-color: transparent; + border-bottom-width: 2px; + border-bottom-style: solid; + border-bottom-color: transparent; + gap: 4px; + + /* text-14-regular */ + font-family: var(--font-family-sans); + font-size: var(--font-size-base); + font-style: normal; + font-weight: var(--font-weight-regular); + line-height: var(--line-height-x-large); /* 171.429% */ + letter-spacing: var(--letter-spacing-normal); + + [data-slot="tabs-trigger"] { + height: 100%; + padding: 4px; + background-color: transparent; + border-bottom-width: 2px; + border-bottom-color: transparent; + } + + [data-slot="tabs-trigger-close-button"] { + display: flex; + align-items: center; + justify-content: center; + } + + [data-component="icon-button"] { + width: 16px; + height: 16px; + margin: 0; + } + + &:has([data-selected]) { + color: var(--text-strong); + background-color: transparent; + border-bottom-color: var(--icon-strong-base); + } + + &:hover:not(:disabled):not([data-selected]) { + color: var(--text-strong); + } + + &:has([data-slot="tabs-trigger-close-button"]) { + padding-right: 0; + [data-slot="tabs-trigger"] { + padding-right: 0; + } + } + } + + /* [data-slot="tabs-content"] { */ + /* } */ + } } diff --git a/packages/ui/src/components/tabs.tsx b/packages/ui/src/components/tabs.tsx index 68acd88d4..d91ad3c41 100644 --- a/packages/ui/src/components/tabs.tsx +++ b/packages/ui/src/components/tabs.tsx @@ -2,7 +2,9 @@ import { Tabs as Kobalte } from "@kobalte/core/tabs" import { Show, splitProps, type JSX } from "solid-js" import type { ComponentProps, ParentProps } from "solid-js" -export interface TabsProps extends ComponentProps {} +export interface TabsProps extends ComponentProps { + variant?: "normal" | "alt" +} export interface TabsListProps extends ComponentProps {} export interface TabsTriggerProps extends ComponentProps { classes?: { @@ -14,11 +16,12 @@ export interface TabsTriggerProps extends ComponentProps export interface TabsContentProps extends ComponentProps {} function TabsRoot(props: TabsProps) { - const [split, rest] = splitProps(props, ["class", "classList"]) + const [split, rest] = splitProps(props, ["class", "classList", "variant"]) return ( ) { ]) return (
) { > {split.children} {(closeButton) => ( -
+
{closeButton()}
)} @@ -81,7 +84,7 @@ function TabsContent(props: ParentProps) { return (