@hackcove/ui 0.2.5 → 0.2.6
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 +18 -2
- package/dist/AppShell.d.ts +27 -2
- package/dist/AppShell.js +25 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ export const Example = () => (
|
|
|
40
40
|
## What is exported
|
|
41
41
|
|
|
42
42
|
- Primitives: `Card` (a bordered panel; `title` and `count` make a small heading; `flush` drops the padding and clips the corners, for a table that runs edge to edge; `bar` moves the title, count and `actions` into a tinted strip over the body; `dashed` is an empty-state shell with centred muted text; `as` is `section` (default), `article` or `div`), `EpicHeader`, `Empty`, `PageTitle` (deprecated: use `PageHeader`), `Pill`, `groupByEpic`, `Markdown`.
|
|
43
|
-
- Layout (see Layout): `AppShell`, `appShellRootClass`, `Container`, `PageHeader`, `Stack`, `Section`, and the page layouts `ListLayout`, `DetailLayout`, `FormLayout`, `ProseLayout`, `CardGrid`, `SidebarLayout`, `FullBleedLayout`, `CenteredLayout`.
|
|
43
|
+
- Layout (see Layout): `AppShell`, `AppShellBrand`, `appShellRootClass`, `appShellRootClassFor`, `Container`, `PageHeader`, `Stack`, `Section`, and the page layouts `ListLayout`, `DetailLayout`, `FormLayout`, `ProseLayout`, `CardGrid`, `SidebarLayout`, `FullBleedLayout`, `CenteredLayout`.
|
|
44
44
|
- Typography roles (each pins size + weight, takes a `color` and an `as`): `Display`, `Lead`,
|
|
45
45
|
`PageHeading`, `SectionHeading`, `SubsectionHeading`, `ModalHeading`, `Subtitle`, `Overline`,
|
|
46
46
|
`Body`, `BodyStrong`, `Caption`. Three opt-in props, none changing today's look: `SectionHeading rule`
|
|
@@ -137,9 +137,25 @@ import { AppShell, PageHeader, Section, Stack, appShellRootClass } from "@hackco
|
|
|
137
137
|
with fixed `py-8`, and an optional `footer`. The header row is filled by slots, left to right:
|
|
138
138
|
`brand` (logo and name, at the brand type with the logo gap), `nav` (a `NavTabs`), `header` (a
|
|
139
139
|
free node, the escape hatch) and `controls` (end-aligned, `gap-2`, centred): no wrapper, no
|
|
140
|
-
`className`.
|
|
140
|
+
`className`. Nothing in `controls` shrinks, and a full-width control there (a `Select` at its
|
|
141
|
+
default `width="full"`) sizes to its content, so a `Select` next to a `ColourSchemeToggle` leaves
|
|
142
|
+
the toggle its square. `hideChromeInPrint` is opt-in: the header and footer are hidden in print and main
|
|
141
143
|
drops its padding (the page margin is then your `@page`); without it a print is unchanged. `bleed` drops main's `Container` for a full-bleed
|
|
142
144
|
page; the header keeps its own, so put a `Container` back around anything that should line up.
|
|
145
|
+
- **`AppShellBrand`** is the `brand` slot as one link: `logo` and `label` inside the same `<a>`, so
|
|
146
|
+
the whole brand goes home (the label names the link; the logo is hidden from assistive tech). With
|
|
147
|
+
`render` it draws your router's `<Link>`, as `NavTabs`, `Breadcrumbs` and `TextLink` do:
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
<AppShell brand={<AppShellBrand logo={<Avatar name="Quotes" size="sm" />} label="Quotes"
|
|
151
|
+
render={(props) => <Link to="/" {...props} />} />} …>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
A plain node in `brand` still works. Stories: `Layout/AppShell › LinkedBrand`, `RouterBrand`.
|
|
155
|
+
- **`appShellRootClassFor(scheme, schemes?)`** is what goes on `<html>` when the server knows the
|
|
156
|
+
scheme: that scheme's class and `appShellRootClass`, joined, so there is no string concatenation:
|
|
157
|
+
`<html className={appShellRootClassFor(scheme, schemes)}>`. `appShellRootClass` and `schemeClass`
|
|
158
|
+
stay exported.
|
|
143
159
|
- **`Container`** is the only place the content width (`max-w-6xl`) and side padding
|
|
144
160
|
(`px-4 sm:px-6`) are defined. **Pages never set their own width or outer padding**: no `max-w-*`,
|
|
145
161
|
no `mx-auto`, no `px-*` on a page. AppShell already gives them the edge.
|
package/dist/AppShell.d.ts
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
import { type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import { type ColourScheme, type ColourSchemeClasses } from "./colourScheme.js";
|
|
3
|
+
import { type LinkRender } from "./link.js";
|
|
2
4
|
/**
|
|
3
5
|
* The class an app puts on its own `<html>`: it always reserves the scrollbar, so centred content
|
|
4
6
|
* does not shift between a short page and a long one. AppShell never touches `<html>` itself.
|
|
5
7
|
*/
|
|
6
8
|
export declare const appShellRootClass = "overflow-y-scroll";
|
|
9
|
+
/**
|
|
10
|
+
* Everything an app puts on its own `<html>` from the scheme the server knows: that scheme's class
|
|
11
|
+
* (`schemeClass`) and `appShellRootClass`, joined. `<html className={appShellRootClassFor(scheme,
|
|
12
|
+
* schemes)}>` replaces `schemeClass(scheme, schemes) + " " + appShellRootClass`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function appShellRootClassFor(scheme: ColourScheme, schemes?: ColourSchemeClasses): string;
|
|
15
|
+
/**
|
|
16
|
+
* The `brand` slot as one link: a logo and a label inside the same `<a>`, so the whole brand is the
|
|
17
|
+
* way home. The label names the link; the logo is decoration and hidden from assistive tech. With
|
|
18
|
+
* `render` it draws the app's own router `<Link>` (`render={(p) => <Link to="/" {...p} />}`).
|
|
19
|
+
*/
|
|
20
|
+
export declare function AppShellBrand({ logo, label, href, render, }: {
|
|
21
|
+
/** The mark before the label, e.g. an `Avatar` or an `<img>`. */
|
|
22
|
+
logo?: ReactNode;
|
|
23
|
+
/** The name beside the logo. It is the link's accessible name. */
|
|
24
|
+
label: ReactNode;
|
|
25
|
+
} & ({
|
|
26
|
+
href: string;
|
|
27
|
+
render?: LinkRender | undefined;
|
|
28
|
+
} | {
|
|
29
|
+
href?: string | undefined;
|
|
30
|
+
render: LinkRender;
|
|
31
|
+
})): ReactElement;
|
|
7
32
|
/**
|
|
8
33
|
* The page frame: a sticky one-row header, an optional second `subnav` row, the main area and an
|
|
9
34
|
* optional footer. Header, main and footer each sit in a `Container`, so pages never set their own
|
|
@@ -15,11 +40,11 @@ export declare const appShellRootClass = "overflow-y-scroll";
|
|
|
15
40
|
* `className` for any of them.
|
|
16
41
|
*/
|
|
17
42
|
export declare function AppShell({ brand, nav, controls, header, subnav, footer, bleed, hideChromeInPrint, children, }: {
|
|
18
|
-
/** The brand: a logo and a name, in the header's brand type, with the gap between them. */
|
|
43
|
+
/** The brand: a logo and a name, in the header's brand type, with the gap between them. `AppShellBrand` makes both one link. */
|
|
19
44
|
brand?: ReactNode;
|
|
20
45
|
/** The main navigation, e.g. `NavTabs` (it is already a `<nav>`). */
|
|
21
46
|
nav?: ReactNode;
|
|
22
|
-
/** The end of the row: a picker, a theme toggle. Gap-2, vertically centred. */
|
|
47
|
+
/** The end of the row: a picker, a theme toggle. Gap-2, vertically centred; no child shrinks, and a full-width control (a `Select` at its default) sizes to its content. */
|
|
23
48
|
controls?: ReactNode;
|
|
24
49
|
/** The escape hatch: a free node in the header row, after `brand` and `nav`. */
|
|
25
50
|
header?: ReactNode;
|
package/dist/AppShell.js
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from "react";
|
|
3
3
|
import { Container } from "./Container.js";
|
|
4
|
-
import {
|
|
4
|
+
import { schemeClass } from "./colourScheme.js";
|
|
5
|
+
import { cx, focusRing } from "./cx.js";
|
|
6
|
+
import { renderLink } from "./link.js";
|
|
5
7
|
/**
|
|
6
8
|
* The class an app puts on its own `<html>`: it always reserves the scrollbar, so centred content
|
|
7
9
|
* does not shift between a short page and a long one. AppShell never touches `<html>` itself.
|
|
8
10
|
*/
|
|
9
11
|
export const appShellRootClass = "overflow-y-scroll";
|
|
12
|
+
/**
|
|
13
|
+
* Everything an app puts on its own `<html>` from the scheme the server knows: that scheme's class
|
|
14
|
+
* (`schemeClass`) and `appShellRootClass`, joined. `<html className={appShellRootClassFor(scheme,
|
|
15
|
+
* schemes)}>` replaces `schemeClass(scheme, schemes) + " " + appShellRootClass`.
|
|
16
|
+
*/
|
|
17
|
+
export function appShellRootClassFor(scheme, schemes) {
|
|
18
|
+
return cx(schemeClass(scheme, schemes), appShellRootClass);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The `brand` slot as one link: a logo and a label inside the same `<a>`, so the whole brand is the
|
|
22
|
+
* way home. The label names the link; the logo is decoration and hidden from assistive tech. With
|
|
23
|
+
* `render` it draws the app's own router `<Link>` (`render={(p) => <Link to="/" {...p} />}`).
|
|
24
|
+
*/
|
|
25
|
+
export function AppShellBrand({ logo, label, href, render, }) {
|
|
26
|
+
return renderLink({
|
|
27
|
+
href,
|
|
28
|
+
render,
|
|
29
|
+
className: cx("flex items-center gap-2 rounded", focusRing),
|
|
30
|
+
children: (_jsxs(_Fragment, { children: [logo && (_jsx("span", { "aria-hidden": "true", className: "flex shrink-0 items-center", children: logo })), _jsx("span", { children: label })] })),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
10
33
|
// One fixed-height row that scrolls sideways and never wraps; its children keep their width.
|
|
11
34
|
const ROW = "flex items-center gap-4 overflow-x-auto whitespace-nowrap *:shrink-0";
|
|
12
35
|
// While a row has more to scroll to on a side, that edge fades out (a mask, so it follows any theme).
|
|
@@ -44,7 +67,7 @@ function ScrollRow({ className, children }) {
|
|
|
44
67
|
}
|
|
45
68
|
/** The header row's slots in order: brand, nav, the free `children`, then controls at the end. */
|
|
46
69
|
function HeaderSlots({ brand, nav, controls, children, }) {
|
|
47
|
-
return (_jsxs(_Fragment, { children: [brand && (_jsx("div", { className: "flex items-center gap-2 text-base font-bold tracking-tight text-slate-900", children: brand })), nav, children, controls && _jsx("div", { className: "ml-auto flex items-center gap-2", children: controls })] }));
|
|
70
|
+
return (_jsxs(_Fragment, { children: [brand && (_jsx("div", { className: "flex items-center gap-2 text-base font-bold tracking-tight text-slate-900", children: brand })), nav, children, controls && (_jsx("div", { className: "ml-auto flex items-center gap-2 *:shrink-0 [&>.w-full]:w-auto", children: controls }))] }));
|
|
48
71
|
}
|
|
49
72
|
/**
|
|
50
73
|
* The page frame: a sticky one-row header, an optional second `subnav` row, the main area and an
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { StatTile, type StatTileTone } from "./StatTile.js";
|
|
|
4
4
|
export { ProgressBar, type BarSize, type BarTone } from "./ProgressBar.js";
|
|
5
5
|
export { SplitBar } from "./SplitBar.js";
|
|
6
6
|
export { Markdown } from "./Markdown.js";
|
|
7
|
-
export { AppShell, appShellRootClass } from "./AppShell.js";
|
|
7
|
+
export { AppShell, AppShellBrand, appShellRootClass, appShellRootClassFor } from "./AppShell.js";
|
|
8
8
|
export { Container } from "./Container.js";
|
|
9
9
|
export { PageHeader, type PageHeaderProps } from "./PageHeader.js";
|
|
10
10
|
export { CardGrid, CenteredLayout, DetailLayout, FormLayout, FullBleedLayout, ListLayout, ProseLayout, SidebarLayout, } from "./PageLayouts.js";
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { StatTile } from "./StatTile.js";
|
|
|
4
4
|
export { ProgressBar } from "./ProgressBar.js";
|
|
5
5
|
export { SplitBar } from "./SplitBar.js";
|
|
6
6
|
export { Markdown } from "./Markdown.js";
|
|
7
|
-
export { AppShell, appShellRootClass } from "./AppShell.js";
|
|
7
|
+
export { AppShell, AppShellBrand, appShellRootClass, appShellRootClassFor } from "./AppShell.js";
|
|
8
8
|
export { Container } from "./Container.js";
|
|
9
9
|
export { PageHeader } from "./PageHeader.js";
|
|
10
10
|
export { CardGrid, CenteredLayout, DetailLayout, FormLayout, FullBleedLayout, ListLayout, ProseLayout, SidebarLayout, } from "./PageLayouts.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hackcove/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Hack Cove React components: primitives, typography roles and a safe Markdown renderer, written against Tailwind v4 utilities. Pair with a @hackcove/theme-* package.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"components",
|