@discuss-ds/ui 0.8.2 → 0.9.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
@@ -803,6 +803,10 @@ interface StimulusCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, '
803
803
  onCaptionChange?: (caption: string) => void;
804
804
  /** Editor mode: placeholder for an empty caption. */
805
805
  captionPlaceholder?: string;
806
+ /** Editor mode: caption length limit. Also what the counter counts against. */
807
+ maxLength?: number;
808
+ /** Editor mode: shows the "n/2000 characters" counter under the caption (it counts live while typing). Defaults to `true`; needs `maxLength`. */
809
+ showCounter?: boolean;
806
810
  /** Shows the trash button. */
807
811
  onRemove?: () => void;
808
812
  /** Accessible name of the trash button. Defaults to "Remove stimulus". */
package/dist/index.d.ts CHANGED
@@ -803,6 +803,10 @@ interface StimulusCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, '
803
803
  onCaptionChange?: (caption: string) => void;
804
804
  /** Editor mode: placeholder for an empty caption. */
805
805
  captionPlaceholder?: string;
806
+ /** Editor mode: caption length limit. Also what the counter counts against. */
807
+ maxLength?: number;
808
+ /** Editor mode: shows the "n/2000 characters" counter under the caption (it counts live while typing). Defaults to `true`; needs `maxLength`. */
809
+ showCounter?: boolean;
806
810
  /** Shows the trash button. */
807
811
  onRemove?: () => void;
808
812
  /** Accessible name of the trash button. Defaults to "Remove stimulus". */
package/dist/index.js CHANGED
@@ -3052,6 +3052,8 @@ RatingScale.displayName = "RatingScale";
3052
3052
  import * as React47 from "react";
3053
3053
  import { Image as ImageIcon2, ImageBroken, Trash as Trash5 } from "@phosphor-icons/react";
3054
3054
  import { jsx as jsx48, jsxs as jsxs34 } from "react/jsx-runtime";
3055
+ var MIN_THUMBNAIL = 80;
3056
+ var MAX_THUMBNAIL = 240;
3055
3057
  var StimulusCard = React47.forwardRef(
3056
3058
  ({
3057
3059
  className,
@@ -3063,6 +3065,8 @@ var StimulusCard = React47.forwardRef(
3063
3065
  caption,
3064
3066
  onCaptionChange,
3065
3067
  captionPlaceholder = "Click to write what the AI moderator says",
3068
+ maxLength,
3069
+ showCounter = true,
3066
3070
  onRemove,
3067
3071
  removeLabel = "Remove stimulus",
3068
3072
  ...props
@@ -3070,27 +3074,44 @@ var StimulusCard = React47.forwardRef(
3070
3074
  const [failedSrc, setFailedSrc] = React47.useState();
3071
3075
  const error = status === "error" || thumbnail !== void 0 && failedSrc === thumbnail;
3072
3076
  const showImage = thumbnail !== void 0 && !error;
3077
+ const [draft, setDraft] = React47.useState(null);
3078
+ const count = (draft ?? caption ?? "").length;
3079
+ const textRef = React47.useRef(null);
3080
+ const [thumbnailSize, setThumbnailSize] = React47.useState(MIN_THUMBNAIL);
3081
+ React47.useEffect(() => {
3082
+ const el = textRef.current;
3083
+ if (!el || typeof ResizeObserver === "undefined") return;
3084
+ const observer = new ResizeObserver(() => {
3085
+ setThumbnailSize(Math.min(MAX_THUMBNAIL, Math.max(MIN_THUMBNAIL, Math.ceil(el.getBoundingClientRect().height))));
3086
+ });
3087
+ observer.observe(el);
3088
+ return () => observer.disconnect();
3089
+ }, []);
3073
3090
  return /* @__PURE__ */ jsxs34(
3074
3091
  "div",
3075
3092
  {
3076
3093
  ref,
3077
3094
  "data-status": error ? "error" : "default",
3078
- className: cn("flex w-full items-center gap-4 rounded-md border border-border p-0.5 pr-4", className),
3095
+ className: cn("flex min-h-21.5 w-full items-center gap-4 rounded-md border border-border p-0.5 pr-4", className),
3079
3096
  ...props,
3080
3097
  children: [
3081
3098
  /* @__PURE__ */ jsxs34("div", { className: "flex min-w-0 flex-1 items-center gap-4", children: [
3082
3099
  /* @__PURE__ */ jsx48(
3083
3100
  "span",
3084
3101
  {
3102
+ style: { width: thumbnailSize, height: thumbnailSize },
3085
3103
  className: cn(
3086
- "flex size-20 shrink-0 items-center justify-center overflow-hidden rounded-sm border",
3104
+ "relative flex shrink-0 items-center justify-center overflow-hidden rounded-sm border",
3087
3105
  error ? "border-destructive-soft-border bg-destructive-soft bg-linear-to-r from-overlay-dark-5 to-overlay-dark-5 text-destructive" : "border-border bg-background bg-linear-to-r from-overlay-dark-5 to-overlay-dark-5 text-surface-foreground",
3088
3106
  showImage && "bg-none"
3089
3107
  ),
3090
- children: showImage ? /* @__PURE__ */ jsx48("img", { src: thumbnail, alt: thumbnailAlt, className: "size-full object-cover", onError: () => setFailedSrc(thumbnail) }) : error ? /* @__PURE__ */ jsx48(ImageBroken, { size: 24, "aria-hidden": true }) : icon ?? /* @__PURE__ */ jsx48(ImageIcon2, { size: 24, "aria-hidden": true })
3108
+ children: showImage ? (
3109
+ // Out of flow, so the image's own size never sets the thumbnail's size.
3110
+ /* @__PURE__ */ jsx48("img", { src: thumbnail, alt: thumbnailAlt, className: "absolute inset-0 size-full object-cover", onError: () => setFailedSrc(thumbnail) })
3111
+ ) : error ? /* @__PURE__ */ jsx48(ImageBroken, { size: 24, "aria-hidden": true }) : icon ?? /* @__PURE__ */ jsx48(ImageIcon2, { size: 24, "aria-hidden": true })
3091
3112
  }
3092
3113
  ),
3093
- /* @__PURE__ */ jsxs34("div", { className: "flex min-w-0 flex-1 flex-col gap-2 text-surface-foreground", children: [
3114
+ /* @__PURE__ */ jsxs34("div", { ref: textRef, className: "flex min-w-0 flex-1 flex-col justify-center gap-2 py-2 text-surface-foreground", children: [
3094
3115
  /* @__PURE__ */ jsx48("p", { className: "w-full text-ui-label", children: label }),
3095
3116
  onCaptionChange ? /* @__PURE__ */ jsx48(
3096
3117
  InlineEdit,
@@ -3101,10 +3122,21 @@ var StimulusCard = React47.forwardRef(
3101
3122
  "aria-label": "Stimulus caption",
3102
3123
  value: caption ?? "",
3103
3124
  placeholder: captionPlaceholder,
3125
+ maxLength,
3104
3126
  onValueChange: onCaptionChange,
3127
+ onDraftChange: setDraft,
3128
+ onEditingChange: (editing) => {
3129
+ if (!editing) setDraft(null);
3130
+ },
3105
3131
  textClassName: "text-body-base"
3106
3132
  }
3107
- ) : /* @__PURE__ */ jsx48("p", { className: "w-full text-body-base [word-break:break-word]", children: caption })
3133
+ ) : /* @__PURE__ */ jsx48("p", { className: "w-full text-body-base [word-break:break-word]", children: caption }),
3134
+ onCaptionChange && showCounter && maxLength !== void 0 && /* @__PURE__ */ jsxs34("p", { className: "text-ui-caption text-secondary-muted-foreground", children: [
3135
+ count,
3136
+ "/",
3137
+ maxLength,
3138
+ " characters"
3139
+ ] })
3108
3140
  ] })
3109
3141
  ] }),
3110
3142
  onRemove && /* @__PURE__ */ jsx48(Button, { type: "button", variant: "destructive", size: "icon-sm", "aria-label": removeLabel, onClick: onRemove, children: /* @__PURE__ */ jsx48(Trash5, { size: 16, "aria-hidden": true }) })