chore: merge dev into v2 (#35591)
Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: James Long <longster@gmail.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 <little-frank@opencord.local> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: runvip <164729189+runvip@users.noreply.github.com> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Julian Coy <julian@ex-machina.co> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: Simon Klee <hello@simonklee.dk> Co-authored-by: Jay <air@live.ca> Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com>
This commit is contained in:
parent
f87998f37f
commit
9e0d3976e1
332 changed files with 24739 additions and 4586 deletions
|
|
@ -127,6 +127,12 @@
|
|||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant="ghost"][data-scope="file-tree-v2"] {
|
||||
> [data-slot="collapsible-trigger"] {
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { scrollKey, scrollTopFromThumbPointer } from "./scroll-view"
|
||||
import { canScrollKey, scrollKey, scrollTopFromThumbPointer } from "./scroll-view"
|
||||
|
||||
describe("scrollKey", () => {
|
||||
test("maps plain navigation keys", () => {
|
||||
|
|
@ -16,6 +16,27 @@ describe("scrollKey", () => {
|
|||
expect(scrollKey({ key: "PageUp", altKey: false, ctrlKey: true, metaKey: false, shiftKey: false })).toBeUndefined()
|
||||
expect(scrollKey({ key: "End", altKey: false, ctrlKey: false, metaKey: false, shiftKey: true })).toBeUndefined()
|
||||
})
|
||||
|
||||
test("maps space and shift-space directions", () => {
|
||||
expect(scrollKey({ key: " ", altKey: false, ctrlKey: false, metaKey: false, shiftKey: false })).toBe("page-down")
|
||||
expect(scrollKey({ key: " ", altKey: false, ctrlKey: false, metaKey: false, shiftKey: true })).toBe("page-up")
|
||||
})
|
||||
})
|
||||
|
||||
describe("canScrollKey", () => {
|
||||
const element = (scrollTop: number, clientHeight = 100, scrollHeight = 300) =>
|
||||
({ scrollTop, clientHeight, scrollHeight }) as HTMLElement
|
||||
|
||||
test("owns upward keys only above the top boundary", () => {
|
||||
expect(canScrollKey(element(50), "page-up")).toBe(true)
|
||||
expect(canScrollKey(element(0), "page-up")).toBe(false)
|
||||
})
|
||||
|
||||
test("owns downward keys only before the bottom boundary", () => {
|
||||
expect(canScrollKey(element(50), "page-down")).toBe(true)
|
||||
expect(canScrollKey(element(200), "page-down")).toBe(false)
|
||||
expect(canScrollKey(element(0, 100, 100), "page-down")).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("scrollTopFromThumbPointer", () => {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
import { onMount, splitProps, type ComponentProps, Show, mergeProps } from "solid-js"
|
||||
import { onCleanup, onMount, splitProps, type ComponentProps, Show, mergeProps } from "solid-js"
|
||||
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useI18n } from "../context/i18n"
|
||||
|
||||
export type ScrollViewThumbVisibility = "hover" | "scroll"
|
||||
|
||||
export interface ScrollViewProps extends ComponentProps<"div"> {
|
||||
viewportRef?: (el: HTMLDivElement) => void
|
||||
orientation?: "vertical" | "horizontal" // currently only vertical is fully implemented for thumb
|
||||
thumbVisibility?: ScrollViewThumbVisibility
|
||||
}
|
||||
|
||||
export const scrollKey = (event: Pick<KeyboardEvent, "key" | "altKey" | "ctrlKey" | "metaKey" | "shiftKey">) => {
|
||||
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return
|
||||
if (event.altKey || event.ctrlKey || event.metaKey) return
|
||||
if (event.shiftKey && event.key !== " ") return
|
||||
|
||||
switch (event.key) {
|
||||
case "PageDown":
|
||||
|
|
@ -24,9 +28,36 @@ export const scrollKey = (event: Pick<KeyboardEvent, "key" | "altKey" | "ctrlKey
|
|||
return "up"
|
||||
case "ArrowDown":
|
||||
return "down"
|
||||
case " ":
|
||||
return event.shiftKey ? "page-up" : "page-down"
|
||||
}
|
||||
}
|
||||
|
||||
export function canScrollKey(element: HTMLElement, key: NonNullable<ReturnType<typeof scrollKey>>) {
|
||||
const up = key === "up" || key === "page-up" || key === "home"
|
||||
return up ? element.scrollTop > 0 : element.scrollTop + element.clientHeight < element.scrollHeight
|
||||
}
|
||||
|
||||
export function scrollKeyOwner(
|
||||
root: HTMLElement,
|
||||
target: EventTarget | null,
|
||||
key: NonNullable<ReturnType<typeof scrollKey>>,
|
||||
) {
|
||||
const element = target instanceof Element ? target : undefined
|
||||
const owner = element?.closest<HTMLElement>("[data-scrollable]")
|
||||
if (!owner || owner === root) return root
|
||||
if (!root.contains(owner)) return owner
|
||||
return canScrollKey(owner, key) ? owner : root
|
||||
}
|
||||
|
||||
export function isScrollKeyTarget(target: EventTarget | null, key: NonNullable<ReturnType<typeof scrollKey>>) {
|
||||
const element = target instanceof HTMLElement ? target : undefined
|
||||
if (!element) return true
|
||||
if (["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName) || element.isContentEditable) return false
|
||||
if ((key === "page-up" || key === "page-down") && element.closest('button, a[href], [role="button"]')) return false
|
||||
return true
|
||||
}
|
||||
|
||||
export function scrollTopFromThumbPointer(input: {
|
||||
pointer: number
|
||||
viewportTop: number
|
||||
|
|
@ -44,10 +75,10 @@ export function scrollTopFromThumbPointer(input: {
|
|||
|
||||
export function ScrollView(props: ScrollViewProps) {
|
||||
const i18n = useI18n()
|
||||
const merged = mergeProps({ orientation: "vertical" }, props)
|
||||
const merged = mergeProps({ orientation: "vertical", thumbVisibility: "hover" }, props)
|
||||
const [local, events, rest] = splitProps(
|
||||
merged,
|
||||
["class", "children", "viewportRef", "orientation", "style"],
|
||||
["class", "children", "viewportRef", "orientation", "thumbVisibility", "style"],
|
||||
[
|
||||
"onScroll",
|
||||
"onWheel",
|
||||
|
|
@ -68,16 +99,37 @@ export function ScrollView(props: ScrollViewProps) {
|
|||
const [state, setState] = createStore({
|
||||
isHovered: false,
|
||||
isDragging: false,
|
||||
isScrolling: false,
|
||||
thumbHeight: 0,
|
||||
thumbTop: 0,
|
||||
showThumb: false,
|
||||
})
|
||||
const isHovered = () => state.isHovered
|
||||
const isDragging = () => state.isDragging
|
||||
const isScrolling = () => state.isScrolling
|
||||
const thumbHeight = () => state.thumbHeight
|
||||
const thumbTop = () => state.thumbTop
|
||||
const showThumb = () => state.showThumb
|
||||
|
||||
let scrollIdleTimer: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
const markScrolling = () => {
|
||||
if (local.thumbVisibility !== "scroll") return
|
||||
setState("isScrolling", true)
|
||||
if (scrollIdleTimer !== undefined) clearTimeout(scrollIdleTimer)
|
||||
scrollIdleTimer = setTimeout(() => setState("isScrolling", false), 800)
|
||||
}
|
||||
|
||||
const thumbVisible = () => {
|
||||
if (isDragging()) return true
|
||||
if (local.thumbVisibility === "scroll") return isScrolling()
|
||||
return isHovered()
|
||||
}
|
||||
|
||||
onCleanup(() => {
|
||||
if (scrollIdleTimer !== undefined) clearTimeout(scrollIdleTimer)
|
||||
})
|
||||
|
||||
const updateThumb = () => {
|
||||
if (!viewportRef) return
|
||||
const { scrollTop, scrollHeight, clientHeight } = viewportRef
|
||||
|
|
@ -160,9 +212,10 @@ export function ScrollView(props: ScrollViewProps) {
|
|||
if (document.activeElement && ["INPUT", "TEXTAREA", "SELECT"].includes(document.activeElement.tagName)) {
|
||||
return
|
||||
}
|
||||
|
||||
const next = scrollKey(e)
|
||||
if (!next) return
|
||||
if (!isScrollKeyTarget(e.target, next)) return
|
||||
if (scrollKeyOwner(viewportRef, e.target, next) !== viewportRef) return
|
||||
|
||||
const scrollAmount = viewportRef.clientHeight * 0.8
|
||||
const lineAmount = 40
|
||||
|
|
@ -208,11 +261,18 @@ export function ScrollView(props: ScrollViewProps) {
|
|||
<div
|
||||
ref={viewportRef}
|
||||
class="scroll-view__viewport"
|
||||
data-scrollable
|
||||
onScroll={(e) => {
|
||||
updateThumb()
|
||||
markScrolling()
|
||||
if (typeof events.onScroll === "function") events.onScroll(e as any)
|
||||
}}
|
||||
onWheel={events.onWheel as any}
|
||||
onWheel={(e) => {
|
||||
markScrolling()
|
||||
const handler = events.onWheel
|
||||
if (typeof handler === "function") handler(e as any)
|
||||
if (Array.isArray(handler)) handler[0](handler[1], e as any)
|
||||
}}
|
||||
onTouchStart={events.onTouchStart as any}
|
||||
onTouchMove={events.onTouchMove as any}
|
||||
onTouchEnd={events.onTouchEnd as any}
|
||||
|
|
@ -236,7 +296,7 @@ export function ScrollView(props: ScrollViewProps) {
|
|||
ref={thumbRef}
|
||||
onPointerDown={onThumbPointerDown}
|
||||
class="scroll-view__thumb"
|
||||
data-visible={isHovered() || isDragging()}
|
||||
data-visible={thumbVisible()}
|
||||
data-dragging={isDragging()}
|
||||
style={{
|
||||
height: `${thumbHeight()}px`,
|
||||
|
|
|
|||
|
|
@ -135,7 +135,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
#review-panel &[data-variant="normal"][data-orientation="horizontal"] {
|
||||
#review-panel &[data-variant="normal"][data-orientation="horizontal"],
|
||||
#terminal-panel &[data-variant="normal"][data-orientation="horizontal"] {
|
||||
background-color: var(--background-stronger);
|
||||
|
||||
[data-slot="tabs-list"] {
|
||||
|
|
@ -267,6 +268,51 @@
|
|||
}
|
||||
}
|
||||
|
||||
#terminal-panel &[data-variant="normal"][data-orientation="horizontal"] {
|
||||
[data-slot="tabs-list"] {
|
||||
height: 52px;
|
||||
padding-right: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
[data-slot="tabs-trigger-wrapper"] {
|
||||
height: 28px;
|
||||
padding-inline: 8px;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-slot="tabs-trigger"] {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
&:has([data-slot="tabs-trigger-close-button"]) {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
&:has([data-selected]) {
|
||||
background-color: var(--v2-background-bg-layer-01);
|
||||
box-shadow: inset 0 0 0 0.5px var(--v2-border-border-muted);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="terminal-tab-title"] {
|
||||
font-family: Inter, sans-serif;
|
||||
font-style: normal;
|
||||
font-weight: 440;
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
letter-spacing: -0.04px;
|
||||
color: var(--v2-text-text-base);
|
||||
font-variation-settings: "slnt" 0;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant="alt"] {
|
||||
[data-slot="tabs-list"] {
|
||||
padding-left: 24px;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue