@hackcove/ui 0.2.1 → 0.2.2

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 CHANGED
@@ -40,7 +40,7 @@ export const Example = () => (
40
40
  - Typography roles (each pins size + weight, takes a `color` and an `as`): `Display`, `Lead`,
41
41
  `PageHeading`, `SectionHeading`, `SubsectionHeading`, `ModalHeading`, `Subtitle`, `Overline`,
42
42
  `Body`, `BodyStrong`, `Caption`.
43
- - Controls: `Button`, `buttonClass` (the classes of a `Button`, for a router's own `<Link>`), `IconButton` (a square button holding one icon; `label` is required and becomes its `aria-label` and `title`), `Input` (a text input; `icon` adds a leading icon inside the field), `Textarea` (a multi-line text input, `rows` defaults to 3), `Select` (a native `<select>` in the control look; takes `<option>` children and keeps the browser's arrow). `Checkbox` (a native checkbox with its label; `className` goes on the label, other props on the input). `CopyButton` (a small text button that writes `text` to the clipboard and swaps `label` ("copy") for `copiedLabel` ("copied"); every other button prop, `disabled` included, goes on the `<button>`). `CopyLine` (one line of mono text in a box with a `CopyButton` beside it; other props go on the root `<div>`). `Field` (a label above a control, `htmlFor` names the control's id; a `hint` sits under it, and an `error` replaces the hint with a `role="alert"` line). `Callout` (a tinted notice box; `tone` is `neutral`, `info` (default), `positive`, `warn` or `danger`; an optional `title` sits in bold above the body). `NavTabs` (a row of pill links in a `<nav>`; `items` are `{ label, href, active? }`, `label` names the landmark, the active one is filled and gets `aria-current="page"`). `navTabClass` (the classes of one tab, for a router's own `<Link>`). `Breadcrumbs` (a `<nav aria-label="Breadcrumb">` over an ordered list; `crumbs` are nodes, the last is the current page and gets `aria-current="page"`, and each crumb truncates). `crumbLinkClass` (the classes of a crumb that links, for a router's own `<Link>`). `KeyValueList` (a `<dl>` of label/value rows; each `KeyValue` takes a `label` and its value as children, the value right-aligned in tabular figures). `Kbd` (a keyboard key in mono; its border takes the surrounding text colour, so it reads on a filled button too). `ToggleChip` (an on/off filter pill; `pressed` sets `aria-pressed` and the filled look, `dot` adds a leading colour dot, and the caller toggles it in `onClick`). `FilterGroup` (a `<fieldset>` with a small uppercase `legend` over a wrapped row of its children, e.g. `ToggleChip`s; `className` goes on the fieldset).
43
+ - Controls: `Button`, `buttonClass` (the classes of a `Button`, for a router's own `<Link>`), `IconButton` (a square button holding one icon; `label` is required and becomes its `aria-label` and `title`), `Input` (a text input; `icon` adds a leading icon inside the field), `Textarea` (a multi-line text input, `rows` defaults to 3), `Select` (a native `<select>` in the control look; takes `<option>` children and keeps the browser's arrow). `Checkbox` (a native checkbox with its label; `className` goes on the label, other props on the input). `CopyButton` (a small text button that writes `text` to the clipboard and swaps `label` ("copy") for `copiedLabel` ("copied"); every other button prop, `disabled` included, goes on the `<button>`). `CopyLine` (one line of mono text in a box with a `CopyButton` beside it; other props go on the root `<div>`). `Field` (a label above a control, `htmlFor` names the control's id; a `hint` sits under it, and an `error` replaces the hint with a `role="alert"` line). `Callout` (a tinted notice box; `tone` is `neutral`, `info` (default), `positive`, `warn` or `danger`; an optional `title` sits in bold above the body). `NavTabs` (a row of pill links in a `<nav>`; `items` are `{ label, href, active? }`, `label` names the landmark, the active one is filled and gets `aria-current="page"`). `navTabClass` (the classes of one tab, for a router's own `<Link>`). `Breadcrumbs` (a `<nav aria-label="Breadcrumb">` over an ordered list; `crumbs` are nodes, the last is the current page and gets `aria-current="page"`, and each crumb truncates). `crumbLinkClass` (the classes of a crumb that links, for a router's own `<Link>`). `KeyValueList` (a `<dl>` of label/value rows; each `KeyValue` takes a `label` and its value as children, the value right-aligned in tabular figures). `Avatar` (a round badge of the initials of `name`, titled with the name and `aria-hidden`; `size` is `sm`, `md` (default) or `lg`; `ring` adds a page-colour border and a negative margin for an overlapping stack; the colour is one of seven stock background classes picked by a hash of the name, remapped by each theme, and `avatarColourClass(name)` returns it to colour something else the same way; `initialsOf(name)` is the two letters). `Kbd` (a keyboard key in mono; its border takes the surrounding text colour, so it reads on a filled button too). `ToggleChip` (an on/off filter pill; `pressed` sets `aria-pressed` and the filled look, `dot` adds a leading colour dot, and the caller toggles it in `onClick`). `FilterGroup` (a `<fieldset>` with a small uppercase `legend` over a wrapped row of its children, e.g. `ToggleChip`s; `className` goes on the fieldset).
44
44
 
45
45
  ## Palette
46
46
 
@@ -0,0 +1,22 @@
1
+ import type { HTMLAttributes, ReactElement } from "react";
2
+ declare const SIZE_CLASSES: {
3
+ readonly sm: "size-6 text-xs";
4
+ readonly md: "size-7 text-xs";
5
+ readonly lg: "size-10 text-base";
6
+ };
7
+ export type AvatarSize = keyof typeof SIZE_CLASSES;
8
+ /** The background class for a name: the same name gets the same colour everywhere. */
9
+ export declare function avatarColourClass(name: string): string;
10
+ /** Up to two letters or digits of a name, upper-cased; `?` when it has none. */
11
+ export declare function initialsOf(name: string): string;
12
+ /**
13
+ * A person as a round badge of initials. It never loads a remote image: that is a request to
14
+ * another server for decoration, and it makes a screenshot depend on the network.
15
+ */
16
+ export declare function Avatar({ name, size, ring, className, ...rest }: {
17
+ name: string;
18
+ size?: AvatarSize;
19
+ /** A ring in the page colour, for a stack of overlapping avatars. */
20
+ ring?: boolean;
21
+ } & Omit<HTMLAttributes<HTMLSpanElement>, "children">): ReactElement;
22
+ export {};
package/dist/Avatar.js ADDED
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from "./cx.js";
3
+ /** The identity colours, in hash order. Each is a stock background utility; a theme remaps them. */
4
+ const COLOUR_CLASSES = [
5
+ "bg-violet-600",
6
+ "bg-orange-600",
7
+ "bg-cyan-600",
8
+ "bg-emerald-600",
9
+ "bg-sky-600",
10
+ "bg-pink-600",
11
+ "bg-amber-600",
12
+ ];
13
+ const SIZE_CLASSES = {
14
+ sm: "size-6 text-xs",
15
+ md: "size-7 text-xs",
16
+ lg: "size-10 text-base",
17
+ };
18
+ /** The background class for a name: the same name gets the same colour everywhere. */
19
+ export function avatarColourClass(name) {
20
+ let hash = 0;
21
+ for (const char of name)
22
+ hash = (hash * 31 + char.charCodeAt(0)) >>> 0;
23
+ return COLOUR_CLASSES[hash % COLOUR_CLASSES.length] ?? COLOUR_CLASSES[0];
24
+ }
25
+ /** Up to two letters or digits of a name, upper-cased; `?` when it has none. */
26
+ export function initialsOf(name) {
27
+ return (name
28
+ .replace(/[^a-z0-9]/gi, "")
29
+ .slice(0, 2)
30
+ .toUpperCase() || "?");
31
+ }
32
+ /**
33
+ * A person as a round badge of initials. It never loads a remote image: that is a request to
34
+ * another server for decoration, and it makes a screenshot depend on the network.
35
+ */
36
+ export function Avatar({ name, size = "md", ring = false, className, ...rest }) {
37
+ return (_jsx("span", { "aria-hidden": "true", title: name, className: cx("inline-grid shrink-0 place-items-center rounded-full font-semibold text-violet-50", SIZE_CLASSES[size], avatarColourClass(name), ring && "-ml-1.5 border-2 border-slate-50 first:ml-0", className), ...rest, children: initialsOf(name) }));
38
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { Card, EpicHeader, Empty, PageTitle, Pill, groupByEpic } from "./ui.js";
2
2
  export { Markdown } from "./Markdown.js";
3
3
  export { Button, buttonClass, type ButtonVariant } from "./Button.js";
4
+ export { Avatar, avatarColourClass, initialsOf, type AvatarSize } from "./Avatar.js";
4
5
  export { Breadcrumbs, crumbLinkClass } from "./Breadcrumbs.js";
5
6
  export { Callout, type CalloutTone } from "./Callout.js";
6
7
  export { Dialog, type DialogProps } from "./Dialog.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { Card, EpicHeader, Empty, PageTitle, Pill, groupByEpic } from "./ui.js";
2
2
  export { Markdown } from "./Markdown.js";
3
3
  export { Button, buttonClass } from "./Button.js";
4
+ export { Avatar, avatarColourClass, initialsOf } from "./Avatar.js";
4
5
  export { Breadcrumbs, crumbLinkClass } from "./Breadcrumbs.js";
5
6
  export { Callout } from "./Callout.js";
6
7
  export { Dialog } from "./Dialog.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hackcove/ui",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
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
  "license": "MIT",
6
6
  "repository": {