@hackcove/ui-react 0.9.2 → 0.10.1

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 (74) hide show
  1. package/MIGRATING.md +21 -0
  2. package/README.md +24 -9
  3. package/dist/Avatar.d.ts +12 -5
  4. package/dist/Avatar.js +17 -12
  5. package/dist/AvatarStack.js +2 -1
  6. package/dist/Button.d.ts +15 -1
  7. package/dist/Button.js +17 -6
  8. package/dist/Card.d.ts +8 -1
  9. package/dist/Card.js +15 -6
  10. package/dist/CardParts.d.ts +2 -1
  11. package/dist/CardParts.js +6 -2
  12. package/dist/ChatComposer.d.ts +35 -0
  13. package/dist/ChatComposer.js +50 -0
  14. package/dist/ChatMessage.d.ts +26 -0
  15. package/dist/ChatMessage.js +21 -0
  16. package/dist/ChatThread.d.ts +25 -0
  17. package/dist/ChatThread.js +59 -0
  18. package/dist/Checkbox.d.ts +1 -1
  19. package/dist/Checkbox.js +1 -13
  20. package/dist/ColourSchemeToggle.d.ts +4 -2
  21. package/dist/ColourSchemeToggle.js +23 -10
  22. package/dist/Dialog.d.ts +2 -1
  23. package/dist/Dialog.js +44 -3
  24. package/dist/Disclosure.d.ts +10 -1
  25. package/dist/Disclosure.js +13 -3
  26. package/dist/Field.d.ts +9 -1
  27. package/dist/Field.js +5 -3
  28. package/dist/Icon.d.ts +4 -2
  29. package/dist/Icon.js +4 -2
  30. package/dist/IconButton.d.ts +2 -0
  31. package/dist/IconButton.js +2 -2
  32. package/dist/Image.d.ts +14 -4
  33. package/dist/Image.js +22 -6
  34. package/dist/InlineEdit.d.ts +22 -0
  35. package/dist/InlineEdit.js +60 -0
  36. package/dist/Input.js +1 -1
  37. package/dist/InputShell.d.ts +9 -1
  38. package/dist/InputShell.js +18 -6
  39. package/dist/KeyValueList.d.ts +9 -2
  40. package/dist/KeyValueList.js +8 -3
  41. package/dist/Lightbox.d.ts +2 -0
  42. package/dist/Lightbox.js +2 -1
  43. package/dist/ListRow.d.ts +11 -0
  44. package/dist/ListRow.js +28 -6
  45. package/dist/MediaTile.d.ts +11 -3
  46. package/dist/MediaTile.js +10 -7
  47. package/dist/PageLayouts.d.ts +9 -3
  48. package/dist/PageLayouts.js +9 -6
  49. package/dist/SideColumn.d.ts +26 -0
  50. package/dist/SideColumn.js +22 -0
  51. package/dist/SidebarLayout.d.ts +5 -2
  52. package/dist/SidebarLayout.js +7 -3
  53. package/dist/Stack.d.ts +10 -2
  54. package/dist/Stack.js +5 -3
  55. package/dist/StatusPages.d.ts +13 -3
  56. package/dist/StatusPages.js +8 -5
  57. package/dist/Switch.d.ts +42 -0
  58. package/dist/Switch.js +56 -0
  59. package/dist/TableEditCell.d.ts +9 -7
  60. package/dist/TableEditCell.js +12 -53
  61. package/dist/ToolCallCard.d.ts +16 -0
  62. package/dist/ToolCallCard.js +16 -0
  63. package/dist/control.js +1 -1
  64. package/dist/foldedColumn.d.ts +18 -0
  65. package/dist/foldedColumn.js +45 -0
  66. package/dist/icons.d.ts +3 -3
  67. package/dist/icons.js +28 -1
  68. package/dist/index.d.ts +7 -0
  69. package/dist/index.js +7 -0
  70. package/dist/labelWords.d.ts +12 -0
  71. package/dist/labelWords.js +14 -0
  72. package/dist/scrollStop.d.ts +27 -0
  73. package/dist/scrollStop.js +68 -0
  74. package/package.json +1 -1
@@ -0,0 +1,27 @@
1
+ import { type HTMLAttributes } from "react";
2
+ /** Anything the keyboard can reach on its own. */
3
+ export declare const TABBABLE = ":is(a[href], button, input:not([type=\"hidden\"]), select, textarea, summary, iframe, audio[controls], video[controls], [contenteditable]:not([contenteditable=\"false\"]), [tabindex]):not([disabled], [tabindex=\"-1\"])";
4
+ /**
5
+ * The keyboard side of a box that scrolls (a `Dialog`'s body, a sticky column): a box that
6
+ * overflows and holds nothing the keyboard can reach has no way to be scrolled from the keyboard,
7
+ * so it takes a tab stop (axe's `scrollable-region-focusable`). A box that holds a link, a field or
8
+ * a button needs none: the keyboard scrolls it by moving focus inside. `attach` is the box's `ref`;
9
+ * `element` is the box once it is mounted; `stop` is whether it takes the tab stop. Attach it to
10
+ * nothing and `stop` stays false.
11
+ */
12
+ export declare function useScrollStop(): {
13
+ element: HTMLElement | null;
14
+ attach: (el: HTMLDivElement | null) => void;
15
+ stop: boolean;
16
+ };
17
+ /**
18
+ * The props a scroller takes while it holds the tab stop: focusable, ringed and, when the column
19
+ * has a name, a group of that name so a screen reader says what the stop is. Nothing otherwise.
20
+ */
21
+ export declare function scrollStopProps(stop: boolean, name?: {
22
+ label?: string | undefined;
23
+ labelledBy?: string | undefined;
24
+ }): {
25
+ props: HTMLAttributes<HTMLDivElement>;
26
+ ring: string;
27
+ };
@@ -0,0 +1,68 @@
1
+ import { useEffect, useState } from "react";
2
+ import { focusRing } from "./cx.js";
3
+ /** Anything the keyboard can reach on its own. */
4
+ export const TABBABLE = ':is(a[href], button, input:not([type="hidden"]), select, textarea, summary, iframe, audio[controls], video[controls], [contenteditable]:not([contenteditable="false"]), [tabindex]):not([disabled], [tabindex="-1"])';
5
+ /** Whether something in `scroller` is on screen and reachable by the keyboard. */
6
+ function holdsTabbable(scroller) {
7
+ for (const el of scroller.querySelectorAll(TABBABLE)) {
8
+ // A `display: none` summary (a folded column's, from its breakpoint up) is not a tab stop.
9
+ // jsdom (a consumer's own tests) has no `checkVisibility`; there everything counts.
10
+ if (typeof el.checkVisibility !== "function" || el.checkVisibility())
11
+ return true;
12
+ }
13
+ return false;
14
+ }
15
+ /**
16
+ * The keyboard side of a box that scrolls (a `Dialog`'s body, a sticky column): a box that
17
+ * overflows and holds nothing the keyboard can reach has no way to be scrolled from the keyboard,
18
+ * so it takes a tab stop (axe's `scrollable-region-focusable`). A box that holds a link, a field or
19
+ * a button needs none: the keyboard scrolls it by moving focus inside. `attach` is the box's `ref`;
20
+ * `element` is the box once it is mounted; `stop` is whether it takes the tab stop. Attach it to
21
+ * nothing and `stop` stays false.
22
+ */
23
+ export function useScrollStop() {
24
+ const [element, setElement] = useState(null);
25
+ const [stop, setStop] = useState(false);
26
+ useEffect(() => {
27
+ // jsdom (a consumer's own tests) has no ResizeObserver.
28
+ if (!element || typeof ResizeObserver === "undefined")
29
+ return undefined;
30
+ const measure = () => setStop(element.scrollHeight > element.clientHeight && !holdsTabbable(element));
31
+ // Each observer reports once when it starts, and again when the box is resized, its content
32
+ // changes or something in it becomes (un)reachable by the keyboard.
33
+ const resize = new ResizeObserver(measure);
34
+ resize.observe(element);
35
+ for (const child of element.children)
36
+ resize.observe(child);
37
+ const change = new MutationObserver(measure);
38
+ change.observe(element, {
39
+ childList: true,
40
+ subtree: true,
41
+ characterData: true,
42
+ attributeFilter: ["disabled", "tabindex", "href", "type", "contenteditable", "open"],
43
+ });
44
+ return () => {
45
+ resize.disconnect();
46
+ change.disconnect();
47
+ };
48
+ }, [element]);
49
+ return { element, attach: setElement, stop: stop && element !== null };
50
+ }
51
+ /**
52
+ * The props a scroller takes while it holds the tab stop: focusable, ringed and, when the column
53
+ * has a name, a group of that name so a screen reader says what the stop is. Nothing otherwise.
54
+ */
55
+ export function scrollStopProps(stop, name = {}) {
56
+ if (!stop)
57
+ return { props: {}, ring: "" };
58
+ const named = name.label !== undefined || name.labelledBy !== undefined;
59
+ return {
60
+ props: {
61
+ tabIndex: 0,
62
+ ...(named ? { role: "group" } : {}),
63
+ ...(name.label !== undefined ? { "aria-label": name.label } : {}),
64
+ ...(name.labelledBy !== undefined ? { "aria-labelledby": name.labelledBy } : {}),
65
+ },
66
+ ring: focusRing,
67
+ };
68
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hackcove/ui-react",
3
- "version": "0.9.2",
3
+ "version": "0.10.1",
4
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",