@hackcove/ui-react 0.9.0 → 0.9.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.
Files changed (50) hide show
  1. package/README.md +18 -8
  2. package/dist/Avatar.d.ts +8 -4
  3. package/dist/Avatar.js +11 -3
  4. package/dist/AvatarStack.js +1 -0
  5. package/dist/Callout.d.ts +8 -2
  6. package/dist/Callout.js +3 -3
  7. package/dist/Card.d.ts +2 -0
  8. package/dist/Card.js +14 -8
  9. package/dist/CardParts.d.ts +6 -0
  10. package/dist/CardParts.js +6 -0
  11. package/dist/Cluster.d.ts +9 -4
  12. package/dist/Cluster.js +12 -5
  13. package/dist/Dialog.d.ts +4 -2
  14. package/dist/Dialog.js +9 -3
  15. package/dist/EmptyState.d.ts +7 -1
  16. package/dist/EmptyState.js +3 -2
  17. package/dist/Field.d.ts +8 -1
  18. package/dist/Field.js +4 -2
  19. package/dist/Image.d.ts +46 -0
  20. package/dist/Image.js +42 -0
  21. package/dist/KeyValueList.d.ts +4 -2
  22. package/dist/KeyValueList.js +3 -3
  23. package/dist/ListRow.d.ts +3 -1
  24. package/dist/ListRow.js +6 -6
  25. package/dist/MediaTile.d.ts +37 -0
  26. package/dist/MediaTile.js +19 -0
  27. package/dist/PageLayouts.d.ts +24 -22
  28. package/dist/PageLayouts.js +25 -21
  29. package/dist/Pill.d.ts +24 -6
  30. package/dist/Pill.js +72 -16
  31. package/dist/ScrollStrip.d.ts +21 -0
  32. package/dist/ScrollStrip.js +12 -0
  33. package/dist/SidebarLayout.d.ts +21 -0
  34. package/dist/SidebarLayout.js +18 -0
  35. package/dist/Stack.d.ts +15 -1
  36. package/dist/Stack.js +12 -2
  37. package/dist/StatTile.d.ts +2 -2
  38. package/dist/StatTile.js +2 -0
  39. package/dist/Table.d.ts +29 -12
  40. package/dist/Table.js +25 -11
  41. package/dist/TableEditCell.d.ts +19 -0
  42. package/dist/TableEditCell.js +58 -0
  43. package/dist/index.d.ts +6 -1
  44. package/dist/index.js +6 -1
  45. package/dist/size.d.ts +1 -1
  46. package/dist/stickyColumn.d.ts +17 -0
  47. package/dist/stickyColumn.js +17 -0
  48. package/dist/typography.d.ts +34 -10
  49. package/dist/typography.js +43 -13
  50. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Children, } from "react";
3
3
  import { cx } from "./cx.js";
4
4
  /** Colour token → slate/violet utility, which a theme remaps. */
@@ -17,6 +17,11 @@ export const TEXT_TONE = {
17
17
  warn: "text-amber-700",
18
18
  danger: "text-red-700",
19
19
  };
20
+ const ALIGN = {
21
+ start: "text-start",
22
+ center: "text-center",
23
+ end: "text-end",
24
+ };
20
25
  /**
21
26
  * Build a semantic component that pins `variantClass` (size + weight + tracking)
22
27
  * and a default colour. The scale/weight for each role therefore lives in exactly
@@ -42,9 +47,19 @@ function inlineRole(displayName, defaultAs, variantClass, defaultColor) {
42
47
  * The one place a pinned role renders. The few roles with an opt-in prop of their own are
43
48
  * written by hand, not by `typography`, and call this too.
44
49
  */
45
- function pinned(defaultAs, variantClass, defaultColor, { as, color, tone, numeric, mono, truncate, children, ...rest }) {
50
+ function pinned(defaultAs, variantClass, defaultColor, { as, color, tone, numeric, mono, truncate, align, children, ...rest }) {
46
51
  const Tag = as ?? defaultAs;
47
- return (_jsx(Tag, { ...rest, ...(truncate ? { title: rest.title ?? plainText(children) } : {}), className: cx(variantClass, tone ? TEXT_TONE[tone] : COLOR[color ?? defaultColor], numeric && "tabular-nums", mono && "font-mono", truncate && "inline-block min-w-0 truncate align-bottom"), children: children }));
52
+ return (_jsx(Tag, { ...rest, ...(truncate ? { title: rest.title ?? plainText(children) } : {}), className: cx(variantClass, tone ? TEXT_TONE[tone] : COLOR[color ?? defaultColor], numeric && "tabular-nums", mono && "font-mono", layoutClass(align, truncate)), children: children }));
53
+ }
54
+ /**
55
+ * The alignment, and the box it needs. An aligned run is block-level, so text-align has a box to set
56
+ * the text in. A truncated run clips: inline-block by default, a block when it is aligned too.
57
+ */
58
+ function layoutClass(align, truncate) {
59
+ const box = truncate
60
+ ? cx("min-w-0 truncate align-bottom", align ? "block" : "inline-block")
61
+ : align && "block";
62
+ return cx(align && ALIGN[align], box);
48
63
  }
49
64
  /** The text of `children` when it is all strings and numbers: what a truncated run shows on hover. */
50
65
  function plainText(children) {
@@ -71,11 +86,16 @@ export const PageHeading = typography("PageHeading", "h1", "text-3xl/9 font-bold
71
86
  /**
72
87
  * Top-level section header. `h2`, text-base/6 semibold, primary text. `rule` sets
73
88
  * a hairline under it (the `Divider` line colour) with the space either side of
74
- * the line built in.
89
+ * the line built in. `description` is a muted line under the heading, outside the `<h2>`, so the
90
+ * heading's name stays the title; the rule then sits under the description.
75
91
  */
76
- export function SectionHeading({ rule, ...props }) {
77
- const variant = cx(rule && "mb-4 border-b border-slate-200/70 pb-2", "text-base/6 font-semibold");
78
- return pinned("h2", variant, "default", props);
92
+ export function SectionHeading({ rule, description, ...props }) {
93
+ const hairline = "mb-4 border-b border-slate-200/70 pb-2";
94
+ const hasDescription = description != null && description !== false;
95
+ if (!hasDescription) {
96
+ return pinned("h2", cx(rule && hairline, "text-base/6 font-semibold"), "default", props);
97
+ }
98
+ return (_jsxs("div", { className: cx("flex flex-col gap-1", rule && hairline), children: [pinned("h2", "text-base/6 font-semibold", "default", props), _jsx(Text, { as: "p", color: "tertiary", children: description })] }));
79
99
  }
80
100
  const SUBSECTION_SIZE = {
81
101
  sm: "text-sm/5 font-semibold",
@@ -93,7 +113,7 @@ export function SubsectionHeading({ size = "md", ...props }) {
93
113
  // --- Inline roles (inline by default) --------------------------------------
94
114
  /**
95
115
  * Small all-caps label above a group. `span`, text-xs semibold uppercase, tertiary. `tone`,
96
- * `numeric`, `mono` and `truncate` are opt-in, as on `Text`.
116
+ * `numeric`, `mono`, `truncate` and `align` are opt-in, as on `Text`.
97
117
  */
98
118
  export const Overline = inlineRole("Overline", "span", "text-xs font-semibold uppercase tracking-wide", "tertiary");
99
119
  /**
@@ -103,10 +123,14 @@ export const Overline = inlineRole("Overline", "span", "text-xs font-semibold up
103
123
  * `preserveNewlines` keeps the line breaks in the text (a user-typed note); runs of spaces still collapse.
104
124
  * `tone` colours it by meaning (`positive` for "Saved.", `danger` for an error; it wins over `color`),
105
125
  * `numeric` sets tabular figures, `mono` the monospace face, and `truncate` cuts it to one line with an
106
- * ellipsis (the full text becomes its `title`).
126
+ * ellipsis (the full text becomes its `title`). `align` sets it at `start`, `center` or `end`.
127
+ * `italic` slants it, `breakWords` breaks a long unbroken string at the edge of its box, and `minWidth="amount"`
128
+ * fixes a width and right-aligns the figure in it (a column of amounts lines up); it does not take `align`.
107
129
  */
108
- export function Text({ strong, preserveNewlines, ...props }) {
109
- return pinned("span", textVariant(strong, preserveNewlines, true), strong ? "default" : "secondary", props);
130
+ export function Text({ strong, preserveNewlines, minWidth, italic, breakWords, ...props }) {
131
+ return pinned("span", cx(textVariant(strong, preserveNewlines, true), italic && "italic", breakWords && "break-words", minWidth === "amount" && "inline-block min-w-24 text-end"), strong ? "default" : "secondary",
132
+ // An amount is always in tabular figures, so it lines up; `numeric` already emits them.
133
+ { ...props, numeric: props.numeric || minWidth === "amount" });
110
134
  }
111
135
  /**
112
136
  * The supporting line under a `Dialog` title: `Text as="p"` without the measure, so
@@ -118,5 +142,11 @@ export function HeaderText({ children }) {
118
142
  function textVariant(strong, preserveNewlines, measure) {
119
143
  return cx(preserveNewlines && "whitespace-pre-line", measure && "max-w-[72ch]", "text-sm/5", strong ? "font-medium" : "font-normal");
120
144
  }
121
- /** Fine print / metadata. `span` (inline; `as="p"` for a block), text-xs/4 normal, tertiary. Takes `tone`, `numeric`, `mono` and `truncate` like `Text`. */
122
- export const Caption = inlineRole("Caption", "span", "text-xs/4 font-normal", "tertiary");
145
+ /**
146
+ * Fine print / metadata. `span` (inline; `as="p"` for a block), text-xs/4 normal, tertiary. Takes `tone`,
147
+ * `numeric`, `mono`, `truncate` and `align` like `Text`, and `italic` for a muted hint and `breakWords`
148
+ * for a long unbroken string such as a URL.
149
+ */
150
+ export function Caption({ italic, breakWords, ...props }) {
151
+ return pinned("span", cx("text-xs/4 font-normal", italic && "italic", breakWords && "break-words"), "tertiary", props);
152
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hackcove/ui-react",
3
- "version": "0.9.0",
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.",
3
+ "version": "0.9.2",
4
+ "description": "Hack Cove React components: primitives, typography roles and a safe Markdown renderer, written against Tailwind v4 utilities. Pair with a theme from @hackcove/themes.",
5
5
  "keywords": [
6
6
  "components",
7
7
  "design-system",