@caqophony/ui 0.6.0 → 0.6.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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CONTENT_GUTTER,
4
4
  Icon
5
- } from "./chunk-ID7VBGJA.js";
5
+ } from "./chunk-KXI4UQPS.js";
6
6
  import {
7
7
  cn
8
8
  } from "./chunk-DP5WQAPO.js";
@@ -127,8 +127,8 @@ function Icon({ name, size, className }) {
127
127
  var SCROLL_HINT_X = "";
128
128
  var SCROLL_HINT_X_MARK = "pointer-events-none sticky right-0 z-10 ml-2 flex w-6 shrink-0 items-center justify-end self-stretch data-[scroll-hint-state=end]:invisible data-[scroll-hint-state=none]:hidden";
129
129
  var SCROLL_HINT_X_PILL = "flex h-full max-h-6 w-6 items-center justify-center rounded-full border border-border bg-card text-muted-foreground shadow-sm";
130
- var SCROLL_HINT_Y = "[container-type:scroll-state]";
131
- var SCROLL_HINT_Y_MARK = "pointer-events-none sticky bottom-1 -mt-7 flex h-6 justify-center supports-[container-type:scroll-state]:[@container_not_scroll-state(scrollable:bottom)]:hidden";
130
+ var SCROLL_HINT_Y_MARK = "pointer-events-none flex h-7 shrink-0 items-end justify-center data-[scroll-hint-state=end]:invisible data-[scroll-hint-state=none]:hidden";
131
+ var SCROLL_HINT_Y_PILL = "flex h-6 items-center rounded-full border border-border bg-card px-2 text-muted-foreground shadow-sm";
132
132
 
133
133
  // src/scroll-hint-mark.tsx
134
134
  import * as React from "react";
@@ -190,6 +190,43 @@ function watchScrollHintX(mark, write) {
190
190
  added.disconnect();
191
191
  };
192
192
  }
193
+ function scrollHintYState(box) {
194
+ if (box.scrollHeight <= box.clientHeight + 1) return "none";
195
+ return box.scrollTop + box.clientHeight >= box.scrollHeight - 1 ? "end" : "more";
196
+ }
197
+ function watchScrollHintY(mark, write) {
198
+ const scroller = mark.previousElementSibling;
199
+ if (scroller === null) {
200
+ throw new Error(
201
+ "watchScrollHintY: the mark has no element before it; it must be the next sibling of the box that scrolls on y"
202
+ );
203
+ }
204
+ let last;
205
+ const update = () => {
206
+ const state = scrollHintYState(scroller);
207
+ if (state === last) return;
208
+ last = state;
209
+ write(state);
210
+ };
211
+ const resize = new ResizeObserver(update);
212
+ const observeChildren = () => {
213
+ for (const child of Array.from(scroller.children)) resize.observe(child);
214
+ };
215
+ resize.observe(scroller);
216
+ observeChildren();
217
+ const added = new MutationObserver(() => {
218
+ observeChildren();
219
+ update();
220
+ });
221
+ added.observe(scroller, { childList: true });
222
+ scroller.addEventListener("scroll", update, { passive: true });
223
+ update();
224
+ return () => {
225
+ scroller.removeEventListener("scroll", update);
226
+ resize.disconnect();
227
+ added.disconnect();
228
+ };
229
+ }
193
230
 
194
231
  // src/scroll-hint-mark.tsx
195
232
  import { jsx as jsx2 } from "react/jsx-runtime";
@@ -217,6 +254,30 @@ function ScrollHintXMark() {
217
254
  }
218
255
  );
219
256
  }
257
+ function ScrollHintYMark() {
258
+ const ref = React.useRef(null);
259
+ React.useEffect(() => {
260
+ const mark = ref.current;
261
+ if (mark === null) {
262
+ throw new Error(
263
+ "ScrollHintYMark: the effect ran with no mounted mark (ref.current is null)"
264
+ );
265
+ }
266
+ return watchScrollHintY(mark, (state) => {
267
+ mark.dataset.scrollHintState = state;
268
+ });
269
+ }, []);
270
+ return /* @__PURE__ */ jsx2(
271
+ "div",
272
+ {
273
+ ref,
274
+ "aria-hidden": "true",
275
+ "data-scroll-hint": "y",
276
+ className: SCROLL_HINT_Y_MARK,
277
+ children: /* @__PURE__ */ jsx2("span", { className: SCROLL_HINT_Y_PILL, children: /* @__PURE__ */ jsx2(Icon, { name: "chevron-down", size: "sm" }) })
278
+ }
279
+ );
280
+ }
220
281
 
221
282
  // src/content-gutter.ts
222
283
  var CONTENT_GUTTER = "px-4 sm:px-6 lg:px-8";
@@ -226,8 +287,7 @@ export {
226
287
  ICON_SIZES,
227
288
  Icon,
228
289
  SCROLL_HINT_X,
229
- SCROLL_HINT_Y,
230
- SCROLL_HINT_Y_MARK,
231
290
  ScrollHintXMark,
291
+ ScrollHintYMark,
232
292
  CONTENT_GUTTER
233
293
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  Icon
4
- } from "./chunk-ID7VBGJA.js";
4
+ } from "./chunk-KXI4UQPS.js";
5
5
  import {
6
6
  cn
7
7
  } from "./chunk-DP5WQAPO.js";
package/dist/index.d.mts CHANGED
@@ -1044,10 +1044,10 @@ declare const DrawerFooter: React.ForwardRefExoticComponent<React.HTMLAttributes
1044
1044
  * Authored by Caqophony
1045
1045
  */
1046
1046
 
1047
- interface FormLayoutProps extends React.FormHTMLAttributes<HTMLFormElement> {
1047
+ interface FormLayoutProps extends React.HTMLAttributes<HTMLDivElement> {
1048
1048
  layout?: 'stacked' | 'inline' | 'two-column';
1049
1049
  }
1050
- declare const FormLayout: React.ForwardRefExoticComponent<FormLayoutProps & React.RefAttributes<HTMLFormElement>>;
1050
+ declare const FormLayout: React.ForwardRefExoticComponent<FormLayoutProps & React.RefAttributes<HTMLDivElement>>;
1051
1051
  interface FormLayoutSectionProps extends React.HTMLAttributes<HTMLDivElement> {
1052
1052
  title?: string;
1053
1053
  description?: string;
@@ -1922,10 +1922,17 @@ declare const StageList: React.ForwardRefExoticComponent<StageListProps & React.
1922
1922
  * One-artifact W1 (#5823): run 7 wrapped a 9-step `Stepper` in
1923
1923
  * `max-h-[160px] overflow-y-auto`, which cut the stepper's line under the
1924
1924
  * clip with no sign there was more. This owns the clamp: a height from the
1925
- * scale, the scroll, the scroll hint (`SCROLL_HINT_Y`: a chevron mark on the
1926
- * end edge while there is more below), and keyboard access
1927
- * (a scrolling region is focusable and named, WCAG 2.1.1). A page never
1928
- * writes `max-h-*` with `overflow-*` around a library component.
1925
+ * scale, the scroll, the scroll hint, and keyboard access (a scrolling region
1926
+ * is focusable and named, WCAG 2.1.1). A page never writes `max-h-*` with
1927
+ * `overflow-*` around a library component.
1928
+ *
1929
+ * #6089: the hint was drawn inside the box, sticky over its last row, and on
1930
+ * the approved application detail it sat over a line in full view ("Target
1931
+ * range discussed with recruiter"). It is now a row of its own under the box
1932
+ * (`ScrollHintYMark`, `scroll-hint.ts`): never over a row, hidden when the
1933
+ * rows fit, and empty (its room kept) once the reader has scrolled to the end.
1934
+ * The region (its `ref`, `className`, `data-testid` and every other prop) is
1935
+ * the box that scrolls, as before; the two sit in a column.
1929
1936
  *
1930
1937
  * Usage:
1931
1938
  * import { ClampedList } from '@caqophony/ui';
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  buttonVariants,
12
12
  useModalSurface,
13
13
  usePanelRef
14
- } from "./chunk-WGWKCFG2.js";
14
+ } from "./chunk-K2B226HX.js";
15
15
  import {
16
16
  Alert,
17
17
  EMPTY_VALUE_GLYPH,
@@ -19,17 +19,16 @@ import {
19
19
  INTENT_SUBTLE_STYLES,
20
20
  IntentBadge,
21
21
  isEmptyValue
22
- } from "./chunk-44GEHNZP.js";
22
+ } from "./chunk-MRVYXQE3.js";
23
23
  import {
24
24
  CONTENT_GUTTER,
25
25
  ICONS,
26
26
  ICON_SIZES,
27
27
  Icon,
28
28
  SCROLL_HINT_X,
29
- SCROLL_HINT_Y,
30
- SCROLL_HINT_Y_MARK,
31
- ScrollHintXMark
32
- } from "./chunk-ID7VBGJA.js";
29
+ ScrollHintXMark,
30
+ ScrollHintYMark
31
+ } from "./chunk-KXI4UQPS.js";
33
32
  import {
34
33
  cn
35
34
  } from "./chunk-DP5WQAPO.js";
@@ -3146,7 +3145,7 @@ import * as React20 from "react";
3146
3145
  import { jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
3147
3146
  var FormLayout = React20.forwardRef(
3148
3147
  ({ className, layout = "stacked", ...props }, ref) => /* @__PURE__ */ jsx31(
3149
- "form",
3148
+ "div",
3150
3149
  {
3151
3150
  ref,
3152
3151
  className: cn(
@@ -4791,34 +4790,25 @@ var MAX_HEIGHTS = {
4791
4790
  lg: "max-h-96"
4792
4791
  };
4793
4792
  var ClampedList = React41.forwardRef(
4794
- ({ className, label, maxHeight = "md", children, ...props }, ref) => /* @__PURE__ */ jsxs44(
4795
- "div",
4796
- {
4797
- ref,
4798
- role: "region",
4799
- "aria-label": label,
4800
- tabIndex: 0,
4801
- className: cn(
4802
- "min-w-0 overflow-y-auto overscroll-contain rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4803
- MAX_HEIGHTS[maxHeight],
4804
- SCROLL_HINT_Y,
4805
- className
4806
- ),
4807
- ...props,
4808
- children: [
4809
- children,
4810
- /* @__PURE__ */ jsx55(
4811
- "div",
4812
- {
4813
- "aria-hidden": "true",
4814
- "data-scroll-hint": "y",
4815
- className: SCROLL_HINT_Y_MARK,
4816
- children: /* @__PURE__ */ jsx55("span", { className: "flex items-center rounded-full border border-border bg-card px-2 text-muted-foreground shadow-sm", children: /* @__PURE__ */ jsx55(Icon, { name: "chevron-down", size: "sm" }) })
4817
- }
4818
- )
4819
- ]
4820
- }
4821
- )
4793
+ ({ className, label, maxHeight = "md", children, ...props }, ref) => /* @__PURE__ */ jsxs44("div", { className: "flex min-w-0 flex-col", children: [
4794
+ /* @__PURE__ */ jsx55(
4795
+ "div",
4796
+ {
4797
+ ref,
4798
+ role: "region",
4799
+ "aria-label": label,
4800
+ tabIndex: 0,
4801
+ className: cn(
4802
+ "min-w-0 overflow-y-auto overscroll-contain rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4803
+ MAX_HEIGHTS[maxHeight],
4804
+ className
4805
+ ),
4806
+ ...props,
4807
+ children
4808
+ }
4809
+ ),
4810
+ /* @__PURE__ */ jsx55(ScrollHintYMark, {})
4811
+ ] })
4822
4812
  );
4823
4813
  ClampedList.displayName = "ClampedList";
4824
4814
 
package/dist/marketing.js CHANGED
@@ -3,17 +3,17 @@ import {
3
3
  Button,
4
4
  Container,
5
5
  NavbarMenu
6
- } from "./chunk-WGWKCFG2.js";
6
+ } from "./chunk-K2B226HX.js";
7
7
  import {
8
8
  ScrollHintXMark
9
- } from "./chunk-ID7VBGJA.js";
9
+ } from "./chunk-KXI4UQPS.js";
10
10
  import {
11
11
  cn
12
12
  } from "./chunk-DP5WQAPO.js";
13
13
 
14
14
  // src/marketing/marketing-header.tsx
15
15
  import * as React from "react";
16
- import { createPortal } from "react-dom";
16
+ import { createPortal, flushSync } from "react-dom";
17
17
  import { jsx, jsxs } from "react/jsx-runtime";
18
18
  var NIGHT_GROUND = "-mb-16 justify-between border-b border-border bg-night-0/80 backdrop-blur-md";
19
19
  var ON_DARK_GROUND = "text-background dark:text-foreground";
@@ -50,7 +50,7 @@ function useFold(refs, actionsHost, hasLinks) {
50
50
  );
51
51
  };
52
52
  measure();
53
- const observer = new ResizeObserver(measure);
53
+ const observer = new ResizeObserver(() => flushSync(measure));
54
54
  for (const el of [
55
55
  bar.current,
56
56
  brandText.current,
package/dist/material.js CHANGED
@@ -1,12 +1,12 @@
1
1
  'use client';
2
2
  import {
3
3
  IntentBadge
4
- } from "./chunk-44GEHNZP.js";
4
+ } from "./chunk-MRVYXQE3.js";
5
5
  import {
6
6
  CONTENT_GUTTER,
7
7
  Icon,
8
8
  ScrollHintXMark
9
- } from "./chunk-ID7VBGJA.js";
9
+ } from "./chunk-KXI4UQPS.js";
10
10
  import {
11
11
  cn
12
12
  } from "./chunk-DP5WQAPO.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caqophony/ui",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Generic, optional React UI reuse library (Gossamer tokens). Centrally maintained by Caqophony.",
5
5
  "license": "MIT",
6
6
  "author": "Caqophony",