@upbound/monarch-blocks 0.7.3 → 0.8.1
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/code-block.d.ts +4 -2
- package/dist/code-block.js +237 -40
- package/dist/code-block.js.map +1 -1
- package/dist/data-table.d.ts +24 -6
- package/dist/data-table.js +45 -27
- package/dist/data-table.js.map +1 -1
- package/dist/{empty-view-DO36LNvk.d.ts → empty-view-CQtcr_Ew.d.ts} +1 -2
- package/dist/graph.d.ts +1 -1
- package/dist/graph.js +22 -7
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +300 -97
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/code-block.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
declare const CODE_BLOCK_LANGUAGES: readonly ["tsx", "typescript", "bash", "shell", "json", "css", "yaml"];
|
|
3
|
+
declare const CODE_BLOCK_LANGUAGES: readonly ["tsx", "typescript", "bash", "shell", "json", "css", "yaml", "python"];
|
|
4
4
|
type CodeBlockLanguage = (typeof CODE_BLOCK_LANGUAGES)[number];
|
|
5
5
|
|
|
6
6
|
type CodeBlockCodeBase = {
|
|
@@ -22,9 +22,11 @@ declare function CodeBlock({ className, variant, size, ...props }: React.Compone
|
|
|
22
22
|
size?: 'default' | 'sm';
|
|
23
23
|
}): React.JSX.Element;
|
|
24
24
|
declare function CodeBlockHeader({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
|
|
25
|
-
declare function CodeBlockBody({ className, code, maxHeight, copyButton, downloadButton, ...props }: Omit<React.ComponentProps<'div'>, 'children'> & {
|
|
25
|
+
declare function CodeBlockBody({ className, code, maxHeight, searchable, copyButton, downloadButton, ...props }: Omit<React.ComponentProps<'div'>, 'children'> & {
|
|
26
26
|
code: CodeBlockCode;
|
|
27
27
|
maxHeight?: string;
|
|
28
|
+
/** Adds a search button to the overlay buttons that opens a find-in-code bar above the code. */
|
|
29
|
+
searchable?: boolean;
|
|
28
30
|
copyButton?: React.ReactNode;
|
|
29
31
|
downloadButton?: React.ReactNode;
|
|
30
32
|
}): React.JSX.Element;
|
package/dist/code-block.js
CHANGED
|
@@ -32,7 +32,7 @@ var __objRest = (source, exclude) => {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
// src/code-block.tsx
|
|
35
|
-
import { useEffect, useState } from "react";
|
|
35
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
36
36
|
import { Icon } from "@upbound/monarch-core";
|
|
37
37
|
|
|
38
38
|
// src/lib/utils.ts
|
|
@@ -44,14 +44,67 @@ import {
|
|
|
44
44
|
CardContent,
|
|
45
45
|
CardFooter,
|
|
46
46
|
CardHeader,
|
|
47
|
+
InputGroup,
|
|
48
|
+
InputGroupAddon,
|
|
49
|
+
InputGroupButton,
|
|
50
|
+
InputGroupInput,
|
|
51
|
+
InputGroupText,
|
|
47
52
|
Tooltip,
|
|
48
53
|
TooltipContent,
|
|
49
54
|
TooltipTrigger
|
|
50
55
|
} from "@upbound/monarch-core";
|
|
51
56
|
|
|
57
|
+
// src/lib/find-text.ts
|
|
58
|
+
function findTextRanges(root, query) {
|
|
59
|
+
var _a, _b;
|
|
60
|
+
const needle = query.toLowerCase();
|
|
61
|
+
if (!needle) return [];
|
|
62
|
+
const doc = (_a = root.ownerDocument) != null ? _a : document;
|
|
63
|
+
const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
64
|
+
const nodes = [];
|
|
65
|
+
const starts = [];
|
|
66
|
+
let text = "";
|
|
67
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
68
|
+
nodes.push(node);
|
|
69
|
+
starts.push(text.length);
|
|
70
|
+
text += (_b = node.nodeValue) != null ? _b : "";
|
|
71
|
+
}
|
|
72
|
+
let nodeIndex = 0;
|
|
73
|
+
const locate = (offset, isEnd) => {
|
|
74
|
+
while (nodeIndex < nodes.length - 1 && (isEnd ? starts[nodeIndex + 1] < offset : starts[nodeIndex + 1] <= offset)) {
|
|
75
|
+
nodeIndex++;
|
|
76
|
+
}
|
|
77
|
+
return [nodes[nodeIndex], offset - starts[nodeIndex]];
|
|
78
|
+
};
|
|
79
|
+
const haystack = text.toLowerCase();
|
|
80
|
+
const ranges = [];
|
|
81
|
+
for (let at = haystack.indexOf(needle); at !== -1; at = haystack.indexOf(needle, at + needle.length)) {
|
|
82
|
+
const range = doc.createRange();
|
|
83
|
+
range.setStart(...locate(at, false));
|
|
84
|
+
range.setEnd(...locate(at + needle.length, true));
|
|
85
|
+
ranges.push(range);
|
|
86
|
+
}
|
|
87
|
+
return ranges;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/lib/search-input.ts
|
|
91
|
+
var SEARCH_INPUT_PROPS = {
|
|
92
|
+
type: "search",
|
|
93
|
+
autoComplete: "off",
|
|
94
|
+
"data-1p-ignore": true,
|
|
95
|
+
// 1Password
|
|
96
|
+
"data-lpignore": "true",
|
|
97
|
+
// LastPass
|
|
98
|
+
"data-bwignore": true,
|
|
99
|
+
// Bitwarden
|
|
100
|
+
"data-form-type": "other"
|
|
101
|
+
// Dashlane
|
|
102
|
+
};
|
|
103
|
+
var SEARCH_INPUT_CLASS = "[&::-webkit-search-cancel-button]:appearance-none";
|
|
104
|
+
|
|
52
105
|
// src/lib/highlight.ts
|
|
53
106
|
import { createHighlighter } from "shiki";
|
|
54
|
-
var CODE_BLOCK_LANGUAGES = ["tsx", "typescript", "bash", "shell", "json", "css", "yaml"];
|
|
107
|
+
var CODE_BLOCK_LANGUAGES = ["tsx", "typescript", "bash", "shell", "json", "css", "yaml", "python"];
|
|
55
108
|
var HIGHLIGHTER_KEY = "__monarch_shiki_highlighter__";
|
|
56
109
|
function getHighlighter() {
|
|
57
110
|
const g = globalThis;
|
|
@@ -130,23 +183,84 @@ var LINE_NUMBER_GUTTER = cn(
|
|
|
130
183
|
"[&_.line]:before:text-[0.75rem]",
|
|
131
184
|
"[&_.line]:before:select-none"
|
|
132
185
|
);
|
|
186
|
+
var SHIKI_DARK_THEME = [
|
|
187
|
+
"dark:[&_.shiki]:text-(--shiki-dark)!",
|
|
188
|
+
"dark:[&_.shiki_span]:text-(--shiki-dark)!",
|
|
189
|
+
"dark:[&_.shiki_span]:[font-style:var(--shiki-dark-font-style)]!",
|
|
190
|
+
"dark:[&_.shiki_span]:[font-weight:var(--shiki-dark-font-weight)]!",
|
|
191
|
+
"dark:[&_.shiki_span]:[text-decoration:var(--shiki-dark-text-decoration)]!"
|
|
192
|
+
];
|
|
193
|
+
var CODE_BLOCK_ICON_BUTTON = cn(
|
|
194
|
+
"border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors",
|
|
195
|
+
"text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
196
|
+
"focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none",
|
|
197
|
+
"group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg"
|
|
198
|
+
);
|
|
199
|
+
function supportsHighlights() {
|
|
200
|
+
return typeof CSS !== "undefined" && "highlights" in CSS && typeof Highlight !== "undefined";
|
|
201
|
+
}
|
|
202
|
+
function useCodeSearch(codeRef, query, renderedCode) {
|
|
203
|
+
const id = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
204
|
+
const names = { all: `code-find-${id}`, current: `code-find-${id}-current` };
|
|
205
|
+
const [ranges, setRanges] = useState([]);
|
|
206
|
+
const [current, setCurrent] = useState(0);
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
const root = codeRef.current;
|
|
209
|
+
const next = root ? findTextRanges(root, query) : [];
|
|
210
|
+
setRanges(next);
|
|
211
|
+
setCurrent(0);
|
|
212
|
+
if (!supportsHighlights() || next.length === 0) return;
|
|
213
|
+
CSS.highlights.set(names.all, new Highlight(...next));
|
|
214
|
+
return () => {
|
|
215
|
+
CSS.highlights.delete(names.all);
|
|
216
|
+
};
|
|
217
|
+
}, [codeRef, query, renderedCode, names.all]);
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
var _a, _b;
|
|
220
|
+
const range = ranges[current];
|
|
221
|
+
if (!range) return;
|
|
222
|
+
(_b = (_a = range.startContainer.parentElement) == null ? void 0 : _a.scrollIntoView) == null ? void 0 : _b.call(_a, { block: "nearest", inline: "nearest" });
|
|
223
|
+
if (!supportsHighlights()) return;
|
|
224
|
+
CSS.highlights.set(names.current, new Highlight(range));
|
|
225
|
+
return () => {
|
|
226
|
+
CSS.highlights.delete(names.current);
|
|
227
|
+
};
|
|
228
|
+
}, [ranges, current, names.current]);
|
|
229
|
+
const step = (by) => setCurrent((i) => ranges.length ? (i + by + ranges.length) % ranges.length : 0);
|
|
230
|
+
return {
|
|
231
|
+
count: ranges.length,
|
|
232
|
+
current,
|
|
233
|
+
next: () => step(1),
|
|
234
|
+
previous: () => step(-1),
|
|
235
|
+
highlightCss: `::highlight(${names.all}){background-color:color-mix(in oklch,var(--status-warning) 30%,transparent)}::highlight(${names.current}){background-color:color-mix(in oklch,var(--status-warning) 65%,transparent)}`
|
|
236
|
+
};
|
|
237
|
+
}
|
|
133
238
|
function CodeBlockBody(_a) {
|
|
134
239
|
var _b = _a, {
|
|
135
240
|
className,
|
|
136
241
|
code,
|
|
137
242
|
maxHeight,
|
|
243
|
+
searchable = false,
|
|
138
244
|
copyButton,
|
|
139
245
|
downloadButton
|
|
140
246
|
} = _b, props = __objRest(_b, [
|
|
141
247
|
"className",
|
|
142
248
|
"code",
|
|
143
249
|
"maxHeight",
|
|
250
|
+
"searchable",
|
|
144
251
|
"copyButton",
|
|
145
252
|
"downloadButton"
|
|
146
253
|
]);
|
|
147
254
|
const { content, lineNumbers } = code;
|
|
148
255
|
const [html, setHtml] = useState(null);
|
|
149
256
|
const language = code.type === "parsed" ? code.language : null;
|
|
257
|
+
const [searchOpen, setSearchOpen] = useState(false);
|
|
258
|
+
const [query, setQuery] = useState("");
|
|
259
|
+
const codeRef = useRef(null);
|
|
260
|
+
const searchInputRef = useRef(null);
|
|
261
|
+
const searchInputId = `code-search-${useId()}`;
|
|
262
|
+
const searchButtonRef = useRef(null);
|
|
263
|
+
const search = useCodeSearch(codeRef, searchOpen ? query : "", html != null ? html : content);
|
|
150
264
|
useEffect(() => {
|
|
151
265
|
if (!language) {
|
|
152
266
|
setHtml(null);
|
|
@@ -160,32 +274,127 @@ function CodeBlockBody(_a) {
|
|
|
160
274
|
cancelled = true;
|
|
161
275
|
};
|
|
162
276
|
}, [content, language]);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
277
|
+
const openSearch = () => {
|
|
278
|
+
var _a2, _b2;
|
|
279
|
+
setSearchOpen(true);
|
|
280
|
+
(_a2 = searchInputRef.current) == null ? void 0 : _a2.focus();
|
|
281
|
+
(_b2 = searchInputRef.current) == null ? void 0 : _b2.select();
|
|
282
|
+
};
|
|
283
|
+
const closeSearch = useCallback(() => {
|
|
284
|
+
var _a2;
|
|
285
|
+
setSearchOpen(false);
|
|
286
|
+
setQuery("");
|
|
287
|
+
(_a2 = searchButtonRef.current) == null ? void 0 : _a2.focus();
|
|
288
|
+
}, []);
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
if (!searchOpen) return;
|
|
291
|
+
const onKeyDown = (event) => {
|
|
292
|
+
if (event.key !== "Escape" || event.target !== searchInputRef.current) return;
|
|
293
|
+
event.preventDefault();
|
|
294
|
+
event.stopPropagation();
|
|
295
|
+
closeSearch();
|
|
296
|
+
};
|
|
297
|
+
window.addEventListener("keydown", onKeyDown, { capture: true });
|
|
298
|
+
return () => window.removeEventListener("keydown", onKeyDown, { capture: true });
|
|
299
|
+
}, [searchOpen, closeSearch]);
|
|
300
|
+
const searchButton = searchable && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
301
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
302
|
+
"button",
|
|
170
303
|
{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
lineNumbers && LINE_NUMBER_GUTTER
|
|
180
|
-
),
|
|
181
|
-
children: html ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: html } }) : /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { className: lineNumbers ? void 0 : "whitespace-pre", children: lineNumbers ? content.split("\n").map((line, i) => /* @__PURE__ */ jsxs("span", { className: "line", children: [
|
|
182
|
-
line,
|
|
183
|
-
"\n"
|
|
184
|
-
] }, i)) : content }) })
|
|
185
|
-
}
|
|
186
|
-
)
|
|
304
|
+
ref: searchButtonRef,
|
|
305
|
+
type: "button",
|
|
306
|
+
"data-slot": "code-block-search-button",
|
|
307
|
+
"aria-label": "Search",
|
|
308
|
+
"aria-expanded": searchOpen,
|
|
309
|
+
onClick: openSearch,
|
|
310
|
+
className: CODE_BLOCK_ICON_BUTTON,
|
|
311
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "magnifying-glass", size: "sm" })
|
|
187
312
|
}
|
|
188
|
-
)
|
|
313
|
+
) }),
|
|
314
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: "Search" })
|
|
315
|
+
] });
|
|
316
|
+
return /* @__PURE__ */ jsxs(CardContent, __spreadProps(__spreadValues({ "data-line-numbers": lineNumbers || void 0, className: cn("relative p-0!", className) }, props), { children: [
|
|
317
|
+
searchable && searchOpen && /* @__PURE__ */ jsxs("div", { "data-slot": "code-block-search", className: "p-2", children: [
|
|
318
|
+
/* @__PURE__ */ jsx("style", { children: search.highlightCss }),
|
|
319
|
+
/* @__PURE__ */ jsxs(InputGroup, { children: [
|
|
320
|
+
/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Icon, { name: "magnifying-glass" }) }),
|
|
321
|
+
/* @__PURE__ */ jsx(
|
|
322
|
+
InputGroupInput,
|
|
323
|
+
__spreadProps(__spreadValues({
|
|
324
|
+
ref: searchInputRef,
|
|
325
|
+
autoFocus: true
|
|
326
|
+
}, SEARCH_INPUT_PROPS), {
|
|
327
|
+
id: searchInputId,
|
|
328
|
+
className: SEARCH_INPUT_CLASS,
|
|
329
|
+
placeholder: "Find\u2026",
|
|
330
|
+
"aria-label": "Find in code",
|
|
331
|
+
value: query,
|
|
332
|
+
onChange: (e) => setQuery(e.target.value),
|
|
333
|
+
onKeyDown: (e) => {
|
|
334
|
+
if (e.key === "Enter") {
|
|
335
|
+
e.preventDefault();
|
|
336
|
+
if (e.shiftKey) search.previous();
|
|
337
|
+
else search.next();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
),
|
|
342
|
+
/* @__PURE__ */ jsxs(InputGroupAddon, { align: "inline-end", children: [
|
|
343
|
+
/* @__PURE__ */ jsx(InputGroupText, { "aria-live": "polite", className: "text-caption tabular-nums", children: query ? search.count ? `${search.current + 1} of ${search.count}` : "No results" : null }),
|
|
344
|
+
/* @__PURE__ */ jsx(
|
|
345
|
+
InputGroupButton,
|
|
346
|
+
{
|
|
347
|
+
size: "icon-xs",
|
|
348
|
+
"aria-label": "Previous match",
|
|
349
|
+
disabled: search.count === 0,
|
|
350
|
+
onClick: search.previous,
|
|
351
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "chevron-up" })
|
|
352
|
+
}
|
|
353
|
+
),
|
|
354
|
+
/* @__PURE__ */ jsx(
|
|
355
|
+
InputGroupButton,
|
|
356
|
+
{
|
|
357
|
+
size: "icon-xs",
|
|
358
|
+
"aria-label": "Next match",
|
|
359
|
+
disabled: search.count === 0,
|
|
360
|
+
onClick: search.next,
|
|
361
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down" })
|
|
362
|
+
}
|
|
363
|
+
),
|
|
364
|
+
/* @__PURE__ */ jsx(InputGroupButton, { size: "icon-xs", "aria-label": "Close search", onClick: closeSearch, children: /* @__PURE__ */ jsx(Icon, { name: "xmark" }) })
|
|
365
|
+
] })
|
|
366
|
+
] })
|
|
367
|
+
] }),
|
|
368
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
369
|
+
(searchButton || copyButton || downloadButton) && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 right-2 z-10 flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code-block:opacity-100 group-hover/code-block:opacity-100", children: [
|
|
370
|
+
searchButton,
|
|
371
|
+
copyButton,
|
|
372
|
+
downloadButton
|
|
373
|
+
] }),
|
|
374
|
+
/* @__PURE__ */ jsx(
|
|
375
|
+
"div",
|
|
376
|
+
{
|
|
377
|
+
className: cn("overflow-x-auto", maxHeight && "overflow-y-auto"),
|
|
378
|
+
style: maxHeight ? { maxHeight } : void 0,
|
|
379
|
+
children: /* @__PURE__ */ jsx(
|
|
380
|
+
"div",
|
|
381
|
+
{
|
|
382
|
+
ref: codeRef,
|
|
383
|
+
className: cn(
|
|
384
|
+
"[&_code]:font-mono [&_pre]:m-0 [&_pre]:p-4 group-data-[size=sm]/code-block:[&_pre]:p-3",
|
|
385
|
+
html && "[&_pre]:bg-transparent!",
|
|
386
|
+
html && SHIKI_DARK_THEME,
|
|
387
|
+
lineNumbers && LINE_NUMBER_GUTTER
|
|
388
|
+
),
|
|
389
|
+
children: html ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: html } }) : /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { className: lineNumbers ? void 0 : "whitespace-pre", children: lineNumbers ? content.split("\n").map((line, i) => /* @__PURE__ */ jsxs("span", { className: "line", children: [
|
|
390
|
+
line,
|
|
391
|
+
"\n"
|
|
392
|
+
] }, i)) : content }) })
|
|
393
|
+
}
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
)
|
|
397
|
+
] })
|
|
189
398
|
] }));
|
|
190
399
|
}
|
|
191
400
|
function CodeBlockFooter(_a) {
|
|
@@ -220,13 +429,7 @@ function CodeBlockCopyButton(_a) {
|
|
|
220
429
|
__spreadProps(__spreadValues({
|
|
221
430
|
"data-slot": "code-block-copy-button",
|
|
222
431
|
onClick: handleCopy,
|
|
223
|
-
className: cn(
|
|
224
|
-
"border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors",
|
|
225
|
-
"text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
226
|
-
"focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none",
|
|
227
|
-
"group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg",
|
|
228
|
-
className
|
|
229
|
-
),
|
|
432
|
+
className: cn(CODE_BLOCK_ICON_BUTTON, className),
|
|
230
433
|
"aria-label": copied ? "Copied" : "Copy code"
|
|
231
434
|
}, props), {
|
|
232
435
|
children: copied ? /* @__PURE__ */ jsx(Icon, { name: "check", size: "sm", className: "text-green-500" }) : /* @__PURE__ */ jsx(Icon, { name: "copy", size: "sm" })
|
|
@@ -267,13 +470,7 @@ function CodeBlockDownloadButton(_a) {
|
|
|
267
470
|
__spreadProps(__spreadValues({
|
|
268
471
|
"data-slot": "code-block-download-button",
|
|
269
472
|
onClick: handleDownload,
|
|
270
|
-
className: cn(
|
|
271
|
-
"border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors",
|
|
272
|
-
"text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
273
|
-
"focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none",
|
|
274
|
-
"group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg",
|
|
275
|
-
className
|
|
276
|
-
),
|
|
473
|
+
className: cn(CODE_BLOCK_ICON_BUTTON, className),
|
|
277
474
|
"aria-label": downloaded ? "Downloaded" : "Download code"
|
|
278
475
|
}, props), {
|
|
279
476
|
children: downloaded ? /* @__PURE__ */ jsx(Icon, { name: "check", size: "sm", className: "text-green-500" }) : /* @__PURE__ */ jsx(Icon, { name: "download", size: "sm" })
|
package/dist/code-block.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/code-block.tsx","../src/lib/utils.ts","../src/lib/highlight.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport { Icon } from '@upbound/monarch-core';\n\nimport { cn } from '@/lib/utils';\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '@upbound/monarch-core';\nimport { highlightCode, type CodeBlockLanguage } from './lib/highlight';\n\nexport type { CodeBlockLanguage } from './lib/highlight';\n\ntype CodeBlockCodeBase = {\n /** Raw code string — used for clipboard copy, download, and as the rendered text. */\n content: string;\n /** Numbers each line in the gutter. */\n lineNumbers?: boolean;\n};\n\nexport type CodeBlockCode =\n | (CodeBlockCodeBase & {\n /** Syntax-highlighted with Shiki inside CodeBlockBody — no highlighting step needed on your end. */\n type: 'parsed';\n language: CodeBlockLanguage;\n })\n | (CodeBlockCodeBase & {\n /** Rendered as plain, unhighlighted text. */\n type: 'raw';\n });\n\nfunction CodeBlock({\n className,\n variant = 'default',\n size = 'default',\n ...props\n}: React.ComponentProps<'div'> & {\n variant?: 'default' | 'terminal';\n size?: 'default' | 'sm';\n}) {\n return (\n <Card\n data-slot=\"code-block\"\n data-variant={variant}\n data-size={size}\n role=\"region\"\n className={cn(\n 'group/code-block gap-0 overflow-hidden py-0',\n size === 'default' && 'text-[13px]',\n size === 'sm' && 'text-body-sm',\n variant === 'terminal' && 'border-terminal-border bg-terminal-bg text-terminal-fg ring-0',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CodeBlockHeader({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <CardHeader\n className={cn(\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted flex flex-row items-center gap-2 border-b bg-[color-mix(in_oklch,var(--muted)_50%,var(--card))] py-4',\n className,\n )}\n {...props}\n />\n );\n}\n\nconst LINE_NUMBER_GUTTER = cn(\n '[&_code]:[counter-reset:line]',\n '[&_.line]:before:[counter-increment:line]',\n '[&_.line]:before:content-[counter(line)]',\n '[&_.line]:before:inline-block',\n '[&_.line]:before:w-6',\n '[&_.line]:before:mr-3',\n '[&_.line]:before:text-right',\n '[&_.line]:before:text-muted-foreground',\n '[&_.line]:before:text-[0.75rem]',\n '[&_.line]:before:select-none',\n);\n\nfunction CodeBlockBody({\n className,\n code,\n maxHeight,\n copyButton,\n downloadButton,\n ...props\n}: Omit<React.ComponentProps<'div'>, 'children'> & {\n code: CodeBlockCode;\n maxHeight?: string;\n copyButton?: React.ReactNode;\n downloadButton?: React.ReactNode;\n}) {\n const { content, lineNumbers } = code;\n const [html, setHtml] = useState<string | null>(null);\n const language = code.type === 'parsed' ? code.language : null;\n\n useEffect(() => {\n if (!language) {\n setHtml(null);\n return;\n }\n\n let cancelled = false;\n highlightCode(content, language).then(result => {\n if (!cancelled) setHtml(result);\n });\n\n return () => {\n cancelled = true;\n };\n }, [content, language]);\n\n // Layout note: the copy/download buttons must stay anchored to the\n // top-right of the visible code area when the content scrolls\n // horizontally. To make that work the relative containing block\n // (CardContent) must NOT be the scrollable element — otherwise\n // `right`-positioning is computed against the scrolled content edge and\n // the buttons move with the scroll. So the overflow lives on an inner\n // wrapper and the absolute button group is its sibling under CardContent.\n return (\n <CardContent data-line-numbers={lineNumbers || undefined} className={cn('relative p-0!', className)} {...props}>\n {(copyButton || downloadButton) && (\n <div className=\"absolute top-2 right-2 z-10 flex items-center gap-1 opacity-0 transition-opacity group-hover/code-block:opacity-100\">\n {copyButton}\n {downloadButton}\n </div>\n )}\n <div\n className={cn('overflow-x-auto', maxHeight && 'overflow-y-auto')}\n style={maxHeight ? { maxHeight } : undefined}\n >\n <div\n className={cn(\n '[&_code]:font-mono [&_pre]:m-0 [&_pre]:p-4 group-data-[size=sm]/code-block:[&_pre]:p-3',\n html && '[&_pre]:bg-transparent!',\n lineNumbers && LINE_NUMBER_GUTTER,\n )}\n >\n {html ? (\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre>\n <code className={lineNumbers ? undefined : 'whitespace-pre'}>\n {lineNumbers\n ? content.split('\\n').map((line, i) => (\n <span key={i} className=\"line\">\n {line}\n {'\\n'}\n </span>\n ))\n : content}\n </code>\n </pre>\n )}\n </div>\n </div>\n </CardContent>\n );\n}\n\nfunction CodeBlockFooter({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <CardFooter\n className={cn(\n 'text-body-sm group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-footer-bg group-data-[variant=terminal]/code-block:text-terminal-muted',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CodeBlockCopyButton({\n code,\n className,\n ...props\n}: Omit<React.ComponentProps<'button'>, 'children'> & {\n code: string;\n}) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n data-slot=\"code-block-copy-button\"\n onClick={handleCopy}\n className={cn(\n 'border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors',\n 'text-muted-foreground hover:bg-accent hover:text-foreground',\n 'focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none',\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg',\n className,\n )}\n aria-label={copied ? 'Copied' : 'Copy code'}\n {...props}\n >\n {copied ? <Icon name=\"check\" size=\"sm\" className=\"text-green-500\" /> : <Icon name=\"copy\" size=\"sm\" />}\n </button>\n </TooltipTrigger>\n <TooltipContent>{copied ? 'Copied' : 'Copy'}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction CodeBlockDownloadButton({\n code,\n fileName,\n mimeType = 'text/plain',\n className,\n ...props\n}: Omit<React.ComponentProps<'button'>, 'children'> & {\n code: string;\n fileName: string;\n mimeType?: string;\n}) {\n const [downloaded, setDownloaded] = useState(false);\n\n const handleDownload = () => {\n const blob = new Blob([code], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n setDownloaded(true);\n setTimeout(() => setDownloaded(false), 2000);\n };\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n data-slot=\"code-block-download-button\"\n onClick={handleDownload}\n className={cn(\n 'border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors',\n 'text-muted-foreground hover:bg-accent hover:text-foreground',\n 'focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none',\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg',\n className,\n )}\n aria-label={downloaded ? 'Downloaded' : 'Download code'}\n {...props}\n >\n {downloaded ? <Icon name=\"check\" size=\"sm\" className=\"text-green-500\" /> : <Icon name=\"download\" size=\"sm\" />}\n </button>\n </TooltipTrigger>\n <TooltipContent>{downloaded ? 'Downloaded' : 'Download'}</TooltipContent>\n </Tooltip>\n );\n}\n\nexport { CodeBlock, CodeBlockHeader, CodeBlockBody, CodeBlockFooter, CodeBlockCopyButton, CodeBlockDownloadButton };\n","export { cn } from '@upbound/monarch-core/utils';\n\nexport function sortBy<T>(items: T[], getKey: (item: T) => string): T[] {\n return [...items].sort((a, b) => getKey(a).localeCompare(getKey(b)));\n}\n","import { createHighlighter, type Highlighter } from 'shiki';\n\nexport const CODE_BLOCK_LANGUAGES = ['tsx', 'typescript', 'bash', 'shell', 'json', 'css', 'yaml'] as const;\n\nexport type CodeBlockLanguage = (typeof CODE_BLOCK_LANGUAGES)[number];\n\nconst HIGHLIGHTER_KEY = '__monarch_shiki_highlighter__';\n\ntype HighlighterGlobal = typeof globalThis & {\n [HIGHLIGHTER_KEY]?: Promise<Highlighter>;\n};\n\n/**\n * Cache the in-flight Promise, not the resolved instance. `createHighlighter`\n * is async — storing only after `await` lets every concurrent CodeBlockBody\n * start a new highlighter (Shiki warns at 10/20/30 instances). `globalThis`\n * keeps the same promise across HMR module re-evals in the same realm.\n */\nfunction getHighlighter(): Promise<Highlighter> {\n const g = globalThis as HighlighterGlobal;\n if (!g[HIGHLIGHTER_KEY]) {\n g[HIGHLIGHTER_KEY] = createHighlighter({\n themes: ['github-light-high-contrast', 'github-dark-dimmed'],\n langs: [...CODE_BLOCK_LANGUAGES],\n }).catch(error => {\n delete g[HIGHLIGHTER_KEY];\n throw error;\n });\n }\n return g[HIGHLIGHTER_KEY];\n}\n\n/**\n * Highlight a code string with Shiki dual themes. Returns raw HTML safe to\n * render via dangerouslySetInnerHTML.\n *\n * Light theme renders as inline `color:` / `background-color:`.\n * Dark theme renders as CSS variables `--shiki-dark` / `--shiki-dark-bg`.\n */\nexport async function highlightCode(code: string, lang: CodeBlockLanguage): Promise<string> {\n const hl = await getHighlighter();\n return hl.codeToHtml(code, {\n lang,\n themes: {\n light: 'github-light-high-contrast',\n dark: 'github-dark-dimmed',\n },\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/code-block.tsx","../src/lib/utils.ts","../src/lib/find-text.ts","../src/lib/search-input.ts","../src/lib/highlight.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport { Icon } from '@upbound/monarch-core';\n\nimport { cn } from '@/lib/utils';\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupInput,\n InputGroupText,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '@upbound/monarch-core';\nimport { findTextRanges } from './lib/find-text';\nimport { SEARCH_INPUT_CLASS, SEARCH_INPUT_PROPS } from './lib/search-input';\nimport { highlightCode, type CodeBlockLanguage } from './lib/highlight';\n\nexport type { CodeBlockLanguage } from './lib/highlight';\n\ntype CodeBlockCodeBase = {\n /** Raw code string — used for clipboard copy, download, and as the rendered text. */\n content: string;\n /** Numbers each line in the gutter. */\n lineNumbers?: boolean;\n};\n\nexport type CodeBlockCode =\n | (CodeBlockCodeBase & {\n /** Syntax-highlighted with Shiki inside CodeBlockBody — no highlighting step needed on your end. */\n type: 'parsed';\n language: CodeBlockLanguage;\n })\n | (CodeBlockCodeBase & {\n /** Rendered as plain, unhighlighted text. */\n type: 'raw';\n });\n\nfunction CodeBlock({\n className,\n variant = 'default',\n size = 'default',\n ...props\n}: React.ComponentProps<'div'> & {\n variant?: 'default' | 'terminal';\n size?: 'default' | 'sm';\n}) {\n return (\n <Card\n data-slot=\"code-block\"\n data-variant={variant}\n data-size={size}\n role=\"region\"\n className={cn(\n 'group/code-block gap-0 overflow-hidden py-0',\n size === 'default' && 'text-[13px]',\n size === 'sm' && 'text-body-sm',\n variant === 'terminal' && 'border-terminal-border bg-terminal-bg text-terminal-fg ring-0',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CodeBlockHeader({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <CardHeader\n className={cn(\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted flex flex-row items-center gap-2 border-b bg-[color-mix(in_oklch,var(--muted)_50%,var(--card))] py-4',\n className,\n )}\n {...props}\n />\n );\n}\n\nconst LINE_NUMBER_GUTTER = cn(\n '[&_code]:[counter-reset:line]',\n '[&_.line]:before:[counter-increment:line]',\n '[&_.line]:before:content-[counter(line)]',\n '[&_.line]:before:inline-block',\n '[&_.line]:before:w-6',\n '[&_.line]:before:mr-3',\n '[&_.line]:before:text-right',\n '[&_.line]:before:text-muted-foreground',\n '[&_.line]:before:text-[0.75rem]',\n '[&_.line]:before:select-none',\n);\n\n// Shiki dual themes emit the light theme inline and the dark theme as --shiki-dark-* variables.\n// Nothing swaps them automatically, so under .dark point each property at its variable.\nconst SHIKI_DARK_THEME = [\n 'dark:[&_.shiki]:text-(--shiki-dark)!',\n 'dark:[&_.shiki_span]:text-(--shiki-dark)!',\n 'dark:[&_.shiki_span]:[font-style:var(--shiki-dark-font-style)]!',\n 'dark:[&_.shiki_span]:[font-weight:var(--shiki-dark-font-weight)]!',\n 'dark:[&_.shiki_span]:[text-decoration:var(--shiki-dark-text-decoration)]!',\n];\n\n// The overlay buttons (search, copy, download) share one look.\nconst CODE_BLOCK_ICON_BUTTON = cn(\n 'border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors',\n 'text-muted-foreground hover:bg-accent hover:text-foreground',\n 'focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none',\n 'group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg',\n);\n\nfunction supportsHighlights() {\n return typeof CSS !== 'undefined' && 'highlights' in CSS && typeof Highlight !== 'undefined';\n}\n\n/**\n * Find-in-code state for one CodeBlockBody. Matches are painted with the CSS Custom Highlight\n * API, so the highlighted markup is never rewritten; browsers without it still get the match\n * count and navigation.\n */\nfunction useCodeSearch(codeRef: React.RefObject<HTMLElement | null>, query: string, renderedCode: unknown) {\n const id = useId().replace(/[^a-zA-Z0-9_-]/g, '');\n const names = { all: `code-find-${id}`, current: `code-find-${id}-current` };\n const [ranges, setRanges] = useState<Range[]>([]);\n const [current, setCurrent] = useState(0);\n\n useEffect(() => {\n const root = codeRef.current;\n const next = root ? findTextRanges(root, query) : [];\n setRanges(next);\n setCurrent(0);\n if (!supportsHighlights() || next.length === 0) return;\n CSS.highlights.set(names.all, new Highlight(...next));\n return () => {\n CSS.highlights.delete(names.all);\n };\n // `renderedCode` changes when Shiki's HTML replaces the plain text.\n }, [codeRef, query, renderedCode, names.all]);\n\n useEffect(() => {\n const range = ranges[current];\n if (!range) return;\n range.startContainer.parentElement?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });\n if (!supportsHighlights()) return;\n CSS.highlights.set(names.current, new Highlight(range));\n return () => {\n CSS.highlights.delete(names.current);\n };\n }, [ranges, current, names.current]);\n\n const step = (by: number) => setCurrent(i => (ranges.length ? (i + by + ranges.length) % ranges.length : 0));\n\n return {\n count: ranges.length,\n current,\n next: () => step(1),\n previous: () => step(-1),\n highlightCss: `::highlight(${names.all}){background-color:color-mix(in oklch,var(--status-warning) 30%,transparent)}::highlight(${names.current}){background-color:color-mix(in oklch,var(--status-warning) 65%,transparent)}`,\n };\n}\n\nfunction CodeBlockBody({\n className,\n code,\n maxHeight,\n searchable = false,\n copyButton,\n downloadButton,\n ...props\n}: Omit<React.ComponentProps<'div'>, 'children'> & {\n code: CodeBlockCode;\n maxHeight?: string;\n /** Adds a search button to the overlay buttons that opens a find-in-code bar above the code. */\n searchable?: boolean;\n copyButton?: React.ReactNode;\n downloadButton?: React.ReactNode;\n}) {\n const { content, lineNumbers } = code;\n const [html, setHtml] = useState<string | null>(null);\n const language = code.type === 'parsed' ? code.language : null;\n\n const [searchOpen, setSearchOpen] = useState(false);\n const [query, setQuery] = useState('');\n const codeRef = useRef<HTMLDivElement>(null);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const searchInputId = `code-search-${useId()}`;\n const searchButtonRef = useRef<HTMLButtonElement>(null);\n const search = useCodeSearch(codeRef, searchOpen ? query : '', html ?? content);\n\n useEffect(() => {\n if (!language) {\n setHtml(null);\n return;\n }\n\n let cancelled = false;\n highlightCode(content, language).then(result => {\n if (!cancelled) setHtml(result);\n });\n\n return () => {\n cancelled = true;\n };\n }, [content, language]);\n\n const openSearch = () => {\n setSearchOpen(true);\n // Already open: move focus back to the field.\n searchInputRef.current?.focus();\n searchInputRef.current?.select();\n };\n\n const closeSearch = useCallback(() => {\n setSearchOpen(false);\n setQuery('');\n searchButtonRef.current?.focus();\n }, []);\n\n // Escape closes the search, not a surrounding dialog or sheet. Radix listens for Escape on the\n // document in the capture phase, so this has to run earlier, on the window.\n useEffect(() => {\n if (!searchOpen) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || event.target !== searchInputRef.current) return;\n event.preventDefault();\n event.stopPropagation();\n closeSearch();\n };\n window.addEventListener('keydown', onKeyDown, { capture: true });\n return () => window.removeEventListener('keydown', onKeyDown, { capture: true });\n }, [searchOpen, closeSearch]);\n\n const searchButton = searchable && (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n ref={searchButtonRef}\n type=\"button\"\n data-slot=\"code-block-search-button\"\n aria-label=\"Search\"\n aria-expanded={searchOpen}\n onClick={openSearch}\n className={CODE_BLOCK_ICON_BUTTON}\n >\n <Icon name=\"magnifying-glass\" size=\"sm\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>Search</TooltipContent>\n </Tooltip>\n );\n\n // Layout note: the overlay buttons must stay anchored to the top-right of\n // the visible code area when the content scrolls horizontally. To make that\n // work their relative containing block must NOT be the scrollable element —\n // otherwise `right`-positioning is computed against the scrolled content\n // edge and the buttons move with the scroll. So the overflow lives on an\n // inner wrapper and the absolute button group is its sibling.\n return (\n <CardContent data-line-numbers={lineNumbers || undefined} className={cn('relative p-0!', className)} {...props}>\n {searchable && searchOpen && (\n <div data-slot=\"code-block-search\" className=\"p-2\">\n <style>{search.highlightCss}</style>\n <InputGroup>\n <InputGroupAddon>\n <Icon name=\"magnifying-glass\" />\n </InputGroupAddon>\n <InputGroupInput\n ref={searchInputRef}\n autoFocus\n {...SEARCH_INPUT_PROPS}\n id={searchInputId}\n className={SEARCH_INPUT_CLASS}\n placeholder=\"Find…\"\n aria-label=\"Find in code\"\n value={query}\n onChange={e => setQuery(e.target.value)}\n onKeyDown={e => {\n if (e.key === 'Enter') {\n e.preventDefault();\n if (e.shiftKey) search.previous();\n else search.next();\n }\n }}\n />\n <InputGroupAddon align=\"inline-end\">\n <InputGroupText aria-live=\"polite\" className=\"text-caption tabular-nums\">\n {query ? (search.count ? `${search.current + 1} of ${search.count}` : 'No results') : null}\n </InputGroupText>\n <InputGroupButton\n size=\"icon-xs\"\n aria-label=\"Previous match\"\n disabled={search.count === 0}\n onClick={search.previous}\n >\n <Icon name=\"chevron-up\" />\n </InputGroupButton>\n <InputGroupButton\n size=\"icon-xs\"\n aria-label=\"Next match\"\n disabled={search.count === 0}\n onClick={search.next}\n >\n <Icon name=\"chevron-down\" />\n </InputGroupButton>\n <InputGroupButton size=\"icon-xs\" aria-label=\"Close search\" onClick={closeSearch}>\n <Icon name=\"xmark\" />\n </InputGroupButton>\n </InputGroupAddon>\n </InputGroup>\n </div>\n )}\n <div className=\"relative\">\n {(searchButton || copyButton || downloadButton) && (\n <div className=\"absolute top-2 right-2 z-10 flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code-block:opacity-100 group-hover/code-block:opacity-100\">\n {searchButton}\n {copyButton}\n {downloadButton}\n </div>\n )}\n <div\n className={cn('overflow-x-auto', maxHeight && 'overflow-y-auto')}\n style={maxHeight ? { maxHeight } : undefined}\n >\n <div\n ref={codeRef}\n className={cn(\n '[&_code]:font-mono [&_pre]:m-0 [&_pre]:p-4 group-data-[size=sm]/code-block:[&_pre]:p-3',\n html && '[&_pre]:bg-transparent!',\n html && SHIKI_DARK_THEME,\n lineNumbers && LINE_NUMBER_GUTTER,\n )}\n >\n {html ? (\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre>\n <code className={lineNumbers ? undefined : 'whitespace-pre'}>\n {lineNumbers\n ? content.split('\\n').map((line, i) => (\n <span key={i} className=\"line\">\n {line}\n {'\\n'}\n </span>\n ))\n : content}\n </code>\n </pre>\n )}\n </div>\n </div>\n </div>\n </CardContent>\n );\n}\n\nfunction CodeBlockFooter({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <CardFooter\n className={cn(\n 'text-body-sm group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-footer-bg group-data-[variant=terminal]/code-block:text-terminal-muted',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CodeBlockCopyButton({\n code,\n className,\n ...props\n}: Omit<React.ComponentProps<'button'>, 'children'> & {\n code: string;\n}) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n data-slot=\"code-block-copy-button\"\n onClick={handleCopy}\n className={cn(CODE_BLOCK_ICON_BUTTON, className)}\n aria-label={copied ? 'Copied' : 'Copy code'}\n {...props}\n >\n {copied ? <Icon name=\"check\" size=\"sm\" className=\"text-green-500\" /> : <Icon name=\"copy\" size=\"sm\" />}\n </button>\n </TooltipTrigger>\n <TooltipContent>{copied ? 'Copied' : 'Copy'}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction CodeBlockDownloadButton({\n code,\n fileName,\n mimeType = 'text/plain',\n className,\n ...props\n}: Omit<React.ComponentProps<'button'>, 'children'> & {\n code: string;\n fileName: string;\n mimeType?: string;\n}) {\n const [downloaded, setDownloaded] = useState(false);\n\n const handleDownload = () => {\n const blob = new Blob([code], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n setDownloaded(true);\n setTimeout(() => setDownloaded(false), 2000);\n };\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n data-slot=\"code-block-download-button\"\n onClick={handleDownload}\n className={cn(CODE_BLOCK_ICON_BUTTON, className)}\n aria-label={downloaded ? 'Downloaded' : 'Download code'}\n {...props}\n >\n {downloaded ? <Icon name=\"check\" size=\"sm\" className=\"text-green-500\" /> : <Icon name=\"download\" size=\"sm\" />}\n </button>\n </TooltipTrigger>\n <TooltipContent>{downloaded ? 'Downloaded' : 'Download'}</TooltipContent>\n </Tooltip>\n );\n}\n\nexport { CodeBlock, CodeBlockHeader, CodeBlockBody, CodeBlockFooter, CodeBlockCopyButton, CodeBlockDownloadButton };\n","export { cn } from '@upbound/monarch-core/utils';\n\nexport function sortBy<T>(items: T[], getKey: (item: T) => string): T[] {\n return [...items].sort((a, b) => getKey(a).localeCompare(getKey(b)));\n}\n","/**\n * Find every case-insensitive occurrence of `query` in the text under `root` and return one DOM\n * `Range` per match. Matches can cross element boundaries (Shiki wraps each token in its own\n * span), and don't overlap.\n */\nexport function findTextRanges(root: Node, query: string): Range[] {\n const needle = query.toLowerCase();\n if (!needle) return [];\n\n const doc = root.ownerDocument ?? document;\n const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n const nodes: Text[] = [];\n const starts: number[] = [];\n let text = '';\n for (let node = walker.nextNode(); node; node = walker.nextNode()) {\n nodes.push(node as Text);\n starts.push(text.length);\n text += node.nodeValue ?? '';\n }\n\n // The text node holding `offset`. An end offset that falls exactly on a node boundary belongs\n // to the node before it, so a match never ends at the start of the next token.\n let nodeIndex = 0;\n const locate = (offset: number, isEnd: boolean): [Text, number] => {\n while (nodeIndex < nodes.length - 1 && (isEnd ? starts[nodeIndex + 1] < offset : starts[nodeIndex + 1] <= offset)) {\n nodeIndex++;\n }\n return [nodes[nodeIndex], offset - starts[nodeIndex]];\n };\n\n const haystack = text.toLowerCase();\n const ranges: Range[] = [];\n for (let at = haystack.indexOf(needle); at !== -1; at = haystack.indexOf(needle, at + needle.length)) {\n const range = doc.createRange();\n range.setStart(...locate(at, false));\n range.setEnd(...locate(at + needle.length, true));\n ranges.push(range);\n }\n return ranges;\n}\n","/**\n * Props for search and find fields, so browsers and password managers don't offer to fill them.\n *\n * - `type=\"search\"` is what Apple's autofill (Safari, iCloud Passwords) goes by; it ignores\n * `autoComplete=\"off\"` and treats a lone text field as a username.\n * - `autoComplete=\"off\"` covers browser autofill.\n * - The `data-*` attributes are the opt-outs 1Password, LastPass, Bitwarden, and Dashlane read.\n *\n * Pair with `SEARCH_INPUT_CLASS`, and give the field an `id` containing \"search\".\n */\nexport const SEARCH_INPUT_PROPS = {\n type: 'search',\n autoComplete: 'off',\n 'data-1p-ignore': true, // 1Password\n 'data-lpignore': 'true', // LastPass\n 'data-bwignore': true, // Bitwarden\n 'data-form-type': 'other', // Dashlane\n} as const;\n\n/** Hides the browser's own clear button on `type=\"search\"` fields. */\nexport const SEARCH_INPUT_CLASS = '[&::-webkit-search-cancel-button]:appearance-none';\n","import { createHighlighter, type Highlighter } from 'shiki';\n\nexport const CODE_BLOCK_LANGUAGES = ['tsx', 'typescript', 'bash', 'shell', 'json', 'css', 'yaml', 'python'] as const;\n\nexport type CodeBlockLanguage = (typeof CODE_BLOCK_LANGUAGES)[number];\n\nconst HIGHLIGHTER_KEY = '__monarch_shiki_highlighter__';\n\ntype HighlighterGlobal = typeof globalThis & {\n [HIGHLIGHTER_KEY]?: Promise<Highlighter>;\n};\n\n/**\n * Cache the in-flight Promise, not the resolved instance. `createHighlighter`\n * is async — storing only after `await` lets every concurrent CodeBlockBody\n * start a new highlighter (Shiki warns at 10/20/30 instances). `globalThis`\n * keeps the same promise across HMR module re-evals in the same realm.\n */\nfunction getHighlighter(): Promise<Highlighter> {\n const g = globalThis as HighlighterGlobal;\n if (!g[HIGHLIGHTER_KEY]) {\n g[HIGHLIGHTER_KEY] = createHighlighter({\n themes: ['github-light-high-contrast', 'github-dark-dimmed'],\n langs: [...CODE_BLOCK_LANGUAGES],\n }).catch(error => {\n delete g[HIGHLIGHTER_KEY];\n throw error;\n });\n }\n return g[HIGHLIGHTER_KEY];\n}\n\n/**\n * Highlight a code string with Shiki dual themes. Returns raw HTML safe to\n * render via dangerouslySetInnerHTML.\n *\n * Light theme renders as inline `color:` / `background-color:`.\n * Dark theme renders as CSS variables `--shiki-dark` / `--shiki-dark-bg`.\n */\nexport async function highlightCode(code: string, lang: CodeBlockLanguage): Promise<string> {\n const hl = await getHighlighter();\n return hl.codeToHtml(code, {\n lang,\n themes: {\n light: 'github-light-high-contrast',\n dark: 'github-dark-dimmed',\n },\n });\n}\n"],"mappings":"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","names":["_a","_b"]}
|
package/dist/data-table.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { ReactNode, Dispatch, SetStateAction, ComponentType } from 'react';
|
|
|
3
3
|
import { RowData, ColumnFilter, Cell, Column, ColumnOrderState, Row, ColumnDef, CellContext, HeaderGroup, TableOptions, TableState, InitialTableState, SortingFn, FilterFn, ExpandedState, ColumnFiltersState, PaginationState, SortingState, VisibilityState, ColumnPinningState, GroupingState, OnChangeFn, ColumnSizingState } from '@tanstack/react-table';
|
|
4
4
|
import { DropdownMenuItem } from '@upbound/monarch-core';
|
|
5
5
|
import { f as CelFilterSchema, g as CelFilterTree, k as CompileResult, i as CelRestrictedOptionSelectPayload } from './types-BULiU2qC.js';
|
|
6
|
-
import { T as TableDataState, a as DataTableEmptyView } from './empty-view-
|
|
7
|
-
export { D as DataTableEmptyConfig, d as defaultEmptyConfig, b as defaultErrorConfig, c as defaultNoFilterFoundConfig } from './empty-view-
|
|
6
|
+
import { T as TableDataState, a as DataTableEmptyView } from './empty-view-CQtcr_Ew.js';
|
|
7
|
+
export { D as DataTableEmptyConfig, d as defaultEmptyConfig, b as defaultErrorConfig, c as defaultNoFilterFoundConfig } from './empty-view-CQtcr_Ew.js';
|
|
8
8
|
|
|
9
9
|
/** A single entry (or nested submenu) in a cell's contextual right-click menu, alongside the built-in "Filter by this value" item. */
|
|
10
10
|
type ContextualMenuAction<TData extends RowData = RowData, TValue = unknown> = {
|
|
@@ -177,7 +177,7 @@ declare function DataTableBody<TData extends RowData>({ tableRows, tableState, c
|
|
|
177
177
|
columns: Column<TData, unknown>[];
|
|
178
178
|
skeletonRowCount?: number;
|
|
179
179
|
renderSkeletonCell?: (columnId: string) => React$1.ReactNode | undefined;
|
|
180
|
-
onRowRender?: (row: TData) => DataTableRowProps;
|
|
180
|
+
onRowRender?: (row: TData, tableRow: Row<TData>) => DataTableRowProps;
|
|
181
181
|
renderExpandedRow?: (row: TData) => React$1.ReactNode;
|
|
182
182
|
showContextualFilter?: boolean;
|
|
183
183
|
onContextCell?: (filter: ColumnFilter, cell: Cell<TData, unknown>) => void;
|
|
@@ -222,6 +222,15 @@ type DataTableFilterVisibilityConfig = {
|
|
|
222
222
|
/** Initial visibility when uncontrolled. @default false */
|
|
223
223
|
defaultVisible?: boolean;
|
|
224
224
|
};
|
|
225
|
+
/** Configures `DataTable`'s toolbar search box. */
|
|
226
|
+
type DataTableSearchConfig = {
|
|
227
|
+
/** Hides the search box and, together with `disableFilters`, turns off filtering. @default false */
|
|
228
|
+
disabled?: boolean;
|
|
229
|
+
/** Placeholder for the search input. @default 'Search…' */
|
|
230
|
+
placeholder?: string;
|
|
231
|
+
/** Additional className for the search box, e.g. to change its width (`w-70` by default). */
|
|
232
|
+
className?: string;
|
|
233
|
+
};
|
|
225
234
|
/** Wires the CEL filter builder into the filter row in place of plain column filters. */
|
|
226
235
|
type DataTableCelFilterConfig<TData extends RowData> = {
|
|
227
236
|
schema: CelFilterSchema;
|
|
@@ -264,7 +273,13 @@ interface DataTableProps<TData extends RowData> {
|
|
|
264
273
|
*/
|
|
265
274
|
fitContent?: boolean;
|
|
266
275
|
serverSidePagination?: boolean;
|
|
267
|
-
|
|
276
|
+
/** Extra props for each body row. `tableRow` is the TanStack row — use `tableRow.depth` to style `getSubRows` sub-rows. */
|
|
277
|
+
onRowRender?(row: TData, tableRow: Row<TData>): DataTableRowProps;
|
|
278
|
+
/**
|
|
279
|
+
* Renders full-width content under an expanded row. For expanded rows that line up with the
|
|
280
|
+
* table's columns, set `config.getSubRows` instead: sub-rows render through the same column
|
|
281
|
+
* `cell` functions, and expansion state works without this prop.
|
|
282
|
+
*/
|
|
268
283
|
renderExpandedRow?: (row: TData) => ReactNode;
|
|
269
284
|
/** Controlled expanded state (optional). When omitted, `DataTable` uses internal state. */
|
|
270
285
|
expanded?: ExpandedState;
|
|
@@ -272,7 +287,10 @@ interface DataTableProps<TData extends RowData> {
|
|
|
272
287
|
ActionsComponent?: ComponentType<{
|
|
273
288
|
state: TableDataState;
|
|
274
289
|
}>;
|
|
290
|
+
/** @deprecated Use `search={{ disabled: true }}`. */
|
|
275
291
|
disableSearch?: boolean;
|
|
292
|
+
/** Configures the toolbar search box. */
|
|
293
|
+
search?: DataTableSearchConfig;
|
|
276
294
|
disableFilters?: boolean;
|
|
277
295
|
disableSorting?: boolean;
|
|
278
296
|
/**
|
|
@@ -350,7 +368,7 @@ interface DataTableProps<TData extends RowData> {
|
|
|
350
368
|
* difference is that columns, headers, rows, cells, and menus render through
|
|
351
369
|
* Monarch's own primitives instead of legacy's hand-rolled ones.
|
|
352
370
|
*/
|
|
353
|
-
declare function DataTable<TData extends RowData>({ id, config, title, description, emptyView, showError, className, fitContent, loading, renderSkeletonCell, serverSidePagination, ActionsComponent: Actions, onRowRender, renderExpandedRow, expanded: parentExpanded, onExpandedChange: parentOnExpandedChange, disableSearch, disableFilters, disableSorting, paginationConfig, disableColumnReordering, disableColumnVisibility, disableColumnResizing, disableColumnPinning, searchTerm: parentGlobalFilter, onSearchTermChange: parentSetGlobalFilter, filters: parentColumnFilters, onFiltersChange: parentSetColumnFilters, pagination: parentPagination, onPaginationChange: parentSetPagination, sorting: parentSorting, onSortingChange: parentSetSorting, columnOrdering: parentColumnOrdering, onColumnOrderingChange: parentSetColumnOrdering, columnVisibility: parentColumnVisibility, onColumnVisibilityChange: parentSetColumnVisibility, columnPinning: parentColumnPinning, onColumnPinningChange: parentSetColumnPinning, grouping: parentGrouping, onGroupingChange: parentSetGrouping, celFilterClassName, showContextualFilter, onContextCell, contextualMenuActions, layout, initialColumnSizing, onColumnSizingPersist, stickyHeader, celFilterConfig, filterVisibility, }: DataTableProps<TData>): React$1.JSX.Element;
|
|
371
|
+
declare function DataTable<TData extends RowData>({ id, config, title, description, emptyView, showError, className, fitContent, loading, renderSkeletonCell, serverSidePagination, ActionsComponent: Actions, onRowRender, renderExpandedRow, expanded: parentExpanded, onExpandedChange: parentOnExpandedChange, disableSearch: deprecatedDisableSearch, search, disableFilters, disableSorting, paginationConfig, disableColumnReordering, disableColumnVisibility, disableColumnResizing, disableColumnPinning, searchTerm: parentGlobalFilter, onSearchTermChange: parentSetGlobalFilter, filters: parentColumnFilters, onFiltersChange: parentSetColumnFilters, pagination: parentPagination, onPaginationChange: parentSetPagination, sorting: parentSorting, onSortingChange: parentSetSorting, columnOrdering: parentColumnOrdering, onColumnOrderingChange: parentSetColumnOrdering, columnVisibility: parentColumnVisibility, onColumnVisibilityChange: parentSetColumnVisibility, columnPinning: parentColumnPinning, onColumnPinningChange: parentSetColumnPinning, grouping: parentGrouping, onGroupingChange: parentSetGrouping, celFilterClassName, showContextualFilter, onContextCell, contextualMenuActions, layout, initialColumnSizing, onColumnSizingPersist, stickyHeader, celFilterConfig, filterVisibility, }: DataTableProps<TData>): React$1.JSX.Element;
|
|
354
372
|
|
|
355
373
|
type ViewFilterProps<TData extends RowData> = {
|
|
356
374
|
columns: Column<TData, unknown>[];
|
|
@@ -370,4 +388,4 @@ type ViewFilterProps<TData extends RowData> = {
|
|
|
370
388
|
*/
|
|
371
389
|
declare function ViewFilter<TData extends RowData>({ columns, columnFilters, onColumnFiltersChange, className, }: ViewFilterProps<TData>): React$1.JSX.Element;
|
|
372
390
|
|
|
373
|
-
export { CellContextualFilter, type CellContextualFilterProps, ColumnOptions, type ColumnOptionsProps, type ContextualMenuAction, DataTable, DataTableBody, type DataTableCelFilterConfig, DataTableCell, DataTableEmptyView, DataTableExpandedRow, type DataTableFilterVisibilityConfig, DataTableGroupRow, DataTableHeader, DataTablePagination, type DataTablePaginationConfig, type DataTablePaginationProps, type DataTableProps, DataTableRow, type DataTableRowProps, DataTableSkeletonBody, DataTableValueCell, type DataTableValueCellProps, ExpandButton, RowActionItem, RowActions, type RowCountRelation, type StickyHeaderConfig, type TableHookOptions, type TableLayout, TimestampCell, ViewFilter, type ViewFilterProps, getActionsColumnDefinition, getExpandColumnDefinition, normalizePageSizeOptions };
|
|
391
|
+
export { CellContextualFilter, type CellContextualFilterProps, ColumnOptions, type ColumnOptionsProps, type ContextualMenuAction, DataTable, DataTableBody, type DataTableCelFilterConfig, DataTableCell, DataTableEmptyView, DataTableExpandedRow, type DataTableFilterVisibilityConfig, DataTableGroupRow, DataTableHeader, DataTablePagination, type DataTablePaginationConfig, type DataTablePaginationProps, type DataTableProps, DataTableRow, type DataTableRowProps, type DataTableSearchConfig, DataTableSkeletonBody, DataTableValueCell, type DataTableValueCellProps, ExpandButton, RowActionItem, RowActions, type RowCountRelation, type StickyHeaderConfig, type TableHookOptions, type TableLayout, TimestampCell, ViewFilter, type ViewFilterProps, getActionsColumnDefinition, getExpandColumnDefinition, normalizePageSizeOptions };
|