@adea-ai/ui 0.118.3 → 0.119.0
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/README.md +7 -3
- package/dist/components/composites/settings/index.d.ts +1 -1
- package/dist/components/composites/settings/index.d.ts.map +1 -1
- package/dist/components/composites/settings/settings-layout.d.ts +18 -2
- package/dist/components/composites/settings/settings-layout.d.ts.map +1 -1
- package/dist/components/composites/settings/settings-layout.js +40 -5
- package/dist/components/composites/settings/settings-layout.js.map +1 -1
- package/dist/components/composites/settings/settings-navigation.d.ts.map +1 -1
- package/dist/components/composites/settings/settings-navigation.js +36 -14
- package/dist/components/composites/settings/settings-navigation.js.map +1 -1
- package/dist/components/ui/code-block/code-block.d.ts +13 -0
- package/dist/components/ui/code-block/code-block.d.ts.map +1 -1
- package/dist/components/ui/code-block/code-block.js +22 -3
- package/dist/components/ui/code-block/code-block.js.map +1 -1
- package/dist/components/ui/item/index.d.ts +1 -1
- package/dist/components/ui/item/index.d.ts.map +1 -1
- package/dist/components/ui/item/item.d.ts +17 -1
- package/dist/components/ui/item/item.d.ts.map +1 -1
- package/dist/components/ui/item/item.js +17 -10
- package/dist/components/ui/item/item.js.map +1 -1
- package/dist/r/registry.json +3 -1
- package/dist/r/settings.json +3 -1
- package/dist/r/src/components/composites/settings/index.ts +5 -1
- package/dist/r/src/components/composites/settings/settings-layout.tsx +48 -5
- package/dist/r/src/components/composites/settings/settings-navigation.tsx +44 -16
- package/dist/r/src/components/ui/code-block/code-block.tsx +27 -2
- package/dist/r/src/components/ui/item/index.ts +1 -0
- package/dist/r/src/components/ui/item/item.tsx +41 -5
- package/package.json +1 -1
- package/registry.json +3 -1
- package/src/components/composites/settings/index.ts +5 -1
- package/src/components/composites/settings/settings-layout.tsx +48 -5
- package/src/components/composites/settings/settings-navigation.tsx +44 -16
- package/src/components/ui/code-block/code-block.tsx +27 -2
- package/src/components/ui/item/index.ts +1 -0
- package/src/components/ui/item/item.tsx +41 -5
package/README.md
CHANGED
|
@@ -284,9 +284,13 @@ appearance view, and `ThemeToggle` is the light/dark switch on its own.
|
|
|
284
284
|
`SettingsLayout` composes a controlled vertical tab root, grouped `SettingsNavigation`,
|
|
285
285
|
and a scrollable panel viewport. Supply the selected `value`, `onChange`, navigation
|
|
286
286
|
groups, and matching `TabsContent` panels. The host owns URL or preference updates
|
|
287
|
-
and panel content;
|
|
288
|
-
|
|
289
|
-
`
|
|
287
|
+
and panel content; below 48rem of viewport width the rail becomes a horizontal,
|
|
288
|
+
scrollable strip of tabs above the panels (groups in order, Left/Right keys,
|
|
289
|
+
`aria-orientation="horizontal"`), and `navigationLayout="rail" | "strip"` pins one
|
|
290
|
+
shape at every width. The layout reports repeated activation through `onReselect`
|
|
291
|
+
and reveals the selected row on either axis by default. Use `SettingsNavigation`
|
|
292
|
+
directly when you already own the surrounding tabs composition; it follows the
|
|
293
|
+
enclosing `Tabs` orientation.
|
|
290
294
|
Shared tabs associate panels with custom trigger IDs in browser and server renders;
|
|
291
295
|
an explicit panel `aria-labelledby` takes precedence. Independent tab roots keep
|
|
292
296
|
their associations separate.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { SettingsField, SettingsPage, SettingsRow, SettingsSection } from './settings';
|
|
2
|
-
export { SettingsLayout, type SettingsLayoutProps } from './settings-layout';
|
|
2
|
+
export { SettingsLayout, type SettingsLayoutProps, type SettingsNavigationLayout, } from './settings-layout';
|
|
3
3
|
export { SettingsNavigation, type SettingsNavigationGroup, type SettingsNavigationItem, type SettingsNavigationProps, } from './settings-navigation';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACtF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACtF,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,GAC9B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,uBAAuB,CAAA"}
|
|
@@ -4,7 +4,7 @@ import { type SettingsNavigationGroup } from './settings-navigation';
|
|
|
4
4
|
export type SettingsLayoutProps = Omit<ComponentProps<typeof Tabs>, 'children' | 'orientation' | 'value' | 'defaultValue' | 'onChange'> & {
|
|
5
5
|
/** Grouped navigation entries; the host owns their values and panel content. */
|
|
6
6
|
groups: readonly SettingsNavigationGroup[];
|
|
7
|
-
/** Accessible name for the
|
|
7
|
+
/** Accessible name for the tab list. */
|
|
8
8
|
'aria-label': string;
|
|
9
9
|
/** The host-controlled selected destination. */
|
|
10
10
|
value: string;
|
|
@@ -14,15 +14,31 @@ export type SettingsLayoutProps = Omit<ComponentProps<typeof Tabs>, 'children' |
|
|
|
14
14
|
onReselect?: (value: string) => void;
|
|
15
15
|
/** Reveal the selected row on value changes and window resizes. Defaults to nearest scroll. */
|
|
16
16
|
revealSelected?: boolean | ((trigger: HTMLButtonElement | undefined) => void);
|
|
17
|
+
/**
|
|
18
|
+
* How the section navigation sits beside the panels. `auto` (the default) is
|
|
19
|
+
* the vertical rail from 48rem of viewport width up and a horizontal,
|
|
20
|
+
* scrollable strip of tabs above the panels below it. `rail` and `strip` pin
|
|
21
|
+
* one shape at every width.
|
|
22
|
+
*/
|
|
23
|
+
navigationLayout?: SettingsNavigationLayout;
|
|
17
24
|
/** Panels must be rendered as `TabsContent` descendants. */
|
|
18
25
|
children?: JSX.Element;
|
|
19
26
|
};
|
|
27
|
+
export type SettingsNavigationLayout = 'auto' | 'rail' | 'strip';
|
|
20
28
|
/**
|
|
21
29
|
* SettingsLayout.
|
|
22
30
|
*
|
|
23
31
|
* A controlled, vertical settings rail beside its scrollable panel viewport.
|
|
24
32
|
* The host keeps ownership of selection, URL/hash state and panel content.
|
|
25
|
-
*
|
|
33
|
+
*
|
|
34
|
+
* Below 48rem the rail becomes a horizontal strip of tabs above the panels.
|
|
35
|
+
* A 6rem rail at phone width clips every label, so this is the default rather
|
|
36
|
+
* than an opt-in. It changes the tab root's orientation, not just its
|
|
37
|
+
* styling: the list's `aria-orientation` and Kobalte's arrow keys (Up/Down on
|
|
38
|
+
* the rail, Left/Right on the strip) follow the axis the reader sees. The
|
|
39
|
+
* breakpoint is the viewport's, like CatalogBrowser's and ContextualSidebar's,
|
|
40
|
+
* and is read before the first render so a phone never paints the rail. A
|
|
41
|
+
* server render, which has no viewport, renders the rail.
|
|
26
42
|
*/
|
|
27
43
|
export declare function SettingsLayout(props: SettingsLayoutProps): JSX.Element;
|
|
28
44
|
//# sourceMappingURL=settings-layout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings-layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAGnD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAsB,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAExF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,cAAc,CAAC,OAAO,IAAI,CAAC,EAC3B,UAAU,GAAG,aAAa,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CACnE,GAAG;IACF,gFAAgF;IAChF,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC1C,
|
|
1
|
+
{"version":3,"file":"settings-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings-layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAGnD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAsB,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAExF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,cAAc,CAAC,OAAO,IAAI,CAAC,EAC3B,UAAU,GAAG,aAAa,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CACnE,GAAG;IACF,gFAAgF;IAChF,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC1C,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAA;IACpB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,+FAA+F;IAC/F,cAAc,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,KAAK,IAAI,CAAC,CAAA;IAC7E;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,wBAAwB,CAAA;IAC3C,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAWhE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,eAgDxD"}
|
|
@@ -2,15 +2,28 @@ import { cn } from "../../../lib/utils.js";
|
|
|
2
2
|
import { Tabs } from "../../ui/tabs/tabs.js";
|
|
3
3
|
import { SettingsNavigation } from "./settings-navigation.js";
|
|
4
4
|
import { createComponent, insert, mergeProps, template } from "solid-js/web";
|
|
5
|
-
import { splitProps } from "solid-js";
|
|
5
|
+
import { createSignal, onCleanup, onMount, splitProps } from "solid-js";
|
|
6
6
|
//#region src/components/composites/settings/settings-layout.tsx
|
|
7
7
|
var _tmpl$ = /*#__PURE__*/ template(`<div data-slot=settings-layout-panels class="min-h-0 min-w-0 flex-1 overflow-y-auto p-4">`);
|
|
8
|
+
/** The `md` breakpoint the rail has always widened at; below it the rail becomes a strip. */
|
|
9
|
+
var RAIL_MEDIA_QUERY = "(min-width: 48rem)";
|
|
10
|
+
function railQuery() {
|
|
11
|
+
return typeof window === "undefined" || typeof window.matchMedia !== "function" ? void 0 : window.matchMedia(RAIL_MEDIA_QUERY);
|
|
12
|
+
}
|
|
8
13
|
/**
|
|
9
14
|
* SettingsLayout.
|
|
10
15
|
*
|
|
11
16
|
* A controlled, vertical settings rail beside its scrollable panel viewport.
|
|
12
17
|
* The host keeps ownership of selection, URL/hash state and panel content.
|
|
13
|
-
*
|
|
18
|
+
*
|
|
19
|
+
* Below 48rem the rail becomes a horizontal strip of tabs above the panels.
|
|
20
|
+
* A 6rem rail at phone width clips every label, so this is the default rather
|
|
21
|
+
* than an opt-in. It changes the tab root's orientation, not just its
|
|
22
|
+
* styling: the list's `aria-orientation` and Kobalte's arrow keys (Up/Down on
|
|
23
|
+
* the rail, Left/Right on the strip) follow the axis the reader sees. The
|
|
24
|
+
* breakpoint is the viewport's, like CatalogBrowser's and ContextualSidebar's,
|
|
25
|
+
* and is read before the first render so a phone never paints the rail. A
|
|
26
|
+
* server render, which has no viewport, renders the rail.
|
|
14
27
|
*/
|
|
15
28
|
function SettingsLayout(props) {
|
|
16
29
|
const [local, rest] = splitProps(props, [
|
|
@@ -20,10 +33,30 @@ function SettingsLayout(props) {
|
|
|
20
33
|
"aria-label",
|
|
21
34
|
"value",
|
|
22
35
|
"onReselect",
|
|
23
|
-
"revealSelected"
|
|
36
|
+
"revealSelected",
|
|
37
|
+
"navigationLayout"
|
|
24
38
|
]);
|
|
39
|
+
const [wide, setWide] = createSignal(railQuery()?.matches ?? true);
|
|
40
|
+
onMount(() => {
|
|
41
|
+
const query = railQuery();
|
|
42
|
+
if (!query) return;
|
|
43
|
+
const update = () => setWide(query.matches);
|
|
44
|
+
update();
|
|
45
|
+
query.addEventListener("change", update);
|
|
46
|
+
onCleanup(() => query.removeEventListener("change", update));
|
|
47
|
+
});
|
|
48
|
+
const layout = () => {
|
|
49
|
+
const requested = local.navigationLayout ?? "auto";
|
|
50
|
+
if (requested !== "auto") return requested;
|
|
51
|
+
return wide() ? "rail" : "strip";
|
|
52
|
+
};
|
|
25
53
|
return createComponent(Tabs, mergeProps(rest, {
|
|
26
|
-
orientation
|
|
54
|
+
get orientation() {
|
|
55
|
+
return layout() === "rail" ? "vertical" : "horizontal";
|
|
56
|
+
},
|
|
57
|
+
get ["data-navigation-layout"]() {
|
|
58
|
+
return layout();
|
|
59
|
+
},
|
|
27
60
|
get value() {
|
|
28
61
|
return local.value;
|
|
29
62
|
},
|
|
@@ -47,7 +80,9 @@ function SettingsLayout(props) {
|
|
|
47
80
|
get revealSelected() {
|
|
48
81
|
return local.revealSelected;
|
|
49
82
|
},
|
|
50
|
-
"class"
|
|
83
|
+
get ["class"]() {
|
|
84
|
+
return layout() === "rail" ? "w-24 md:w-sidebar" : void 0;
|
|
85
|
+
}
|
|
51
86
|
}), (() => {
|
|
52
87
|
var _el$ = _tmpl$();
|
|
53
88
|
insert(_el$, () => local.children);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-layout.js","names":["ComponentProps","JSX","splitProps","cn","Tabs","SettingsNavigation","SettingsNavigationGroup","SettingsLayoutProps","Omit","groups","value","onChange","onReselect","revealSelected","trigger","HTMLButtonElement","children","Element","SettingsLayout","props","local","rest","_$createComponent","_$mergeProps","orientation","class","aria-label","_el$","_tmpl$","_$insert","_$createComponent","_$mergeProps"],"sources":["../../../../src/components/composites/settings/settings-layout.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Tabs } from '../../ui/tabs'\nimport { SettingsNavigation, type SettingsNavigationGroup } from './settings-navigation'\n\nexport type SettingsLayoutProps = Omit<\n ComponentProps<typeof Tabs>,\n 'children' | 'orientation' | 'value' | 'defaultValue' | 'onChange'\n> & {\n /** Grouped navigation entries; the host owns their values and panel content. */\n groups: readonly SettingsNavigationGroup[]\n /** Accessible name for the
|
|
1
|
+
{"version":3,"file":"settings-layout.js","names":["ComponentProps","JSX","createSignal","onCleanup","onMount","splitProps","cn","Tabs","SettingsNavigation","SettingsNavigationGroup","SettingsLayoutProps","Omit","groups","value","onChange","onReselect","revealSelected","trigger","HTMLButtonElement","navigationLayout","SettingsNavigationLayout","children","Element","RAIL_MEDIA_QUERY","railQuery","MediaQueryList","window","matchMedia","undefined","SettingsLayout","props","local","rest","wide","setWide","matches","query","update","addEventListener","removeEventListener","layout","requested","_$createComponent","_$mergeProps","orientation","data-navigation-layout","class","aria-label","_el$","_tmpl$","_$insert","_$createComponent","_$mergeProps"],"sources":["../../../../src/components/composites/settings/settings-layout.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { createSignal, onCleanup, onMount, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Tabs } from '../../ui/tabs'\nimport { SettingsNavigation, type SettingsNavigationGroup } from './settings-navigation'\n\nexport type SettingsLayoutProps = Omit<\n ComponentProps<typeof Tabs>,\n 'children' | 'orientation' | 'value' | 'defaultValue' | 'onChange'\n> & {\n /** Grouped navigation entries; the host owns their values and panel content. */\n groups: readonly SettingsNavigationGroup[]\n /** Accessible name for the tab list. */\n 'aria-label': string\n /** The host-controlled selected destination. */\n value: string\n /** Updates the host-controlled selected destination. */\n onChange: (value: string) => void\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scroll. */\n revealSelected?: boolean | ((trigger: HTMLButtonElement | undefined) => void)\n /**\n * How the section navigation sits beside the panels. `auto` (the default) is\n * the vertical rail from 48rem of viewport width up and a horizontal,\n * scrollable strip of tabs above the panels below it. `rail` and `strip` pin\n * one shape at every width.\n */\n navigationLayout?: SettingsNavigationLayout\n /** Panels must be rendered as `TabsContent` descendants. */\n children?: JSX.Element\n}\n\nexport type SettingsNavigationLayout = 'auto' | 'rail' | 'strip'\n\n/** The `md` breakpoint the rail has always widened at; below it the rail becomes a strip. */\nconst RAIL_MEDIA_QUERY = '(min-width: 48rem)'\n\nfunction railQuery(): MediaQueryList | undefined {\n return typeof window === 'undefined' || typeof window.matchMedia !== 'function'\n ? undefined\n : window.matchMedia(RAIL_MEDIA_QUERY)\n}\n\n/**\n * SettingsLayout.\n *\n * A controlled, vertical settings rail beside its scrollable panel viewport.\n * The host keeps ownership of selection, URL/hash state and panel content.\n *\n * Below 48rem the rail becomes a horizontal strip of tabs above the panels.\n * A 6rem rail at phone width clips every label, so this is the default rather\n * than an opt-in. It changes the tab root's orientation, not just its\n * styling: the list's `aria-orientation` and Kobalte's arrow keys (Up/Down on\n * the rail, Left/Right on the strip) follow the axis the reader sees. The\n * breakpoint is the viewport's, like CatalogBrowser's and ContextualSidebar's,\n * and is read before the first render so a phone never paints the rail. A\n * server render, which has no viewport, renders the rail.\n */\nexport function SettingsLayout(props: SettingsLayoutProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'children',\n 'groups',\n 'aria-label',\n 'value',\n 'onReselect',\n 'revealSelected',\n 'navigationLayout',\n ])\n\n const [wide, setWide] = createSignal(railQuery()?.matches ?? true)\n onMount(() => {\n const query = railQuery()\n if (!query) return\n const update = () => setWide(query.matches)\n update()\n query.addEventListener('change', update)\n onCleanup(() => query.removeEventListener('change', update))\n })\n const layout = (): 'rail' | 'strip' => {\n const requested = local.navigationLayout ?? 'auto'\n if (requested !== 'auto') return requested\n return wide() ? 'rail' : 'strip'\n }\n\n return (\n <Tabs\n {...rest}\n orientation={layout() === 'rail' ? 'vertical' : 'horizontal'}\n data-navigation-layout={layout()}\n value={local.value}\n class={cn('min-h-0 min-w-0 flex-1 gap-0', local.class)}\n >\n <SettingsNavigation\n aria-label={local['aria-label']}\n groups={local.groups}\n value={local.value}\n onReselect={local.onReselect}\n revealSelected={local.revealSelected}\n class={layout() === 'rail' ? 'w-24 md:w-sidebar' : undefined}\n />\n <div data-slot=\"settings-layout-panels\" class=\"min-h-0 min-w-0 flex-1 overflow-y-auto p-4\">\n {local.children}\n </div>\n </Tabs>\n )\n}\n"],"mappings":";;;;;;;;AAoCA,IAAMuB,mBAAmB;AAEzB,SAASC,YAAwC;CAC/C,OAAO,OAAOE,WAAW,eAAe,OAAOA,OAAOC,eAAe,aACjEC,KAAAA,IACAF,OAAOC,WAAWJ,gBAAgB;AACxC;;;;;;;;;;;;;;;;AAiBA,SAAgBM,eAAeC,OAA4B;CACzD,MAAM,CAACC,OAAOC,QAAQ3B,WAAWyB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAkB,CACnB;CAED,MAAM,CAACG,MAAMC,WAAWhC,aAAasB,UAAU,CAAC,EAAEW,WAAW,IAAI;CACjE/B,cAAc;EACZ,MAAMgC,QAAQZ,UAAU;EACxB,IAAI,CAACY,OAAO;EACZ,MAAMC,eAAeH,QAAQE,MAAMD,OAAO;EAC1CE,OAAO;EACPD,MAAME,iBAAiB,UAAUD,MAAM;EACvClC,gBAAgBiC,MAAMG,oBAAoB,UAAUF,MAAM,CAAC;CAC7D,CAAC;CACD,MAAMG,eAAiC;EACrC,MAAMC,YAAYV,MAAMZ,oBAAoB;EAC5C,IAAIsB,cAAc,QAAQ,OAAOA;EACjC,OAAOR,KAAK,IAAI,SAAS;CAC3B;CAEA,OAAAS,gBACGnC,MAAIoC,WACCX,MAAI;EAAA,IACRY,cAAW;GAAA,OAAEJ,OAAO,MAAM,SAAS,aAAa;EAAY;EAAA,KAAA,4BAAA;GAAA,OACpCA,OAAO;EAAC;EAAA,IAChC3B,QAAK;GAAA,OAAEkB,MAAMlB;EAAK;EAAA,KAAA,WAAA;GAAA,OACXP,GAAG,gCAAgCyB,MAAMe,KAAK;EAAC;EAAA,IAAAzB,WAAA;GAAA,OAAA,CAAAqB,gBAErDlC,oBAAkB;IAAA,KAAA,gBAAA;KAAA,OACLuB,MAAM;IAAa;IAAA,IAC/BnB,SAAM;KAAA,OAAEmB,MAAMnB;IAAM;IAAA,IACpBC,QAAK;KAAA,OAAEkB,MAAMlB;IAAK;IAAA,IAClBE,aAAU;KAAA,OAAEgB,MAAMhB;IAAU;IAAA,IAC5BC,iBAAc;KAAA,OAAEe,MAAMf;IAAc;IAAA,KAAA,WAAA;KAAA,OAC7BwB,OAAO,MAAM,SAAS,sBAAsBZ,KAAAA;IAAS;GAAA,CAAA,UAAA;IAAA,IAAAoB,OAAAC,OAAA;IAAAC,OAAAF,YAG3DjB,MAAMV,QAAQ;IAAA,OAAA2B;GAAA,EAAA,CAAA,CAAA;EAAA;CAAA,CAAA,CAAA;AAIvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-navigation.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"settings-navigation.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInC,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAGzE,gFAAgF;AAChF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,kEAAkE;AAClE,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,SAAS,sBAAsB,EAAE,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,aAAa,EACb,YAAY,GAAG,UAAU,GAAG,aAAa,CAC1C,GAAG;IACF,6EAA6E;IAC7E,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC1C,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC;iGAC6F;IAC7F,cAAc,CAAC,EACX,OAAO,GACP,CAAC,CACC,OAAO,EAAE,iBAAiB,GAAG,SAAS,KACnC,IAAI,CAAC,CAAA,CAAC,8EAA8E;IAC7F,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAkDD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,eA2GhE"}
|
|
@@ -2,11 +2,12 @@ import { cn } from "../../../lib/utils.js";
|
|
|
2
2
|
import { Text } from "../../ui/typography/typography.js";
|
|
3
3
|
import { TabsList, TabsTrigger } from "../../ui/tabs/tabs.js";
|
|
4
4
|
import { sidebarNavItemClass, sidebarNavItemStateClass } from "../../layout/sidebar-nav/sidebar-nav.js";
|
|
5
|
-
import { createComponent, insert, memo, mergeProps, setAttribute, template } from "solid-js/web";
|
|
6
|
-
import { createEffect, createUniqueId, onCleanup, splitProps } from "solid-js";
|
|
5
|
+
import { className, createComponent, effect, insert, memo, mergeProps, setAttribute, template } from "solid-js/web";
|
|
6
|
+
import { Show, createEffect, createUniqueId, onCleanup, splitProps } from "solid-js";
|
|
7
|
+
import { useTabsContext } from "@kobalte/core/tabs";
|
|
7
8
|
//#region src/components/composites/settings/settings-navigation.tsx
|
|
8
|
-
var _tmpl$ = /*#__PURE__*/ template(`<div role=none data-slot=settings-navigation-group
|
|
9
|
-
var _tmpl$2 = /*#__PURE__*/ template(`<span
|
|
9
|
+
var _tmpl$ = /*#__PURE__*/ template(`<div role=none data-slot=settings-navigation-group><span class=sr-only>`);
|
|
10
|
+
var _tmpl$2 = /*#__PURE__*/ template(`<span>`);
|
|
10
11
|
/**
|
|
11
12
|
* SettingsNavigation.
|
|
12
13
|
*
|
|
@@ -15,6 +16,14 @@ var _tmpl$2 = /*#__PURE__*/ template(`<span class="min-w-0 truncate">`);
|
|
|
15
16
|
* the controlled value, panel content, icons and any persistence or routing.
|
|
16
17
|
* Use ordinary tabs for a short, ungrouped row of view choices.
|
|
17
18
|
*
|
|
19
|
+
* The shape follows the enclosing `Tabs` orientation rather than a prop of its
|
|
20
|
+
* own, so the visual axis can never disagree with the arrow keys Kobalte
|
|
21
|
+
* binds: `vertical` is the grouped rail, `horizontal` is a single scrollable
|
|
22
|
+
* strip of tabs (what `SettingsLayout` shows below 48rem). The strip keeps the
|
|
23
|
+
* groups in order, separated by a rule; their visible headings drop out, since
|
|
24
|
+
* an overline between tabs reads as another tab, but each tab still carries its
|
|
25
|
+
* group as its accessible description.
|
|
26
|
+
*
|
|
18
27
|
* Kobalte owns roving focus, arrow keys, selection and tab/panel ARIA. This
|
|
19
28
|
* component adds no URL, storage or host-specific state. Set
|
|
20
29
|
* `revealSelected={false}` when the surrounding layout does not use an internal
|
|
@@ -52,11 +61,14 @@ function SettingsNavigation(props) {
|
|
|
52
61
|
"onReselect",
|
|
53
62
|
"revealSelected"
|
|
54
63
|
]);
|
|
64
|
+
const tabs = useTabsContext();
|
|
65
|
+
const strip = () => tabs.orientation() === "horizontal";
|
|
55
66
|
const triggers = /* @__PURE__ */ new Map();
|
|
56
67
|
const groupIdPrefix = `settings-navigation-${createUniqueId()}`;
|
|
57
68
|
createEffect(() => {
|
|
58
69
|
const value = local.value;
|
|
59
70
|
const reveal = local.revealSelected;
|
|
71
|
+
strip();
|
|
60
72
|
if (reveal === false) return;
|
|
61
73
|
const revealCurrent = () => {
|
|
62
74
|
const trigger = triggers.get(value);
|
|
@@ -72,9 +84,11 @@ function SettingsNavigation(props) {
|
|
|
72
84
|
get ["aria-label"]() {
|
|
73
85
|
return local["aria-label"];
|
|
74
86
|
},
|
|
75
|
-
orientation
|
|
87
|
+
get orientation() {
|
|
88
|
+
return tabs.orientation();
|
|
89
|
+
},
|
|
76
90
|
get ["class"]() {
|
|
77
|
-
return cn("flex
|
|
91
|
+
return cn("relative flex min-w-0 shrink-0 justify-start", strip() ? "scroll-fade w-full flex-row items-center gap-1 overflow-x-auto overflow-y-hidden border-b border-border px-2 py-1.5" : "max-h-full min-h-0 flex-col items-stretch gap-2 overflow-x-hidden overflow-y-auto px-2 py-2", local.class);
|
|
78
92
|
}
|
|
79
93
|
}, rest, { get children() {
|
|
80
94
|
return local.groups.map((group, groupIndex) => {
|
|
@@ -83,18 +97,24 @@ function SettingsNavigation(props) {
|
|
|
83
97
|
var _el$ = _tmpl$(), _el$2 = _el$.firstChild;
|
|
84
98
|
setAttribute(_el$2, "id", groupId);
|
|
85
99
|
insert(_el$2, () => group.label);
|
|
86
|
-
insert(_el$, createComponent(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"class": "px-2 py-1.5 text-sidebar-muted-foreground",
|
|
100
|
+
insert(_el$, createComponent(Show, {
|
|
101
|
+
get when() {
|
|
102
|
+
return !strip();
|
|
103
|
+
},
|
|
91
104
|
get children() {
|
|
92
|
-
return
|
|
105
|
+
return createComponent(Text, {
|
|
106
|
+
variant: "overline",
|
|
107
|
+
tone: "inherit",
|
|
108
|
+
"aria-hidden": "true",
|
|
109
|
+
"class": "px-2 py-1.5 text-sidebar-muted-foreground",
|
|
110
|
+
get children() {
|
|
111
|
+
return group.label;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
93
114
|
}
|
|
94
115
|
}), null);
|
|
95
116
|
insert(_el$, () => group.items.map((item) => createComponent(TabsTrigger, {
|
|
96
117
|
"data-slot": "settings-navigation-trigger",
|
|
97
|
-
orientation: "vertical",
|
|
98
118
|
"aria-describedby": groupId,
|
|
99
119
|
get value() {
|
|
100
120
|
return item.value;
|
|
@@ -110,16 +130,18 @@ function SettingsNavigation(props) {
|
|
|
110
130
|
if (local.value === item.value) local.onReselect?.(item.value);
|
|
111
131
|
},
|
|
112
132
|
get ["class"]() {
|
|
113
|
-
return cn(sidebarNavItemClass, sidebarNavItemStateClass.idle, "h-row-lg
|
|
133
|
+
return cn(sidebarNavItemClass, sidebarNavItemStateClass.idle, "h-row-lg justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle data-[selected]:text-foreground", strip() ? "w-auto shrink-0" : "w-full");
|
|
114
134
|
},
|
|
115
135
|
get children() {
|
|
116
136
|
return [memo(() => item.icon), (() => {
|
|
117
137
|
var _el$3 = _tmpl$2();
|
|
118
138
|
insert(_el$3, () => item.label);
|
|
139
|
+
effect(() => className(_el$3, cn("min-w-0", { truncate: !strip() })));
|
|
119
140
|
return _el$3;
|
|
120
141
|
})()];
|
|
121
142
|
}
|
|
122
143
|
})), null);
|
|
144
|
+
effect(() => className(_el$, cn("flex gap-0.5", strip() ? "shrink-0 flex-row items-center not-first:ms-1 not-first:border-s not-first:border-border not-first:ps-2" : "min-w-0 flex-col")));
|
|
123
145
|
return _el$;
|
|
124
146
|
})();
|
|
125
147
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-navigation.js","names":["JSX","createEffect","createUniqueId","onCleanup","splitProps","cn","sidebarNavItemClass","sidebarNavItemStateClass","TabsList","TabsTrigger","TabsListProps","Text","SettingsNavigationItem","value","label","icon","Element","disabled","SettingsNavigationGroup","items","SettingsNavigationProps","Omit","groups","onReselect","revealSelected","trigger","HTMLButtonElement","scrollContainerNearest","container","HTMLElement","row","style","window","getComputedStyle","box","getBoundingClientRect","rowBox","overflowX","left","scrollLeft","right","overflowY","top","scrollTop","bottom","revealNearest","parentElement","SettingsNavigation","props","local","rest","triggers","Map","groupIdPrefix","reveal","revealCurrent","get","addEventListener","removeEventListener","_$createComponent","_$mergeProps","aria-label","orientation","class","children","map","group","groupIndex","groupId","_el$","_tmpl$","_el$2","firstChild","_$setAttribute","_$insert","variant","tone","item","ref","element","set","delete","onClick","idle","_$memo","_el$3","_tmpl$2","_$createComponent","_$mergeProps","_$memo"],"sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"sourcesContent":["import type { JSX } from 'solid-js'\nimport { createEffect, createUniqueId, onCleanup, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { sidebarNavItemClass, sidebarNavItemStateClass } from '../../layout/sidebar-nav/sidebar-nav'\nimport { TabsList, TabsTrigger, type TabsListProps } from '../../ui/tabs'\nimport { Text } from '../../ui/typography'\n\n/** One host-owned destination in a settings tab list. Values must be unique. */\nexport type SettingsNavigationItem = {\n value: string\n label: string\n icon?: JSX.Element\n disabled?: boolean\n}\n\n/** A visible, accessibly named group of settings destinations. */\nexport type SettingsNavigationGroup = {\n label: string\n items: readonly SettingsNavigationItem[]\n}\n\nexport type SettingsNavigationProps = Omit<\n TabsListProps,\n 'appearance' | 'children' | 'orientation'\n> & {\n /** Groups are presentation only; the host owns the tab values and panels. */\n groups: readonly SettingsNavigationGroup[]\n /** The same controlled value supplied to the enclosing `Tabs`. */\n value: string\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scrolling\n * of the ancestors that expose a scroll affordance; hidden-overflow ancestors never move. */\n revealSelected?:\n | boolean\n | ((\n trigger: HTMLButtonElement | undefined\n ) => void) /** Every tab list needs a label independent of its visible group headings. */\n 'aria-label': string\n}\n\n/**\n * SettingsNavigation.\n *\n * A grouped vertical tab list for sibling settings panels. It composes the\n * shared Tabs behaviour with the shared sidebar row recipe; the host supplies\n * the controlled value, panel content, icons and any persistence or routing.\n * Use ordinary tabs for a short, ungrouped row of view choices.\n *\n * Kobalte owns roving focus, arrow keys, selection and tab/panel ARIA. This\n * component adds no URL, storage or host-specific state. Set\n * `revealSelected={false}` when the surrounding layout does not use an internal\n * scrolling viewport.\n */\n/**\n * Reveal by scrolling only the ancestors that expose a scroll affordance\n * (`auto` or `scroll`), by the exact nearest-edge delta. `scrollIntoView` also\n * pans `overflow: hidden` ancestors, which are programmatically scrollable but\n * give the user no way back, so one over-pan left earlier rows permanently\n * clipped outside the viewport with no scroll to recover them (#182).\n */\nfunction scrollContainerNearest(container: HTMLElement, row: HTMLElement): void {\n const style = window.getComputedStyle(container)\n const box = container.getBoundingClientRect()\n const rowBox = row.getBoundingClientRect()\n if (style.overflowX === 'auto' || style.overflowX === 'scroll') {\n if (rowBox.left < box.left) container.scrollLeft += rowBox.left - box.left\n else if (rowBox.right > box.right) container.scrollLeft += rowBox.right - box.right\n }\n if (style.overflowY === 'auto' || style.overflowY === 'scroll') {\n if (rowBox.top < box.top) container.scrollTop += rowBox.top - box.top\n else if (rowBox.bottom > box.bottom) container.scrollTop += rowBox.bottom - box.bottom\n }\n}\n\nfunction revealNearest(row: HTMLElement): void {\n for (let container = row.parentElement; container; container = container.parentElement) {\n scrollContainerNearest(container, row)\n }\n}\n\nexport function SettingsNavigation(props: SettingsNavigationProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'aria-label',\n 'groups',\n 'value',\n 'onReselect',\n 'revealSelected',\n ])\n const triggers = new Map<string, HTMLButtonElement>()\n const groupIdPrefix = `settings-navigation-${createUniqueId()}`\n\n createEffect(() => {\n const value = local.value\n const reveal = local.revealSelected\n if (reveal === false) return\n\n const revealCurrent = () => {\n const trigger = triggers.get(value)\n if (typeof reveal === 'function') reveal(trigger)\n else if (trigger) revealNearest(trigger)\n }\n\n revealCurrent()\n window.addEventListener('resize', revealCurrent)\n onCleanup(() => window.removeEventListener('resize', revealCurrent))\n })\n\n return (\n <TabsList\n data-slot=\"settings-navigation\"\n aria-label={local['aria-label']}\n orientation=\"vertical\"\n class={cn(\n 'flex max-h-full min-h-0 min-w-0 shrink-0 flex-col items-stretch justify-start gap-2 overflow-x-hidden overflow-y-auto px-2 py-2',\n local.class\n )}\n {...rest}\n >\n {local.groups.map((group, groupIndex) => {\n const groupId = `${groupIdPrefix}-group-${groupIndex}`\n return (\n <div\n role=\"none\"\n data-slot=\"settings-navigation-group\"\n class=\"flex min-w-0 flex-col gap-0.5\"\n >\n <span id={groupId} class=\"sr-only\">\n {group.label}\n </span>\n {/* The overline role on the sidebar's own muted foreground. */}\n <Text\n variant=\"overline\"\n tone=\"inherit\"\n aria-hidden=\"true\"\n class=\"px-2 py-1.5 text-sidebar-muted-foreground\"\n >\n {group.label}\n </Text>\n {group.items.map((item) => (\n <TabsTrigger\n data-slot=\"settings-navigation-trigger\"\n orientation=\"vertical\"\n aria-describedby={groupId}\n value={item.value}\n disabled={item.disabled}\n ref={(element: HTMLButtonElement | undefined) => {\n if (element) triggers.set(item.value, element)\n else triggers.delete(item.value)\n }}\n onClick={() => {\n if (local.value === item.value) local.onReselect?.(item.value)\n }}\n class={cn(\n sidebarNavItemClass,\n sidebarNavItemStateClass.idle,\n 'h-row-lg w-full justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle data-[selected]:text-foreground'\n )}\n >\n {item.icon}\n <span class=\"min-w-0 truncate\">{item.label}</span>\n </TabsTrigger>\n ))}\n </div>\n )\n })}\n </TabsList>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,SAAS2B,uBAAuBC,WAAwBE,KAAwB;CAC9E,MAAMC,QAAQC,OAAOC,iBAAiBL,SAAS;CAC/C,MAAMM,MAAMN,UAAUO,sBAAsB;CAC5C,MAAMC,SAASN,IAAIK,sBAAsB;CACzC,IAAIJ,MAAMM,cAAc,UAAUN,MAAMM,cAAc,UAAU;EAC9D,IAAID,OAAOE,OAAOJ,IAAII,MAAMV,UAAUW,cAAcH,OAAOE,OAAOJ,IAAII;OACjE,IAAIF,OAAOI,QAAQN,IAAIM,OAAOZ,UAAUW,cAAcH,OAAOI,QAAQN,IAAIM;CAChF;CACA,IAAIT,MAAMU,cAAc,UAAUV,MAAMU,cAAc,UAAU;EAC9D,IAAIL,OAAOM,MAAMR,IAAIQ,KAAKd,UAAUe,aAAaP,OAAOM,MAAMR,IAAIQ;OAC7D,IAAIN,OAAOQ,SAASV,IAAIU,QAAQhB,UAAUe,aAAaP,OAAOQ,SAASV,IAAIU;CAClF;AACF;AAEA,SAASC,cAAcf,KAAwB;CAC7C,KAAK,IAAIF,YAAYE,IAAIgB,eAAelB,WAAWA,YAAYA,UAAUkB,eACvEnB,uBAAuBC,WAAWE,GAAG;AAEzC;AAEA,SAAgBiB,mBAAmBC,OAAgC;CACjE,MAAM,CAACC,OAAOC,QAAQ9C,WAAW4C,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;CAAgB,CACjB;CACD,MAAMG,2BAAW,IAAIC,IAA+B;CACpD,MAAMC,gBAAgB,uBAAuBnD,eAAe;CAE5DD,mBAAmB;EACjB,MAAMY,QAAQoC,MAAMpC;EACpB,MAAMyC,SAASL,MAAMzB;EACrB,IAAI8B,WAAW,OAAO;EAEtB,MAAMC,sBAAsB;GAC1B,MAAM9B,UAAU0B,SAASK,IAAI3C,KAAK;GAClC,IAAI,OAAOyC,WAAW,YAAYA,OAAO7B,OAAO;QAC3C,IAAIA,SAASoB,cAAcpB,OAAO;EACzC;EAEA8B,cAAc;EACdvB,OAAOyB,iBAAiB,UAAUF,aAAa;EAC/CpD,gBAAgB6B,OAAO0B,oBAAoB,UAAUH,aAAa,CAAC;CACrE,CAAC;CAED,OAAAI,gBACGnD,UAAQoD,WAAA;EAAA,aAAA;EAAA,KAAA,gBAAA;GAAA,OAEKX,MAAM;EAAa;EAC/Ba,aAAW;EAAA,KAAA,WAAA;GAAA,OACJzD,GACL,mIACA4C,MAAMc,KACR;EAAC;CAAA,GACGb,MAAI,EAAA,IAAAc,WAAA;EAAA,OAEPf,MAAM3B,OAAO2C,KAAKC,OAAOC,eAAe;GACvC,MAAMC,UAAU,GAAGf,cAAa,SAAUc;GAC1C,cAAA;IAAA,IAAAE,OAAAC,OAAA,GAAAC,QAAAF,KAAAG;IAAAC,aAAAF,OAAA,MAMcH,OAAO;IAAAM,OAAAH,aACdL,MAAMpD,KAAK;IAAA4D,OAAAL,MAAAV,gBAGbhD,MAAI;KACHgE,SAAO;KACPC,MAAI;KAAA,eAAA;KAAA,SAAA;KAAA,IAAAZ,WAAA;MAAA,OAIHE,MAAMpD;KAAK;IAAA,CAAA,GAAA,IAAA;IAAA4D,OAAAL,YAEbH,MAAM/C,MAAM8C,KAAKY,SAAIlB,gBACnBlD,aAAW;KAAA,aAAA;KAEVqD,aAAW;KAAA,oBACOM;KAAO,IACzBvD,QAAK;MAAA,OAAEgE,KAAKhE;KAAK;KAAA,IACjBI,WAAQ;MAAA,OAAE4D,KAAK5D;KAAQ;KAAA6D,MACjBC,YAA2C;MAC/C,IAAIA,SAAS5B,SAAS6B,IAAIH,KAAKhE,OAAOkE,OAAO;WACxC5B,SAAS8B,OAAOJ,KAAKhE,KAAK;KACjC;KACAqE,eAAe;MACb,IAAIjC,MAAMpC,UAAUgE,KAAKhE,OAAOoC,MAAM1B,aAAasD,KAAKhE,KAAK;KAC/D;KAAC,KAAA,WAAA;MAAA,OACMR,GACLC,qBACAC,yBAAyB4E,MACzB,8HACF;KAAC;KAAA,IAAAnB,WAAA;MAAA,OAAA,CAAAoB,WAEAP,KAAK9D,IAAI,UAAA;OAAA,IAAAsE,QAAAC,QAAA;OAAAZ,OAAAW,aACsBR,KAAK/D,KAAK;OAAA,OAAAuE;MAAA,EAAA,CAAA,CAAA;KAAA;IAAA,CAAA,CAE7C,GAAC,IAAA;IAAA,OAAAhB;GAAA,EAAA,CAAA;EAGR,CAAC;CAAC,EAAA,CAAA,CAAA;AAGR"}
|
|
1
|
+
{"version":3,"file":"settings-navigation.js","names":["useTabsContext","JSX","createEffect","createUniqueId","onCleanup","Show","splitProps","cn","sidebarNavItemClass","sidebarNavItemStateClass","TabsList","TabsTrigger","TabsListProps","Text","SettingsNavigationItem","value","label","icon","Element","disabled","SettingsNavigationGroup","items","SettingsNavigationProps","Omit","groups","onReselect","revealSelected","trigger","HTMLButtonElement","scrollContainerNearest","container","HTMLElement","row","style","window","getComputedStyle","box","getBoundingClientRect","rowBox","overflowX","left","scrollLeft","right","overflowY","top","scrollTop","bottom","revealNearest","parentElement","SettingsNavigation","props","local","rest","tabs","strip","orientation","triggers","Map","groupIdPrefix","reveal","revealCurrent","get","addEventListener","removeEventListener","_$createComponent","_$mergeProps","aria-label","class","children","map","group","groupIndex","groupId","_el$","_tmpl$","_el$2","firstChild","_$setAttribute","_$insert","when","variant","tone","item","ref","element","set","delete","onClick","idle","_$memo","_el$3","_tmpl$2","_$effect","_$className","truncate","_$createComponent","_$mergeProps","_$memo","_$className"],"sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"sourcesContent":["import { useTabsContext } from '@kobalte/core/tabs'\nimport type { JSX } from 'solid-js'\nimport { createEffect, createUniqueId, onCleanup, Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { sidebarNavItemClass, sidebarNavItemStateClass } from '../../layout/sidebar-nav/sidebar-nav'\nimport { TabsList, TabsTrigger, type TabsListProps } from '../../ui/tabs'\nimport { Text } from '../../ui/typography'\n\n/** One host-owned destination in a settings tab list. Values must be unique. */\nexport type SettingsNavigationItem = {\n value: string\n label: string\n icon?: JSX.Element\n disabled?: boolean\n}\n\n/** A visible, accessibly named group of settings destinations. */\nexport type SettingsNavigationGroup = {\n label: string\n items: readonly SettingsNavigationItem[]\n}\n\nexport type SettingsNavigationProps = Omit<\n TabsListProps,\n 'appearance' | 'children' | 'orientation'\n> & {\n /** Groups are presentation only; the host owns the tab values and panels. */\n groups: readonly SettingsNavigationGroup[]\n /** The same controlled value supplied to the enclosing `Tabs`. */\n value: string\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scrolling\n * of the ancestors that expose a scroll affordance; hidden-overflow ancestors never move. */\n revealSelected?:\n | boolean\n | ((\n trigger: HTMLButtonElement | undefined\n ) => void) /** Every tab list needs a label independent of its visible group headings. */\n 'aria-label': string\n}\n\n/**\n * SettingsNavigation.\n *\n * A grouped vertical tab list for sibling settings panels. It composes the\n * shared Tabs behaviour with the shared sidebar row recipe; the host supplies\n * the controlled value, panel content, icons and any persistence or routing.\n * Use ordinary tabs for a short, ungrouped row of view choices.\n *\n * The shape follows the enclosing `Tabs` orientation rather than a prop of its\n * own, so the visual axis can never disagree with the arrow keys Kobalte\n * binds: `vertical` is the grouped rail, `horizontal` is a single scrollable\n * strip of tabs (what `SettingsLayout` shows below 48rem). The strip keeps the\n * groups in order, separated by a rule; their visible headings drop out, since\n * an overline between tabs reads as another tab, but each tab still carries its\n * group as its accessible description.\n *\n * Kobalte owns roving focus, arrow keys, selection and tab/panel ARIA. This\n * component adds no URL, storage or host-specific state. Set\n * `revealSelected={false}` when the surrounding layout does not use an internal\n * scrolling viewport.\n */\n/**\n * Reveal by scrolling only the ancestors that expose a scroll affordance\n * (`auto` or `scroll`), by the exact nearest-edge delta. `scrollIntoView` also\n * pans `overflow: hidden` ancestors, which are programmatically scrollable but\n * give the user no way back, so one over-pan left earlier rows permanently\n * clipped outside the viewport with no scroll to recover them (#182).\n */\nfunction scrollContainerNearest(container: HTMLElement, row: HTMLElement): void {\n const style = window.getComputedStyle(container)\n const box = container.getBoundingClientRect()\n const rowBox = row.getBoundingClientRect()\n if (style.overflowX === 'auto' || style.overflowX === 'scroll') {\n if (rowBox.left < box.left) container.scrollLeft += rowBox.left - box.left\n else if (rowBox.right > box.right) container.scrollLeft += rowBox.right - box.right\n }\n if (style.overflowY === 'auto' || style.overflowY === 'scroll') {\n if (rowBox.top < box.top) container.scrollTop += rowBox.top - box.top\n else if (rowBox.bottom > box.bottom) container.scrollTop += rowBox.bottom - box.bottom\n }\n}\n\nfunction revealNearest(row: HTMLElement): void {\n for (let container = row.parentElement; container; container = container.parentElement) {\n scrollContainerNearest(container, row)\n }\n}\n\nexport function SettingsNavigation(props: SettingsNavigationProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'aria-label',\n 'groups',\n 'value',\n 'onReselect',\n 'revealSelected',\n ])\n const tabs = useTabsContext()\n const strip = () => tabs.orientation() === 'horizontal'\n const triggers = new Map<string, HTMLButtonElement>()\n const groupIdPrefix = `settings-navigation-${createUniqueId()}`\n\n createEffect(() => {\n const value = local.value\n const reveal = local.revealSelected\n // Tracked so a rail that becomes a strip (or back) re-reveals on the new axis.\n strip()\n if (reveal === false) return\n\n const revealCurrent = () => {\n const trigger = triggers.get(value)\n if (typeof reveal === 'function') reveal(trigger)\n else if (trigger) revealNearest(trigger)\n }\n\n revealCurrent()\n window.addEventListener('resize', revealCurrent)\n onCleanup(() => window.removeEventListener('resize', revealCurrent))\n })\n\n return (\n <TabsList\n data-slot=\"settings-navigation\"\n aria-label={local['aria-label']}\n orientation={tabs.orientation()}\n class={cn(\n // Positioned so the visually hidden group names are laid out (and\n // clipped) inside the list: unpositioned, their absolute boxes escape a\n // scrolled strip and widen the page by however far it has scrolled.\n 'relative flex min-w-0 shrink-0 justify-start',\n strip()\n ? // The block padding is the room the focus ring needs: a scroller\n // clips at its padding edge, and this one scrolls sideways only.\n 'scroll-fade w-full flex-row items-center gap-1 overflow-x-auto overflow-y-hidden border-b border-border px-2 py-1.5'\n : 'max-h-full min-h-0 flex-col items-stretch gap-2 overflow-x-hidden overflow-y-auto px-2 py-2',\n local.class\n )}\n {...rest}\n >\n {local.groups.map((group, groupIndex) => {\n const groupId = `${groupIdPrefix}-group-${groupIndex}`\n return (\n <div\n role=\"none\"\n data-slot=\"settings-navigation-group\"\n class={cn(\n 'flex gap-0.5',\n strip()\n ? 'shrink-0 flex-row items-center not-first:ms-1 not-first:border-s not-first:border-border not-first:ps-2'\n : 'min-w-0 flex-col'\n )}\n >\n <span id={groupId} class=\"sr-only\">\n {group.label}\n </span>\n <Show when={!strip()}>\n {/* The overline role on the sidebar's own muted foreground. */}\n <Text\n variant=\"overline\"\n tone=\"inherit\"\n aria-hidden=\"true\"\n class=\"px-2 py-1.5 text-sidebar-muted-foreground\"\n >\n {group.label}\n </Text>\n </Show>\n {group.items.map((item) => (\n <TabsTrigger\n data-slot=\"settings-navigation-trigger\"\n aria-describedby={groupId}\n value={item.value}\n disabled={item.disabled}\n ref={(element: HTMLButtonElement | undefined) => {\n if (element) triggers.set(item.value, element)\n else triggers.delete(item.value)\n }}\n onClick={() => {\n if (local.value === item.value) local.onReselect?.(item.value)\n }}\n class={cn(\n sidebarNavItemClass,\n sidebarNavItemStateClass.idle,\n 'h-row-lg justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle data-[selected]:text-foreground',\n strip() ? 'w-auto shrink-0' : 'w-full'\n )}\n >\n {item.icon}\n <span class={cn('min-w-0', { truncate: !strip() })}>{item.label}</span>\n </TabsTrigger>\n ))}\n </div>\n )\n })}\n </TabsList>\n )\n}\n"],"mappings":"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"}
|
|
@@ -57,6 +57,19 @@ export type CodeBlockProps = Omit<ComponentProps<'div'>, 'children'> & {
|
|
|
57
57
|
showLineNumbers?: boolean;
|
|
58
58
|
/** The height above which the action row is repeated below the block. */
|
|
59
59
|
tallThreshold?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Cap the code body's height and scroll it vertically past that. A number is
|
|
62
|
+
* pixels; a string is any CSS length (`'20rem'`, `'50vh'`). Unset, the block
|
|
63
|
+
* grows with its content.
|
|
64
|
+
*/
|
|
65
|
+
maxHeight?: number | string;
|
|
66
|
+
/**
|
|
67
|
+
* Soft-wrap long lines inside the block instead of scrolling sideways. Off by
|
|
68
|
+
* default: wrapped code loses its column alignment, so it is for prose-like
|
|
69
|
+
* output (logs, long commands) in a narrow column. Wraps the plain renderer and
|
|
70
|
+
* any `pre` inside `highlight` output; line numbers stay on the first visual line.
|
|
71
|
+
*/
|
|
72
|
+
wrap?: boolean;
|
|
60
73
|
};
|
|
61
74
|
/** The height a block must exceed before its actions are repeated. */
|
|
62
75
|
export declare const TALL_CODE_BLOCK_PX = 480;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/code-block/code-block.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,GAAG,EACT,MAAM,UAAU,CAAA;AAKjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oFAAoF;IACpF,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC,OAAO,CAAA;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/code-block/code-block.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,GAAG,EACT,MAAM,UAAU,CAAA;AAKjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oFAAoF;IACpF,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC,OAAO,CAAA;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC3B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,MAAM,CAAA;AAkBrC,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,eAqI9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,eAYvD"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
2
|
import { Button } from "../button/button.js";
|
|
3
|
-
import { createComponent, effect, insert, memo, mergeProps, setAttribute, setStyleProperty, spread, template, use } from "solid-js/web";
|
|
3
|
+
import { className, createComponent, effect, insert, memo, mergeProps, setAttribute, setStyleProperty, spread, style, template, use } from "solid-js/web";
|
|
4
4
|
import { Check, Copy } from "lucide-solid";
|
|
5
5
|
import { For, Show, createMemo, createSignal, splitProps } from "solid-js";
|
|
6
6
|
//#region src/components/ui/code-block/code-block.tsx
|
|
7
7
|
var _tmpl$ = /*#__PURE__*/ template(`<div class="flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code:opacity-100 group-hover/code:opacity-100">`);
|
|
8
8
|
var _tmpl$2 = /*#__PURE__*/ template(`<p class="px-3 pb-2 text-xs text-muted-foreground italic">Generating…`);
|
|
9
9
|
var _tmpl$3 = /*#__PURE__*/ template(`<div data-code-block-footer class="flex items-center justify-end border-t border-transparent px-3 py-1 transition-colors group-focus-within/code:border-border group-hover/code:border-border">`);
|
|
10
|
-
var _tmpl$4 = /*#__PURE__*/ template(`<div><div class="flex items-center justify-between gap-2 px-3 py-1"><span class="truncate font-code text-code text-muted-foreground"></span></div><div
|
|
10
|
+
var _tmpl$4 = /*#__PURE__*/ template(`<div><div class="flex items-center justify-between gap-2 px-3 py-1"><span class="truncate font-code text-code text-muted-foreground"></span></div><div data-slot=code-block-body tabindex=0 role=region>`);
|
|
11
11
|
var _tmpl$5 = /*#__PURE__*/ template(`<pre class="m-0 px-3 py-2"><code class="font-code text-code">`);
|
|
12
12
|
var _tmpl$6 = /*#__PURE__*/ template(`<span class="grid grid-cols-[3ch_1fr] gap-3"><span class="text-right text-muted-foreground select-none"aria-hidden=true></span><span>`);
|
|
13
13
|
var _tmpl$7 = /*#__PURE__*/ template(`<div class="px-3 py-2">`);
|
|
@@ -42,6 +42,8 @@ function CodeBlock(props) {
|
|
|
42
42
|
"lineHeight",
|
|
43
43
|
"showLineNumbers",
|
|
44
44
|
"tallThreshold",
|
|
45
|
+
"maxHeight",
|
|
46
|
+
"wrap",
|
|
45
47
|
"class",
|
|
46
48
|
"ref"
|
|
47
49
|
]);
|
|
@@ -51,6 +53,7 @@ function CodeBlock(props) {
|
|
|
51
53
|
const label = () => local.title ?? local.language ?? "code";
|
|
52
54
|
const regionLabel = () => local.language ? `${local.language} code` : "code";
|
|
53
55
|
const lines = createMemo(() => local.code.replace(/\n$/, "").split("\n"));
|
|
56
|
+
const maxHeight = () => typeof local.maxHeight === "number" ? `${local.maxHeight}px` : local.maxHeight;
|
|
54
57
|
const copy = async () => {
|
|
55
58
|
const ok = await copyText(local.code);
|
|
56
59
|
setCopied(ok);
|
|
@@ -123,6 +126,7 @@ function CodeBlock(props) {
|
|
|
123
126
|
var _el$0 = _tmpl$6(), _el$1 = _el$0.firstChild, _el$10 = _el$1.nextSibling;
|
|
124
127
|
insert(_el$1, () => index() + 1);
|
|
125
128
|
insert(_el$10, line || " ");
|
|
129
|
+
effect(() => className(_el$10, cn({ "min-w-0": local.wrap })));
|
|
126
130
|
return _el$0;
|
|
127
131
|
})()
|
|
128
132
|
});
|
|
@@ -156,7 +160,22 @@ function CodeBlock(props) {
|
|
|
156
160
|
return _el$7;
|
|
157
161
|
}
|
|
158
162
|
}), null);
|
|
159
|
-
effect(() =>
|
|
163
|
+
effect((_p$) => {
|
|
164
|
+
var _v$ = local.wrap ? "" : void 0, _v$2 = cn("scroll-fade overflow-x-auto", {
|
|
165
|
+
"overflow-y-auto": local.maxHeight !== void 0,
|
|
166
|
+
"[&_pre]:whitespace-pre-wrap [&_pre]:[overflow-wrap:anywhere]": local.wrap
|
|
167
|
+
}), _v$3 = maxHeight() === void 0 ? void 0 : { "max-height": maxHeight() }, _v$4 = regionLabel();
|
|
168
|
+
_v$ !== _p$.e && setAttribute(_el$5, "data-wrap", _p$.e = _v$);
|
|
169
|
+
_v$2 !== _p$.t && className(_el$5, _p$.t = _v$2);
|
|
170
|
+
_p$.a = style(_el$5, _v$3, _p$.a);
|
|
171
|
+
_v$4 !== _p$.o && setAttribute(_el$5, "aria-label", _p$.o = _v$4);
|
|
172
|
+
return _p$;
|
|
173
|
+
}, {
|
|
174
|
+
e: void 0,
|
|
175
|
+
t: void 0,
|
|
176
|
+
a: void 0,
|
|
177
|
+
o: void 0
|
|
178
|
+
});
|
|
160
179
|
return _el$2;
|
|
161
180
|
})();
|
|
162
181
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-block.js","names":["createMemo","createSignal","For","Show","splitProps","ComponentProps","JSX","Check","Copy","Button","cn","CodeBlockProps","Omit","code","language","highlight","Element","complete","title","actions","copyLabel","copiedLabel","lineHeight","showLineNumbers","tallThreshold","TALL_CODE_BLOCK_PX","copyText","text","Promise","navigator","clipboard","writeText","CodeBlock","props","local","rest","copied","setCopied","failed","setFailed","tall","setTall","label","regionLabel","lines","replace","split","copy","ok","setTimeout","Actions","_el$","_tmpl$","_$insert","_$createComponent","variant","size","aria-label","_$memo","undefined","onClick","children","when","fallback","_el$2","_tmpl$4","_el$3","firstChild","_el$4","_el$5","nextSibling","_$use","element","ref","observer","ResizeObserver","offsetHeight","observe","_$spread","_$mergeProps","class","_el$8","_tmpl$5","_el$9","each","line","index","_el$0","_tmpl$6","_el$1","_el$10","_$effect","_$p","_$setStyleProperty","_el$11","_tmpl$7","_tmpl$2","_el$7","_tmpl$3","_$setAttribute","InlineCode","_el$12","_tmpl$8","_$createComponent","_$memo","_$mergeProps","_$setStyleProperty","_$setAttribute"],"sources":["../../../../src/components/ui/code-block/code-block.tsx"],"sourcesContent":["import {\n createMemo,\n createSignal,\n For,\n Show,\n splitProps,\n type ComponentProps,\n type JSX,\n} from 'solid-js'\nimport { Check, Copy } from 'lucide-solid'\nimport { Button } from '../button'\nimport { cn } from '#lib/utils'\n\n/**\n * CodeBlock.\n *\n * A fenced block, rendered as a surface. The library ships no highlighter —\n * syntax colouring is a large dependency with its own theme format, and a\n * component that bundled one would force every consumer to pay for it. This\n * renders the block, its chrome, and its behaviour; a caller that wants\n * highlighting passes the result through `highlight`, which is the seam a\n * Shiki/Pierre/Prism integration plugs into.\n *\n * Four details are the reason this exists rather than a styled `<pre>`:\n *\n * **Copy confirms only on success.** `navigator.clipboard.writeText` rejects\n * outside a secure context, in a sandboxed frame, and when the document is not\n * focused. Flipping to a tick unconditionally confirms a copy that did not\n * happen, and the reader pastes something stale and does not know why. The\n * rejection is caught and the button reports the failure instead.\n *\n * **A tall block repeats its action row at the bottom.** Copying from a block\n * that is taller than the viewport otherwise costs a scroll back to the top and\n * another scroll down to where you were. The threshold is a fixed 480px rather\n * than viewport-relative so the behaviour does not change when the window is\n * resized.\n *\n * **The scroller is focusable and named.** A horizontally scrollable region has\n * to be reachable by keyboard — this is axe's `scrollable-region-focusable`, and\n * a region that scrolls but cannot be focused is a WCAG 2.1.1 failure. It is a\n * `region` with a label rather than a bare `tabindex`, so the tab stop announces\n * what it is.\n *\n * **Resolving a streaming block must not move the reader.** When `complete` is\n * false the block shows the code it has plus a generating indicator; the\n * indicator is inside the scroller's own box and does not add a line, because a\n * block that grows the moment its content resolves shifts everything below it.\n * If a caller's `highlight` changes the line box, that is the caller's height to\n * hold — `lineHeight` is a prop for exactly that reason.\n */\nexport type CodeBlockProps = Omit<ComponentProps<'div'>, 'children'> & {\n code: string\n /** The language, shown in the header and used as the region's label. */\n language?: string\n /** The code, transformed — highlighted HTML or a component. Omit for plain text. */\n highlight?: (code: string) => JSX.Element\n /** False while the block is still streaming. Adds a generating indicator. */\n complete?: boolean\n /** A filename or title, shown instead of the language. */\n title?: string\n /** Extra header controls, before the copy button. */\n actions?: JSX.Element\n /** Copy label. Override for a non-English application. */\n copyLabel?: string\n copiedLabel?: string\n /** Line height of the rendered code. Set it when `highlight` changes the box. */\n lineHeight?: string\n /** A line-number gutter. Off by default; a short snippet rarely needs one. */\n showLineNumbers?: boolean\n /** The height above which the action row is repeated below the block. */\n tallThreshold?: number\n}\n\n/** The height a block must exceed before its actions are repeated. */\nexport const TALL_CODE_BLOCK_PX = 480\n\n/**\n * Copy, reporting whether it worked. `navigator.clipboard` is absent in an\n * insecure context, and `writeText` rejects when the document is not focused —\n * both are normal in an embedded or iframed application, and neither should\n * silently look like success.\n */\nasync function copyText(text: string): Promise<boolean> {\n try {\n if (!navigator.clipboard) return false\n await navigator.clipboard.writeText(text)\n return true\n } catch {\n return false\n }\n}\n\nexport function CodeBlock(props: CodeBlockProps) {\n const [local, rest] = splitProps(props, [\n 'code',\n 'language',\n 'highlight',\n 'complete',\n 'title',\n 'actions',\n 'copyLabel',\n 'copiedLabel',\n 'lineHeight',\n 'showLineNumbers',\n 'tallThreshold',\n 'class',\n 'ref',\n ])\n\n const [copied, setCopied] = createSignal(false)\n const [failed, setFailed] = createSignal(false)\n const [tall, setTall] = createSignal(false)\n\n const label = () => local.title ?? local.language ?? 'code'\n const regionLabel = () => (local.language ? `${local.language} code` : 'code')\n const lines = createMemo(() => local.code.replace(/\\n$/, '').split('\\n'))\n\n const copy = async () => {\n const ok = await copyText(local.code)\n setCopied(ok)\n setFailed(!ok)\n if (ok) setTimeout(() => setCopied(false), 1500)\n }\n\n const Actions = () => (\n <div class=\"flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code:opacity-100 group-hover/code:opacity-100\">\n {local.actions}\n <Button\n variant=\"ghost\"\n size=\"icon-2xs\"\n aria-label={copied() ? (local.copiedLabel ?? 'Copied') : (local.copyLabel ?? 'Copy code')}\n title={failed() ? 'Copying is unavailable in this context' : undefined}\n onClick={() => void copy()}\n >\n <Show when={copied()} fallback={<Copy aria-hidden=\"true\" />}>\n <Check aria-hidden=\"true\" />\n </Show>\n </Button>\n </div>\n )\n\n return (\n <div\n class={cn('group/code overflow-hidden rounded-xl border border-border bg-card', local.class)}\n {...rest}\n ref={(element) => {\n // Composed rather than replaced: a caller's ref must survive.\n if (typeof local.ref === 'function') local.ref(element)\n else if (local.ref) local.ref = element\n // Measured rather than assumed: the caller controls the content, so only\n // the rendered box can say whether the action row has scrolled away.\n const observer = new ResizeObserver(() => {\n setTall(element.offsetHeight > (local.tallThreshold ?? TALL_CODE_BLOCK_PX))\n })\n observer.observe(element)\n }}\n >\n <div class=\"flex items-center justify-between gap-2 px-3 py-1\">\n <span class=\"truncate font-code text-code text-muted-foreground\">{label()}</span>\n <Actions />\n </div>\n <div\n class=\"scroll-fade overflow-x-auto\"\n // A region that scrolls must be focusable, and a tab stop needs a name.\n tabindex=\"0\"\n role=\"region\"\n aria-label={regionLabel()}\n >\n <Show\n when={local.highlight}\n fallback={\n <pre class=\"m-0 px-3 py-2\" style={{ 'line-height': local.lineHeight ?? '1.5' }}>\n <code class=\"font-code text-code\">\n <Show when={local.showLineNumbers} fallback={local.code.replace(/\\n$/, '')}>\n <For each={lines()}>\n {(line, index) => (\n <span class=\"grid grid-cols-[3ch_1fr] gap-3\">\n <span\n class=\"text-right text-muted-foreground select-none\"\n aria-hidden=\"true\"\n >\n {index() + 1}\n </span>\n <span>{line || ' '}</span>\n </span>\n )}\n </For>\n </Show>\n </code>\n </pre>\n }\n >\n {(highlight) => <div class=\"px-3 py-2\">{highlight()(local.code)}</div>}\n </Show>\n {/*\n In the scroller's box, and a single line: the indicator must not add\n height when it disappears, or a streaming block shifts everything below\n it as it resolves.\n */}\n <Show when={!local.complete}>\n <p class=\"px-3 pb-2 text-xs text-muted-foreground italic\">Generating…</p>\n </Show>\n </div>\n <Show when={tall()}>\n <div\n data-code-block-footer=\"\"\n class=\"flex items-center justify-end border-t border-transparent px-3 py-1 transition-colors group-focus-within/code:border-border group-hover/code:border-border\"\n >\n <Actions />\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * InlineCode.\n *\n * The `code` inside a sentence. It is a component rather than a class because it\n * has to hold three properties at once: it uses the shared code family and size\n * preference, and it wraps rather than overflows a narrow column\n * (`overflow-wrap: anywhere` — an identifier with\n * no spaces is one long token), and it must be legible against whatever surface\n * it lands on, which is why the fill is a token and not a fixed grey.\n */\nexport function InlineCode(props: ComponentProps<'code'>) {\n const [local, rest] = splitProps(props, ['class'])\n\n return (\n <code\n class={cn(\n 'rounded-sm bg-muted px-[0.4em] py-[0.15em] font-code text-code text-foreground [overflow-wrap:anywhere]',\n local.class\n )}\n {...rest}\n />\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"code-block.js","names":["createMemo","createSignal","For","Show","splitProps","ComponentProps","JSX","Check","Copy","Button","cn","CodeBlockProps","Omit","code","language","highlight","Element","complete","title","actions","copyLabel","copiedLabel","lineHeight","showLineNumbers","tallThreshold","maxHeight","wrap","TALL_CODE_BLOCK_PX","copyText","text","Promise","navigator","clipboard","writeText","CodeBlock","props","local","rest","copied","setCopied","failed","setFailed","tall","setTall","label","regionLabel","lines","replace","split","copy","ok","setTimeout","Actions","_el$","_tmpl$","_$insert","_$createComponent","variant","size","aria-label","_$memo","undefined","onClick","children","when","fallback","_el$2","_tmpl$4","_el$3","firstChild","_el$4","_el$5","nextSibling","_$use","element","ref","observer","ResizeObserver","offsetHeight","observe","_$spread","_$mergeProps","class","_el$8","_tmpl$5","_el$9","each","line","index","_el$0","_tmpl$6","_el$1","_el$10","_$effect","_$className","_$p","_$setStyleProperty","_el$11","_tmpl$7","_tmpl$2","_el$7","_tmpl$3","_p$","_v$","_v$2","_v$3","_v$4","e","_$setAttribute","t","a","_$style","o","InlineCode","_el$12","_tmpl$8","_$createComponent","_$memo","_$mergeProps","_$className","_$setStyleProperty","_$setAttribute","_$style"],"sources":["../../../../src/components/ui/code-block/code-block.tsx"],"sourcesContent":["import {\n createMemo,\n createSignal,\n For,\n Show,\n splitProps,\n type ComponentProps,\n type JSX,\n} from 'solid-js'\nimport { Check, Copy } from 'lucide-solid'\nimport { Button } from '../button'\nimport { cn } from '#lib/utils'\n\n/**\n * CodeBlock.\n *\n * A fenced block, rendered as a surface. The library ships no highlighter —\n * syntax colouring is a large dependency with its own theme format, and a\n * component that bundled one would force every consumer to pay for it. This\n * renders the block, its chrome, and its behaviour; a caller that wants\n * highlighting passes the result through `highlight`, which is the seam a\n * Shiki/Pierre/Prism integration plugs into.\n *\n * Four details are the reason this exists rather than a styled `<pre>`:\n *\n * **Copy confirms only on success.** `navigator.clipboard.writeText` rejects\n * outside a secure context, in a sandboxed frame, and when the document is not\n * focused. Flipping to a tick unconditionally confirms a copy that did not\n * happen, and the reader pastes something stale and does not know why. The\n * rejection is caught and the button reports the failure instead.\n *\n * **A tall block repeats its action row at the bottom.** Copying from a block\n * that is taller than the viewport otherwise costs a scroll back to the top and\n * another scroll down to where you were. The threshold is a fixed 480px rather\n * than viewport-relative so the behaviour does not change when the window is\n * resized.\n *\n * **The scroller is focusable and named.** A horizontally scrollable region has\n * to be reachable by keyboard — this is axe's `scrollable-region-focusable`, and\n * a region that scrolls but cannot be focused is a WCAG 2.1.1 failure. It is a\n * `region` with a label rather than a bare `tabindex`, so the tab stop announces\n * what it is.\n *\n * **Resolving a streaming block must not move the reader.** When `complete` is\n * false the block shows the code it has plus a generating indicator; the\n * indicator is inside the scroller's own box and does not add a line, because a\n * block that grows the moment its content resolves shifts everything below it.\n * If a caller's `highlight` changes the line box, that is the caller's height to\n * hold — `lineHeight` is a prop for exactly that reason.\n */\nexport type CodeBlockProps = Omit<ComponentProps<'div'>, 'children'> & {\n code: string\n /** The language, shown in the header and used as the region's label. */\n language?: string\n /** The code, transformed — highlighted HTML or a component. Omit for plain text. */\n highlight?: (code: string) => JSX.Element\n /** False while the block is still streaming. Adds a generating indicator. */\n complete?: boolean\n /** A filename or title, shown instead of the language. */\n title?: string\n /** Extra header controls, before the copy button. */\n actions?: JSX.Element\n /** Copy label. Override for a non-English application. */\n copyLabel?: string\n copiedLabel?: string\n /** Line height of the rendered code. Set it when `highlight` changes the box. */\n lineHeight?: string\n /** A line-number gutter. Off by default; a short snippet rarely needs one. */\n showLineNumbers?: boolean\n /** The height above which the action row is repeated below the block. */\n tallThreshold?: number\n /**\n * Cap the code body's height and scroll it vertically past that. A number is\n * pixels; a string is any CSS length (`'20rem'`, `'50vh'`). Unset, the block\n * grows with its content.\n */\n maxHeight?: number | string\n /**\n * Soft-wrap long lines inside the block instead of scrolling sideways. Off by\n * default: wrapped code loses its column alignment, so it is for prose-like\n * output (logs, long commands) in a narrow column. Wraps the plain renderer and\n * any `pre` inside `highlight` output; line numbers stay on the first visual line.\n */\n wrap?: boolean\n}\n\n/** The height a block must exceed before its actions are repeated. */\nexport const TALL_CODE_BLOCK_PX = 480\n\n/**\n * Copy, reporting whether it worked. `navigator.clipboard` is absent in an\n * insecure context, and `writeText` rejects when the document is not focused —\n * both are normal in an embedded or iframed application, and neither should\n * silently look like success.\n */\nasync function copyText(text: string): Promise<boolean> {\n try {\n if (!navigator.clipboard) return false\n await navigator.clipboard.writeText(text)\n return true\n } catch {\n return false\n }\n}\n\nexport function CodeBlock(props: CodeBlockProps) {\n const [local, rest] = splitProps(props, [\n 'code',\n 'language',\n 'highlight',\n 'complete',\n 'title',\n 'actions',\n 'copyLabel',\n 'copiedLabel',\n 'lineHeight',\n 'showLineNumbers',\n 'tallThreshold',\n 'maxHeight',\n 'wrap',\n 'class',\n 'ref',\n ])\n\n const [copied, setCopied] = createSignal(false)\n const [failed, setFailed] = createSignal(false)\n const [tall, setTall] = createSignal(false)\n\n const label = () => local.title ?? local.language ?? 'code'\n const regionLabel = () => (local.language ? `${local.language} code` : 'code')\n const lines = createMemo(() => local.code.replace(/\\n$/, '').split('\\n'))\n const maxHeight = () =>\n typeof local.maxHeight === 'number' ? `${local.maxHeight}px` : local.maxHeight\n\n const copy = async () => {\n const ok = await copyText(local.code)\n setCopied(ok)\n setFailed(!ok)\n if (ok) setTimeout(() => setCopied(false), 1500)\n }\n\n const Actions = () => (\n <div class=\"flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code:opacity-100 group-hover/code:opacity-100\">\n {local.actions}\n <Button\n variant=\"ghost\"\n size=\"icon-2xs\"\n aria-label={copied() ? (local.copiedLabel ?? 'Copied') : (local.copyLabel ?? 'Copy code')}\n title={failed() ? 'Copying is unavailable in this context' : undefined}\n onClick={() => void copy()}\n >\n <Show when={copied()} fallback={<Copy aria-hidden=\"true\" />}>\n <Check aria-hidden=\"true\" />\n </Show>\n </Button>\n </div>\n )\n\n return (\n <div\n class={cn('group/code overflow-hidden rounded-xl border border-border bg-card', local.class)}\n {...rest}\n ref={(element) => {\n // Composed rather than replaced: a caller's ref must survive.\n if (typeof local.ref === 'function') local.ref(element)\n else if (local.ref) local.ref = element\n // Measured rather than assumed: the caller controls the content, so only\n // the rendered box can say whether the action row has scrolled away.\n const observer = new ResizeObserver(() => {\n setTall(element.offsetHeight > (local.tallThreshold ?? TALL_CODE_BLOCK_PX))\n })\n observer.observe(element)\n }}\n >\n <div class=\"flex items-center justify-between gap-2 px-3 py-1\">\n <span class=\"truncate font-code text-code text-muted-foreground\">{label()}</span>\n <Actions />\n </div>\n <div\n data-slot=\"code-block-body\"\n data-wrap={local.wrap ? '' : undefined}\n class={cn('scroll-fade overflow-x-auto', {\n // A capped body scrolls on both axes; the region is already a tab stop,\n // so the keyboard reaches the vertical overflow the same way.\n 'overflow-y-auto': local.maxHeight !== undefined,\n '[&_pre]:whitespace-pre-wrap [&_pre]:[overflow-wrap:anywhere]': local.wrap,\n })}\n style={maxHeight() === undefined ? undefined : { 'max-height': maxHeight() }}\n // A region that scrolls must be focusable, and a tab stop needs a name.\n tabindex=\"0\"\n role=\"region\"\n aria-label={regionLabel()}\n >\n <Show\n when={local.highlight}\n fallback={\n <pre class=\"m-0 px-3 py-2\" style={{ 'line-height': local.lineHeight ?? '1.5' }}>\n <code class=\"font-code text-code\">\n <Show when={local.showLineNumbers} fallback={local.code.replace(/\\n$/, '')}>\n <For each={lines()}>\n {(line, index) => (\n <span class=\"grid grid-cols-[3ch_1fr] gap-3\">\n <span\n class=\"text-right text-muted-foreground select-none\"\n aria-hidden=\"true\"\n >\n {index() + 1}\n </span>\n <span class={cn({ 'min-w-0': local.wrap })}>{line || ' '}</span>\n </span>\n )}\n </For>\n </Show>\n </code>\n </pre>\n }\n >\n {(highlight) => <div class=\"px-3 py-2\">{highlight()(local.code)}</div>}\n </Show>\n {/*\n In the scroller's box, and a single line: the indicator must not add\n height when it disappears, or a streaming block shifts everything below\n it as it resolves.\n */}\n <Show when={!local.complete}>\n <p class=\"px-3 pb-2 text-xs text-muted-foreground italic\">Generating…</p>\n </Show>\n </div>\n <Show when={tall()}>\n <div\n data-code-block-footer=\"\"\n class=\"flex items-center justify-end border-t border-transparent px-3 py-1 transition-colors group-focus-within/code:border-border group-hover/code:border-border\"\n >\n <Actions />\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * InlineCode.\n *\n * The `code` inside a sentence. It is a component rather than a class because it\n * has to hold three properties at once: it uses the shared code family and size\n * preference, and it wraps rather than overflows a narrow column\n * (`overflow-wrap: anywhere` — an identifier with\n * no spaces is one long token), and it must be legible against whatever surface\n * it lands on, which is why the fill is a token and not a fixed grey.\n */\nexport function InlineCode(props: ComponentProps<'code'>) {\n const [local, rest] = splitProps(props, ['class'])\n\n return (\n <code\n class={cn(\n 'rounded-sm bg-muted px-[0.4em] py-[0.15em] font-code text-code text-foreground [overflow-wrap:anywhere]',\n local.class\n )}\n {...rest}\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;AAuFA,IAAa2B,qBAAqB;;;;;;;AAQlC,eAAeC,SAASC,MAAgC;CACtD,IAAI;EACF,IAAI,CAACE,UAAUC,WAAW,OAAO;EACjC,MAAMD,UAAUC,UAAUC,UAAUJ,IAAI;EACxC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgBK,UAAUC,OAAuB;CAC/C,MAAM,CAACC,OAAOC,QAAQjC,WAAW+B,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAK,CACN;CAED,MAAM,CAACG,QAAQC,aAAatC,aAAa,KAAK;CAC9C,MAAM,CAACuC,QAAQC,aAAaxC,aAAa,KAAK;CAC9C,MAAM,CAACyC,MAAMC,WAAW1C,aAAa,KAAK;CAE1C,MAAM2C,cAAcR,MAAMlB,SAASkB,MAAMtB,YAAY;CACrD,MAAM+B,oBAAqBT,MAAMtB,WAAW,GAAGsB,MAAMtB,SAAQ,SAAU;CACvE,MAAMgC,QAAQ9C,iBAAiBoC,MAAMvB,KAAKkC,QAAQ,OAAO,EAAE,CAAC,CAACC,MAAM,IAAI,CAAC;CACxE,MAAMvB,kBACJ,OAAOW,MAAMX,cAAc,WAAW,GAAGW,MAAMX,UAAS,MAAOW,MAAMX;CAEvE,MAAMwB,OAAO,YAAY;EACvB,MAAMC,KAAK,MAAMtB,SAASQ,MAAMvB,IAAI;EACpC0B,UAAUW,EAAE;EACZT,UAAU,CAACS,EAAE;EACb,IAAIA,IAAIC,iBAAiBZ,UAAU,KAAK,GAAG,IAAI;CACjD;CAEA,MAAMa,uBAAU;EAAA,IAAAC,OAAAC,OAAA;EAAAC,OAAAF,YAEXjB,MAAMjB,SAAO,IAAA;EAAAoC,OAAAF,MAAAG,gBACb/C,QAAM;GACLgD,SAAO;GACPC,MAAI;GAAA,KAAA,gBAAA;IAAA,OACQE,WAAA,CAAA,CAAAtB,OAAO,CAAC,CAAA,CAAA,IAAIF,MAAMf,eAAe,WAAae,MAAMhB,aAAa;GAAY;GAAA,IACzFF,QAAK;IAAA,OAAEsB,OAAO,IAAI,2CAA2CqB,KAAAA;GAAS;GACtEC,eAAe,KAAKb,KAAK;GAAC,IAAAc,WAAA;IAAA,OAAAP,gBAEzBrD,MAAI;KAAA,IAAC6D,OAAI;MAAA,OAAE1B,OAAO;KAAC;KAAA,IAAE2B,WAAQ;MAAA,OAAAT,gBAAGhD,MAAI,EAAA,eAAA,OAAA,CAAA;KAAA;KAAA,IAAAuD,WAAA;MAAA,OAAAP,gBAClCjD,OAAK,EAAA,eAAA,OAAA,CAAA;KAAA;IAAA,CAAA;GAAA;EAAA,CAAA,GAAA,IAAA;EAAA,OAAA8C;CAAA,EAAA,CAAA;CAMd,cAAA;EAAA,IAAAa,QAAAC,QAAA,GAAAC,QAAAF,MAAAG,YAAAC,QAAAF,MAAAC,YAAAE,QAAAH,MAAAI;EAAAC,KAIUC,YAAY;GAEhB,IAAI,OAAOtC,MAAMuC,QAAQ,YAAYvC,MAAMuC,IAAID,OAAO;QACjD,IAAItC,MAAMuC,KAAKvC,MAAMuC,MAAMD;GAMhCE,IAHqBC,qBAAqB;IACxClC,QAAQ+B,QAAQI,gBAAgB1C,MAAMZ,iBAAAA,IAAoC;GAC5E,CACAoD,CAAAA,CAASG,QAAQL,OAAO;EAC1B,GAACR,KAAA;EAAAc,OAAAd,OAAAe,WAAA,EAAA,KAAA,WAAA;GAAA,OAZMvE,GAAG,sEAAsE0B,MAAM8C,KAAK;EAAC,EAAA,GACxF7C,IAAI,GAAA,OAAA,IAAA;EAAAkB,OAAAe,OAc4D1B,KAAK;EAAAW,OAAAa,OAAAZ,gBACtEJ,SAAO,CAAA,CAAA,GAAA,IAAA;EAAAG,OAAAgB,OAAAf,gBAiBPrD,MAAI;GAAA,IACH6D,OAAI;IAAA,OAAE5B,MAAMrB;GAAS;GAAA,IACrBkD,WAAQ;IAAA,cAAA;KAAA,IAAAkB,QAAAC,QAAA,GAAAC,QAAAF,MAAAd;KAAAd,OAAA8B,OAAA7B,gBAGDrD,MAAI;MAAA,IAAC6D,OAAI;OAAA,OAAE5B,MAAMb;MAAe;MAAA,IAAE0C,WAAQ;OAAA,OAAE7B,MAAMvB,KAAKkC,QAAQ,OAAO,EAAE;MAAC;MAAA,IAAAgB,WAAA;OAAA,OAAAP,gBACvEtD,KAAG;QAAA,IAACoF,OAAI;SAAA,OAAExC,MAAM;QAAC;QAAAiB,WACdwB,MAAMC,iBAAK;SAAA,IAAAC,QAAAC,QAAA,GAAAC,QAAAF,MAAApB,YAAAuB,SAAAD,MAAAnB;SAAAjB,OAAAoC,aAMNH,MAAM,IAAI,CAAC;SAAAjC,OAAAqC,QAE+BL,QAAQ,GAAG;SAAAM,aAAAC,UAAAF,QAA3ClF,GAAG,EAAE,WAAW0B,MAAMV,KAAK,CAAC,CAAC,CAAA;SAAA,OAAA+D;QAAA,EAAA,CAAA;OAE7C,CAAA;MAAA;KAAA,CAAA,CAAA;KAAAI,QAAAE,QAAAC,iBAAAb,OAAA,eAd0C/C,MAAMd,cAAc,KAAK,CAAA;KAAA,OAAA6D;IAAA,EAAA,CAAA;GAAA;GAAApB,WAqB5EhD,qBAAS;IAAA,IAAAkF,SAAAC,QAAA;IAAA3C,OAAA0C,cAA6BlF,UAAU,CAAC,CAACqB,MAAMvB,IAAI,CAAC;IAAA,OAAAoF;GAAA,EAAA,CAAA;EAAO,CAAA,GAAA,IAAA;EAAA1C,OAAAgB,OAAAf,gBAOvErD,MAAI;GAAA,IAAC6D,OAAI;IAAA,OAAE,CAAC5B,MAAMnB;GAAQ;GAAA,IAAA8C,WAAA;IAAA,OAAAoC,QAAA;GAAA;EAAA,CAAA,GAAA,IAAA;EAAA5C,OAAAW,OAAAV,gBAI5BrD,MAAI;GAAA,IAAC6D,OAAI;IAAA,OAAEtB,KAAK;GAAC;GAAA,IAAAqB,WAAA;IAAA,IAAAqC,QAAAC,QAAA;IAAA9C,OAAA6C,OAAA5C,gBAKbJ,SAAO,CAAA,CAAA,CAAA;IAAA,OAAAgD;GAAA;EAAA,CAAA,GAAA,IAAA;EAAAP,QAAAS,QAAA;GAAA,IAAAC,MArDCnE,MAAMV,OAAO,KAAKmC,KAAAA,GAAS2C,OAC/B9F,GAAG,+BAA+B;IAGvC,mBAAmB0B,MAAMX,cAAcoC,KAAAA;IACvC,gEAAgEzB,MAAMV;GACxE,CAAC,GAAC+E,OACKhF,UAAU,MAAMoC,KAAAA,IAAYA,KAAAA,IAAY,EAAE,cAAcpC,UAAU,EAAE,GAACiF,OAIhE7D,YAAY;GAAC0D,QAAAD,IAAAK,KAAAC,aAAArC,OAAA,aAAA+B,IAAAK,IAAAJ,GAAA;GAAAC,SAAAF,IAAAO,KAAAf,UAAAvB,OAAA+B,IAAAO,IAAAL,IAAA;GAAAF,IAAAQ,IAAAC,MAAAxC,OAAAkC,MAAAH,IAAAQ,CAAA;GAAAJ,SAAAJ,IAAAU,KAAAJ,aAAArC,OAAA,cAAA+B,IAAAU,IAAAN,IAAA;GAAA,OAAAJ;EAAA,GAAA;GAAAK,GAAA9C,KAAAA;GAAAgD,GAAAhD,KAAAA;GAAAiD,GAAAjD,KAAAA;GAAAmD,GAAAnD,KAAAA;EAAA,CAAA;EAAA,OAAAK;CAAA,EAAA,CAAA;AA+CjC;;;;;;;;;;;AAYA,SAAgB+C,WAAW9E,OAA+B;CACxD,MAAM,CAACC,OAAOC,QAAQjC,WAAW+B,OAAO,CAAC,OAAO,CAAC;CAEjD,cAAA;EAAA,IAAA+E,SAAAC,QAAA;EAAAnC,OAAAkC,QAAAjC,WAAA,EAAA,KAAA,WAAA;GAAA,OAEWvE,GACL,2GACA0B,MAAM8C,KACR;EAAC,EAAA,GACG7C,IAAI,GAAA,OAAA,KAAA;EAAA,OAAA6E;CAAA,EAAA,CAAA;AAGd"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, itemVariants, type ItemProps, } from './item';
|
|
1
|
+
export { Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, itemVariants, type ItemDescriptionProps, type ItemProps, } from './item';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/item/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,eAAe,EACf,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EACT,YAAY,EACZ,KAAK,SAAS,GACf,MAAM,QAAQ,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/item/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,eAAe,EACf,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EACT,YAAY,EACZ,KAAK,oBAAoB,EACzB,KAAK,SAAS,GACf,MAAM,QAAQ,CAAA"}
|
|
@@ -28,10 +28,26 @@ export type ItemProps = ComponentProps<'div'> & ItemVariantProps & {
|
|
|
28
28
|
media?: JSX.Element;
|
|
29
29
|
/** A trailing slot: a badge, a timestamp, a menu trigger. */
|
|
30
30
|
trailing?: JSX.Element;
|
|
31
|
+
/**
|
|
32
|
+
* Move the trailing slot onto its own line, end-aligned under the body, once
|
|
33
|
+
* the item is narrower than 28rem — the width `ListRow` stacks its trailing
|
|
34
|
+
* controls at. Off by default: a row of badges and an icon button fits beside
|
|
35
|
+
* the body, but a pair of labelled buttons squeezes a phone-width body to a
|
|
36
|
+
* few characters.
|
|
37
|
+
*/
|
|
38
|
+
stackTrailing?: boolean;
|
|
31
39
|
};
|
|
32
40
|
export declare function Item(props: ItemProps): JSX.Element;
|
|
33
41
|
export declare function ItemTitle(props: ComponentProps<'div'>): JSX.Element;
|
|
34
|
-
export
|
|
42
|
+
export type ItemDescriptionProps = ComponentProps<'div'> & {
|
|
43
|
+
/**
|
|
44
|
+
* Clamp to two lines. On by default, so a grid of items keeps one height; pass
|
|
45
|
+
* `false` where the description is the content — a setting's explanation, an
|
|
46
|
+
* error the reader has to read in full.
|
|
47
|
+
*/
|
|
48
|
+
clamp?: boolean;
|
|
49
|
+
};
|
|
50
|
+
export declare function ItemDescription(props: ItemDescriptionProps): JSX.Element;
|
|
35
51
|
/**
|
|
36
52
|
* A group of items with an optional heading. It is a real `role="list"` with
|
|
37
53
|
* `role="listitem"` children, so a screen reader reports the count — which is how a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/item/item.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAGtD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY;;;8EAgBvB,CAAA;AAEF,KAAK,gBAAgB,GAAG,YAAY,CAAC,OAAO,YAAY,CAAC,CAAA;AAEzD,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,GAC3C,gBAAgB,GAAG;IACjB,qDAAqD;IACrD,KAAK,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACnB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/item/item.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAGtD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY;;;8EAgBvB,CAAA;AAEF,KAAK,gBAAgB,GAAG,YAAY,CAAC,OAAO,YAAY,CAAC,CAAA;AAEzD,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,GAC3C,gBAAgB,GAAG;IACjB,qDAAqD;IACrD,KAAK,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACnB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACtB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAEH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,eAiDpC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eAGrD;AAED,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG;IACzD;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,eAa1D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kCAAkC,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CACtD,eAgBF;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,eAE9C;AAED,wDAAwD;AACxD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eASrD"}
|