@zentauri-ui/zentauri-components 2.1.7 → 2.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/cli/props.json +350 -3
- package/cli/registry.json +11 -0
- package/dist/{chunk-DBNGLT5U.mjs → chunk-3HBC34NF.mjs} +4 -4
- package/dist/{chunk-DBNGLT5U.mjs.map → chunk-3HBC34NF.mjs.map} +1 -1
- package/dist/{chunk-FUCW5GPE.mjs → chunk-3RC5IG6O.mjs} +55 -8
- package/dist/chunk-3RC5IG6O.mjs.map +1 -0
- package/dist/chunk-4SLVTSHM.js +241 -0
- package/dist/chunk-4SLVTSHM.js.map +1 -0
- package/dist/{chunk-TJ2EWPER.js → chunk-4TPE5DEG.js} +63 -7
- package/dist/chunk-4TPE5DEG.js.map +1 -0
- package/dist/{chunk-G7FVHZRB.js → chunk-7CZDJTPD.js} +12 -12
- package/dist/{chunk-G7FVHZRB.js.map → chunk-7CZDJTPD.js.map} +1 -1
- package/dist/{chunk-5ELR6MIN.js → chunk-7DGPRPWM.js} +6 -6
- package/dist/{chunk-5ELR6MIN.js.map → chunk-7DGPRPWM.js.map} +1 -1
- package/dist/chunk-GP3FUS2H.mjs +26 -0
- package/dist/chunk-GP3FUS2H.mjs.map +1 -0
- package/dist/chunk-IHDM7AHY.mjs +233 -0
- package/dist/chunk-IHDM7AHY.mjs.map +1 -0
- package/dist/chunk-MWG7LHAK.js +19 -0
- package/dist/{chunk-5FU57ZVQ.js.map → chunk-MWG7LHAK.js.map} +1 -1
- package/dist/{chunk-7UXPXCKV.mjs → chunk-OLT7P7JO.mjs} +3 -3
- package/dist/{chunk-7UXPXCKV.mjs.map → chunk-OLT7P7JO.mjs.map} +1 -1
- package/dist/chunk-PAISX7YL.js +38 -0
- package/dist/chunk-PAISX7YL.js.map +1 -0
- package/dist/{chunk-KVSRUAXP.mjs → chunk-VN7FE5RR.mjs} +3 -3
- package/dist/{chunk-KVSRUAXP.mjs.map → chunk-VN7FE5RR.mjs.map} +1 -1
- package/dist/design-system/code-diff.d.ts +18 -0
- package/dist/design-system/code-diff.d.ts.map +1 -0
- package/dist/design-system/facade.js +8 -7
- package/dist/design-system/facade.js.map +1 -1
- package/dist/design-system/facade.mjs +7 -6
- package/dist/design-system/facade.mjs.map +1 -1
- package/dist/design-system/index.d.ts +2 -0
- package/dist/design-system/index.d.ts.map +1 -1
- package/dist/design-system/split-button.d.ts +25 -0
- package/dist/design-system/split-button.d.ts.map +1 -0
- package/dist/ui/buttons/animated.js +10 -9
- package/dist/ui/buttons/animated.js.map +1 -1
- package/dist/ui/buttons/animated.mjs +8 -7
- package/dist/ui/buttons/animated.mjs.map +1 -1
- package/dist/ui/buttons.js +11 -10
- package/dist/ui/buttons.mjs +9 -8
- package/dist/ui/code-diff/code-diff-base.d.ts +6 -0
- package/dist/ui/code-diff/code-diff-base.d.ts.map +1 -0
- package/dist/ui/code-diff/code-diff.d.ts +6 -0
- package/dist/ui/code-diff/code-diff.d.ts.map +1 -0
- package/dist/ui/code-diff/index.d.ts +4 -0
- package/dist/ui/code-diff/index.d.ts.map +1 -0
- package/dist/ui/code-diff/types.d.ts +26 -0
- package/dist/ui/code-diff/types.d.ts.map +1 -0
- package/dist/ui/code-diff/variants.d.ts +11 -0
- package/dist/ui/code-diff/variants.d.ts.map +1 -0
- package/dist/ui/code-diff.js +302 -0
- package/dist/ui/code-diff.js.map +1 -0
- package/dist/ui/code-diff.mjs +297 -0
- package/dist/ui/code-diff.mjs.map +1 -0
- package/dist/ui/data-table.js +22 -21
- package/dist/ui/data-table.js.map +1 -1
- package/dist/ui/data-table.mjs +12 -11
- package/dist/ui/data-table.mjs.map +1 -1
- package/dist/ui/dropdown/dropdown.d.ts +1 -1
- package/dist/ui/dropdown/dropdown.d.ts.map +1 -1
- package/dist/ui/dropdown/types.d.ts +2 -2
- package/dist/ui/dropdown/types.d.ts.map +1 -1
- package/dist/ui/dropdown.js +31 -231
- package/dist/ui/dropdown.js.map +1 -1
- package/dist/ui/dropdown.mjs +4 -229
- package/dist/ui/dropdown.mjs.map +1 -1
- package/dist/ui/dynamic-stepper.js +20 -19
- package/dist/ui/dynamic-stepper.js.map +1 -1
- package/dist/ui/dynamic-stepper.mjs +9 -8
- package/dist/ui/dynamic-stepper.mjs.map +1 -1
- package/dist/ui/pagination.js +12 -11
- package/dist/ui/pagination.mjs +9 -8
- package/dist/ui/split-button/index.d.ts +4 -0
- package/dist/ui/split-button/index.d.ts.map +1 -0
- package/dist/ui/split-button/split-button-base.d.ts +6 -0
- package/dist/ui/split-button/split-button-base.d.ts.map +1 -0
- package/dist/ui/split-button/split-button.d.ts +6 -0
- package/dist/ui/split-button/split-button.d.ts.map +1 -0
- package/dist/ui/split-button/types.d.ts +30 -0
- package/dist/ui/split-button/types.d.ts.map +1 -0
- package/dist/ui/split-button/variants.d.ts +16 -0
- package/dist/ui/split-button/variants.d.ts.map +1 -0
- package/dist/ui/split-button.js +288 -0
- package/dist/ui/split-button.js.map +1 -0
- package/dist/ui/split-button.mjs +279 -0
- package/dist/ui/split-button.mjs.map +1 -0
- package/package.json +4 -1
- package/src/design-system/code-diff.ts +37 -0
- package/src/design-system/index.ts +2 -0
- package/src/design-system/split-button.ts +38 -0
- package/src/ui/code-diff/code-diff-base.tsx +284 -0
- package/src/ui/code-diff/code-diff.test.tsx +50 -0
- package/src/ui/code-diff/code-diff.tsx +8 -0
- package/src/ui/code-diff/index.ts +15 -0
- package/src/ui/code-diff/types.ts +31 -0
- package/src/ui/code-diff/variants.ts +49 -0
- package/src/ui/dropdown/dropdown.tsx +7 -3
- package/src/ui/dropdown/types.ts +2 -2
- package/src/ui/split-button/index.ts +19 -0
- package/src/ui/split-button/split-button-base.tsx +232 -0
- package/src/ui/split-button/split-button.test.tsx +208 -0
- package/src/ui/split-button/split-button.tsx +9 -0
- package/src/ui/split-button/types.ts +46 -0
- package/src/ui/split-button/variants.ts +46 -0
- package/dist/chunk-5FU57ZVQ.js +0 -19
- package/dist/chunk-FUCW5GPE.mjs.map +0 -1
- package/dist/chunk-TJ2EWPER.js.map +0 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const zuiCodeDiffBase = [
|
|
2
|
+
"relative overflow-auto rounded-lg border border-[color:var(--zui-code-diff-border,var(--zui-border,#0000001a))] dark:border-[color:var(--zui-code-diff-border-dark,var(--zui-border-dark,#ffffff1a))]",
|
|
3
|
+
"bg-[var(--zui-code-diff-bg,var(--zui-surface,oklch(98.4%_0.003_247.858)))] dark:bg-[var(--zui-code-diff-bg-dark,var(--zui-surface-dark,oklch(12.9%_0.042_264.695)))]",
|
|
4
|
+
"text-[color:var(--zui-code-diff-fg,var(--zui-fg,oklch(20.8%_0.042_265.755)))] dark:text-[color:var(--zui-code-diff-fg-dark,var(--zui-fg-dark,oklch(98.4%_0.003_247.858)))]",
|
|
5
|
+
] as const;
|
|
6
|
+
|
|
7
|
+
export const zuiCodeDiffSizes = {
|
|
8
|
+
sm: "text-xs leading-5",
|
|
9
|
+
md: "text-sm leading-6",
|
|
10
|
+
lg: "text-base leading-7",
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
export const zuiCodeDiffHeaderBase =
|
|
14
|
+
"sticky top-0 z-10 flex items-center justify-between border-b border-[color:var(--zui-code-diff-border,var(--zui-border,#0000001a))] dark:border-[color:var(--zui-code-diff-border-dark,var(--zui-border-dark,#ffffff1a))] bg-[var(--zui-code-diff-header-bg,var(--zui-surface-muted,oklch(92.9%_0.013_255.508)))] dark:bg-[var(--zui-code-diff-header-bg-dark,var(--zui-surface-muted-dark,oklch(27.9%_0.041_260.031)))] px-4 py-2";
|
|
15
|
+
|
|
16
|
+
export const zuiCodeDiffTableBase = "w-full border-collapse table-fixed";
|
|
17
|
+
|
|
18
|
+
export const zuiCodeDiffLineNumberBase =
|
|
19
|
+
"select-none text-right align-top whitespace-nowrap border-r border-[color:var(--zui-code-diff-border,var(--zui-border,#0000001a))] dark:border-[color:var(--zui-code-diff-border-dark,var(--zui-border-dark,#ffffff1a))] px-3 text-[color:var(--zui-code-diff-line-number-fg,var(--zui-fg-muted,oklch(55.2%_0.046_257.417)))] dark:text-[color:var(--zui-code-diff-line-number-fg-dark,var(--zui-fg-muted-dark,oklch(70.8%_0.015_256.243)))]";
|
|
20
|
+
|
|
21
|
+
export const zuiCodeDiffLineContentBase =
|
|
22
|
+
"whitespace-pre-wrap break-all px-4 align-top";
|
|
23
|
+
|
|
24
|
+
export const zuiCodeDiffLineAdded =
|
|
25
|
+
"bg-[var(--zui-code-diff-added-bg,oklch(92.8%_0.109_150.96))] dark:bg-[var(--zui-code-diff-added-bg-dark,oklch(26.8%_0.077_146.44))] text-[color:var(--zui-code-diff-added-fg,oklch(29.1%_0.065_148.99))] dark:text-[color:var(--zui-code-diff-added-fg-dark,oklch(74%_0.131_149.02))]";
|
|
26
|
+
|
|
27
|
+
export const zuiCodeDiffLineRemoved =
|
|
28
|
+
"bg-[var(--zui-code-diff-removed-bg,oklch(93.1%_0.08_22.4))] dark:bg-[var(--zui-code-diff-removed-bg-dark,oklch(26.9%_0.07_22.54))] text-[color:var(--zui-code-diff-removed-fg,oklch(30.7%_0.06_28.07))] dark:text-[color:var(--zui-code-diff-removed-fg-dark,oklch(74.2%_0.127_24.75))]";
|
|
29
|
+
|
|
30
|
+
export const zuiCodeDiffLineUnchanged = "";
|
|
31
|
+
|
|
32
|
+
export const zuiCodeDiffGutterMarker =
|
|
33
|
+
"inline-block w-4 text-center select-none";
|
|
34
|
+
|
|
35
|
+
export const zuiCodeDiffAppearances = {
|
|
36
|
+
default: "",
|
|
37
|
+
} as const;
|
|
@@ -7,6 +7,7 @@ export * from "./badge";
|
|
|
7
7
|
export * from "./breadcrumb";
|
|
8
8
|
export * from "./button";
|
|
9
9
|
export * from "./card";
|
|
10
|
+
export * from "./code-diff";
|
|
10
11
|
export * from "./checkbox";
|
|
11
12
|
export * from "./combobox";
|
|
12
13
|
export * from "./command";
|
|
@@ -32,6 +33,7 @@ export * from "./radio-group";
|
|
|
32
33
|
export * from "./scroll-area";
|
|
33
34
|
export * from "./select";
|
|
34
35
|
export * from "./skeleton";
|
|
36
|
+
export * from "./split-button";
|
|
35
37
|
export * from "./slider";
|
|
36
38
|
export * from "./spinner";
|
|
37
39
|
export * from "./table";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const zuiSplitButtonRoot =
|
|
2
|
+
"inline-flex align-middle data-[full-width=true]:w-full";
|
|
3
|
+
|
|
4
|
+
export const zuiSplitButtonFullWidth = "w-full";
|
|
5
|
+
|
|
6
|
+
export const zuiSplitButtonDropdown =
|
|
7
|
+
"data-[full-width=true]:block data-[full-width=true]:w-full";
|
|
8
|
+
|
|
9
|
+
export const zuiSplitButtonGroup =
|
|
10
|
+
"inline-flex items-stretch data-[full-width=true]:w-full";
|
|
11
|
+
|
|
12
|
+
export const zuiSplitButtonPrimary =
|
|
13
|
+
"rounded-r-none data-[full-width=true]:min-w-0 data-[full-width=true]:flex-1";
|
|
14
|
+
|
|
15
|
+
export const zuiSplitButtonTrigger =
|
|
16
|
+
"rounded-l-none border-l border-[color:var(--zui-split-button-separator,var(--zui-border,#ffffff33))] dark:border-[color:var(--zui-split-button-separator-dark,var(--zui-border-dark,#00000033))] px-2.5";
|
|
17
|
+
|
|
18
|
+
export const zuiSplitButtonTriggerSizes = {
|
|
19
|
+
sm: "min-w-8 px-2",
|
|
20
|
+
md: "min-w-10 px-2.5",
|
|
21
|
+
lg: "min-w-11 px-3",
|
|
22
|
+
xl: "min-w-12 px-3.5",
|
|
23
|
+
"2xl": "min-w-14 px-4",
|
|
24
|
+
"3xl": "min-w-16 px-4",
|
|
25
|
+
"4xl": "min-w-18 px-5",
|
|
26
|
+
"5xl": "min-w-20 px-5",
|
|
27
|
+
"6xl": "min-w-22 px-6",
|
|
28
|
+
"7xl": "min-w-24 px-6",
|
|
29
|
+
"8xl": "min-w-26 px-7",
|
|
30
|
+
"9xl": "min-w-28 px-7",
|
|
31
|
+
"10xl": "min-w-30 px-8",
|
|
32
|
+
icon: "min-w-10 px-0",
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
export const zuiSplitButtonContent =
|
|
36
|
+
"min-w-[var(--zui-split-button-menu-min-width,12rem)]";
|
|
37
|
+
|
|
38
|
+
export const zuiSplitButtonItemDisabled = "pointer-events-none opacity-50";
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { diffLines } from "diff";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
|
|
8
|
+
import type { CodeDiffBaseProps, DiffLine } from "./types";
|
|
9
|
+
import {
|
|
10
|
+
codeDiffLineContentVariants,
|
|
11
|
+
codeDiffLineNumberVariants,
|
|
12
|
+
codeDiffVariants,
|
|
13
|
+
} from "./variants";
|
|
14
|
+
|
|
15
|
+
function computeDiff(oldCode: string, newCode: string): DiffLine[] {
|
|
16
|
+
const changes = diffLines(oldCode, newCode);
|
|
17
|
+
const lines: DiffLine[] = [];
|
|
18
|
+
let oldLineNum = 1;
|
|
19
|
+
let newLineNum = 1;
|
|
20
|
+
|
|
21
|
+
for (const change of changes) {
|
|
22
|
+
const changeLines = change.value.replace(/\n$/, "").split("\n");
|
|
23
|
+
for (const content of changeLines) {
|
|
24
|
+
if (change.added) {
|
|
25
|
+
lines.push({
|
|
26
|
+
type: "added",
|
|
27
|
+
content,
|
|
28
|
+
oldLineNumber: null,
|
|
29
|
+
newLineNumber: newLineNum++,
|
|
30
|
+
});
|
|
31
|
+
} else if (change.removed) {
|
|
32
|
+
lines.push({
|
|
33
|
+
type: "removed",
|
|
34
|
+
content,
|
|
35
|
+
oldLineNumber: oldLineNum++,
|
|
36
|
+
newLineNumber: null,
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
lines.push({
|
|
40
|
+
type: "unchanged",
|
|
41
|
+
content,
|
|
42
|
+
oldLineNumber: oldLineNum++,
|
|
43
|
+
newLineNumber: newLineNum++,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return lines;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface SplitRow {
|
|
52
|
+
oldLine: DiffLine | null;
|
|
53
|
+
newLine: DiffLine | null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function toSplitRows(lines: DiffLine[]): SplitRow[] {
|
|
57
|
+
const rows: SplitRow[] = [];
|
|
58
|
+
let i = 0;
|
|
59
|
+
|
|
60
|
+
while (i < lines.length) {
|
|
61
|
+
const current = lines[i]!;
|
|
62
|
+
|
|
63
|
+
if (current.type === "unchanged") {
|
|
64
|
+
rows.push({ oldLine: current, newLine: current });
|
|
65
|
+
i++;
|
|
66
|
+
} else if (current.type === "removed") {
|
|
67
|
+
const removedBlock: DiffLine[] = [];
|
|
68
|
+
while (i < lines.length && lines[i]!.type === "removed") {
|
|
69
|
+
removedBlock.push(lines[i]!);
|
|
70
|
+
i++;
|
|
71
|
+
}
|
|
72
|
+
const addedBlock: DiffLine[] = [];
|
|
73
|
+
while (i < lines.length && lines[i]!.type === "added") {
|
|
74
|
+
addedBlock.push(lines[i]!);
|
|
75
|
+
i++;
|
|
76
|
+
}
|
|
77
|
+
const maxLen = Math.max(removedBlock.length, addedBlock.length);
|
|
78
|
+
for (let j = 0; j < maxLen; j++) {
|
|
79
|
+
rows.push({
|
|
80
|
+
oldLine: j < removedBlock.length ? removedBlock[j]! : null,
|
|
81
|
+
newLine: j < addedBlock.length ? addedBlock[j]! : null,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
} else if (current.type === "added") {
|
|
85
|
+
const addedBlock: DiffLine[] = [];
|
|
86
|
+
while (i < lines.length && lines[i]!.type === "added") {
|
|
87
|
+
addedBlock.push(lines[i]!);
|
|
88
|
+
i++;
|
|
89
|
+
}
|
|
90
|
+
for (const added of addedBlock) {
|
|
91
|
+
rows.push({ oldLine: null, newLine: added });
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
i++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return rows;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function LineNumberCell({
|
|
101
|
+
lineNumber,
|
|
102
|
+
type,
|
|
103
|
+
showLineNumbers,
|
|
104
|
+
}: {
|
|
105
|
+
lineNumber: number | null;
|
|
106
|
+
type: DiffLine["type"];
|
|
107
|
+
showLineNumbers: boolean;
|
|
108
|
+
}) {
|
|
109
|
+
return (
|
|
110
|
+
<td
|
|
111
|
+
className={codeDiffLineNumberVariants({ type })}
|
|
112
|
+
style={{ width: showLineNumbers ? "4.5rem" : "2rem" }}
|
|
113
|
+
>
|
|
114
|
+
{showLineNumbers && lineNumber !== null ? lineNumber : ""}
|
|
115
|
+
</td>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function LineContentCell({
|
|
120
|
+
line,
|
|
121
|
+
type,
|
|
122
|
+
showGutterMarkers,
|
|
123
|
+
}: {
|
|
124
|
+
line: string;
|
|
125
|
+
type: DiffLine["type"];
|
|
126
|
+
showGutterMarkers: boolean;
|
|
127
|
+
}) {
|
|
128
|
+
return (
|
|
129
|
+
<td className={codeDiffLineContentVariants({ type })}>
|
|
130
|
+
{showGutterMarkers ? (
|
|
131
|
+
<span className="inline-block w-4 select-none text-center">
|
|
132
|
+
{type === "added" ? "+" : type === "removed" ? "-" : " "}
|
|
133
|
+
</span>
|
|
134
|
+
) : null}
|
|
135
|
+
<span>{line || " "}</span>
|
|
136
|
+
</td>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function UnifiedView({
|
|
141
|
+
lines,
|
|
142
|
+
showLineNumbers,
|
|
143
|
+
showGutterMarkers,
|
|
144
|
+
}: {
|
|
145
|
+
lines: DiffLine[];
|
|
146
|
+
showLineNumbers: boolean;
|
|
147
|
+
showGutterMarkers: boolean;
|
|
148
|
+
}) {
|
|
149
|
+
return (
|
|
150
|
+
<table className="w-full border-collapse table-fixed">
|
|
151
|
+
<tbody>
|
|
152
|
+
{lines.map((line, idx) => (
|
|
153
|
+
<tr key={idx}>
|
|
154
|
+
<LineNumberCell
|
|
155
|
+
lineNumber={line.oldLineNumber}
|
|
156
|
+
type={line.type}
|
|
157
|
+
showLineNumbers={showLineNumbers}
|
|
158
|
+
/>
|
|
159
|
+
<LineNumberCell
|
|
160
|
+
lineNumber={line.newLineNumber}
|
|
161
|
+
type={line.type}
|
|
162
|
+
showLineNumbers={showLineNumbers}
|
|
163
|
+
/>
|
|
164
|
+
<LineContentCell
|
|
165
|
+
line={line.content}
|
|
166
|
+
type={line.type}
|
|
167
|
+
showGutterMarkers={showGutterMarkers}
|
|
168
|
+
/>
|
|
169
|
+
</tr>
|
|
170
|
+
))}
|
|
171
|
+
</tbody>
|
|
172
|
+
</table>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function SplitView({
|
|
177
|
+
lines,
|
|
178
|
+
showLineNumbers,
|
|
179
|
+
showGutterMarkers,
|
|
180
|
+
}: {
|
|
181
|
+
lines: DiffLine[];
|
|
182
|
+
showLineNumbers: boolean;
|
|
183
|
+
showGutterMarkers: boolean;
|
|
184
|
+
}) {
|
|
185
|
+
const rows = toSplitRows(lines);
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<table className="w-full border-collapse table-fixed">
|
|
189
|
+
<colgroup>
|
|
190
|
+
<col style={{ width: showLineNumbers ? "4.5rem" : "2rem" }} />
|
|
191
|
+
<col />
|
|
192
|
+
<col style={{ width: showLineNumbers ? "4.5rem" : "2rem" }} />
|
|
193
|
+
<col />
|
|
194
|
+
</colgroup>
|
|
195
|
+
<tbody>
|
|
196
|
+
{rows.map((row, idx) => (
|
|
197
|
+
<tr key={idx}>
|
|
198
|
+
<LineNumberCell
|
|
199
|
+
lineNumber={row.oldLine?.oldLineNumber ?? null}
|
|
200
|
+
type={row.oldLine?.type ?? "unchanged"}
|
|
201
|
+
showLineNumbers={showLineNumbers}
|
|
202
|
+
/>
|
|
203
|
+
<LineContentCell
|
|
204
|
+
line={row.oldLine?.content ?? ""}
|
|
205
|
+
type={row.oldLine?.type ?? "unchanged"}
|
|
206
|
+
showGutterMarkers={showGutterMarkers}
|
|
207
|
+
/>
|
|
208
|
+
<LineNumberCell
|
|
209
|
+
lineNumber={row.newLine?.newLineNumber ?? null}
|
|
210
|
+
type={row.newLine?.type ?? "unchanged"}
|
|
211
|
+
showLineNumbers={showLineNumbers}
|
|
212
|
+
/>
|
|
213
|
+
<LineContentCell
|
|
214
|
+
line={row.newLine?.content ?? ""}
|
|
215
|
+
type={row.newLine?.type ?? "unchanged"}
|
|
216
|
+
showGutterMarkers={showGutterMarkers}
|
|
217
|
+
/>
|
|
218
|
+
</tr>
|
|
219
|
+
))}
|
|
220
|
+
</tbody>
|
|
221
|
+
</table>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function CodeDiffBase({
|
|
226
|
+
className,
|
|
227
|
+
appearance,
|
|
228
|
+
size,
|
|
229
|
+
oldCode,
|
|
230
|
+
newCode,
|
|
231
|
+
viewType = "unified",
|
|
232
|
+
showLineNumbers = true,
|
|
233
|
+
showGutterMarkers = true,
|
|
234
|
+
oldTitle = "Old",
|
|
235
|
+
newTitle = "New",
|
|
236
|
+
ref,
|
|
237
|
+
as: Wrapper = "div",
|
|
238
|
+
...rest
|
|
239
|
+
}: CodeDiffBaseProps) {
|
|
240
|
+
const lines = useMemo(
|
|
241
|
+
() => computeDiff(oldCode ?? "", newCode ?? ""),
|
|
242
|
+
[oldCode, newCode],
|
|
243
|
+
);
|
|
244
|
+
const hasChanges = lines.some(
|
|
245
|
+
(l) => l.type === "added" || l.type === "removed",
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<Wrapper
|
|
250
|
+
ref={ref}
|
|
251
|
+
data-slot="code-diff"
|
|
252
|
+
className={cn(codeDiffVariants({ appearance, size }), className)}
|
|
253
|
+
{...rest}
|
|
254
|
+
>
|
|
255
|
+
{hasChanges ? (
|
|
256
|
+
<div className="sticky top-0 flex items-center justify-between border-b border-[color:var(--zui-code-diff-border,var(--zui-border,#0000001a))] dark:border-[color:var(--zui-code-diff-border-dark,var(--zui-border-dark,#ffffff1a))] bg-[var(--zui-code-diff-header-bg,var(--zui-surface-muted,oklch(92.9%_0.013_255.508)))] dark:bg-[var(--zui-code-diff-header-bg-dark,var(--zui-surface-muted-dark,oklch(27.9%_0.041_260.031)))] px-4 py-2">
|
|
257
|
+
<span className="text-xs font-medium">
|
|
258
|
+
{lines.filter((l) => l.type === "added").length} additions{" "}
|
|
259
|
+
<span className="mx-1">•</span>{" "}
|
|
260
|
+
{lines.filter((l) => l.type === "removed").length} deletions
|
|
261
|
+
</span>
|
|
262
|
+
<span className="text-xs text-[color:var(--zui-code-diff-header-fg,var(--zui-fg-muted,oklch(55.2%_0.046_257.417)))] dark:text-[color:var(--zui-code-diff-header-fg-dark,var(--zui-fg-muted-dark,oklch(70.8%_0.015_256.243)))]">
|
|
263
|
+
{oldTitle} → {newTitle}
|
|
264
|
+
</span>
|
|
265
|
+
</div>
|
|
266
|
+
) : null}
|
|
267
|
+
{viewType === "split" ? (
|
|
268
|
+
<SplitView
|
|
269
|
+
lines={lines}
|
|
270
|
+
showLineNumbers={showLineNumbers}
|
|
271
|
+
showGutterMarkers={showGutterMarkers}
|
|
272
|
+
/>
|
|
273
|
+
) : (
|
|
274
|
+
<UnifiedView
|
|
275
|
+
lines={lines}
|
|
276
|
+
showLineNumbers={showLineNumbers}
|
|
277
|
+
showGutterMarkers={showGutterMarkers}
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
280
|
+
</Wrapper>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
CodeDiffBase.displayName = "CodeDiff";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createRef } from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { CodeDiff } from "./code-diff";
|
|
6
|
+
|
|
7
|
+
const oldCode = "line one\nline two\nline three";
|
|
8
|
+
const newCode = "line one\nline two modified\nline three\nline four";
|
|
9
|
+
|
|
10
|
+
describe("CodeDiff", () => {
|
|
11
|
+
it("should expose displayName", () => {
|
|
12
|
+
expect(CodeDiff.displayName).toBe("CodeDiff");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should stamp data-slot", () => {
|
|
16
|
+
render(<CodeDiff oldCode={oldCode} newCode={newCode} />);
|
|
17
|
+
const root = document.querySelector('[data-slot="code-diff"]');
|
|
18
|
+
expect(root).toBeTruthy();
|
|
19
|
+
expect(root?.getAttribute("data-slot")).toBe("code-diff");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should render diff lines", () => {
|
|
23
|
+
render(<CodeDiff oldCode={oldCode} newCode={newCode} />);
|
|
24
|
+
expect(screen.getByText("line one")).toBeInTheDocument();
|
|
25
|
+
expect(screen.getByText("line two modified")).toBeInTheDocument();
|
|
26
|
+
expect(screen.getByText("line four")).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should show additions and deletions count", () => {
|
|
30
|
+
render(<CodeDiff oldCode={oldCode} newCode={newCode} />);
|
|
31
|
+
expect(screen.getByText(/additions/)).toBeInTheDocument();
|
|
32
|
+
expect(screen.getByText(/deletions/)).toBeInTheDocument();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should render unchanged code when no changes", () => {
|
|
36
|
+
render(<CodeDiff oldCode="same" newCode="same" />);
|
|
37
|
+
expect(screen.getByText("same")).toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should forward ref", () => {
|
|
41
|
+
const ref = createRef<HTMLDivElement>();
|
|
42
|
+
render(<CodeDiff ref={ref} oldCode={oldCode} newCode={newCode} />);
|
|
43
|
+
expect(ref.current?.getAttribute("data-slot")).toBe("code-diff");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should render in split view", () => {
|
|
47
|
+
render(<CodeDiff oldCode={oldCode} newCode={newCode} viewType="split" />);
|
|
48
|
+
expect(screen.getAllByText("line one").length).toBeGreaterThanOrEqual(1);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { CodeDiff } from "./code-diff";
|
|
4
|
+
export type {
|
|
5
|
+
CodeDiffBaseProps,
|
|
6
|
+
CodeDiffProps,
|
|
7
|
+
CodeDiffVariantProps,
|
|
8
|
+
CodeDiffViewType,
|
|
9
|
+
DiffLine,
|
|
10
|
+
} from "./types";
|
|
11
|
+
export {
|
|
12
|
+
codeDiffLineContentVariants,
|
|
13
|
+
codeDiffLineNumberVariants,
|
|
14
|
+
codeDiffVariants,
|
|
15
|
+
} from "./variants";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { VariantProps } from "class-variance-authority";
|
|
2
|
+
import type { ComponentPropsWithRef, ElementType } from "react";
|
|
3
|
+
|
|
4
|
+
import type { codeDiffVariants } from "./variants";
|
|
5
|
+
|
|
6
|
+
export type CodeDiffVariantProps = VariantProps<typeof codeDiffVariants>;
|
|
7
|
+
|
|
8
|
+
export type CodeDiffViewType = "split" | "unified";
|
|
9
|
+
|
|
10
|
+
export interface CodeDiffBaseProps extends ComponentPropsWithRef<"div"> {
|
|
11
|
+
oldCode: string;
|
|
12
|
+
newCode: string;
|
|
13
|
+
language?: string;
|
|
14
|
+
viewType?: CodeDiffViewType;
|
|
15
|
+
showLineNumbers?: boolean;
|
|
16
|
+
showGutterMarkers?: boolean;
|
|
17
|
+
oldTitle?: string;
|
|
18
|
+
newTitle?: string;
|
|
19
|
+
appearance?: CodeDiffVariantProps["appearance"];
|
|
20
|
+
size?: CodeDiffVariantProps["size"];
|
|
21
|
+
as?: ElementType;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type CodeDiffProps = Omit<CodeDiffBaseProps, "as">;
|
|
25
|
+
|
|
26
|
+
export interface DiffLine {
|
|
27
|
+
type: "added" | "removed" | "unchanged";
|
|
28
|
+
content: string;
|
|
29
|
+
oldLineNumber: number | null;
|
|
30
|
+
newLineNumber: number | null;
|
|
31
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
zuiCodeDiffAppearances,
|
|
5
|
+
zuiCodeDiffBase,
|
|
6
|
+
zuiCodeDiffLineAdded,
|
|
7
|
+
zuiCodeDiffLineContentBase,
|
|
8
|
+
zuiCodeDiffLineNumberBase,
|
|
9
|
+
zuiCodeDiffLineRemoved,
|
|
10
|
+
zuiCodeDiffLineUnchanged,
|
|
11
|
+
zuiCodeDiffSizes,
|
|
12
|
+
} from "../../design-system/code-diff";
|
|
13
|
+
|
|
14
|
+
export const codeDiffVariants = cva(zuiCodeDiffBase, {
|
|
15
|
+
variants: {
|
|
16
|
+
appearance: zuiCodeDiffAppearances,
|
|
17
|
+
size: zuiCodeDiffSizes,
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
appearance: "default",
|
|
21
|
+
size: "md",
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const codeDiffLineNumberVariants = cva(zuiCodeDiffLineNumberBase, {
|
|
26
|
+
variants: {
|
|
27
|
+
type: {
|
|
28
|
+
added: zuiCodeDiffLineAdded,
|
|
29
|
+
removed: zuiCodeDiffLineRemoved,
|
|
30
|
+
unchanged: zuiCodeDiffLineUnchanged,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
defaultVariants: {
|
|
34
|
+
type: "unchanged",
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const codeDiffLineContentVariants = cva(zuiCodeDiffLineContentBase, {
|
|
39
|
+
variants: {
|
|
40
|
+
type: {
|
|
41
|
+
added: zuiCodeDiffLineAdded,
|
|
42
|
+
removed: zuiCodeDiffLineRemoved,
|
|
43
|
+
unchanged: zuiCodeDiffLineUnchanged,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
defaultVariants: {
|
|
47
|
+
type: "unchanged",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
@@ -37,10 +37,12 @@ const useDropdown = () => {
|
|
|
37
37
|
========================= */
|
|
38
38
|
export const Dropdown = ({
|
|
39
39
|
children,
|
|
40
|
+
className,
|
|
40
41
|
defaultOpen = false,
|
|
41
42
|
open: controlledOpen,
|
|
42
43
|
onOpenChange,
|
|
43
44
|
multiSelect = false,
|
|
45
|
+
...props
|
|
44
46
|
}: DropdownProps) => {
|
|
45
47
|
const menuId = `${useId()}-menu`;
|
|
46
48
|
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
@@ -82,7 +84,9 @@ export const Dropdown = ({
|
|
|
82
84
|
menuId,
|
|
83
85
|
}}
|
|
84
86
|
>
|
|
85
|
-
<div className="relative inline-block"
|
|
87
|
+
<div className={cn("relative inline-block", className)} {...props}>
|
|
88
|
+
{children}
|
|
89
|
+
</div>
|
|
86
90
|
</DropdownContext.Provider>
|
|
87
91
|
);
|
|
88
92
|
};
|
|
@@ -180,10 +184,10 @@ export const DropdownItem = ({
|
|
|
180
184
|
...props
|
|
181
185
|
}: DropdownItemProps) => {
|
|
182
186
|
const { toggleSelect, selectedValues } = useDropdown();
|
|
183
|
-
const isSelected = selectedValues.includes(value);
|
|
187
|
+
const isSelected = value !== undefined && selectedValues.includes(value);
|
|
184
188
|
|
|
185
189
|
const handleClick = () => {
|
|
186
|
-
toggleSelect(value);
|
|
190
|
+
if (value !== undefined) toggleSelect(value);
|
|
187
191
|
onSelect?.();
|
|
188
192
|
};
|
|
189
193
|
|
package/src/ui/dropdown/types.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type DropdownContextType = {
|
|
|
14
14
|
|
|
15
15
|
type Variant = keyof typeof zuiDropdownTriggerVariants;
|
|
16
16
|
|
|
17
|
-
export type DropdownProps = {
|
|
17
|
+
export type DropdownProps = HTMLAttributes<HTMLDivElement> & {
|
|
18
18
|
children: ReactNode;
|
|
19
19
|
defaultOpen?: boolean;
|
|
20
20
|
open?: boolean;
|
|
@@ -37,7 +37,7 @@ export type DropdownContentProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
37
37
|
|
|
38
38
|
export type DropdownItemProps = HTMLAttributes<HTMLDivElement> & {
|
|
39
39
|
children: ReactNode;
|
|
40
|
-
value
|
|
40
|
+
value?: string;
|
|
41
41
|
onSelect?: () => void;
|
|
42
42
|
leftIcon?: ReactNode;
|
|
43
43
|
rightIcon?: ReactNode;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { SplitButton } from "./split-button";
|
|
4
|
+
export type {
|
|
5
|
+
SplitButtonAppearance,
|
|
6
|
+
SplitButtonItem,
|
|
7
|
+
SplitButtonProps,
|
|
8
|
+
SplitButtonSize,
|
|
9
|
+
SplitButtonVariant,
|
|
10
|
+
} from "./types";
|
|
11
|
+
export {
|
|
12
|
+
splitButtonContentVariants,
|
|
13
|
+
splitButtonDropdownVariants,
|
|
14
|
+
splitButtonGroupVariants,
|
|
15
|
+
splitButtonItemDisabledVariants,
|
|
16
|
+
splitButtonPrimaryVariants,
|
|
17
|
+
splitButtonRootVariants,
|
|
18
|
+
splitButtonTriggerVariants,
|
|
19
|
+
} from "./variants";
|