@dreamlake/dockit 0.2.28 → 0.2.29
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/CodeBlock.js +164 -125
- package/dist/components/CodeTabs.d.ts +1 -1
- package/dist/components/CodeTabs.js +14 -47
- package/dist/components/code-tabs-context.d.ts +1 -1
- package/dist/styles.css +41 -8
- package/dist/vendor/@dreamlake/uikit/src/components/TabbedContainer/TabbedContainer.js +87 -0
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useContext, useRef, useState } from "react";
|
|
3
3
|
import { CodeTabsContext } from "./code-tabs-context.js";
|
|
4
4
|
import { useLineNumbers } from "../lib/use-line-numbers.js";
|
|
@@ -23,6 +23,7 @@ function CodeBlock({ children, filename, lang }) {
|
|
|
23
23
|
const embedded = useContext(CodeTabsContext);
|
|
24
24
|
const wrapRef = useRef(null);
|
|
25
25
|
const [copied, setCopied] = useState(false);
|
|
26
|
+
const [wrap, setWrap] = useState(false);
|
|
26
27
|
const [showLines, setShowLines] = useLineNumbers();
|
|
27
28
|
function copy() {
|
|
28
29
|
const el = wrapRef.current?.querySelector("pre code");
|
|
@@ -36,131 +37,169 @@ function CodeBlock({ children, filename, lang }) {
|
|
|
36
37
|
}
|
|
37
38
|
);
|
|
38
39
|
}
|
|
39
|
-
return /* @__PURE__ */ jsxs(
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
style: { gap: 6 },
|
|
79
|
-
children: [
|
|
80
|
-
/* @__PURE__ */ jsx(
|
|
81
|
-
"button",
|
|
82
|
-
{
|
|
83
|
-
type: "button",
|
|
84
|
-
"aria-pressed": showLines,
|
|
85
|
-
title: showLines ? ":set nonu" : ":set nu",
|
|
86
|
-
onClick: () => setShowLines(!showLines),
|
|
87
|
-
style: headerBtnStyle(showLines),
|
|
88
|
-
children: /* @__PURE__ */ jsx(HashIcon, {})
|
|
89
|
-
}
|
|
90
|
-
),
|
|
91
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: copy, style: headerBtnStyle(copied), children: copied ? "Copied" : "Copy" })
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
|
-
)
|
|
95
|
-
]
|
|
96
|
-
}
|
|
97
|
-
),
|
|
98
|
-
!filename && /* @__PURE__ */ jsxs(
|
|
99
|
-
"div",
|
|
100
|
-
{
|
|
101
|
-
className: "absolute top-0 right-0 z-10 flex items-center opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity duration-150",
|
|
102
|
-
style: { gap: embedded ? 8 : 6, padding: embedded ? "3px 0 3px 8px" : "8px 8px", isolation: "isolate" },
|
|
103
|
-
children: [
|
|
104
|
-
/* @__PURE__ */ jsx(
|
|
105
|
-
"span",
|
|
106
|
-
{
|
|
107
|
-
"aria-hidden": "true",
|
|
108
|
-
style: {
|
|
109
|
-
position: "absolute",
|
|
110
|
-
inset: 4,
|
|
111
|
-
zIndex: -1,
|
|
112
|
-
pointerEvents: "none",
|
|
113
|
-
borderRadius: 6,
|
|
114
|
-
background: embedded ? "var(--color-doc-template-bg)" : "color-mix(in srgb, var(--color-doc-template-code) 88%, transparent)",
|
|
115
|
-
boxShadow: embedded ? void 0 : "0 0 10px 6px var(--color-doc-template-code)",
|
|
116
|
-
backdropFilter: "blur(8px)",
|
|
117
|
-
WebkitBackdropFilter: "blur(8px)"
|
|
40
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
41
|
+
embedded && (filename || lang) && /* @__PURE__ */ jsx("span", { className: "dockit-code-tabs-label", title: filename || lang, children: filename || lang }),
|
|
42
|
+
/* @__PURE__ */ jsxs(
|
|
43
|
+
"div",
|
|
44
|
+
{
|
|
45
|
+
ref: wrapRef,
|
|
46
|
+
"data-code-block": true,
|
|
47
|
+
"data-wrap": wrap ? "on" : "off",
|
|
48
|
+
"data-line-numbers": showLines ? "on" : "off",
|
|
49
|
+
className: embedded ? "group relative" : "group relative border border-doc-template-faint bg-doc-template-code",
|
|
50
|
+
style: {
|
|
51
|
+
margin: embedded ? 0 : "18px 0 22px",
|
|
52
|
+
borderRadius: embedded ? 0 : "var(--radius-doc-template)",
|
|
53
|
+
overflow: embedded ? "visible" : "hidden"
|
|
54
|
+
},
|
|
55
|
+
children: [
|
|
56
|
+
filename && !embedded && /* @__PURE__ */ jsxs(
|
|
57
|
+
"div",
|
|
58
|
+
{
|
|
59
|
+
className: "flex items-center border-b border-doc-template-faint text-doc-template-muted",
|
|
60
|
+
style: {
|
|
61
|
+
gap: 12,
|
|
62
|
+
flexWrap: "wrap",
|
|
63
|
+
padding: "6px 10px 6px 14px",
|
|
64
|
+
fontFamily: "var(--font-doc-template-mono)",
|
|
65
|
+
fontSize: 10,
|
|
66
|
+
letterSpacing: "0.06em",
|
|
67
|
+
background: "color-mix(in srgb, var(--color-doc-template-ink) 3%, transparent)"
|
|
68
|
+
},
|
|
69
|
+
children: [
|
|
70
|
+
/* @__PURE__ */ jsx(
|
|
71
|
+
"span",
|
|
72
|
+
{
|
|
73
|
+
style: {
|
|
74
|
+
fontWeight: 600,
|
|
75
|
+
textTransform: "none",
|
|
76
|
+
letterSpacing: 0
|
|
77
|
+
},
|
|
78
|
+
children: filename
|
|
118
79
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
80
|
+
),
|
|
81
|
+
lang && !embedded && /* @__PURE__ */ jsx(
|
|
82
|
+
"span",
|
|
83
|
+
{
|
|
84
|
+
className: "uppercase opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity duration-150",
|
|
85
|
+
style: { fontWeight: 600, letterSpacing: "0.06em" },
|
|
86
|
+
children: lang
|
|
87
|
+
}
|
|
88
|
+
),
|
|
89
|
+
/* @__PURE__ */ jsx("span", { style: { flex: 1 } }),
|
|
90
|
+
/* @__PURE__ */ jsxs(
|
|
91
|
+
"span",
|
|
92
|
+
{
|
|
93
|
+
className: "dockit-code-controls inline-flex items-center opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity duration-150",
|
|
94
|
+
style: { gap: 6 },
|
|
95
|
+
children: [
|
|
96
|
+
/* @__PURE__ */ jsx(
|
|
97
|
+
"button",
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
"aria-label": "Line numbers",
|
|
101
|
+
"aria-pressed": showLines,
|
|
102
|
+
title: showLines ? ":set nonu" : ":set nu",
|
|
103
|
+
onClick: () => setShowLines(!showLines),
|
|
104
|
+
style: headerBtnStyle(showLines),
|
|
105
|
+
children: /* @__PURE__ */ jsx(HashIcon, {})
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
/* @__PURE__ */ jsx(
|
|
109
|
+
"button",
|
|
110
|
+
{
|
|
111
|
+
type: "button",
|
|
112
|
+
"aria-pressed": wrap,
|
|
113
|
+
onClick: () => setWrap(!wrap),
|
|
114
|
+
style: headerBtnStyle(wrap),
|
|
115
|
+
children: "Wrap"
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: copy, style: headerBtnStyle(copied), children: copied ? "Copied" : "Copy" })
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
(!filename || embedded) && /* @__PURE__ */ jsxs(
|
|
126
|
+
"div",
|
|
127
|
+
{
|
|
128
|
+
className: "dockit-code-controls absolute top-0 right-0 z-10 flex items-center opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity duration-150",
|
|
129
|
+
style: { gap: 6, padding: "8px 8px", isolation: "isolate" },
|
|
130
|
+
children: [
|
|
131
|
+
/* @__PURE__ */ jsx(
|
|
132
|
+
"span",
|
|
133
|
+
{
|
|
134
|
+
"aria-hidden": "true",
|
|
135
|
+
style: {
|
|
136
|
+
position: "absolute",
|
|
137
|
+
inset: 4,
|
|
138
|
+
zIndex: -1,
|
|
139
|
+
pointerEvents: "none",
|
|
140
|
+
borderRadius: 6,
|
|
141
|
+
background: "color-mix(in srgb, var(--color-doc-template-code) 88%, transparent)",
|
|
142
|
+
boxShadow: "0 0 10px 6px var(--color-doc-template-code)",
|
|
143
|
+
backdropFilter: "blur(8px)",
|
|
144
|
+
WebkitBackdropFilter: "blur(8px)"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
),
|
|
148
|
+
lang && !embedded && /* @__PURE__ */ jsx(
|
|
149
|
+
"span",
|
|
150
|
+
{
|
|
151
|
+
className: "text-doc-template-muted uppercase",
|
|
152
|
+
style: {
|
|
153
|
+
fontFamily: "var(--font-doc-template-mono)",
|
|
154
|
+
fontSize: 10,
|
|
155
|
+
fontWeight: 600,
|
|
156
|
+
letterSpacing: "0.04em",
|
|
157
|
+
marginRight: 2
|
|
158
|
+
},
|
|
159
|
+
children: lang
|
|
160
|
+
}
|
|
161
|
+
),
|
|
162
|
+
/* @__PURE__ */ jsx(
|
|
163
|
+
"button",
|
|
164
|
+
{
|
|
165
|
+
type: "button",
|
|
166
|
+
"aria-label": "Line numbers",
|
|
167
|
+
"aria-pressed": showLines,
|
|
168
|
+
title: showLines ? ":set nonu" : ":set nu",
|
|
169
|
+
onClick: () => setShowLines(!showLines),
|
|
170
|
+
style: headerBtnStyle(showLines),
|
|
171
|
+
children: /* @__PURE__ */ jsx(HashIcon, {})
|
|
172
|
+
}
|
|
173
|
+
),
|
|
174
|
+
/* @__PURE__ */ jsx(
|
|
175
|
+
"button",
|
|
176
|
+
{
|
|
177
|
+
type: "button",
|
|
178
|
+
"aria-pressed": wrap,
|
|
179
|
+
onClick: () => setWrap(!wrap),
|
|
180
|
+
style: headerBtnStyle(wrap),
|
|
181
|
+
children: "Wrap"
|
|
182
|
+
}
|
|
183
|
+
),
|
|
184
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: copy, style: headerBtnStyle(copied), children: copied ? "Copied" : "Copy" })
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
/* @__PURE__ */ jsx(
|
|
189
|
+
"div",
|
|
190
|
+
{
|
|
191
|
+
style: {
|
|
192
|
+
fontFamily: "var(--font-doc-template-mono)",
|
|
193
|
+
fontSize: 12.5,
|
|
194
|
+
lineHeight: 1.3
|
|
195
|
+
},
|
|
196
|
+
children
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
]
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
] });
|
|
164
203
|
}
|
|
165
204
|
function HashIcon() {
|
|
166
205
|
return /* @__PURE__ */ jsxs(
|
|
@@ -9,5 +9,5 @@ export interface CodeTabsProps {
|
|
|
9
9
|
children: ReactNode;
|
|
10
10
|
'aria-label'?: string;
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
12
|
+
/** UIKit owns the frame and keyboard navigation; Dockit owns fenced examples. */
|
|
13
13
|
export declare function CodeTabs({ children, 'aria-label': label }: CodeTabsProps): import("react").JSX.Element | null;
|
|
@@ -1,58 +1,25 @@
|
|
|
1
|
-
import { jsx, Fragment
|
|
2
|
-
import { Children, isValidElement
|
|
3
|
-
import {
|
|
1
|
+
import { jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Children, isValidElement } from "react";
|
|
3
|
+
import { TabbedContainer } from "../vendor/@dreamlake/uikit/src/components/TabbedContainer/TabbedContainer.js";
|
|
4
4
|
import { CodeTabsContext } from "./code-tabs-context.js";
|
|
5
5
|
function CodeExample({ children }) {
|
|
6
6
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
7
7
|
}
|
|
8
8
|
function CodeTabs({ children, "aria-label": label = "Code examples" }) {
|
|
9
9
|
const examples = Children.toArray(children).filter(isValidElement);
|
|
10
|
-
const [selected, setSelected] = useState(0);
|
|
11
|
-
const active = Math.min(selected, Math.max(0, examples.length - 1));
|
|
12
|
-
const id = useId();
|
|
13
|
-
const buttons = useRef([]);
|
|
14
10
|
if (!examples.length) return null;
|
|
15
|
-
return /* @__PURE__ */ jsx("div", { "data-code-tabs": true,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
id: `${id}-tab-${index}`,
|
|
25
|
-
"aria-controls": `${id}-panel-${index}`,
|
|
26
|
-
"aria-selected": active === index,
|
|
27
|
-
tabIndex: active === index ? 0 : -1,
|
|
28
|
-
onKeyDown: (event) => {
|
|
29
|
-
let next;
|
|
30
|
-
if (event.key === "ArrowRight") next = (index + 1) % examples.length;
|
|
31
|
-
else if (event.key === "ArrowLeft") next = (index - 1 + examples.length) % examples.length;
|
|
32
|
-
else if (event.key === "Home") next = 0;
|
|
33
|
-
else if (event.key === "End") next = examples.length - 1;
|
|
34
|
-
else return;
|
|
35
|
-
event.preventDefault();
|
|
36
|
-
setSelected(next);
|
|
37
|
-
buttons.current[next]?.focus();
|
|
38
|
-
},
|
|
39
|
-
children: example.props.label
|
|
40
|
-
},
|
|
41
|
-
example.key ?? index
|
|
42
|
-
)) }) }),
|
|
43
|
-
/* @__PURE__ */ jsx(CodeTabsContext.Provider, { value: true, children: examples.map((example, index) => /* @__PURE__ */ jsx(
|
|
44
|
-
"div",
|
|
45
|
-
{
|
|
46
|
-
role: "tabpanel",
|
|
47
|
-
id: `${id}-panel-${index}`,
|
|
48
|
-
"aria-labelledby": `${id}-tab-${index}`,
|
|
49
|
-
hidden: active !== index,
|
|
50
|
-
tabIndex: 0,
|
|
11
|
+
return /* @__PURE__ */ jsx("div", { "data-code-tabs": true, children: /* @__PURE__ */ jsx(CodeTabsContext.Provider, { value: true, children: /* @__PURE__ */ jsx(
|
|
12
|
+
TabbedContainer,
|
|
13
|
+
{
|
|
14
|
+
"aria-label": label,
|
|
15
|
+
className: "dockit-code-tabs",
|
|
16
|
+
items: examples.map((example, index) => ({
|
|
17
|
+
value: String(example.key ?? index),
|
|
18
|
+
label: example.props.label,
|
|
51
19
|
children: example.props.children
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
] }) });
|
|
20
|
+
}))
|
|
21
|
+
}
|
|
22
|
+
) }) });
|
|
56
23
|
}
|
|
57
24
|
export {
|
|
58
25
|
CodeExample,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/** Internal: a fenced block shares the surrounding tabs' frame
|
|
1
|
+
/** Internal: a fenced block shares the surrounding tabs' frame; its label sits beside the tabs. */
|
|
2
2
|
export declare const CodeTabsContext: import('react').Context<boolean>;
|
package/dist/styles.css
CHANGED
|
@@ -382,19 +382,26 @@
|
|
|
382
382
|
/* One frame and toolbar for ordinary MDX fenced examples. Inactive panels keep
|
|
383
383
|
their code/control state but are absent from keyboard and accessibility flow. */
|
|
384
384
|
.dockit-code-tabs {
|
|
385
|
+
position: relative;
|
|
386
|
+
min-width: 0;
|
|
385
387
|
margin: 18px 0 22px;
|
|
386
388
|
border-radius: 6px 6px var(--radius-doc-template) var(--radius-doc-template);
|
|
387
389
|
overflow: hidden;
|
|
388
390
|
}
|
|
389
|
-
.dockit-code-tabs-header {
|
|
391
|
+
.dockit-code-tabs .uikit-tabbed-container-header {
|
|
390
392
|
background: var(--color-doc-template-bg);
|
|
391
393
|
min-height: 30px;
|
|
392
|
-
padding: 0
|
|
394
|
+
padding: 0 min(32%, 12ch) 0 0;
|
|
393
395
|
}
|
|
394
|
-
.dockit-code-tabs-
|
|
396
|
+
.dockit-code-tabs .uikit-tabbed-container-tabs { overflow-x: auto; min-height: 30px; gap: 2px; border: 0; }
|
|
395
397
|
/* Raised file-tab geometry follows Studio's chat TabStrip. UIKit supplies the
|
|
396
398
|
compound primitives; the active code-colored bottom edge opens the baseline. */
|
|
397
|
-
.dockit-code-tabs-
|
|
399
|
+
.dockit-code-tabs .uikit-tabbed-container-tabs [role='tab'] {
|
|
400
|
+
flex-shrink: 0;
|
|
401
|
+
font-family: var(--font-doc-template-mono);
|
|
402
|
+
font-size: 12px;
|
|
403
|
+
font-weight: 500;
|
|
404
|
+
color: var(--color-doc-template-muted);
|
|
398
405
|
margin: 0 1px;
|
|
399
406
|
padding: 3px 9px 4px;
|
|
400
407
|
border: 1px solid transparent;
|
|
@@ -402,19 +409,21 @@
|
|
|
402
409
|
background: transparent;
|
|
403
410
|
line-height: 18px;
|
|
404
411
|
}
|
|
405
|
-
.dockit-code-tabs-
|
|
406
|
-
.dockit-code-tabs-
|
|
412
|
+
.dockit-code-tabs .uikit-tabbed-container-tabs [role='tab']:first-child { margin-left: 0; }
|
|
413
|
+
.dockit-code-tabs .uikit-tabbed-container-tabs [role='tab'][aria-selected='false']:hover {
|
|
407
414
|
border-color: var(--color-doc-template-faint);
|
|
408
415
|
border-bottom-color: transparent;
|
|
409
416
|
color: var(--color-doc-template-ink);
|
|
410
417
|
}
|
|
411
|
-
.dockit-code-tabs-
|
|
418
|
+
.dockit-code-tabs .uikit-tabbed-container-tabs [role='tab'][aria-selected='true'] {
|
|
419
|
+
color: var(--color-doc-template-ink);
|
|
420
|
+
font-weight: 600;
|
|
412
421
|
z-index: 1;
|
|
413
422
|
border-color: var(--color-doc-template-faint);
|
|
414
423
|
border-bottom-color: var(--color-doc-template-code);
|
|
415
424
|
background: var(--color-doc-template-code);
|
|
416
425
|
}
|
|
417
|
-
.dockit-code-tabs-
|
|
426
|
+
.dockit-code-tabs .uikit-tabbed-container-tabs [role='tab']:focus-visible,
|
|
418
427
|
.dockit-code-tabs [role='tabpanel']:focus-visible {
|
|
419
428
|
outline: 2px solid var(--color-doc-template-accent);
|
|
420
429
|
outline-offset: -3px;
|
|
@@ -435,3 +444,27 @@
|
|
|
435
444
|
.doc-content-row > * { min-width: 0; }
|
|
436
445
|
@media (min-width: 1024px) { .doc-content { --doc-row-bleed: 40px; } }
|
|
437
446
|
.doc-content[data-doc-fullscreen] { --doc-row-bleed: 0px; }
|
|
447
|
+
|
|
448
|
+
/* Labels stay in the tab row; actions are positioned by each code block. The
|
|
449
|
+
hidden/inert UIKit panel also hides its label, without client-only portals. */
|
|
450
|
+
.dockit-code-tabs-label {
|
|
451
|
+
position: absolute;
|
|
452
|
+
top: 0;
|
|
453
|
+
right: 10px;
|
|
454
|
+
max-width: calc(min(32%, 12ch) - 16px);
|
|
455
|
+
line-height: 29px;
|
|
456
|
+
white-space: nowrap;
|
|
457
|
+
overflow: hidden;
|
|
458
|
+
text-overflow: ellipsis;
|
|
459
|
+
font: 600 10px/29px var(--font-doc-template-mono);
|
|
460
|
+
color: var(--color-doc-template-muted);
|
|
461
|
+
}
|
|
462
|
+
[data-code-block][data-wrap='on'] pre {
|
|
463
|
+
white-space: pre-wrap;
|
|
464
|
+
overflow-wrap: anywhere;
|
|
465
|
+
}
|
|
466
|
+
@media (hover: none) {
|
|
467
|
+
.dockit-code-controls { opacity: 1; }
|
|
468
|
+
/* Touch has no hover-away state: leave room so actions never mask code. */
|
|
469
|
+
[data-code-block]:has(> .dockit-code-controls) > div:last-child { padding-top: 30px; }
|
|
470
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useId, useRef } from "react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { Tabs, TabsList, TabsTrigger } from "../Tabs/Tabs.js";
|
|
5
|
+
function TabbedContainer({
|
|
6
|
+
items,
|
|
7
|
+
value,
|
|
8
|
+
defaultValue,
|
|
9
|
+
onValueChange,
|
|
10
|
+
"aria-label": label,
|
|
11
|
+
keepMounted = true,
|
|
12
|
+
activationMode = "click",
|
|
13
|
+
className,
|
|
14
|
+
contentClassName
|
|
15
|
+
}) {
|
|
16
|
+
const [internal, setInternal] = useState(defaultValue ?? items[0]?.value);
|
|
17
|
+
const id = useId(), buttons = useRef([]);
|
|
18
|
+
const selected = value ?? internal;
|
|
19
|
+
const active = items.findIndex((item) => item.value === selected);
|
|
20
|
+
const index = active < 0 ? 0 : active;
|
|
21
|
+
const choose = (next) => {
|
|
22
|
+
if (value === void 0) setInternal(next);
|
|
23
|
+
onValueChange?.(next);
|
|
24
|
+
};
|
|
25
|
+
if (!items.length) return null;
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
"div",
|
|
28
|
+
{
|
|
29
|
+
className: cn("uikit-tabbed-container", className),
|
|
30
|
+
"data-first-active": index === 0,
|
|
31
|
+
children: /* @__PURE__ */ jsxs(Tabs, { value: items[index].value, onValueChange: choose, children: [
|
|
32
|
+
/* @__PURE__ */ jsx("div", { className: "uikit-tabbed-container-header", children: /* @__PURE__ */ jsx(TabsList, { "aria-label": label, className: "uikit-tabbed-container-tabs", children: items.map((item, i) => /* @__PURE__ */ jsx(
|
|
33
|
+
TabsTrigger,
|
|
34
|
+
{
|
|
35
|
+
value: item.value,
|
|
36
|
+
ref: (node) => {
|
|
37
|
+
buttons.current[i] = node;
|
|
38
|
+
},
|
|
39
|
+
id: `${id}-tab-${i}`,
|
|
40
|
+
"aria-controls": `${id}-panel-${i}`,
|
|
41
|
+
tabIndex: index === i ? 0 : -1,
|
|
42
|
+
onPointerEnter: (event) => {
|
|
43
|
+
if (activationMode === "hover" && event.pointerType === "mouse" && index !== i)
|
|
44
|
+
choose(item.value);
|
|
45
|
+
},
|
|
46
|
+
onKeyDown: (event) => {
|
|
47
|
+
let next;
|
|
48
|
+
if (event.key === "ArrowRight") next = (i + 1) % items.length;
|
|
49
|
+
else if (event.key === "ArrowLeft")
|
|
50
|
+
next = (i + items.length - 1) % items.length;
|
|
51
|
+
else if (event.key === "Home") next = 0;
|
|
52
|
+
else if (event.key === "End") next = items.length - 1;
|
|
53
|
+
else return;
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
choose(items[next].value);
|
|
56
|
+
buttons.current[next]?.focus();
|
|
57
|
+
},
|
|
58
|
+
children: item.label
|
|
59
|
+
},
|
|
60
|
+
item.value
|
|
61
|
+
)) }) }),
|
|
62
|
+
items.map(
|
|
63
|
+
(item, i) => (keepMounted || index === i) && /* @__PURE__ */ jsx(
|
|
64
|
+
"div",
|
|
65
|
+
{
|
|
66
|
+
role: "tabpanel",
|
|
67
|
+
id: `${id}-panel-${i}`,
|
|
68
|
+
"aria-labelledby": `${id}-tab-${i}`,
|
|
69
|
+
hidden: index !== i,
|
|
70
|
+
inert: index !== i,
|
|
71
|
+
tabIndex: 0,
|
|
72
|
+
className: cn(
|
|
73
|
+
"uikit-tabbed-container-content",
|
|
74
|
+
contentClassName
|
|
75
|
+
),
|
|
76
|
+
children: item.children
|
|
77
|
+
},
|
|
78
|
+
item.value
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
] })
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
TabbedContainer
|
|
87
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dreamlake/dockit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.29",
|
|
4
4
|
"description": "The DreamLake documentation shell — Vike + React + Tailwind v4 + MDX docs framework with sidebar, TOC, search, and theming",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"yaml": "^2.9.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
+
"@dreamlake/uikit": "0.2.3",
|
|
70
71
|
"@mdx-js/react": "^3.1.0",
|
|
71
72
|
"@types/node": "^22.20.1",
|
|
72
73
|
"@types/react": "latest",
|
|
@@ -78,8 +79,7 @@
|
|
|
78
79
|
"vike": "^0.4.210",
|
|
79
80
|
"vike-react": "^0.5.0",
|
|
80
81
|
"vite": "^7.0.0",
|
|
81
|
-
"vite-plugin-dts": "^4.3.0"
|
|
82
|
-
"@dreamlake/uikit": "0.1.15"
|
|
82
|
+
"vite-plugin-dts": "^4.3.0"
|
|
83
83
|
},
|
|
84
84
|
"scripts": {
|
|
85
85
|
"build": "vite build",
|