@kolkrabbi/kol-component 0.12.3 → 0.12.4
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/package.json +7 -6
- package/src/molecules/CodeBlock.jsx +74 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.4",
|
|
4
4
|
"description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,16 +23,17 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@floating-ui/react": "^0.27.19",
|
|
25
25
|
"embla-carousel-react": "^8.6.0",
|
|
26
|
-
"
|
|
26
|
+
"refractor": "^5.0.0",
|
|
27
|
+
"@kolkrabbi/kol-icons": "0.8.3"
|
|
27
28
|
},
|
|
28
29
|
"peerDependencies": {
|
|
29
|
-
"react": "^18.3.0 || ^19.0.0",
|
|
30
|
-
"react-dom": "^18.3.0 || ^19.0.0",
|
|
31
|
-
"react-router-dom": "^6.0.0 || ^7.0.0",
|
|
32
30
|
"framer-motion": "^12.0.0",
|
|
33
31
|
"gsap": "^3.13.0",
|
|
34
32
|
"hls.js": "^1.6.0",
|
|
35
|
-
"opentype.js": "^1.3.4"
|
|
33
|
+
"opentype.js": "^1.3.4",
|
|
34
|
+
"react": "^18.3.0 || ^19.0.0",
|
|
35
|
+
"react-dom": "^18.3.0 || ^19.0.0",
|
|
36
|
+
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
36
37
|
},
|
|
37
38
|
"peerDependenciesMeta": {
|
|
38
39
|
"react-router-dom": {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { refractor } from 'refractor'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
* CodeBlock — a chrome'd code block with language chip, filename chip,
|
|
5
|
+
* CodeBlock — a chrome'd code block with language chip, filename chip, copy,
|
|
6
|
+
* and syntax highlighting (refractor — the same Prism engine the Sanity
|
|
7
|
+
* studio's code-input uses, so schema languages match 1:1).
|
|
5
8
|
*
|
|
6
9
|
* Accepts three input shapes (first match wins per field):
|
|
7
10
|
* • Portable Text: `value={{ code, language, filename }}` — the Sanity code-block
|
|
@@ -9,19 +12,85 @@ import CopyButton from '../atoms/CopyButton.jsx'
|
|
|
9
12
|
* • Direct props: `code` / `language` / `filename`.
|
|
10
13
|
* • Children: `<CodeBlock language="js">{'…'}</CodeBlock>` (the original API).
|
|
11
14
|
*
|
|
15
|
+
* Token colors are theme concerns: refractor's `token x` classes render as
|
|
16
|
+
* `kol-tok kol-tok-x` spans, colored by kol-theme (light + dark). An
|
|
17
|
+
* unregistered/absent language renders plain — never crashes.
|
|
18
|
+
*
|
|
12
19
|
* DOM order is copy → filename → lang → pre so the `.kol-codeblock-*` + pre
|
|
13
20
|
* adjacency rules in the chrome CSS can pad the pre when chips are present.
|
|
14
21
|
*/
|
|
22
|
+
|
|
23
|
+
/* Sanity/code-input language ids → refractor grammar names */
|
|
24
|
+
const LANG_ALIASES = { js: 'javascript', ts: 'typescript', sh: 'bash', shell: 'bash', html: 'markup', xml: 'markup' }
|
|
25
|
+
|
|
26
|
+
/* hast → React. refractor emits { type: 'element'|'text', tagName, properties, children }. */
|
|
27
|
+
const renderNode = (node, key) => {
|
|
28
|
+
if (node.type === 'text') return node.value
|
|
29
|
+
const cls = (node.properties?.className || [])
|
|
30
|
+
.map((c) => (c === 'token' ? 'kol-tok' : `kol-tok-${c}`))
|
|
31
|
+
.join(' ')
|
|
32
|
+
return (
|
|
33
|
+
<span key={key} className={cls}>
|
|
34
|
+
{node.children.map(renderNode)}
|
|
35
|
+
</span>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const highlight = (src, language) => {
|
|
40
|
+
const lang = LANG_ALIASES[language] ?? language
|
|
41
|
+
if (!lang || !refractor.registered(lang)) return src
|
|
42
|
+
try {
|
|
43
|
+
return refractor.highlight(src, lang).children.map(renderNode)
|
|
44
|
+
} catch {
|
|
45
|
+
return src
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Icon-swap copy control — the studio primitive's minimal form (clipboard →
|
|
50
|
+
* checkmark), ported 2026-07-28. Positioning comes from .kol-codeblock-copy. */
|
|
51
|
+
function CopyControl({ text }) {
|
|
52
|
+
const [copied, setCopied] = useState(false)
|
|
53
|
+
const onCopy = async () => {
|
|
54
|
+
try {
|
|
55
|
+
await navigator.clipboard.writeText(text)
|
|
56
|
+
setCopied(true)
|
|
57
|
+
setTimeout(() => setCopied(false), 1800)
|
|
58
|
+
} catch {
|
|
59
|
+
/* clipboard blocked — silent */
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return (
|
|
63
|
+
<button
|
|
64
|
+
type="button"
|
|
65
|
+
className="kol-codeblock-copy"
|
|
66
|
+
onClick={onCopy}
|
|
67
|
+
aria-label={copied ? 'Copied' : 'Copy to clipboard'}
|
|
68
|
+
title={copied ? 'Copied' : 'Copy'}
|
|
69
|
+
>
|
|
70
|
+
{copied ? (
|
|
71
|
+
<svg viewBox="0 0 20 20" width="14" height="14" aria-hidden="true">
|
|
72
|
+
<path d="M5 10 L9 14 L15 6" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
|
|
73
|
+
</svg>
|
|
74
|
+
) : (
|
|
75
|
+
<svg viewBox="0 0 20 20" width="14" height="14" aria-hidden="true">
|
|
76
|
+
<rect x="6" y="3" width="10" height="12" rx="1" fill="none" stroke="currentColor" strokeWidth="1.4" />
|
|
77
|
+
<rect x="4" y="6" width="10" height="12" rx="1" fill="none" stroke="currentColor" strokeWidth="1.4" />
|
|
78
|
+
</svg>
|
|
79
|
+
)}
|
|
80
|
+
</button>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
15
84
|
export default function CodeBlock({ children, code, language, filename, value }) {
|
|
16
|
-
const src = code ?? value?.code ?? children
|
|
85
|
+
const src = String(code ?? value?.code ?? children ?? '')
|
|
17
86
|
const lang = language ?? value?.language
|
|
18
87
|
const file = filename ?? value?.filename
|
|
19
88
|
return (
|
|
20
89
|
<div className="kol-codeblock">
|
|
21
|
-
<
|
|
90
|
+
<CopyControl text={src} />
|
|
22
91
|
{file && <span className="kol-codeblock-filename">{file}</span>}
|
|
23
92
|
{lang && <span className="kol-codeblock-lang">{lang}</span>}
|
|
24
|
-
<pre><code>{src}</code></pre>
|
|
93
|
+
<pre><code>{highlight(src, lang)}</code></pre>
|
|
25
94
|
</div>
|
|
26
95
|
)
|
|
27
96
|
}
|