@arcfusionz/arc-primitive-ui 0.2.12 → 0.2.14
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/dist/components/AILoader/AILoader.js +2 -2
- package/dist/components/Alert/Alert.js +2 -2
- package/dist/components/Avatar/Avatar.js +4 -4
- package/dist/components/Badge/Badge.js +2 -2
- package/dist/components/Button/Button.js +3 -3
- package/dist/components/Calendar/Calendar.js +2 -2
- package/dist/components/CodeBlock/CodeBlock.d.ts +69 -0
- package/dist/components/CodeBlock/CodeBlock.js +201 -0
- package/dist/components/CodeBlock/index.d.ts +2 -0
- package/dist/components/CodeBlock/index.js +2 -0
- package/dist/components/CodeBlock/sqlHighlight.js +218 -0
- package/dist/components/Field/Field.js +2 -2
- package/dist/components/FileUpload/FileUpload.d.ts +190 -0
- package/dist/components/FileUpload/FileUpload.js +515 -0
- package/dist/components/FileUpload/index.d.ts +2 -0
- package/dist/components/FileUpload/index.js +2 -0
- package/dist/components/Stepper/Stepper.js +2 -2
- package/dist/components/Switch/Switch.js +2 -2
- package/dist/components/SystemState/SystemState.d.ts +4 -3
- package/dist/components/SystemState/SystemState.js +15 -4
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -3
- package/dist/lib/cn.js +2 -1
- package/package.json +1 -1
- package/src/styles/theme.css +23 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { forwardRef, useId } from "react";
|
|
3
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { useRender } from "@base-ui/react/use-render";
|
|
5
5
|
//#region src/components/AILoader/AILoader.tsx
|
|
6
6
|
const rootClasses = "relative inline-flex shrink-0 items-center justify-center align-middle select-none";
|
|
@@ -58,7 +58,7 @@ const AILoader = forwardRef(({ state = "default", size = "md", tone = "brand", t
|
|
|
58
58
|
role: processing ? "status" : "img",
|
|
59
59
|
"aria-label": processing ? hasText ? label : void 0 : accessibleLabel,
|
|
60
60
|
className: cn(rootClasses, toneClasses[tone], hasText && gapClasses[size], className),
|
|
61
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
61
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
62
62
|
/* @__PURE__ */ jsxs("svg", {
|
|
63
63
|
viewBox: "0 0 24 24",
|
|
64
64
|
fill: "none",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { useRender } from "@base-ui/react/use-render";
|
|
5
5
|
import { Button } from "@base-ui/react/button";
|
|
6
6
|
//#region src/components/Alert/Alert.tsx
|
|
@@ -163,7 +163,7 @@ const Alert = forwardRef(({ variant = "neutral", appearance = "soft", title, ico
|
|
|
163
163
|
variant,
|
|
164
164
|
appearance
|
|
165
165
|
}), className),
|
|
166
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
166
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
167
167
|
resolvedIcon != null && typeof resolvedIcon !== "boolean" && /* @__PURE__ */ jsx("span", {
|
|
168
168
|
"aria-hidden": "true",
|
|
169
169
|
className: cn(iconSlotClasses, !solid && iconColorClasses[variant]),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { Children, createContext, forwardRef, useContext, useMemo } from "react";
|
|
3
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { useRender } from "@base-ui/react/use-render";
|
|
5
5
|
import { Avatar } from "@base-ui/react/avatar";
|
|
6
6
|
//#region src/components/Avatar/Avatar.tsx
|
|
@@ -110,7 +110,7 @@ const Avatar$1 = forwardRef(({ size, shape, src, alt, name, fallback, fallbackDe
|
|
|
110
110
|
shape: resolvedShape
|
|
111
111
|
}), group != null && "ring-2 ring-background", className),
|
|
112
112
|
...rest,
|
|
113
|
-
children: [useShorthand && /* @__PURE__ */ jsxs(Fragment, { children: [src != null && /* @__PURE__ */ jsx(AvatarImage, {
|
|
113
|
+
children: [useShorthand && /* @__PURE__ */ jsxs(Fragment$1, { children: [src != null && /* @__PURE__ */ jsx(AvatarImage, {
|
|
114
114
|
src,
|
|
115
115
|
alt: alt ?? ""
|
|
116
116
|
}), /* @__PURE__ */ jsx(AvatarFallback, {
|
|
@@ -165,7 +165,7 @@ const AvatarBadge = forwardRef(({ label, render, className, children, ...rest },
|
|
|
165
165
|
ref,
|
|
166
166
|
props: {
|
|
167
167
|
className: cn("absolute end-0 bottom-0 block rounded-full bg-success ring-2 ring-background", badgeSizeClasses[size], className),
|
|
168
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [label != null && /* @__PURE__ */ jsx("span", {
|
|
168
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [label != null && /* @__PURE__ */ jsx("span", {
|
|
169
169
|
className: "sr-only",
|
|
170
170
|
children: label
|
|
171
171
|
}), children] }),
|
|
@@ -202,7 +202,7 @@ const AvatarGroup = forwardRef(({ size = "md", shape = "circle", max, total, ren
|
|
|
202
202
|
ref,
|
|
203
203
|
props: {
|
|
204
204
|
className: cn("flex items-center", groupSpacingClasses[size], className),
|
|
205
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [visible, surplus > 0 && /* @__PURE__ */ jsx(Avatar$1, { children: `+${surplus}` })] }),
|
|
205
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [visible, surplus > 0 && /* @__PURE__ */ jsx(Avatar$1, { children: `+${surplus}` })] }),
|
|
206
206
|
...rest
|
|
207
207
|
}
|
|
208
208
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { useRender } from "@base-ui/react/use-render";
|
|
5
5
|
import { Button } from "@base-ui/react/button";
|
|
6
6
|
//#region src/components/Badge/Badge.tsx
|
|
@@ -88,7 +88,7 @@ const Badge = forwardRef(({ variant = "neutral", appearance = "soft", size = "md
|
|
|
88
88
|
size,
|
|
89
89
|
pill
|
|
90
90
|
}), className),
|
|
91
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
91
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
92
92
|
dot && /* @__PURE__ */ jsx("span", {
|
|
93
93
|
"aria-hidden": "true",
|
|
94
94
|
className: "size-1.5 shrink-0 rounded-full bg-current"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { forwardRef, useRef } from "react";
|
|
3
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { Button } from "@base-ui/react/button";
|
|
5
5
|
//#region src/components/Button/Button.tsx
|
|
6
6
|
const baseClasses = "relative inline-flex items-center justify-center whitespace-nowrap select-none rounded-md font-sans font-semibold transition-colors duration-150 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50";
|
|
@@ -128,7 +128,7 @@ const Button$1 = forwardRef(({ variant = "primary", size = "md", fullWidth = fal
|
|
|
128
128
|
if (event.detail === 0 && rippleContainerRef.current && !prefersReducedMotion()) spawnRipple(rippleContainerRef.current);
|
|
129
129
|
onClick?.(event);
|
|
130
130
|
};
|
|
131
|
-
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
131
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
132
132
|
startIcon != null && /* @__PURE__ */ jsx("span", {
|
|
133
133
|
"aria-hidden": "true",
|
|
134
134
|
className: "inline-flex items-center",
|
|
@@ -161,7 +161,7 @@ const Button$1 = forwardRef(({ variant = "primary", size = "md", fullWidth = fal
|
|
|
161
161
|
ref: rippleContainerRef,
|
|
162
162
|
"aria-hidden": "true",
|
|
163
163
|
className: "pointer-events-none absolute inset-0 overflow-hidden rounded-[inherit] [contain:strict]"
|
|
164
|
-
}), loading ? loadingText != null ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Spinner, {}), loadingText] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("span", {
|
|
164
|
+
}), loading ? loadingText != null ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Spinner, {}), loadingText] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
165
165
|
className: "absolute inset-0 flex items-center justify-center",
|
|
166
166
|
children: [/* @__PURE__ */ jsx(Spinner, {}), /* @__PURE__ */ jsx("span", {
|
|
167
167
|
className: "sr-only",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { useRender } from "@base-ui/react/use-render";
|
|
5
5
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
6
6
|
//#region src/components/Calendar/Calendar.tsx
|
|
@@ -441,7 +441,7 @@ const Calendar = forwardRef((props, forwardedRef) => {
|
|
|
441
441
|
role: "group",
|
|
442
442
|
"aria-label": ariaLabel,
|
|
443
443
|
className: cn("inline-flex flex-col p-3 font-sans", className),
|
|
444
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
444
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
445
445
|
role: "status",
|
|
446
446
|
className: "sr-only",
|
|
447
447
|
children: announcement
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
//#region src/components/CodeBlock/CodeBlock.d.ts
|
|
4
|
+
interface CodeBlockVariantsOptions {
|
|
5
|
+
/** Additional classes merged after the Code Block frame recipe. */
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Class list for an element styled as a Code Block frame (dark `code` surface,
|
|
10
|
+
* 6px border, `font-code`). Useful when a consumer needs the same shell around
|
|
11
|
+
* a bespoke body that this component's props do not cover.
|
|
12
|
+
*/
|
|
13
|
+
declare function codeBlockVariants({ className }?: CodeBlockVariantsOptions): string;
|
|
14
|
+
interface CodeBlockProps extends Omit<ComponentPropsWithoutRef<"div">, "children" | "className" | "prefix"> {
|
|
15
|
+
/**
|
|
16
|
+
* The source text. Used as the copy payload and, unless `children` is
|
|
17
|
+
* provided, rendered as the code body — syntax-highlighted when `language`
|
|
18
|
+
* is a SQL dialect, otherwise plain. A single trailing newline is trimmed.
|
|
19
|
+
*/
|
|
20
|
+
code?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Custom body rendered in place of `code`, for pre-highlighted output from a
|
|
23
|
+
* full engine (Shiki/Prism) or any non-SQL language. SQL is highlighted
|
|
24
|
+
* in-house, so `children` is only needed to go beyond that. Copy still uses
|
|
25
|
+
* `code` when set, otherwise the rendered text. Line numbers and
|
|
26
|
+
* `highlightLines` do not apply to a custom `children` body.
|
|
27
|
+
*/
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* Language label shown in the header, e.g. `"sql"`; also emitted as
|
|
31
|
+
* `data-language`. When it names a SQL dialect (`sql`, `postgres`, `mysql`,
|
|
32
|
+
* …) the code is colored by the built-in SQL highlighter — the only language
|
|
33
|
+
* highlighted in-house. Other languages render plain unless you pass
|
|
34
|
+
* highlighted `children`.
|
|
35
|
+
*/
|
|
36
|
+
language?: string;
|
|
37
|
+
/** File name shown as the header title; also becomes the code region's accessible name. */
|
|
38
|
+
filename?: string;
|
|
39
|
+
/** Render a line-number gutter. Applies to the `code` path (plain or SQL), not a custom `children` body. */
|
|
40
|
+
showLineNumbers?: boolean;
|
|
41
|
+
/** 1-based line numbers to emphasize with a band and left accent. Applies to the `code` path, not a custom `children` body. */
|
|
42
|
+
highlightLines?: number[];
|
|
43
|
+
/** Soft-wrap long lines instead of the default horizontal scroll. */
|
|
44
|
+
wrap?: boolean;
|
|
45
|
+
/** Show the copy-to-clipboard control. */
|
|
46
|
+
copyable?: boolean;
|
|
47
|
+
/** Accessible name for the copy control. */
|
|
48
|
+
copyLabel?: string;
|
|
49
|
+
/** Message announced (politely) once the copy succeeds. */
|
|
50
|
+
copiedLabel?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Accessible name for the scrollable code region when there is no `filename`
|
|
53
|
+
* to name it. Defaults to `"<language> code"` or `"Code snippet"`.
|
|
54
|
+
*/
|
|
55
|
+
codeLabel?: string;
|
|
56
|
+
/** Cap the code area height (number → px), enabling vertical scroll. */
|
|
57
|
+
maxHeight?: number | string;
|
|
58
|
+
/**
|
|
59
|
+
* Replace the default `div` root, e.g. `render={<figure />}`. The header and
|
|
60
|
+
* code region are provided as children; choose an element that fits the
|
|
61
|
+
* surrounding document.
|
|
62
|
+
*/
|
|
63
|
+
render?: useRender.RenderProp;
|
|
64
|
+
/** Additional classes merged after the Code Block frame recipe. */
|
|
65
|
+
className?: string;
|
|
66
|
+
}
|
|
67
|
+
declare const CodeBlock: import("react").ForwardRefExoticComponent<CodeBlockProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { CodeBlock, CodeBlockProps, CodeBlockVariantsOptions, codeBlockVariants };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { highlightSqlLines } from "./sqlHighlight.js";
|
|
3
|
+
import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
6
|
+
//#region src/components/CodeBlock/CodeBlock.tsx
|
|
7
|
+
const containerClasses = "relative overflow-hidden rounded-md border border-slate-800 bg-code font-code text-sm text-code-foreground";
|
|
8
|
+
/**
|
|
9
|
+
* Class list for an element styled as a Code Block frame (dark `code` surface,
|
|
10
|
+
* 6px border, `font-code`). Useful when a consumer needs the same shell around
|
|
11
|
+
* a bespoke body that this component's props do not cover.
|
|
12
|
+
*/
|
|
13
|
+
function codeBlockVariants({ className } = {}) {
|
|
14
|
+
return cn(containerClasses, className);
|
|
15
|
+
}
|
|
16
|
+
const SQL_LANGUAGES = /* @__PURE__ */ new Set([
|
|
17
|
+
"sql",
|
|
18
|
+
"postgres",
|
|
19
|
+
"postgresql",
|
|
20
|
+
"psql",
|
|
21
|
+
"plpgsql",
|
|
22
|
+
"mysql",
|
|
23
|
+
"mariadb",
|
|
24
|
+
"sqlite",
|
|
25
|
+
"tsql",
|
|
26
|
+
"mssql",
|
|
27
|
+
"plsql",
|
|
28
|
+
"oracle",
|
|
29
|
+
"bigquery",
|
|
30
|
+
"snowflake",
|
|
31
|
+
"redshift"
|
|
32
|
+
]);
|
|
33
|
+
function isSqlLanguage(language) {
|
|
34
|
+
return language != null && SQL_LANGUAGES.has(language.toLowerCase());
|
|
35
|
+
}
|
|
36
|
+
const sqlTokenClasses = {
|
|
37
|
+
keyword: "text-code-keyword",
|
|
38
|
+
function: "text-code-function",
|
|
39
|
+
string: "text-code-string",
|
|
40
|
+
number: "text-code-number",
|
|
41
|
+
comment: "text-code-comment italic",
|
|
42
|
+
punctuation: "text-code-punctuation",
|
|
43
|
+
identifier: "",
|
|
44
|
+
whitespace: ""
|
|
45
|
+
};
|
|
46
|
+
/** Render one line of SQL tokens as colored spans (or a space to hold height). */
|
|
47
|
+
function renderSqlLine(tokens) {
|
|
48
|
+
if (tokens.length === 0) return " ";
|
|
49
|
+
return tokens.map((token, index) => {
|
|
50
|
+
const className = sqlTokenClasses[token.type];
|
|
51
|
+
return className ? /* @__PURE__ */ jsx("span", {
|
|
52
|
+
className,
|
|
53
|
+
children: token.value
|
|
54
|
+
}, index) : /* @__PURE__ */ jsx("span", { children: token.value }, index);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function CopyIcon(props) {
|
|
58
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
59
|
+
viewBox: "0 0 24 24",
|
|
60
|
+
fill: "none",
|
|
61
|
+
stroke: "currentColor",
|
|
62
|
+
strokeWidth: 2,
|
|
63
|
+
strokeLinecap: "round",
|
|
64
|
+
strokeLinejoin: "round",
|
|
65
|
+
...props,
|
|
66
|
+
children: [/* @__PURE__ */ jsx("rect", {
|
|
67
|
+
x: "9",
|
|
68
|
+
y: "9",
|
|
69
|
+
width: "13",
|
|
70
|
+
height: "13",
|
|
71
|
+
rx: "2"
|
|
72
|
+
}), /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function CheckIcon(props) {
|
|
76
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
77
|
+
viewBox: "0 0 24 24",
|
|
78
|
+
fill: "none",
|
|
79
|
+
stroke: "currentColor",
|
|
80
|
+
strokeWidth: 2,
|
|
81
|
+
strokeLinecap: "round",
|
|
82
|
+
strokeLinejoin: "round",
|
|
83
|
+
...props,
|
|
84
|
+
children: /* @__PURE__ */ jsx("path", { d: "m5 13 4 4L19 7" })
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function CopyButton({ getText, copyLabel, copiedLabel, floating = false }) {
|
|
88
|
+
const [copied, setCopied] = useState(false);
|
|
89
|
+
const resetTimer = useRef(void 0);
|
|
90
|
+
useEffect(() => () => clearTimeout(resetTimer.current), []);
|
|
91
|
+
const handleCopy = () => {
|
|
92
|
+
const write = navigator?.clipboard?.writeText;
|
|
93
|
+
if (!write) return;
|
|
94
|
+
write.call(navigator.clipboard, getText()).then(() => {
|
|
95
|
+
setCopied(true);
|
|
96
|
+
clearTimeout(resetTimer.current);
|
|
97
|
+
resetTimer.current = setTimeout(() => setCopied(false), 2e3);
|
|
98
|
+
}, () => {});
|
|
99
|
+
};
|
|
100
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
101
|
+
type: "button",
|
|
102
|
+
onClick: handleCopy,
|
|
103
|
+
"aria-label": copyLabel,
|
|
104
|
+
className: cn("inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-slate-400", "transition-colors hover:bg-background/10 hover:text-code-foreground", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", "[&_svg]:pointer-events-none [&_svg]:size-4", floating && "absolute right-2 top-2 z-10 bg-code/90 text-slate-300"),
|
|
105
|
+
children: [copied ? /* @__PURE__ */ jsx(CheckIcon, {
|
|
106
|
+
"aria-hidden": "true",
|
|
107
|
+
className: "text-success-400"
|
|
108
|
+
}) : /* @__PURE__ */ jsx(CopyIcon, { "aria-hidden": "true" }), /* @__PURE__ */ jsx("span", {
|
|
109
|
+
className: "sr-only",
|
|
110
|
+
"aria-live": "polite",
|
|
111
|
+
children: copied ? copiedLabel : ""
|
|
112
|
+
})]
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const CodeBlock = forwardRef(({ code, children, language, filename, showLineNumbers = false, highlightLines, wrap = false, copyable = true, copyLabel = "Copy code", copiedLabel = "Copied to clipboard", codeLabel, maxHeight, render, className, ...rest }, ref) => {
|
|
116
|
+
const preRef = useRef(null);
|
|
117
|
+
const filenameId = useId();
|
|
118
|
+
const getCopyText = useCallback(() => code ?? preRef.current?.textContent ?? "", [code]);
|
|
119
|
+
const trimmed = code == null ? null : code.replace(/\n$/, "");
|
|
120
|
+
const sqlMode = children == null && trimmed != null && isSqlLanguage(language);
|
|
121
|
+
const plainLines = useMemo(() => children == null && trimmed != null && !isSqlLanguage(language) ? trimmed.split("\n") : null, [
|
|
122
|
+
children,
|
|
123
|
+
trimmed,
|
|
124
|
+
language
|
|
125
|
+
]);
|
|
126
|
+
const codeLines = useMemo(() => sqlMode && trimmed != null ? highlightSqlLines(trimmed) : null, [sqlMode, trimmed]) ?? plainLines;
|
|
127
|
+
const highlightSet = useMemo(() => new Set(highlightLines ?? []), [highlightLines]);
|
|
128
|
+
const gutterMinWidth = codeLines ? `${String(codeLines.length).length}ch` : void 0;
|
|
129
|
+
const hasHeader = Boolean(filename) || Boolean(language);
|
|
130
|
+
const preStyle = maxHeight == null ? void 0 : { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight };
|
|
131
|
+
const copyButton = copyable ? /* @__PURE__ */ jsx(CopyButton, {
|
|
132
|
+
getText: getCopyText,
|
|
133
|
+
copyLabel,
|
|
134
|
+
copiedLabel,
|
|
135
|
+
floating: !hasHeader
|
|
136
|
+
}) : null;
|
|
137
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [hasHeader && /* @__PURE__ */ jsxs("div", {
|
|
138
|
+
className: "flex items-center justify-between gap-3 border-b border-slate-800 bg-slate-950 px-4 py-2",
|
|
139
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
140
|
+
className: "flex min-w-0 items-center gap-2",
|
|
141
|
+
children: filename && /* @__PURE__ */ jsx("span", {
|
|
142
|
+
id: filenameId,
|
|
143
|
+
className: "truncate font-sans text-sm text-slate-300",
|
|
144
|
+
children: filename
|
|
145
|
+
})
|
|
146
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
147
|
+
className: "flex shrink-0 items-center gap-3",
|
|
148
|
+
children: [language && /* @__PURE__ */ jsx("span", {
|
|
149
|
+
className: "font-sans text-xs font-medium uppercase tracking-wider text-slate-400",
|
|
150
|
+
children: language
|
|
151
|
+
}), copyButton]
|
|
152
|
+
})]
|
|
153
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
154
|
+
className: "relative",
|
|
155
|
+
children: [!hasHeader && copyButton, /* @__PURE__ */ jsx("pre", {
|
|
156
|
+
ref: preRef,
|
|
157
|
+
tabIndex: 0,
|
|
158
|
+
"aria-labelledby": filename ? filenameId : void 0,
|
|
159
|
+
"aria-label": filename ? void 0 : codeLabel ?? (language ? `${language} code` : "Code snippet"),
|
|
160
|
+
style: preStyle,
|
|
161
|
+
className: cn("m-0 overflow-auto py-3 leading-relaxed whitespace-normal", "focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring"),
|
|
162
|
+
children: children != null ? /* @__PURE__ */ jsx("code", {
|
|
163
|
+
className: cn("block px-4", wrap ? "whitespace-pre-wrap break-words" : "whitespace-pre"),
|
|
164
|
+
children
|
|
165
|
+
}) : /* @__PURE__ */ jsx("code", {
|
|
166
|
+
className: "block",
|
|
167
|
+
children: codeLines?.map((line, index) => {
|
|
168
|
+
const lineNumber = index + 1;
|
|
169
|
+
const highlighted = highlightSet.has(lineNumber);
|
|
170
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
171
|
+
"data-line": lineNumber,
|
|
172
|
+
className: cn("flex items-start border-l-2 border-transparent", wrap ? "w-full" : "w-max min-w-full", highlighted && "border-primary-500 bg-background/10"),
|
|
173
|
+
children: [showLineNumbers && /* @__PURE__ */ jsx("span", {
|
|
174
|
+
"aria-hidden": "true",
|
|
175
|
+
style: { minWidth: gutterMinWidth },
|
|
176
|
+
className: "shrink-0 select-none pr-4 pl-4 text-right text-slate-400 tabular-nums",
|
|
177
|
+
children: lineNumber
|
|
178
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
179
|
+
className: cn("min-w-0", showLineNumbers ? "pr-4" : "px-4", wrap ? "whitespace-pre-wrap break-words" : "whitespace-pre"),
|
|
180
|
+
children: typeof line === "string" ? line.length ? line : " " : renderSqlLine(line)
|
|
181
|
+
})]
|
|
182
|
+
}, index);
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
})]
|
|
186
|
+
})] });
|
|
187
|
+
return useRender({
|
|
188
|
+
defaultTagName: "div",
|
|
189
|
+
render,
|
|
190
|
+
ref,
|
|
191
|
+
props: {
|
|
192
|
+
...rest,
|
|
193
|
+
"data-language": language,
|
|
194
|
+
className: codeBlockVariants({ className }),
|
|
195
|
+
children: content
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
CodeBlock.displayName = "CodeBlock";
|
|
200
|
+
//#endregion
|
|
201
|
+
export { CodeBlock, codeBlockVariants };
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
//#region src/components/CodeBlock/sqlHighlight.ts
|
|
2
|
+
const KEYWORDS = /* @__PURE__ */ new Set([
|
|
3
|
+
"ADD",
|
|
4
|
+
"ALL",
|
|
5
|
+
"ALTER",
|
|
6
|
+
"AND",
|
|
7
|
+
"ANY",
|
|
8
|
+
"AS",
|
|
9
|
+
"ASC",
|
|
10
|
+
"BEGIN",
|
|
11
|
+
"BETWEEN",
|
|
12
|
+
"BY",
|
|
13
|
+
"CASCADE",
|
|
14
|
+
"CASE",
|
|
15
|
+
"CAST",
|
|
16
|
+
"CHECK",
|
|
17
|
+
"COLLATE",
|
|
18
|
+
"COLUMN",
|
|
19
|
+
"COMMIT",
|
|
20
|
+
"CONSTRAINT",
|
|
21
|
+
"CREATE",
|
|
22
|
+
"CROSS",
|
|
23
|
+
"CURRENT",
|
|
24
|
+
"DATABASE",
|
|
25
|
+
"DECLARE",
|
|
26
|
+
"DEFAULT",
|
|
27
|
+
"DELETE",
|
|
28
|
+
"DESC",
|
|
29
|
+
"DISTINCT",
|
|
30
|
+
"DO",
|
|
31
|
+
"DROP",
|
|
32
|
+
"ELSE",
|
|
33
|
+
"END",
|
|
34
|
+
"EXCEPT",
|
|
35
|
+
"EXISTS",
|
|
36
|
+
"EXPLAIN",
|
|
37
|
+
"FALSE",
|
|
38
|
+
"FETCH",
|
|
39
|
+
"FILTER",
|
|
40
|
+
"FIRST",
|
|
41
|
+
"FOLLOWING",
|
|
42
|
+
"FOR",
|
|
43
|
+
"FOREIGN",
|
|
44
|
+
"FROM",
|
|
45
|
+
"FULL",
|
|
46
|
+
"FUNCTION",
|
|
47
|
+
"GENERATED",
|
|
48
|
+
"GRANT",
|
|
49
|
+
"GROUP",
|
|
50
|
+
"HAVING",
|
|
51
|
+
"IF",
|
|
52
|
+
"ILIKE",
|
|
53
|
+
"IN",
|
|
54
|
+
"INDEX",
|
|
55
|
+
"INNER",
|
|
56
|
+
"INSERT",
|
|
57
|
+
"INTERSECT",
|
|
58
|
+
"INTO",
|
|
59
|
+
"IS",
|
|
60
|
+
"JOIN",
|
|
61
|
+
"KEY",
|
|
62
|
+
"LANGUAGE",
|
|
63
|
+
"LAST",
|
|
64
|
+
"LATERAL",
|
|
65
|
+
"LEFT",
|
|
66
|
+
"LIKE",
|
|
67
|
+
"LIMIT",
|
|
68
|
+
"MATERIALIZED",
|
|
69
|
+
"NATURAL",
|
|
70
|
+
"NEXT",
|
|
71
|
+
"NOT",
|
|
72
|
+
"NOTHING",
|
|
73
|
+
"NULL",
|
|
74
|
+
"NULLS",
|
|
75
|
+
"OFFSET",
|
|
76
|
+
"ON",
|
|
77
|
+
"ONLY",
|
|
78
|
+
"OR",
|
|
79
|
+
"ORDER",
|
|
80
|
+
"OUTER",
|
|
81
|
+
"OVER",
|
|
82
|
+
"PARTITION",
|
|
83
|
+
"PRECEDING",
|
|
84
|
+
"PRIMARY",
|
|
85
|
+
"PROCEDURE",
|
|
86
|
+
"RANGE",
|
|
87
|
+
"RECURSIVE",
|
|
88
|
+
"REFERENCES",
|
|
89
|
+
"RETURNING",
|
|
90
|
+
"RETURNS",
|
|
91
|
+
"REVOKE",
|
|
92
|
+
"RIGHT",
|
|
93
|
+
"ROLLBACK",
|
|
94
|
+
"ROW",
|
|
95
|
+
"ROWS",
|
|
96
|
+
"SCHEMA",
|
|
97
|
+
"SELECT",
|
|
98
|
+
"SEQUENCE",
|
|
99
|
+
"SET",
|
|
100
|
+
"SOME",
|
|
101
|
+
"TABLE",
|
|
102
|
+
"TEMP",
|
|
103
|
+
"TEMPORARY",
|
|
104
|
+
"THEN",
|
|
105
|
+
"TRANSACTION",
|
|
106
|
+
"TRIGGER",
|
|
107
|
+
"TRUE",
|
|
108
|
+
"TRUNCATE",
|
|
109
|
+
"UNBOUNDED",
|
|
110
|
+
"UNION",
|
|
111
|
+
"UNIQUE",
|
|
112
|
+
"UNKNOWN",
|
|
113
|
+
"UPDATE",
|
|
114
|
+
"USING",
|
|
115
|
+
"VALUES",
|
|
116
|
+
"VIEW",
|
|
117
|
+
"WHEN",
|
|
118
|
+
"WHERE",
|
|
119
|
+
"WINDOW",
|
|
120
|
+
"WITH",
|
|
121
|
+
"BIGINT",
|
|
122
|
+
"BIT",
|
|
123
|
+
"BOOL",
|
|
124
|
+
"BOOLEAN",
|
|
125
|
+
"BYTEA",
|
|
126
|
+
"CHAR",
|
|
127
|
+
"CLOB",
|
|
128
|
+
"BLOB",
|
|
129
|
+
"DATE",
|
|
130
|
+
"DECIMAL",
|
|
131
|
+
"DOUBLE",
|
|
132
|
+
"FLOAT",
|
|
133
|
+
"INT",
|
|
134
|
+
"INTEGER",
|
|
135
|
+
"INTERVAL",
|
|
136
|
+
"JSON",
|
|
137
|
+
"JSONB",
|
|
138
|
+
"MONEY",
|
|
139
|
+
"NCHAR",
|
|
140
|
+
"NUMERIC",
|
|
141
|
+
"NVARCHAR",
|
|
142
|
+
"PRECISION",
|
|
143
|
+
"REAL",
|
|
144
|
+
"SERIAL",
|
|
145
|
+
"SMALLINT",
|
|
146
|
+
"TEXT",
|
|
147
|
+
"TIME",
|
|
148
|
+
"TIMESTAMP",
|
|
149
|
+
"TIMESTAMPTZ",
|
|
150
|
+
"UUID",
|
|
151
|
+
"VARCHAR"
|
|
152
|
+
]);
|
|
153
|
+
const SQL_TOKEN_PATTERN = /(\/\*[\s\S]*?\*\/|--[^\n\r]*|#[^\n\r]*)|('(?:[^'\\]|\\.|'')*')|("(?:[^"\\]|\\.|"")*"|`(?:[^`]|``)*`)|((?:\d+(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?)|(\w+)|(\s+)|([(),;.[\]{}]+|::|[-+*/%<>=!|&^~@?]+)|([\s\S])/g;
|
|
154
|
+
function classifyWord(word, source, nextIndex) {
|
|
155
|
+
if (KEYWORDS.has(word.toUpperCase())) return "keyword";
|
|
156
|
+
let index = nextIndex;
|
|
157
|
+
while (index < source.length && (source[index] === " " || source[index] === " ")) index += 1;
|
|
158
|
+
return source[index] === "(" ? "function" : "identifier";
|
|
159
|
+
}
|
|
160
|
+
function tokenizeSql(source) {
|
|
161
|
+
const tokens = [];
|
|
162
|
+
SQL_TOKEN_PATTERN.lastIndex = 0;
|
|
163
|
+
let match;
|
|
164
|
+
while ((match = SQL_TOKEN_PATTERN.exec(source)) !== null) {
|
|
165
|
+
const [full, comment, str, quotedIdentifier, num, word, whitespace, punctuation] = match;
|
|
166
|
+
if (comment != null) tokens.push({
|
|
167
|
+
type: "comment",
|
|
168
|
+
value: comment
|
|
169
|
+
});
|
|
170
|
+
else if (str != null) tokens.push({
|
|
171
|
+
type: "string",
|
|
172
|
+
value: str
|
|
173
|
+
});
|
|
174
|
+
else if (quotedIdentifier != null) tokens.push({
|
|
175
|
+
type: "identifier",
|
|
176
|
+
value: quotedIdentifier
|
|
177
|
+
});
|
|
178
|
+
else if (num != null) tokens.push({
|
|
179
|
+
type: "number",
|
|
180
|
+
value: num
|
|
181
|
+
});
|
|
182
|
+
else if (word != null) tokens.push({
|
|
183
|
+
type: classifyWord(word, source, SQL_TOKEN_PATTERN.lastIndex),
|
|
184
|
+
value: word
|
|
185
|
+
});
|
|
186
|
+
else if (whitespace != null) tokens.push({
|
|
187
|
+
type: "whitespace",
|
|
188
|
+
value: whitespace
|
|
189
|
+
});
|
|
190
|
+
else if (punctuation != null) tokens.push({
|
|
191
|
+
type: "punctuation",
|
|
192
|
+
value: punctuation
|
|
193
|
+
});
|
|
194
|
+
else tokens.push({
|
|
195
|
+
type: "identifier",
|
|
196
|
+
value: full
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return tokens;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Tokenize `source` and group the tokens by line. Multi-line tokens (block
|
|
203
|
+
* comments, multi-line strings) are split at newlines so each line renders
|
|
204
|
+
* independently. Returns one array of tokens per source line.
|
|
205
|
+
*/
|
|
206
|
+
function highlightSqlLines(source) {
|
|
207
|
+
const lines = [[]];
|
|
208
|
+
for (const token of tokenizeSql(source)) token.value.split("\n").forEach((part, index) => {
|
|
209
|
+
if (index > 0) lines.push([]);
|
|
210
|
+
if (part.length > 0) lines[lines.length - 1].push({
|
|
211
|
+
type: token.type,
|
|
212
|
+
value: part
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
return lines;
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
export { highlightSqlLines };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { FieldContext } from "./FieldContext.js";
|
|
3
3
|
import { forwardRef, useContext, useMemo } from "react";
|
|
4
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { Field } from "@base-ui/react/field";
|
|
6
6
|
import { useController, useFormContext } from "react-hook-form";
|
|
7
7
|
//#region src/components/Field/Field.tsx
|
|
@@ -212,7 +212,7 @@ const FieldError = forwardRef(({ className, children, match, ...rest }, ref) =>
|
|
|
212
212
|
match: resolvedMatch,
|
|
213
213
|
className: cn(errorClasses, className),
|
|
214
214
|
...rest,
|
|
215
|
-
children: content != null ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ErrorIcon, {}), /* @__PURE__ */ jsx("span", {
|
|
215
|
+
children: content != null ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ErrorIcon, {}), /* @__PURE__ */ jsx("span", {
|
|
216
216
|
className: "min-w-0",
|
|
217
217
|
children: content
|
|
218
218
|
})] }) : void 0
|