|
|
@@ -29,6 +29,9 @@ export function TabNavItem(props: {
|
|
|
dragging?: boolean
|
|
|
pressed?: boolean
|
|
|
hidden?: boolean
|
|
|
+ tabKey: string
|
|
|
+ dragActive: boolean
|
|
|
+ onPointerDown: (event: PointerEvent) => void
|
|
|
}) {
|
|
|
const language = useLanguage()
|
|
|
const [editing, setEditing] = createSignal(false)
|
|
|
@@ -170,105 +173,112 @@ export function TabNavItem(props: {
|
|
|
|
|
|
return (
|
|
|
<div
|
|
|
- ref={(el) => {
|
|
|
- tabRoot = el
|
|
|
- forwardTabRef(props.ref, el)
|
|
|
- }}
|
|
|
- data-titlebar-tab
|
|
|
- data-slot="titlebar-tab-item"
|
|
|
- data-title-overflow={titleOverflowing()}
|
|
|
- data-editing={editing()}
|
|
|
- class="group relative flex h-7 w-full min-w-0 max-w-56 select-none flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] px-1.5 [container-type:inline-size] [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[editing='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
|
|
|
- classList={{ invisible: props.hidden }}
|
|
|
- data-active={props.active}
|
|
|
- data-dragging={props.dragging}
|
|
|
- data-pressed={props.pressed}
|
|
|
- onMouseDown={(event) => {
|
|
|
- if (event.button !== 1) return
|
|
|
- closeTab(event)
|
|
|
- }}
|
|
|
+ data-titlebar-tab-slot
|
|
|
+ data-tab-key={props.tabKey}
|
|
|
+ class="relative flex w-56 min-w-7 max-w-56 flex-shrink"
|
|
|
+ classList={{ invisible: props.hidden, "pointer-events-none": props.dragActive }}
|
|
|
+ onPointerDown={props.onPointerDown}
|
|
|
>
|
|
|
- <Show when={props.session()}>
|
|
|
- {(session) => {
|
|
|
- return (
|
|
|
- <a
|
|
|
- data-slot="tab-link"
|
|
|
- data-titlebar-tab-link
|
|
|
- href={props.href}
|
|
|
- draggable={false}
|
|
|
- onDragStart={(event) => {
|
|
|
- event.preventDefault()
|
|
|
- event.stopPropagation()
|
|
|
- }}
|
|
|
- onClick={(event) => {
|
|
|
- event.preventDefault()
|
|
|
- if (editing()) return
|
|
|
- if (props.suppressNavigation?.()) return
|
|
|
- props.onNavigate()
|
|
|
- }}
|
|
|
- class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base group-data-[editing='true']:text-v2-text-text-base [-webkit-user-drag:none]"
|
|
|
- >
|
|
|
- <span data-slot="project-avatar-slot">
|
|
|
- <SessionTabAvatar
|
|
|
- project={project()}
|
|
|
- directory={session().directory}
|
|
|
- sessionId={session().id}
|
|
|
- activeServer={props.activeServer}
|
|
|
- />
|
|
|
- </span>
|
|
|
- <span
|
|
|
- ref={(el) => {
|
|
|
- titleEl = el
|
|
|
- titleEl.textContent = session().title
|
|
|
- }}
|
|
|
- data-slot="tab-title"
|
|
|
- data-titlebar-tab-title
|
|
|
- class="min-w-0 flex-1 outline-none leading-4"
|
|
|
- classList={{
|
|
|
- "overflow-hidden text-clip whitespace-nowrap": !editing(),
|
|
|
- "select-text": editing(),
|
|
|
- }}
|
|
|
- contenteditable={editing() ? true : undefined}
|
|
|
- onDblClick={openRename}
|
|
|
- onKeyDown={(event) => {
|
|
|
- event.stopPropagation()
|
|
|
- if (event.key === "Enter") {
|
|
|
- event.preventDefault()
|
|
|
- void closeRename(true)
|
|
|
- return
|
|
|
- }
|
|
|
- if (event.key !== "Escape") return
|
|
|
+ <div
|
|
|
+ ref={(el) => {
|
|
|
+ tabRoot = el
|
|
|
+ forwardTabRef(props.ref, el)
|
|
|
+ }}
|
|
|
+ data-titlebar-tab
|
|
|
+ data-slot="titlebar-tab-item"
|
|
|
+ data-title-overflow={titleOverflowing()}
|
|
|
+ data-editing={editing()}
|
|
|
+ class="group relative flex h-7 w-full min-w-0 select-none flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] px-1.5 [container-type:inline-size] [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[editing='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
|
|
|
+ data-active={props.active}
|
|
|
+ data-dragging={props.dragging}
|
|
|
+ data-pressed={props.pressed}
|
|
|
+ onMouseDown={(event) => {
|
|
|
+ if (event.button !== 1) return
|
|
|
+ closeTab(event)
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Show when={props.session()}>
|
|
|
+ {(session) => {
|
|
|
+ return (
|
|
|
+ <a
|
|
|
+ data-slot="tab-link"
|
|
|
+ data-titlebar-tab-link
|
|
|
+ href={props.href}
|
|
|
+ draggable={false}
|
|
|
+ onDragStart={(event) => {
|
|
|
event.preventDefault()
|
|
|
- titleEl.textContent = session().title
|
|
|
- void closeRename(false)
|
|
|
- }}
|
|
|
- onBlur={() => void closeRename(true)}
|
|
|
- onPointerDown={(event) => {
|
|
|
- if (!editing()) return
|
|
|
event.stopPropagation()
|
|
|
}}
|
|
|
onClick={(event) => {
|
|
|
- if (!editing()) return
|
|
|
event.preventDefault()
|
|
|
+ if (editing()) return
|
|
|
+ if (props.suppressNavigation?.()) return
|
|
|
+ props.onNavigate()
|
|
|
}}
|
|
|
- />
|
|
|
- </a>
|
|
|
- )
|
|
|
- }}
|
|
|
- </Show>
|
|
|
-
|
|
|
- <div data-slot="tab-close">
|
|
|
- <IconButtonV2
|
|
|
- size="small"
|
|
|
- variant="ghost-muted"
|
|
|
- class="hover-reveal relative z-10 group-hover:opacity-100 group-data-[active=true]:opacity-100 group-data-[editing=true]:opacity-100"
|
|
|
- onPointerDown={(event) => {
|
|
|
- event.preventDefault()
|
|
|
- event.stopPropagation()
|
|
|
+ class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base group-data-[editing='true']:text-v2-text-text-base [-webkit-user-drag:none]"
|
|
|
+ >
|
|
|
+ <span data-slot="project-avatar-slot">
|
|
|
+ <SessionTabAvatar
|
|
|
+ project={project()}
|
|
|
+ directory={session().directory}
|
|
|
+ sessionId={session().id}
|
|
|
+ activeServer={props.activeServer}
|
|
|
+ />
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ ref={(el) => {
|
|
|
+ titleEl = el
|
|
|
+ titleEl.textContent = session().title
|
|
|
+ }}
|
|
|
+ data-slot="tab-title"
|
|
|
+ data-titlebar-tab-title
|
|
|
+ class="min-w-0 flex-1 outline-none leading-4"
|
|
|
+ classList={{
|
|
|
+ "overflow-hidden text-clip whitespace-nowrap": !editing(),
|
|
|
+ "select-text": editing(),
|
|
|
+ }}
|
|
|
+ contenteditable={editing() ? true : undefined}
|
|
|
+ onDblClick={openRename}
|
|
|
+ onKeyDown={(event) => {
|
|
|
+ event.stopPropagation()
|
|
|
+ if (event.key === "Enter") {
|
|
|
+ event.preventDefault()
|
|
|
+ void closeRename(true)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (event.key !== "Escape") return
|
|
|
+ event.preventDefault()
|
|
|
+ titleEl.textContent = session().title
|
|
|
+ void closeRename(false)
|
|
|
+ }}
|
|
|
+ onBlur={() => void closeRename(true)}
|
|
|
+ onPointerDown={(event) => {
|
|
|
+ if (!editing()) return
|
|
|
+ event.stopPropagation()
|
|
|
+ }}
|
|
|
+ onClick={(event) => {
|
|
|
+ if (!editing()) return
|
|
|
+ event.preventDefault()
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </a>
|
|
|
+ )
|
|
|
}}
|
|
|
- onClick={closeTab}
|
|
|
- icon={<IconV2 name="xmark-small" />}
|
|
|
- />
|
|
|
+ </Show>
|
|
|
+
|
|
|
+ <div data-slot="tab-close" class="group-hover:bg-[var(--tab-bg)] group-data-[active=true]:bg-[var(--tab-bg)]">
|
|
|
+ <IconButtonV2
|
|
|
+ size="small"
|
|
|
+ variant="ghost-muted"
|
|
|
+ class="hover-reveal relative z-10 group-hover:opacity-100 group-data-[active=true]:opacity-100 group-data-[editing=true]:opacity-100"
|
|
|
+ onPointerDown={(event) => {
|
|
|
+ event.preventDefault()
|
|
|
+ event.stopPropagation()
|
|
|
+ }}
|
|
|
+ onClick={closeTab}
|
|
|
+ icon={<IconV2 name="xmark-small" />}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
|
@@ -285,6 +295,9 @@ export function DraftTabItem(props: {
|
|
|
dragging?: boolean
|
|
|
pressed?: boolean
|
|
|
hidden?: boolean
|
|
|
+ tabKey: string
|
|
|
+ dragActive: boolean
|
|
|
+ onPointerDown: (event: PointerEvent) => void
|
|
|
}) {
|
|
|
const closeTab = (event: MouseEvent) => {
|
|
|
event.preventDefault()
|
|
|
@@ -293,58 +306,69 @@ export function DraftTabItem(props: {
|
|
|
}
|
|
|
return (
|
|
|
<div
|
|
|
- ref={(el) => forwardTabRef(props.ref, el)}
|
|
|
- data-titlebar-tab
|
|
|
- data-slot="titlebar-tab-item"
|
|
|
- data-active={props.active}
|
|
|
- data-dragging={props.dragging}
|
|
|
- data-pressed={props.pressed}
|
|
|
- class="group relative flex h-7 w-full min-w-0 max-w-56 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--tab-bg)] pl-1.5 pr-8 [container-type:inline-size] whitespace-nowrap [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-overlay-simple-overlay-pressed)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true'][data-pressed='true']:[--tab-bg:var(--v2-overlay-simple-overlay-pressed)]"
|
|
|
- classList={{ invisible: props.hidden }}
|
|
|
- onMouseDown={(event) => {
|
|
|
- if (event.button !== 1) return
|
|
|
- closeTab(event)
|
|
|
- }}
|
|
|
+ data-titlebar-tab-slot
|
|
|
+ data-tab-key={props.tabKey}
|
|
|
+ class="relative flex w-56 min-w-7 max-w-56 flex-shrink"
|
|
|
+ classList={{ invisible: props.hidden, "pointer-events-none": props.dragActive }}
|
|
|
+ onPointerDown={props.onPointerDown}
|
|
|
>
|
|
|
- <a
|
|
|
- data-slot="tab-link"
|
|
|
- data-titlebar-tab-link
|
|
|
- href={props.href}
|
|
|
- draggable={false}
|
|
|
- onDragStart={(event) => {
|
|
|
- event.preventDefault()
|
|
|
- event.stopPropagation()
|
|
|
+ <div
|
|
|
+ ref={(el) => forwardTabRef(props.ref, el)}
|
|
|
+ data-titlebar-tab
|
|
|
+ data-slot="titlebar-tab-item"
|
|
|
+ data-active={props.active}
|
|
|
+ data-dragging={props.dragging}
|
|
|
+ data-pressed={props.pressed}
|
|
|
+ class="group relative flex h-7 w-full min-w-0 select-none flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] px-1.5 [container-type:inline-size] [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
|
|
|
+ onMouseDown={(event) => {
|
|
|
+ if (event.button !== 1) return
|
|
|
+ closeTab(event)
|
|
|
}}
|
|
|
- onClick={(event) => {
|
|
|
- event.preventDefault()
|
|
|
- if (props.suppressNavigation?.()) return
|
|
|
- props.onNavigate()
|
|
|
- }}
|
|
|
- class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden text-[13px] font-medium leading-5 text-v2-text-text-faint group-data-[active='true']:text-[var(--v2-text-text-base)]"
|
|
|
>
|
|
|
- <span class="flex size-4 shrink-0 items-center justify-center">
|
|
|
- <IconV2 name="edit" />
|
|
|
- </span>
|
|
|
- <span data-titlebar-tab-title class="truncate leading-5">
|
|
|
- {props.title}
|
|
|
- </span>
|
|
|
- </a>
|
|
|
- <div data-slot="tab-close" class="absolute right-0 inset-y-0 flex w-7 items-center justify-center">
|
|
|
- <IconButtonV2
|
|
|
- size="small"
|
|
|
- variant="ghost-muted"
|
|
|
- onPointerDown={(event) => {
|
|
|
+ <a
|
|
|
+ data-slot="tab-link"
|
|
|
+ data-titlebar-tab-link
|
|
|
+ href={props.href}
|
|
|
+ draggable={false}
|
|
|
+ onDragStart={(event) => {
|
|
|
event.preventDefault()
|
|
|
event.stopPropagation()
|
|
|
}}
|
|
|
- onMouseDown={(event) => {
|
|
|
+ onClick={(event) => {
|
|
|
event.preventDefault()
|
|
|
- event.stopPropagation()
|
|
|
+ if (props.suppressNavigation?.()) return
|
|
|
+ props.onNavigate()
|
|
|
}}
|
|
|
- onClick={closeTab}
|
|
|
- icon={<IconV2 name="xmark-small" />}
|
|
|
- aria-label="Close tab"
|
|
|
- />
|
|
|
+ class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base [-webkit-user-drag:none]"
|
|
|
+ >
|
|
|
+ <span class="flex size-4 shrink-0 items-center justify-center">
|
|
|
+ <IconV2 name="edit" />
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ data-titlebar-tab-title
|
|
|
+ class="min-w-0 flex-1 overflow-hidden text-clip whitespace-nowrap outline-none leading-4"
|
|
|
+ >
|
|
|
+ {props.title}
|
|
|
+ </span>
|
|
|
+ </a>
|
|
|
+ <div data-slot="tab-close" class="group-hover:bg-[var(--tab-bg)] group-data-[active=true]:bg-[var(--tab-bg)]">
|
|
|
+ <IconButtonV2
|
|
|
+ size="small"
|
|
|
+ variant="ghost-muted"
|
|
|
+ onPointerDown={(event) => {
|
|
|
+ event.preventDefault()
|
|
|
+ event.stopPropagation()
|
|
|
+ }}
|
|
|
+ onMouseDown={(event) => {
|
|
|
+ event.preventDefault()
|
|
|
+ event.stopPropagation()
|
|
|
+ }}
|
|
|
+ class="hover-reveal relative z-10 group-hover:opacity-100 group-data-[active=true]:opacity-100 group-data-[editing=true]:opacity-100"
|
|
|
+ onClick={closeTab}
|
|
|
+ icon={<IconV2 name="xmark-small" />}
|
|
|
+ aria-label="Close tab"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|