@dreamlake/dockit 0.2.27 → 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.
@@ -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
- "div",
41
- {
42
- ref: wrapRef,
43
- "data-line-numbers": showLines ? "on" : "off",
44
- className: embedded ? void 0 : "group relative border border-doc-template-faint bg-doc-template-code",
45
- style: {
46
- margin: embedded ? 0 : "18px 0 22px",
47
- borderRadius: embedded ? 0 : "var(--radius-doc-template)",
48
- overflow: embedded ? "visible" : "hidden"
49
- },
50
- children: [
51
- filename && /* @__PURE__ */ jsxs(
52
- "div",
53
- {
54
- className: "flex items-center border-b border-doc-template-faint text-doc-template-muted",
55
- style: {
56
- gap: 12,
57
- padding: "6px 10px 6px 14px",
58
- fontFamily: "var(--font-doc-template-mono)",
59
- fontSize: 10,
60
- letterSpacing: "0.06em",
61
- background: "color-mix(in srgb, var(--color-doc-template-ink) 3%, transparent)"
62
- },
63
- children: [
64
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, textTransform: "none", letterSpacing: 0 }, children: filename }),
65
- lang && /* @__PURE__ */ jsx(
66
- "span",
67
- {
68
- className: "uppercase opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity duration-150",
69
- style: { fontWeight: 600, letterSpacing: "0.06em" },
70
- children: lang
71
- }
72
- ),
73
- /* @__PURE__ */ jsx("span", { style: { flex: 1 } }),
74
- /* @__PURE__ */ jsxs(
75
- "span",
76
- {
77
- className: "inline-flex items-center opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity duration-150",
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
- lang && /* @__PURE__ */ jsx(
122
- "span",
123
- {
124
- className: "text-doc-template-muted uppercase",
125
- style: {
126
- fontFamily: "var(--font-doc-template-mono)",
127
- fontSize: 10,
128
- fontWeight: 600,
129
- letterSpacing: "0.04em",
130
- marginRight: 2
131
- },
132
- children: lang
133
- }
134
- ),
135
- /* @__PURE__ */ jsx(
136
- "button",
137
- {
138
- type: "button",
139
- "aria-pressed": showLines,
140
- title: showLines ? ":set nonu" : ":set nu",
141
- onClick: () => setShowLines(!showLines),
142
- style: headerBtnStyle(showLines),
143
- children: /* @__PURE__ */ jsx(HashIcon, {})
144
- }
145
- ),
146
- /* @__PURE__ */ jsx("button", { type: "button", onClick: copy, style: headerBtnStyle(copied), children: copied ? "Copied" : "Copy" })
147
- ]
148
- }
149
- ),
150
- /* @__PURE__ */ jsx(
151
- "div",
152
- {
153
- style: {
154
- fontFamily: "var(--font-doc-template-mono)",
155
- fontSize: 12.5,
156
- lineHeight: 1.3
157
- },
158
- children
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
- /** Equivalent fenced examples in one frame; arrow keys activate and focus tabs. */
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, jsxs } from "react/jsx-runtime";
2
- import { Children, isValidElement, useState, useId, useRef } from "react";
3
- import { Tabs, TabsList, TabsTrigger } from "../vendor/@dreamlake/uikit/src/components/Tabs/Tabs.js";
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, "data-first-active": active === 0, className: "dockit-code-tabs group relative", children: /* @__PURE__ */ jsxs(Tabs, { value: String(active), onValueChange: (value) => setSelected(Number(value)), children: [
16
- /* @__PURE__ */ jsx("div", { className: "dockit-code-tabs-header", children: /* @__PURE__ */ jsx(TabsList, { "aria-label": label, className: "dockit-code-tabs-list border-0 font-doc-template-ui", children: examples.map((example, index) => /* @__PURE__ */ jsx(
17
- TabsTrigger,
18
- {
19
- value: String(index),
20
- className: "shrink-0 font-doc-template-mono font-medium data-[active]:font-semibold text-doc-template-muted hover:text-doc-template-ink data-[active]:text-doc-template-ink text-[12px]",
21
- ref: (element) => {
22
- buttons.current[index] = element;
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
- example.key ?? index
54
- )) })
55
- ] }) });
20
+ }))
21
+ }
22
+ ) }) });
56
23
  }
57
24
  export {
58
25
  CodeExample,
@@ -0,0 +1,7 @@
1
+ import { HTMLAttributes } from 'react';
2
+ export interface ContentRowProps extends HTMLAttributes<HTMLDivElement> {
3
+ /** Expand into the prose gutters, staying inside the content column. */
4
+ width?: "wide" | "text";
5
+ }
6
+ /** A responsive row for diagrams, examples and comparisons wider than prose. */
7
+ export declare function ContentRow({ width, className, ...props }: ContentRowProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ function ContentRow({
3
+ width = "wide",
4
+ className = "",
5
+ ...props
6
+ }) {
7
+ return /* @__PURE__ */ jsx(
8
+ "div",
9
+ {
10
+ ...props,
11
+ className: `doc-content-row ${className}`,
12
+ "data-width": width
13
+ }
14
+ );
15
+ }
16
+ export {
17
+ ContentRow
18
+ };
@@ -1,2 +1,2 @@
1
- /** Internal: a fenced block shares the surrounding tabs' frame and toolbar. */
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>;
@@ -2,6 +2,7 @@ import { ComponentPropsWithoutRef } from 'react';
2
2
  import { Table } from './Table';
3
3
  import { Callout } from './Callout';
4
4
  import { Chip } from './Chip';
5
+ import { ContentRow } from './ContentRow';
5
6
  import { Preview } from './Preview';
6
7
  import { StatusTable, TestStatus } from './StatusTable';
7
8
  /**
@@ -27,6 +28,7 @@ export declare const mdxComponents: {
27
28
  Callout: typeof Callout;
28
29
  Chip: typeof Chip;
29
30
  Preview: typeof Preview;
31
+ ContentRow: typeof ContentRow;
30
32
  StatusTable: typeof StatusTable;
31
33
  TestStatus: typeof TestStatus;
32
34
  };
@@ -4,6 +4,7 @@ import { CodeBlock } from "./CodeBlock.js";
4
4
  import { Table } from "./Table.js";
5
5
  import { Callout } from "./Callout.js";
6
6
  import { Chip } from "./Chip.js";
7
+ import { ContentRow } from "./ContentRow.js";
7
8
  import { Preview } from "./Preview.js";
8
9
  import { TestStatus, StatusTable } from "./StatusTable.js";
9
10
  const InsidePreContext = createContext(false);
@@ -341,6 +342,7 @@ const mdxComponents = {
341
342
  Callout,
342
343
  Chip,
343
344
  Preview,
345
+ ContentRow,
344
346
  StatusTable,
345
347
  TestStatus
346
348
  };
package/dist/index.d.ts CHANGED
@@ -73,3 +73,4 @@ export interface InitDocsOptions {
73
73
  }
74
74
  /** One call wires the whole shell: site config, navigation, search, tabs. */
75
75
  export declare function initDocs(opts: InitDocsOptions): void;
76
+ export { ContentRow, type ContentRowProps } from './components/ContentRow';
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ import { TOC } from "./components/TOC.js";
30
30
  import { Topbar } from "./components/Topbar.js";
31
31
  import { VersionBadge } from "./components/VersionBadge.js";
32
32
  import { Layout, useMerge } from "./renderer/Layout.js";
33
+ import { ContentRow } from "./components/ContentRow.js";
33
34
  function initDocs(opts) {
34
35
  if (opts.pages && opts.pageMetadata) {
35
36
  throw new Error("initDocs: provide pageMetadata or pages, not both");
@@ -48,6 +49,7 @@ export {
48
49
  CodeBlock,
49
50
  CodeExample,
50
51
  CodeTabs,
52
+ ContentRow,
51
53
  DOC_PREFIX,
52
54
  DocFooter,
53
55
  Layout,
@@ -145,6 +145,7 @@ function Layout({ children }) {
145
145
  /* @__PURE__ */ jsxs(
146
146
  "main",
147
147
  {
148
+ "data-doc-fullscreen": isFullscreen || void 0,
148
149
  ...current?.noindex ? {} : { "data-pagefind-body": true },
149
150
  ...current?.hidden ? { "data-pagefind-meta": "hidden:true" } : {},
150
151
  className: isFullscreen ? "doc-content w-full min-w-0 h-full overflow-auto p-4" : "doc-content w-full min-w-0 px-[18px] pb-20 lg:px-[56px] lg:pb-[120px] lg:max-w-[760px]",
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 156px 0 0;
394
+ padding: 0 min(32%, 12ch) 0 0;
393
395
  }
394
- .dockit-code-tabs-list { overflow-x: auto; min-height: 30px; gap: 2px; }
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-list [role='tab'] {
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-list [role='tab']:first-child { margin-left: 0; }
406
- .dockit-code-tabs-list [role='tab'][aria-selected='false']:hover {
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-list [role='tab'][aria-selected='true'] {
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-list [role='tab']:focus-visible,
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;
@@ -427,3 +436,35 @@
427
436
  }
428
437
  .dockit-code-tabs[data-first-active='true'] [role='tabpanel'] { border-top-left-radius: 0; }
429
438
  .dockit-code-tabs [role='tabpanel'][hidden] { display: none; }
439
+
440
+ /* ContentRow borrows only the main column gutters, never the sidebar/TOC. */
441
+ .doc-content { --doc-row-bleed: 6px; }
442
+ .doc-content-row { min-width: 0; margin-block: 24px; }
443
+ .doc-content-row[data-width="wide"] { width: calc(100% + 2 * var(--doc-row-bleed, 0px)); margin-inline: calc(-1 * var(--doc-row-bleed, 0px)); }
444
+ .doc-content-row > * { min-width: 0; }
445
+ @media (min-width: 1024px) { .doc-content { --doc-row-bleed: 40px; } }
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.27",
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",