@geomak/ui 6.29.1 → 6.30.0

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/dist/index.d.cts CHANGED
@@ -1381,6 +1381,8 @@ interface ChatProps {
1381
1381
  disabled?: boolean;
1382
1382
  /** Hide the composer entirely (read-only transcript). */
1383
1383
  hideComposer?: boolean;
1384
+ /** Shows a transcript skeleton (use while loading history, before messages arrive). */
1385
+ loading?: boolean;
1384
1386
  /** Shown when there are no messages. */
1385
1387
  emptyState?: React__default.ReactNode;
1386
1388
  /** Overall height — the message list scrolls within it. Default `480`. */
@@ -1406,7 +1408,7 @@ interface ChatProps {
1406
1408
  * onSend={(text) => send(text)}
1407
1409
  * />
1408
1410
  */
1409
- declare function Chat({ messages, currentUserId, onSend, typingNames, title, subtitle, avatar, headerActions, placeholder, disabled, hideComposer, emptyState, height, className, style, }: ChatProps): react_jsx_runtime.JSX.Element;
1411
+ declare function Chat({ messages, currentUserId, onSend, typingNames, title, subtitle, avatar, headerActions, placeholder, disabled, hideComposer, loading, emptyState, height, className, style, }: ChatProps): react_jsx_runtime.JSX.Element;
1410
1412
 
1411
1413
  type StatisticSize = 'sm' | 'md' | 'lg';
1412
1414
  type DeltaDirection = 'up' | 'down' | 'neutral';
package/dist/index.d.ts CHANGED
@@ -1381,6 +1381,8 @@ interface ChatProps {
1381
1381
  disabled?: boolean;
1382
1382
  /** Hide the composer entirely (read-only transcript). */
1383
1383
  hideComposer?: boolean;
1384
+ /** Shows a transcript skeleton (use while loading history, before messages arrive). */
1385
+ loading?: boolean;
1384
1386
  /** Shown when there are no messages. */
1385
1387
  emptyState?: React__default.ReactNode;
1386
1388
  /** Overall height — the message list scrolls within it. Default `480`. */
@@ -1406,7 +1408,7 @@ interface ChatProps {
1406
1408
  * onSend={(text) => send(text)}
1407
1409
  * />
1408
1410
  */
1409
- declare function Chat({ messages, currentUserId, onSend, typingNames, title, subtitle, avatar, headerActions, placeholder, disabled, hideComposer, emptyState, height, className, style, }: ChatProps): react_jsx_runtime.JSX.Element;
1411
+ declare function Chat({ messages, currentUserId, onSend, typingNames, title, subtitle, avatar, headerActions, placeholder, disabled, hideComposer, loading, emptyState, height, className, style, }: ChatProps): react_jsx_runtime.JSX.Element;
1410
1412
 
1411
1413
  type StatisticSize = 'sm' | 'md' | 'lg';
1412
1414
  type DeltaDirection = 'up' | 'down' | 'neutral';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { colors_default } from './chunk-I2P4JJDB.js';
2
2
  export { colors_default as COLORS, PALETTE as palette, semanticTokens, vars } from './chunk-I2P4JJDB.js';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
- import React28, { createContext, useState, useEffect, useMemo, useId, useCallback, useRef, useLayoutEffect, useContext, useSyncExternalStore } from 'react';
4
+ import React28, { createContext, useState, useEffect, useMemo, useId, useCallback, useRef, useContext, useSyncExternalStore, useLayoutEffect } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
7
7
  import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
@@ -2267,6 +2267,19 @@ var ArrowDown = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "
2267
2267
  function TypingDots() {
2268
2268
  return /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-1", "aria-hidden": "true", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-bounce rounded-full bg-foreground-muted", style: { animationDelay: `${i * 0.15}s` } }, i)) });
2269
2269
  }
2270
+ var SKELETON_ROWS = [
2271
+ { own: false, w: 150 },
2272
+ { own: false, w: 110 },
2273
+ { own: true, w: 180 },
2274
+ { own: false, w: 130 },
2275
+ { own: true, w: 90 }
2276
+ ];
2277
+ function ChatSkeleton() {
2278
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", "aria-hidden": "true", children: SKELETON_ROWS.map((r, i) => /* @__PURE__ */ jsxs("div", { className: ["flex items-end gap-2", r.own ? "flex-row-reverse" : ""].filter(Boolean).join(" "), children: [
2279
+ !r.own && /* @__PURE__ */ jsx("span", { className: "h-6 w-6 flex-shrink-0 animate-pulse rounded-full bg-surface" }),
2280
+ /* @__PURE__ */ jsx("span", { className: "h-8 animate-pulse rounded-2xl bg-surface", style: { width: r.w } })
2281
+ ] }, i)) });
2282
+ }
2270
2283
  function Chat({
2271
2284
  messages,
2272
2285
  currentUserId,
@@ -2279,6 +2292,7 @@ function Chat({
2279
2292
  placeholder = "Write a message\u2026",
2280
2293
  disabled = false,
2281
2294
  hideComposer = false,
2295
+ loading = false,
2282
2296
  emptyState,
2283
2297
  height = 480,
2284
2298
  className = "",
@@ -2288,7 +2302,6 @@ function Chat({
2288
2302
  const atBottomRef = useRef(true);
2289
2303
  const [showJump, setShowJump] = useState(false);
2290
2304
  const [draft, setDraft] = useState("");
2291
- const taRef = useRef(null);
2292
2305
  const hasHeader = title != null || subtitle != null || avatar != null || headerActions != null;
2293
2306
  const isTyping = typingNames.length > 0;
2294
2307
  const scrollToBottom = useCallback((smooth = true) => {
@@ -2310,12 +2323,6 @@ function Chat({
2310
2323
  useEffect(() => {
2311
2324
  scrollToBottom(false);
2312
2325
  }, [scrollToBottom]);
2313
- useLayoutEffect(() => {
2314
- const ta = taRef.current;
2315
- if (!ta) return;
2316
- ta.style.height = "auto";
2317
- ta.style.height = `${Math.min(ta.scrollHeight, 120)}px`;
2318
- }, [draft]);
2319
2326
  const send = () => {
2320
2327
  const text = draft.trim();
2321
2328
  if (!text || disabled) return;
@@ -2344,7 +2351,7 @@ function Chat({
2344
2351
  ] }),
2345
2352
  /* @__PURE__ */ jsxs("div", { className: "relative flex-1 overflow-hidden", children: [
2346
2353
  /* @__PURE__ */ jsxs("div", { ref: listRef, onScroll, className: "flex h-full flex-col gap-1 overflow-y-auto bg-background px-4 py-3", children: [
2347
- messages.length === 0 && !isTyping ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center text-center text-sm text-foreground-muted", children: emptyState ?? "No messages yet. Say hello \u{1F44B}" }) : messages.map((m, i) => {
2354
+ loading && messages.length === 0 ? /* @__PURE__ */ jsx(ChatSkeleton, {}) : messages.length === 0 && !isTyping ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center text-center text-sm text-foreground-muted", children: emptyState ?? "No messages yet. Say hello \u{1F44B}" }) : messages.map((m, i) => {
2348
2355
  const own = m.authorId === currentUserId;
2349
2356
  const prev = messages[i - 1];
2350
2357
  const next = messages[i + 1];
@@ -2399,15 +2406,14 @@ function Chat({
2399
2406
  /* @__PURE__ */ jsx(
2400
2407
  "textarea",
2401
2408
  {
2402
- ref: taRef,
2403
- rows: 1,
2409
+ rows: 2,
2404
2410
  value: draft,
2405
2411
  disabled,
2406
2412
  placeholder,
2407
2413
  onChange: (e) => setDraft(e.target.value),
2408
2414
  onKeyDown,
2409
2415
  "aria-label": "Message",
2410
- className: `${fieldShell({ size: "md", hasError: false, disabled, sized: false })} max-h-[120px] flex-1 resize-none px-3 py-2 leading-snug`
2416
+ className: `${fieldShell({ size: "md", hasError: false, disabled, sized: false })} h-[4.5rem] flex-1 resize-none px-3 py-2 leading-snug`
2411
2417
  }
2412
2418
  ),
2413
2419
  /* @__PURE__ */ jsx(
@@ -5525,9 +5531,10 @@ function Pagination({
5525
5531
  }, [serverSide, options.perPage, picker]);
5526
5532
  const currentOpt = picker.find((o) => o.key === displayPerPageKey);
5527
5533
  const currentPerPageLabel = currentOpt?.label ?? currentOpt?.value ?? options.perPage ?? "";
5528
- const navBtn = (icon, disabled, onClick, title) => /* @__PURE__ */ jsx(Button_default, { variant: "outline", size: "sm", disabled, onClick, icon, className: "w-7 !px-0 focus-visible:!ring-[3px] focus-visible:!ring-focus-ring focus-visible:!ring-offset-0", "aria-label": title, title });
5529
- const chevronRight = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
5530
- const doubleChevronRight = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 5l7 7-7 7M5 5l7 7-7 7" }) });
5534
+ const FOCUS = "focus-visible:!ring-0 focus-visible:!border-accent";
5535
+ const navBtn = (icon, disabled, onClick, title) => /* @__PURE__ */ jsx(Button_default, { variant: "outline", size: "sm", disabled, onClick, icon, className: `w-7 !px-0 ${FOCUS}`, "aria-label": title, title });
5536
+ const chevronRight = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 6l6 6-6 6" }) });
5537
+ const doubleChevronRight = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l6 6-6 6M12 6l6 6-6 6" }) });
5531
5538
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-x-4 gap-y-3", children: [
5532
5539
  options.withPicker && /* @__PURE__ */ jsxs("div", { className: "mr-auto flex items-center gap-2", children: [
5533
5540
  /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap text-xs text-foreground-muted", children: "Rows per page" }),
@@ -5537,7 +5544,7 @@ function Pagination({
5537
5544
  variant: "outline",
5538
5545
  size: "sm",
5539
5546
  side: "top",
5540
- className: "focus-visible:!ring-[3px] focus-visible:!ring-focus-ring focus-visible:!ring-offset-0",
5547
+ className: FOCUS,
5541
5548
  label: String(currentPerPageLabel),
5542
5549
  items: picker.map((o) => ({
5543
5550
  key: o.key,
@@ -5553,7 +5560,7 @@ function Pagination({
5553
5560
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
5554
5561
  navBtn(/* @__PURE__ */ jsx("span", { className: "inline-flex rotate-180", children: doubleChevronRight }), activePage === 0, () => onPageChange(0), "First page"),
5555
5562
  navBtn(/* @__PURE__ */ jsx("span", { className: "inline-flex rotate-180", children: chevronRight }), activePage === 0, () => activePage > 0 && onPageChange(activePage - 1), "Previous page"),
5556
- /* @__PURE__ */ jsxs("span", { className: "px-2 text-sm tabular-nums text-foreground-secondary select-none", children: [
5563
+ /* @__PURE__ */ jsxs("span", { className: "px-2 text-xs tabular-nums text-foreground-secondary select-none", children: [
5557
5564
  activePage + 1,
5558
5565
  " ",
5559
5566
  /* @__PURE__ */ jsxs("span", { className: "text-foreground-muted", children: [