chore: merge dev into v2 (#34788)

Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
Co-authored-by: Kit Langton <kit.langton@gmail.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Affan Ali <93028901+affanali2k3@users.noreply.github.com>
Co-authored-by: affanali2k3 <affanalikhanxx@gmail.com>
Co-authored-by: Frank <frank@anoma.ly>
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 <little-frank@opencord.local>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Jay V <air@live.ca>
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com>
Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com>
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
Co-authored-by: Ben Guthrie <benjee.012@gmail.com>
Co-authored-by: Dax <mail@thdxr.com>
Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com>
Co-authored-by: Max Anderson <max.a.anderson95@gmail.com>
Co-authored-by: Brendan Allan <git@brendonovich.dev>
Co-authored-by: Jack <jack@anoma.ly>
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
Co-authored-by: Dustin Deus <deusdustin@gmail.com>
Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box>
Co-authored-by: Aiden Cline <aidenpcline@gmail.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>
This commit is contained in:
James Long 2026-07-01 17:12:00 -04:00 committed by GitHub
commit 8c94e9005f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
590 changed files with 15772 additions and 5530 deletions

View file

@ -2,11 +2,15 @@
transform: rotate(-90deg);
[data-slot="progress-circle-background"] {
stroke: var(--border-weak-base);
stroke: var(--progress-circle-background, var(--border-weak-base));
}
[data-slot="progress-circle-background-overlay"] {
stroke: var(--progress-circle-background-overlay, transparent);
}
[data-slot="progress-circle-progress"] {
stroke: var(--border-active);
stroke: var(--progress-circle-progress, var(--border-active));
transition: stroke-dashoffset 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
}

View file

@ -1,6 +1,6 @@
import { type ComponentProps, createMemo, splitProps } from "solid-js"
export interface ProgressCircleProps extends Pick<ComponentProps<"svg">, "class" | "classList"> {
export interface ProgressCircleProps extends Pick<ComponentProps<"svg">, "class" | "classList" | "style"> {
percentage: number
size?: number
strokeWidth?: number
@ -43,6 +43,13 @@ export function ProgressCircle(props: ProgressCircleProps) {
data-slot="progress-circle-background"
stroke-width={strokeWidth()}
/>
<circle
cx={center}
cy={center}
r={radius()}
data-slot="progress-circle-background-overlay"
stroke-width={strokeWidth()}
/>
<circle
cx={center}
cy={center}

View file

@ -107,6 +107,8 @@ export function Tooltip(props: TooltipProps) {
<Match when={true}>
<KobalteTooltip
gutter={4}
openDelay={400}
skipDelayDuration={300}
{...others}
closeDelay={0}
ignoreSafeArea={local.ignoreSafeArea ?? true}

View file

@ -28,7 +28,7 @@
outline: none;
}
[data-component="button-v2"]:is(:focus-visible, [data-state="focus"]):not(:disabled) {
[data-component="button-v2"]:is(:focus-visible, [data-state="focus"]):not(:disabled):not([data-variant="loading"]) {
outline: 2px solid var(--v2-border-border-focus);
outline-offset: 2.5px;
}
@ -113,6 +113,26 @@
cursor: not-allowed;
}
/* Outline */
[data-component="button-v2"][data-variant="outline"] {
background-color: transparent;
color: var(--v2-text-text-base);
box-shadow: inset 0 0 0 1px var(--v2-border-border-muted);
}
[data-component="button-v2"][data-variant="outline"]:is(:hover, [data-state="hover"]):not(:disabled) {
background-color: var(--v2-overlay-simple-overlay-hover);
}
[data-component="button-v2"][data-variant="outline"]:is(:active, [data-state="pressed"]):not(:disabled) {
background-color: var(--v2-overlay-simple-overlay-pressed);
}
[data-component="button-v2"][data-variant="outline"]:is(:disabled, [data-state="disabled"]) {
opacity: 0.5;
cursor: not-allowed;
}
/* Contrast */
[data-component="button-v2"][data-variant="contrast"] {
background-image:
@ -150,6 +170,15 @@
cursor: not-allowed;
}
/* Loading */
[data-component="button-v2"][data-variant="loading"] {
background: #f2f2f2;
color: var(--v2-text-text-base);
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.08);
cursor: default;
pointer-events: none;
}
/* Ghost */
[data-component="button-v2"][data-variant="ghost"] {
background-color: transparent;

View file

@ -1,11 +1,11 @@
import { ButtonV2 } from "./button-v2"
const docs = `### Overview
Button v2 with three visual variants and two sizes.
Button v2 with visual variants and three sizes.
### API
- \`variant\`: "neutral" | "danger" | "contrast" | "ghost" | "ghost-muted".
- \`size\`: "normal" | "large".
- \`variant\`: "neutral" | "danger" | "contrast" | "ghost" | "ghost-muted" | "loading".
- \`size\`: "small" | "normal" | "large".
- \`icon\`: Optional icon name.
- Inherits Kobalte Button props and native button attributes.
@ -39,7 +39,7 @@ export default {
},
variant: {
control: "select",
options: ["neutral", "danger", "contrast", "ghost", "ghost-muted"],
options: ["neutral", "danger", "contrast", "ghost", "ghost-muted", "loading"],
},
size: {
control: "select",
@ -67,6 +67,7 @@ export const Variants = {
<ButtonV2 variant="ghost-muted" icon="edit">
Ghost muted
</ButtonV2>
<ButtonV2 variant="loading">Loading</ButtonV2>
</div>
),
}
@ -116,7 +117,7 @@ export const Icon = {
export const AllStates = {
render: () => {
const variants = ["neutral", "danger", "contrast", "ghost", "ghost-muted"] as const
const variants = ["neutral", "danger", "contrast", "ghost", "ghost-muted", "loading"] as const
const states = ["default", "hover", "pressed", "focus", "disabled"] as const
const toTitleCase = (value: string) => value.charAt(0).toUpperCase() + value.slice(1)
return (

View file

@ -7,7 +7,7 @@ export interface ButtonV2Props
extends ComponentProps<typeof Kobalte>,
Pick<ComponentProps<"button">, "class" | "classList" | "children"> {
size?: "small" | "normal" | "large"
variant?: "neutral" | "danger" | "contrast" | "ghost" | "ghost-muted"
variant?: "neutral" | "danger" | "outline" | "contrast" | "ghost" | "ghost-muted" | "loading"
icon?: IconProps["name"]
}

View file

@ -59,6 +59,7 @@
margin: 0;
flex: 1;
min-width: 0;
user-select: none;
font-weight: 530;
font-size: 13px;
line-height: 16px;
@ -80,6 +81,7 @@
margin: 0;
flex: none;
flex-grow: 0;
user-select: none;
font-weight: 530;
font-size: 15px;
line-height: 20px;
@ -91,6 +93,7 @@
[data-slot="dialog-description"] {
flex: none;
flex-grow: 0;
user-select: none;
font-weight: 440;
font-size: 13px;
line-height: 20px;

View file

@ -110,3 +110,5 @@ export function Dialog(props: DialogProps) {
</div>
)
}
export const DialogV2 = Dialog

View file

@ -9,6 +9,14 @@ const icons = {
viewBox: "0 0 16 16",
body: `<path d="M7.5 13.3333H1.5V2H6.83333L8.83333 4H14.8333V6M10.1667 11.3333H15.5M12.8333 8.66667V14" stroke="currentColor" stroke-miterlimit="10" stroke-linecap="square"/>`,
},
folder: {
viewBox: "0 0 16 16",
body: `<path d="M2.545 3.364V12.636H13.455V5H8.545L6.909 3.364H2.545Z" stroke="currentColor" stroke-miterlimit="10" stroke-linecap="square"/>`,
},
branch: {
viewBox: "0 0 16 16",
body: `<path d="M5.118 5.686V10.314M5.118 5.686C5.97 5.686 6.661 4.995 6.661 4.143C6.661 3.291 5.97 2.6 5.118 2.6C4.266 2.6 3.575 3.291 3.575 4.143C3.575 4.995 4.266 5.686 5.118 5.686ZM5.118 10.314C4.266 10.314 3.575 11.005 3.575 11.857C3.575 12.709 4.266 13.4 5.118 13.4C5.97 13.4 6.661 12.709 6.661 11.857M5.118 10.314C5.97 10.314 6.661 11.005 6.661 11.857M10.882 5.686C11.734 5.686 12.425 4.995 12.425 4.143C12.425 3.291 11.734 2.6 10.882 2.6C10.03 2.6 9.339 3.291 9.339 4.143C9.339 4.995 10.03 5.686 10.882 5.686ZM10.882 5.686V9.457C10.882 10.783 9.807 11.857 8.482 11.857H6.661" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>`,
},
"grid-plus": {
viewBox: "0 0 16 16",
body: `<path d="M13.9948 11.668H9.32812M11.6641 9.33203V13.9987M6.66667 9.33203V13.9987H2V9.33203H6.66667ZM6.66667 2V6.66667H2V2H6.66667ZM13.9948 2V6.66667H9.32812V2H13.9948Z" stroke="currentColor" stroke-miterlimit="10" stroke-linecap="square"/>`,
@ -85,6 +93,22 @@ const icons = {
viewBox: "0 0 16 16",
body: `<path d="M2.5 7.5H3.5V8.5H2.5V7.5Z" stroke="currentColor"/><path d="M7.5 7.5H8.5V8.5H7.5V7.5Z" stroke="currentColor"/><path d="M12.5 7.5H13.5V8.5H12.5V7.5Z" stroke="currentColor"/>`,
},
"outline-sliders": {
viewBox: "0 0 16 16",
body: `<path d="M11.7779 4.66675H14.4446M11.7779 4.66675C11.7779 5.77132 10.8825 6.66675 9.77789 6.66675C8.67332 6.66675 7.77789 5.77132 7.77789 4.66675M11.7779 4.66675C11.7779 3.56218 10.8825 2.66675 9.77789 2.66675C8.67332 2.66675 7.77789 3.56218 7.77789 4.66675M1.55566 4.66675H7.77789M4.22233 11.3334H1.55566M4.22233 11.3334C4.22233 12.438 5.11776 13.3334 6.22233 13.3334C7.3269 13.3334 8.22233 12.438 8.22233 11.3334M4.22233 11.3334C4.22233 10.2288 5.11776 9.33341 6.22233 9.33341C7.3269 9.33341 8.22233 10.2288 8.22233 11.3334M14.4446 11.3334H8.22233" stroke="currentColor"/>`,
},
"outline-copy": {
viewBox: "0 0 16 16",
body: `<path d="M4.14908 11.0081H1.76282V1.51758H9.1038V2.55588M14.2225 4.99681H6.75397V14.4873H14.2225V4.99681Z" stroke="currentColor"/>`,
},
"outline-square-arrow": {
viewBox: "0 0 16 16",
body: `<path d="M13.5555 6.66656V2.44434H9.33326M13.5555 2.44434L7.99993 7.99989M13.5555 9.33324V13.5555C13.5555 13.5555 12.7599 13.5555 11.7777 13.5555H2.44438C2.44438 13.5555 2.44438 12.7599 2.44438 11.7777V4.22213C2.44438 3.2399 2.44434 2.44435 2.44434 2.44435H6.66661" stroke="currentColor"/>`,
},
reset: {
viewBox: "0 0 20 20",
body: `<path d="M5.83333 4.16406L2.5 7.4974L5.83333 10.8307M3.33333 7.4974H17.9167V15.4141H10" stroke="currentColor" stroke-linecap="square"/>`,
},
archive: {
viewBox: "0 0 16 16",
body: `<path d="M13.1112 13.5555V14.0555H13.6112V13.5555H13.1112ZM2.889 13.5555H2.389L2.389 14.0555H2.889V13.5555ZM3.38901 5.55546L3.38901 5.05546L2.38901 5.05546L2.38901 5.55546L2.88901 5.55546L3.38901 5.55546ZM14.4446 2.44434H14.9446V1.94434L14.4446 1.94434L14.4446 2.44434ZM14.4446 5.55545L14.4446 6.05545L14.9446 6.05545V5.55545H14.4446ZM1.55566 5.55546L1.05566 5.55545L1.05566 6.05546L1.55566 6.05546L1.55566 5.55546ZM1.5557 2.44436L1.5557 1.94436L1.05571 1.94436L1.0557 2.44435L1.5557 2.44436ZM13.1112 5.55546H12.6112V13.5555H13.1112H13.6112V5.55546H13.1112ZM2.889 13.5555H3.389L3.38901 5.55546L2.88901 5.55546L2.38901 5.55546L2.389 13.5555H2.889ZM14.4446 2.44434H13.9446V5.55545H14.4446H14.9446V2.44434H14.4446ZM1.55566 5.55546L2.05566 5.55547L2.0557 2.44436L1.5557 2.44436L1.0557 2.44435L1.05566 5.55545L1.55566 5.55546ZM6.22234 8.22213V8.72213H9.7779V8.22213V7.72213H6.22234V8.22213ZM13.1112 13.5555V13.0555H2.889V13.5555V14.0555H13.1112V13.5555ZM1.5557 2.44436L1.5557 2.94436L14.4446 2.94434L14.4446 2.44434L14.4446 1.94434L1.5557 1.94436L1.5557 2.44436ZM14.4446 5.55545L14.4446 5.05545L1.55566 5.05546L1.55566 5.55546L1.55566 6.05546L14.4446 6.05545L14.4446 5.55545Z" fill="currentColor"/>`,

View file

@ -0,0 +1,32 @@
[data-component="loader-v2"] {
--_duration: 900ms;
display: block;
flex-shrink: 0;
color: var(--v2-icon-icon-muted, #808080);
animation: loader-v2-spin var(--_duration) linear infinite;
transform-origin: center;
}
[data-component="loader-v2"] [data-slot="loader-v2-background"] {
stroke: currentColor;
opacity: 0.2;
}
[data-component="loader-v2"] [data-slot="loader-v2-progress"] {
stroke: currentColor;
stroke-linecap: round;
transform: rotate(-90deg);
transform-origin: center;
}
@keyframes loader-v2-spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
[data-component="loader-v2"] {
animation: none;
}
}

View file

@ -0,0 +1,42 @@
import { LoaderV2 } from "./loader-v2"
const docs = `### Overview
Circular v2 loader for compact loading states.
### API
- Accepts standard SVG props.
### Behavior
- The foreground ring covers 33% of the circumference and rotates continuously.
### Accessibility
- Sets \`aria-hidden="true"\` by default.
`
export default {
title: "UI V2/Loader",
id: "components-loader-v2",
component: LoaderV2,
tags: ["autodocs"],
parameters: {
docs: {
description: {
component: docs,
},
},
},
}
export const Basic = {
render: () => <LoaderV2 />,
}
export const Sizes = {
render: () => (
<div style={{ display: "flex", gap: "16px", "align-items": "center" }}>
<LoaderV2 width={12} height={12} />
<LoaderV2 />
<LoaderV2 width={24} height={24} />
</div>
),
}

View file

@ -0,0 +1,31 @@
import { splitProps, type ComponentProps } from "solid-js"
import "./loader-v2.css"
export function LoaderV2(props: ComponentProps<"svg">) {
const [local, rest] = splitProps(props, ["class", "classList", "width", "height"])
return (
<svg
{...rest}
class={local.class}
classList={local.classList}
width={local.width ?? 16}
height={local.height ?? 16}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-component="loader-v2"
aria-hidden={rest["aria-hidden"] ?? "true"}
>
<circle cx="8" cy="8" r="6" data-slot="loader-v2-background" stroke-width="2" />
<circle
cx="8"
cy="8"
r="6"
data-slot="loader-v2-progress"
pathLength="100"
stroke-width="2"
stroke-dasharray="33 67"
/>
</svg>
)
}

View file

@ -0,0 +1,13 @@
[data-component="progress-circle-v2"] {
display: block;
transform: rotate(-90deg);
}
[data-component="progress-circle-v2"] [data-slot="progress-circle-v2-background"] {
stroke: var(--v2-background-bg-layer-04);
}
[data-component="progress-circle-v2"] [data-slot="progress-circle-v2-progress"] {
stroke: var(--v2-icon-icon-muted);
transition: stroke-dashoffset 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}

View file

@ -0,0 +1,52 @@
import { type ComponentProps, createMemo, splitProps } from "solid-js"
import "./progress-circle-v2.css"
export interface ProgressCircleV2Props extends Pick<ComponentProps<"svg">, "class" | "classList"> {
percentage: number
size?: number
strokeWidth?: number
}
export function ProgressCircleV2(props: ProgressCircleV2Props) {
const [split, rest] = splitProps(props, ["percentage", "size", "strokeWidth", "class", "classList"])
const size = () => split.size ?? 14
const strokeWidth = () => split.strokeWidth ?? 1.5
const viewBoxSize = 14
const center = viewBoxSize / 2
const radius = () => center - strokeWidth() / 2
const circumference = createMemo(() => 2 * Math.PI * radius())
const offset = createMemo(() => circumference() * (1 - Math.max(0, Math.min(100, split.percentage || 0)) / 100))
return (
<svg
{...rest}
width={size()}
height={size()}
viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}
fill="none"
data-component="progress-circle-v2"
classList={{
...split.classList,
[split.class ?? ""]: !!split.class,
}}
>
<circle
cx={center}
cy={center}
r={radius()}
data-slot="progress-circle-v2-background"
stroke-width={strokeWidth()}
/>
<circle
cx={center}
cy={center}
r={radius()}
data-slot="progress-circle-v2-progress"
stroke-width={strokeWidth()}
stroke-dasharray={circumference().toString()}
stroke-dashoffset={offset()}
/>
</svg>
)
}

View file

@ -45,6 +45,7 @@
}
[data-slot="radio-v2-item"] {
position: relative;
display: flex;
flex-direction: row;
align-items: flex-start;
@ -131,10 +132,6 @@
linear-gradient(180deg, var(--v2-alpha-light-20) 0%, var(--v2-alpha-light-0) 100%), var(--v2-background-bg-accent);
}
&:where([data-checked][data-disabled]) [data-slot="radio-v2-item-control"] {
opacity: 0.5;
}
&:where([data-invalid]):where(:not([data-checked])) [data-slot="radio-v2-item-control"] {
background: var(--v2-state-bg-danger);
box-shadow: inset 0 0 0 0.5px var(--v2-state-border-danger);
@ -195,8 +192,11 @@
user-select: none;
}
&:where([data-disabled]) [data-slot="radio-v2-item-label"],
&:where([data-disabled]) [data-slot="radio-v2-item-label-text"] {
color: var(--v2-text-text-muted);
}
&:where([data-disabled]) [data-slot="radio-v2-item-description"] {
opacity: 0.5;
color: var(--v2-text-text-faint);
}
}

View file

@ -55,3 +55,10 @@
transform: scale(0.96);
}
}
@media (prefers-reduced-motion: reduce) {
[data-component="tooltip-v2"],
[data-component="tooltip-v2"][data-closed] {
animation: none;
}
}

View file

@ -88,8 +88,9 @@ export function TooltipV2(props: TooltipV2Props) {
<Match when={true}>
<KobalteTooltip
gutter={4}
openDelay={400}
skipDelayDuration={300}
{...others}
openDelay={0}
closeDelay={0}
ignoreSafeArea={local.ignoreSafeArea ?? true}
open={local.forceOpen || state.open}