@tooee/layout 0.1.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 +5 -0
- package/dist/AppLayout.d.ts +27 -0
- package/dist/AppLayout.d.ts.map +1 -0
- package/dist/AppLayout.js +13 -0
- package/dist/AppLayout.js.map +1 -0
- package/dist/SearchBar.d.ts +10 -0
- package/dist/SearchBar.d.ts.map +1 -0
- package/dist/SearchBar.js +18 -0
- package/dist/SearchBar.js.map +1 -0
- package/dist/StatusBar.d.ts +10 -0
- package/dist/StatusBar.d.ts.map +1 -0
- package/dist/StatusBar.js +14 -0
- package/dist/StatusBar.js.map +1 -0
- package/dist/TitleBar.d.ts +7 -0
- package/dist/TitleBar.d.ts.map +1 -0
- package/dist/TitleBar.js +14 -0
- package/dist/TitleBar.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
- package/src/AppLayout.tsx +74 -0
- package/src/SearchBar.tsx +55 -0
- package/src/StatusBar.tsx +33 -0
- package/src/TitleBar.tsx +25 -0
- package/src/index.ts +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ReactNode, RefObject } from "react";
|
|
2
|
+
import type { ScrollBoxRenderable } from "@opentui/core";
|
|
3
|
+
import type { StatusBarItem } from "./StatusBar.jsx";
|
|
4
|
+
import type { SearchBarProps } from "./SearchBar.jsx";
|
|
5
|
+
export interface AppLayoutSearchBar extends SearchBarProps {
|
|
6
|
+
active: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface AppLayoutProps {
|
|
9
|
+
titleBar?: {
|
|
10
|
+
title: string;
|
|
11
|
+
subtitle?: string;
|
|
12
|
+
};
|
|
13
|
+
statusBar: {
|
|
14
|
+
items: StatusBarItem[];
|
|
15
|
+
};
|
|
16
|
+
scrollRef?: RefObject<ScrollBoxRenderable | null>;
|
|
17
|
+
scrollProps?: {
|
|
18
|
+
stickyScroll?: boolean;
|
|
19
|
+
stickyStart?: "bottom" | "top";
|
|
20
|
+
focused?: boolean;
|
|
21
|
+
};
|
|
22
|
+
searchBar?: AppLayoutSearchBar;
|
|
23
|
+
overlay?: ReactNode;
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
export declare function AppLayout({ titleBar, statusBar, scrollRef, scrollProps, searchBar, overlay, children, }: AppLayoutProps): ReactNode;
|
|
27
|
+
//# sourceMappingURL=AppLayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLayout.d.ts","sourceRoot":"","sources":["../src/AppLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAGxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIrD,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/C,SAAS,EAAE;QAAE,KAAK,EAAE,aAAa,EAAE,CAAA;KAAE,CAAA;IACrC,SAAS,CAAC,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;IACjD,WAAW,CAAC,EAAE;QACZ,YAAY,CAAC,EAAE,OAAO,CAAA;QACtB,WAAW,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;QAC9B,OAAO,CAAC,EAAE,OAAO,CAAA;KAClB,CAAA;IACD,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,SAAS,EACT,OAAO,EACP,QAAQ,GACT,EAAE,cAAc,aAqChB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { TitleBar } from "./TitleBar.jsx";
|
|
3
|
+
import { StatusBar } from "./StatusBar.jsx";
|
|
4
|
+
import { SearchBar } from "./SearchBar.jsx";
|
|
5
|
+
import { useTheme } from "@tooee/themes";
|
|
6
|
+
import { useCurrentOverlay } from "@tooee/overlays";
|
|
7
|
+
export function AppLayout({ titleBar, statusBar, scrollRef, scrollProps, searchBar, overlay, children, }) {
|
|
8
|
+
const { theme } = useTheme();
|
|
9
|
+
const contextOverlay = useCurrentOverlay();
|
|
10
|
+
const activeOverlay = overlay ?? contextOverlay;
|
|
11
|
+
return (_jsxs("box", { flexDirection: "column", width: "100%", height: "100%", backgroundColor: theme.background, children: [titleBar && _jsx(TitleBar, { title: titleBar.title, subtitle: titleBar.subtitle }), _jsxs("box", { style: { flexGrow: 1, position: "relative" }, children: [_jsx("scrollbox", { ref: scrollRef, style: { flexGrow: 1 }, stickyScroll: scrollProps?.stickyScroll, stickyStart: scrollProps?.stickyStart, focused: scrollProps?.focused ?? true, children: children }), activeOverlay && (_jsx("box", { position: "absolute", left: 0, top: 0, width: "100%", height: "100%", children: activeOverlay }))] }), searchBar?.active ? (_jsx(SearchBar, { query: searchBar.query, onQueryChange: searchBar.onQueryChange, onSubmit: searchBar.onSubmit, onCancel: searchBar.onCancel, matchCount: searchBar.matchCount, currentMatch: searchBar.currentMatch })) : (_jsx(StatusBar, { items: statusBar.items }))] }));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=AppLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLayout.js","sourceRoot":"","sources":["../src/AppLayout.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAoBnD,MAAM,UAAU,SAAS,CAAC,EACxB,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,SAAS,EACT,OAAO,EACP,QAAQ,GACO;IACf,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC5B,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAA;IAC1C,MAAM,aAAa,GAAG,OAAO,IAAI,cAAc,CAAA;IAC/C,OAAO,CACL,eAAK,aAAa,EAAC,QAAQ,EAAC,KAAK,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,EAAC,eAAe,EAAE,KAAK,CAAC,UAAU,aACrF,QAAQ,IAAI,KAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAAI,EAC7E,eAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,aAC/C,oBACE,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EACtB,YAAY,EAAE,WAAW,EAAE,YAAY,EACvC,WAAW,EAAE,WAAW,EAAE,WAAW,EACrC,OAAO,EAAE,WAAW,EAAE,OAAO,IAAI,IAAI,YAEpC,QAAQ,GACC,EACX,aAAa,IAAI,CAChB,cAAK,QAAQ,EAAC,UAAU,EAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,YACjE,aAAa,GACV,CACP,IACG,EACL,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CACnB,KAAC,SAAS,IACR,KAAK,EAAE,SAAS,CAAC,KAAK,EACtB,aAAa,EAAE,SAAS,CAAC,aAAa,EACtC,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAC5B,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAC5B,UAAU,EAAE,SAAS,CAAC,UAAU,EAChC,YAAY,EAAE,SAAS,CAAC,YAAY,GACpC,CACH,CAAC,CAAC,CAAC,CACF,KAAC,SAAS,IAAC,KAAK,EAAE,SAAS,CAAC,KAAK,GAAI,CACtC,IACG,CACP,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface SearchBarProps {
|
|
2
|
+
query: string;
|
|
3
|
+
onQueryChange: (query: string) => void;
|
|
4
|
+
onSubmit: () => void;
|
|
5
|
+
onCancel: () => void;
|
|
6
|
+
matchCount?: number;
|
|
7
|
+
currentMatch?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function SearchBar({ query, onQueryChange, onSubmit, onCancel: _onCancel, matchCount, currentMatch, }: SearchBarProps): import("react").ReactNode;
|
|
10
|
+
//# sourceMappingURL=SearchBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../src/SearchBar.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EAAE,SAAS,EACnB,UAAU,EACV,YAAY,GACb,EAAE,cAAc,6BAoChB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { useTheme } from "@tooee/themes";
|
|
3
|
+
export function SearchBar({ query, onQueryChange, onSubmit, onCancel: _onCancel, matchCount, currentMatch, }) {
|
|
4
|
+
const { theme } = useTheme();
|
|
5
|
+
const matchDisplay = matchCount !== undefined && matchCount > 0
|
|
6
|
+
? `${(currentMatch ?? 0) + 1}/${matchCount}`
|
|
7
|
+
: matchCount === 0 && query.length > 0
|
|
8
|
+
? "No matches"
|
|
9
|
+
: "";
|
|
10
|
+
return (_jsxs("box", { style: {
|
|
11
|
+
flexDirection: "row",
|
|
12
|
+
flexShrink: 0,
|
|
13
|
+
backgroundColor: theme.backgroundPanel,
|
|
14
|
+
paddingLeft: 1,
|
|
15
|
+
paddingRight: 1,
|
|
16
|
+
}, children: [_jsx("text", { content: "/", style: { fg: theme.accent } }), _jsx("input", { value: query, focused: true, onInput: onQueryChange, onSubmit: onSubmit, backgroundColor: "transparent", focusedBackgroundColor: "transparent", textColor: theme.text, cursorColor: theme.accent, cursorStyle: { style: "line", blinking: true }, style: { flexGrow: 1 } }), matchDisplay ? _jsx("text", { content: ` ${matchDisplay}`, style: { fg: theme.textMuted } }) : null] }));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=SearchBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchBar.js","sourceRoot":"","sources":["../src/SearchBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAWxC,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EAAE,SAAS,EACnB,UAAU,EACV,YAAY,GACG;IACf,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE5B,MAAM,YAAY,GAChB,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG,CAAC;QACxC,CAAC,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,UAAU,EAAE;QAC5C,CAAC,CAAC,UAAU,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YACpC,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,EAAE,CAAA;IAEV,OAAO,CACL,eACE,KAAK,EAAE;YACL,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,CAAC;YACb,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,CAAC;SAChB,aAED,eAAM,OAAO,EAAC,GAAG,EAAC,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,GAAI,EACjD,gBACE,KAAK,EAAE,KAAK,EACZ,OAAO,QACP,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,eAAe,EAAC,aAAa,EAC7B,sBAAsB,EAAC,aAAa,EACpC,SAAS,EAAE,KAAK,CAAC,IAAI,EACrB,WAAW,EAAE,KAAK,CAAC,MAAM,EACzB,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAC9C,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,GACtB,EACD,YAAY,CAAC,CAAC,CAAC,eAAM,OAAO,EAAE,IAAI,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,GAAI,CAAC,CAAC,CAAC,IAAI,IACxF,CACP,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface StatusBarProps {
|
|
2
|
+
items: StatusBarItem[];
|
|
3
|
+
}
|
|
4
|
+
export interface StatusBarItem {
|
|
5
|
+
label: string;
|
|
6
|
+
value?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function StatusBar({ items }: StatusBarProps): import("react").ReactNode;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=StatusBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../src/StatusBar.tsx"],"names":[],"mappings":"AAEA,UAAU,cAAc;IACtB,KAAK,EAAE,aAAa,EAAE,CAAA;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,cAAc,6BAqBlD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { useTheme } from "@tooee/themes";
|
|
3
|
+
export function StatusBar({ items }) {
|
|
4
|
+
const { theme } = useTheme();
|
|
5
|
+
return (_jsx("box", { style: {
|
|
6
|
+
flexDirection: "row",
|
|
7
|
+
flexShrink: 0,
|
|
8
|
+
backgroundColor: theme.backgroundPanel,
|
|
9
|
+
padding: 0,
|
|
10
|
+
paddingLeft: 1,
|
|
11
|
+
paddingRight: 1,
|
|
12
|
+
}, children: items.map((item, index) => (_jsxs("box", { style: { marginRight: 2, flexDirection: "row" }, children: [_jsx("text", { content: item.label, style: { fg: theme.textMuted } }), item.value && _jsx("text", { content: ` ${item.value}`, style: { fg: theme.text } })] }, index))) }));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=StatusBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../src/StatusBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAWxC,MAAM,UAAU,SAAS,CAAC,EAAE,KAAK,EAAkB;IACjD,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC5B,OAAO,CACL,cACE,KAAK,EAAE;YACL,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,CAAC;YACb,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,CAAC;SAChB,YAEA,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC1B,eAAiB,KAAK,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,aAC9D,eAAM,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,GAAI,EAC5D,IAAI,CAAC,KAAK,IAAI,eAAM,OAAO,EAAE,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,GAAI,KAFrE,KAAK,CAGT,CACP,CAAC,GACE,CACP,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TitleBar.d.ts","sourceRoot":"","sources":["../src/TitleBar.tsx"],"names":[],"mappings":"AAEA,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,aAAa,6BAiB1D"}
|
package/dist/TitleBar.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { useTheme } from "@tooee/themes";
|
|
3
|
+
export function TitleBar({ title, subtitle }) {
|
|
4
|
+
const { theme } = useTheme();
|
|
5
|
+
return (_jsxs("box", { style: {
|
|
6
|
+
flexDirection: "row",
|
|
7
|
+
flexShrink: 0,
|
|
8
|
+
backgroundColor: theme.backgroundPanel,
|
|
9
|
+
padding: 0,
|
|
10
|
+
paddingLeft: 1,
|
|
11
|
+
paddingRight: 1,
|
|
12
|
+
}, children: [_jsx("text", { content: title, style: { fg: theme.primary } }), subtitle && _jsx("text", { content: ` — ${subtitle}`, style: { fg: theme.textMuted } })] }));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=TitleBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TitleBar.js","sourceRoot":"","sources":["../src/TitleBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAOxC,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAiB;IACzD,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC5B,OAAO,CACL,eACE,KAAK,EAAE;YACL,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,CAAC;YACb,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,CAAC;SAChB,aAED,eAAM,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,GAAI,EACrD,QAAQ,IAAI,eAAM,OAAO,EAAE,MAAM,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,GAAI,IAC5E,CACP,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { AppLayout } from "./AppLayout.jsx";
|
|
2
|
+
export type { AppLayoutProps, AppLayoutSearchBar } from "./AppLayout.jsx";
|
|
3
|
+
export { StatusBar } from "./StatusBar.jsx";
|
|
4
|
+
export type { StatusBarItem } from "./StatusBar.jsx";
|
|
5
|
+
export { TitleBar } from "./TitleBar.jsx";
|
|
6
|
+
export { SearchBar } from "./SearchBar.jsx";
|
|
7
|
+
export type { SearchBarProps } from "./SearchBar.jsx";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tooee/layout",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Standard app chrome (title bar, status bar) for Tooee",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Gareth Andrew",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/gingerhendrix/tooee.git",
|
|
10
|
+
"directory": "packages/layout"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/gingerhendrix/tooee",
|
|
13
|
+
"bugs": "https://github.com/gingerhendrix/tooee/issues",
|
|
14
|
+
"keywords": ["tui", "terminal", "cli", "opentui", "layout"],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": {
|
|
19
|
+
"@tooee/source": "./src/index.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": ["dist", "src"],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"typecheck": "tsc --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@tooee/themes": "0.0.0",
|
|
30
|
+
"@tooee/overlays": "0.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@opentui/core": "^0.1.67",
|
|
34
|
+
"@opentui/react": "^0.1.67",
|
|
35
|
+
"@types/bun": "^1.3.5",
|
|
36
|
+
"@types/react": "^19.1.10",
|
|
37
|
+
"typescript": "^5.8.3"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@opentui/core": "^0.1.67",
|
|
41
|
+
"@opentui/react": "^0.1.67",
|
|
42
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ReactNode, RefObject } from "react"
|
|
2
|
+
import type { ScrollBoxRenderable } from "@opentui/core"
|
|
3
|
+
import { TitleBar } from "./TitleBar.jsx"
|
|
4
|
+
import { StatusBar } from "./StatusBar.jsx"
|
|
5
|
+
import type { StatusBarItem } from "./StatusBar.jsx"
|
|
6
|
+
import { SearchBar } from "./SearchBar.jsx"
|
|
7
|
+
import type { SearchBarProps } from "./SearchBar.jsx"
|
|
8
|
+
import { useTheme } from "@tooee/themes"
|
|
9
|
+
import { useCurrentOverlay } from "@tooee/overlays"
|
|
10
|
+
|
|
11
|
+
export interface AppLayoutSearchBar extends SearchBarProps {
|
|
12
|
+
active: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AppLayoutProps {
|
|
16
|
+
titleBar?: { title: string; subtitle?: string }
|
|
17
|
+
statusBar: { items: StatusBarItem[] }
|
|
18
|
+
scrollRef?: RefObject<ScrollBoxRenderable | null>
|
|
19
|
+
scrollProps?: {
|
|
20
|
+
stickyScroll?: boolean
|
|
21
|
+
stickyStart?: "bottom" | "top"
|
|
22
|
+
focused?: boolean
|
|
23
|
+
}
|
|
24
|
+
searchBar?: AppLayoutSearchBar
|
|
25
|
+
overlay?: ReactNode
|
|
26
|
+
children: ReactNode
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function AppLayout({
|
|
30
|
+
titleBar,
|
|
31
|
+
statusBar,
|
|
32
|
+
scrollRef,
|
|
33
|
+
scrollProps,
|
|
34
|
+
searchBar,
|
|
35
|
+
overlay,
|
|
36
|
+
children,
|
|
37
|
+
}: AppLayoutProps) {
|
|
38
|
+
const { theme } = useTheme()
|
|
39
|
+
const contextOverlay = useCurrentOverlay()
|
|
40
|
+
const activeOverlay = overlay ?? contextOverlay
|
|
41
|
+
return (
|
|
42
|
+
<box flexDirection="column" width="100%" height="100%" backgroundColor={theme.background}>
|
|
43
|
+
{titleBar && <TitleBar title={titleBar.title} subtitle={titleBar.subtitle} />}
|
|
44
|
+
<box style={{ flexGrow: 1, position: "relative" }}>
|
|
45
|
+
<scrollbox
|
|
46
|
+
ref={scrollRef}
|
|
47
|
+
style={{ flexGrow: 1 }}
|
|
48
|
+
stickyScroll={scrollProps?.stickyScroll}
|
|
49
|
+
stickyStart={scrollProps?.stickyStart}
|
|
50
|
+
focused={scrollProps?.focused ?? true}
|
|
51
|
+
>
|
|
52
|
+
{children}
|
|
53
|
+
</scrollbox>
|
|
54
|
+
{activeOverlay && (
|
|
55
|
+
<box position="absolute" left={0} top={0} width="100%" height="100%">
|
|
56
|
+
{activeOverlay}
|
|
57
|
+
</box>
|
|
58
|
+
)}
|
|
59
|
+
</box>
|
|
60
|
+
{searchBar?.active ? (
|
|
61
|
+
<SearchBar
|
|
62
|
+
query={searchBar.query}
|
|
63
|
+
onQueryChange={searchBar.onQueryChange}
|
|
64
|
+
onSubmit={searchBar.onSubmit}
|
|
65
|
+
onCancel={searchBar.onCancel}
|
|
66
|
+
matchCount={searchBar.matchCount}
|
|
67
|
+
currentMatch={searchBar.currentMatch}
|
|
68
|
+
/>
|
|
69
|
+
) : (
|
|
70
|
+
<StatusBar items={statusBar.items} />
|
|
71
|
+
)}
|
|
72
|
+
</box>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useTheme } from "@tooee/themes"
|
|
2
|
+
|
|
3
|
+
export interface SearchBarProps {
|
|
4
|
+
query: string
|
|
5
|
+
onQueryChange: (query: string) => void
|
|
6
|
+
onSubmit: () => void
|
|
7
|
+
onCancel: () => void
|
|
8
|
+
matchCount?: number
|
|
9
|
+
currentMatch?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function SearchBar({
|
|
13
|
+
query,
|
|
14
|
+
onQueryChange,
|
|
15
|
+
onSubmit,
|
|
16
|
+
onCancel: _onCancel,
|
|
17
|
+
matchCount,
|
|
18
|
+
currentMatch,
|
|
19
|
+
}: SearchBarProps) {
|
|
20
|
+
const { theme } = useTheme()
|
|
21
|
+
|
|
22
|
+
const matchDisplay =
|
|
23
|
+
matchCount !== undefined && matchCount > 0
|
|
24
|
+
? `${(currentMatch ?? 0) + 1}/${matchCount}`
|
|
25
|
+
: matchCount === 0 && query.length > 0
|
|
26
|
+
? "No matches"
|
|
27
|
+
: ""
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<box
|
|
31
|
+
style={{
|
|
32
|
+
flexDirection: "row",
|
|
33
|
+
flexShrink: 0,
|
|
34
|
+
backgroundColor: theme.backgroundPanel,
|
|
35
|
+
paddingLeft: 1,
|
|
36
|
+
paddingRight: 1,
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
<text content="/" style={{ fg: theme.accent }} />
|
|
40
|
+
<input
|
|
41
|
+
value={query}
|
|
42
|
+
focused
|
|
43
|
+
onInput={onQueryChange}
|
|
44
|
+
onSubmit={onSubmit}
|
|
45
|
+
backgroundColor="transparent"
|
|
46
|
+
focusedBackgroundColor="transparent"
|
|
47
|
+
textColor={theme.text}
|
|
48
|
+
cursorColor={theme.accent}
|
|
49
|
+
cursorStyle={{ style: "line", blinking: true }}
|
|
50
|
+
style={{ flexGrow: 1 }}
|
|
51
|
+
/>
|
|
52
|
+
{matchDisplay ? <text content={` ${matchDisplay}`} style={{ fg: theme.textMuted }} /> : null}
|
|
53
|
+
</box>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useTheme } from "@tooee/themes"
|
|
2
|
+
|
|
3
|
+
interface StatusBarProps {
|
|
4
|
+
items: StatusBarItem[]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface StatusBarItem {
|
|
8
|
+
label: string
|
|
9
|
+
value?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function StatusBar({ items }: StatusBarProps) {
|
|
13
|
+
const { theme } = useTheme()
|
|
14
|
+
return (
|
|
15
|
+
<box
|
|
16
|
+
style={{
|
|
17
|
+
flexDirection: "row",
|
|
18
|
+
flexShrink: 0,
|
|
19
|
+
backgroundColor: theme.backgroundPanel,
|
|
20
|
+
padding: 0,
|
|
21
|
+
paddingLeft: 1,
|
|
22
|
+
paddingRight: 1,
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
{items.map((item, index) => (
|
|
26
|
+
<box key={index} style={{ marginRight: 2, flexDirection: "row" }}>
|
|
27
|
+
<text content={item.label} style={{ fg: theme.textMuted }} />
|
|
28
|
+
{item.value && <text content={` ${item.value}`} style={{ fg: theme.text }} />}
|
|
29
|
+
</box>
|
|
30
|
+
))}
|
|
31
|
+
</box>
|
|
32
|
+
)
|
|
33
|
+
}
|
package/src/TitleBar.tsx
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useTheme } from "@tooee/themes"
|
|
2
|
+
|
|
3
|
+
interface TitleBarProps {
|
|
4
|
+
title: string
|
|
5
|
+
subtitle?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function TitleBar({ title, subtitle }: TitleBarProps) {
|
|
9
|
+
const { theme } = useTheme()
|
|
10
|
+
return (
|
|
11
|
+
<box
|
|
12
|
+
style={{
|
|
13
|
+
flexDirection: "row",
|
|
14
|
+
flexShrink: 0,
|
|
15
|
+
backgroundColor: theme.backgroundPanel,
|
|
16
|
+
padding: 0,
|
|
17
|
+
paddingLeft: 1,
|
|
18
|
+
paddingRight: 1,
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<text content={title} style={{ fg: theme.primary }} />
|
|
22
|
+
{subtitle && <text content={` — ${subtitle}`} style={{ fg: theme.textMuted }} />}
|
|
23
|
+
</box>
|
|
24
|
+
)
|
|
25
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { AppLayout } from "./AppLayout.jsx"
|
|
2
|
+
export type { AppLayoutProps, AppLayoutSearchBar } from "./AppLayout.jsx"
|
|
3
|
+
export { StatusBar } from "./StatusBar.jsx"
|
|
4
|
+
export type { StatusBarItem } from "./StatusBar.jsx"
|
|
5
|
+
export { TitleBar } from "./TitleBar.jsx"
|
|
6
|
+
export { SearchBar } from "./SearchBar.jsx"
|
|
7
|
+
export type { SearchBarProps } from "./SearchBar.jsx"
|