@discuss-ds/ui 0.8.2 → 0.9.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.
package/dist/index.d.cts CHANGED
@@ -586,7 +586,7 @@ interface MediaInfoProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'tit
586
586
  }
587
587
  declare function MediaInfo({ name, details, onRemove, removeLabel, large, className, ...props }: MediaInfoProps): React.JSX.Element;
588
588
  interface ImagePreviewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
589
- /** The image (an `<img>` filling the area). Leave empty for the placeholder fill. */
589
+ /** The image (an `<img>`). The preview area is as tall as the image at full width, up to 336px. Leave empty for the fixed-height placeholder fill. */
590
590
  children?: React.ReactNode;
591
591
  name: string;
592
592
  details?: string[];
@@ -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
@@ -586,7 +586,7 @@ interface MediaInfoProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'tit
586
586
  }
587
587
  declare function MediaInfo({ name, details, onRemove, removeLabel, large, className, ...props }: MediaInfoProps): React.JSX.Element;
588
588
  interface ImagePreviewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
589
- /** The image (an `<img>` filling the area). Leave empty for the placeholder fill. */
589
+ /** The image (an `<img>`). The preview area is as tall as the image at full width, up to 336px. Leave empty for the fixed-height placeholder fill. */
590
590
  children?: React.ReactNode;
591
591
  name: string;
592
592
  details?: string[];
@@ -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
@@ -2249,7 +2249,17 @@ function MediaInfo({ name, details, onRemove, removeLabel, large, className, ...
2249
2249
  }
2250
2250
  var ImagePreview = React37.forwardRef(
2251
2251
  ({ className, children, name, details, onRemove, ...props }, ref) => /* @__PURE__ */ jsxs27("div", { ref, className: cn("flex w-full flex-col overflow-hidden rounded-lg border border-border", className), ...props, children: [
2252
- /* @__PURE__ */ jsx38("div", { className: "flex h-84 w-full items-center justify-center bg-accent-soft [&>img]:size-full [&>img]:object-contain", children }),
2252
+ /* @__PURE__ */ jsx38(
2253
+ "div",
2254
+ {
2255
+ className: cn(
2256
+ "flex w-full items-center justify-center bg-accent-soft",
2257
+ // With an image the area hugs it (full width, its own height, at most 336px tall); the empty placeholder keeps the fixed height.
2258
+ children ? "[&>img]:block [&>img]:h-auto [&>img]:max-h-84 [&>img]:w-full [&>img]:object-contain" : "h-84"
2259
+ ),
2260
+ children
2261
+ }
2262
+ ),
2253
2263
  /* @__PURE__ */ jsx38(MediaInfo, { name, details, onRemove, large: true })
2254
2264
  ] })
2255
2265
  );
@@ -3052,6 +3062,8 @@ RatingScale.displayName = "RatingScale";
3052
3062
  import * as React47 from "react";
3053
3063
  import { Image as ImageIcon2, ImageBroken, Trash as Trash5 } from "@phosphor-icons/react";
3054
3064
  import { jsx as jsx48, jsxs as jsxs34 } from "react/jsx-runtime";
3065
+ var MIN_THUMBNAIL = 80;
3066
+ var MAX_THUMBNAIL = 240;
3055
3067
  var StimulusCard = React47.forwardRef(
3056
3068
  ({
3057
3069
  className,
@@ -3063,6 +3075,8 @@ var StimulusCard = React47.forwardRef(
3063
3075
  caption,
3064
3076
  onCaptionChange,
3065
3077
  captionPlaceholder = "Click to write what the AI moderator says",
3078
+ maxLength,
3079
+ showCounter = true,
3066
3080
  onRemove,
3067
3081
  removeLabel = "Remove stimulus",
3068
3082
  ...props
@@ -3070,27 +3084,44 @@ var StimulusCard = React47.forwardRef(
3070
3084
  const [failedSrc, setFailedSrc] = React47.useState();
3071
3085
  const error = status === "error" || thumbnail !== void 0 && failedSrc === thumbnail;
3072
3086
  const showImage = thumbnail !== void 0 && !error;
3087
+ const [draft, setDraft] = React47.useState(null);
3088
+ const count = (draft ?? caption ?? "").length;
3089
+ const textRef = React47.useRef(null);
3090
+ const [thumbnailSize, setThumbnailSize] = React47.useState(MIN_THUMBNAIL);
3091
+ React47.useEffect(() => {
3092
+ const el = textRef.current;
3093
+ if (!el || typeof ResizeObserver === "undefined") return;
3094
+ const observer = new ResizeObserver(() => {
3095
+ setThumbnailSize(Math.min(MAX_THUMBNAIL, Math.max(MIN_THUMBNAIL, Math.ceil(el.getBoundingClientRect().height))));
3096
+ });
3097
+ observer.observe(el);
3098
+ return () => observer.disconnect();
3099
+ }, []);
3073
3100
  return /* @__PURE__ */ jsxs34(
3074
3101
  "div",
3075
3102
  {
3076
3103
  ref,
3077
3104
  "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),
3105
+ 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
3106
  ...props,
3080
3107
  children: [
3081
3108
  /* @__PURE__ */ jsxs34("div", { className: "flex min-w-0 flex-1 items-center gap-4", children: [
3082
3109
  /* @__PURE__ */ jsx48(
3083
3110
  "span",
3084
3111
  {
3112
+ style: { width: thumbnailSize, height: thumbnailSize },
3085
3113
  className: cn(
3086
- "flex size-20 shrink-0 items-center justify-center overflow-hidden rounded-sm border",
3114
+ "relative flex shrink-0 items-center justify-center overflow-hidden rounded-sm border",
3087
3115
  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
3116
  showImage && "bg-none"
3089
3117
  ),
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 })
3118
+ children: showImage ? (
3119
+ // Out of flow, so the image's own size never sets the thumbnail's size.
3120
+ /* @__PURE__ */ jsx48("img", { src: thumbnail, alt: thumbnailAlt, className: "absolute inset-0 size-full object-cover", onError: () => setFailedSrc(thumbnail) })
3121
+ ) : error ? /* @__PURE__ */ jsx48(ImageBroken, { size: 24, "aria-hidden": true }) : icon ?? /* @__PURE__ */ jsx48(ImageIcon2, { size: 24, "aria-hidden": true })
3091
3122
  }
3092
3123
  ),
3093
- /* @__PURE__ */ jsxs34("div", { className: "flex min-w-0 flex-1 flex-col gap-2 text-surface-foreground", children: [
3124
+ /* @__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
3125
  /* @__PURE__ */ jsx48("p", { className: "w-full text-ui-label", children: label }),
3095
3126
  onCaptionChange ? /* @__PURE__ */ jsx48(
3096
3127
  InlineEdit,
@@ -3101,10 +3132,21 @@ var StimulusCard = React47.forwardRef(
3101
3132
  "aria-label": "Stimulus caption",
3102
3133
  value: caption ?? "",
3103
3134
  placeholder: captionPlaceholder,
3135
+ maxLength,
3104
3136
  onValueChange: onCaptionChange,
3137
+ onDraftChange: setDraft,
3138
+ onEditingChange: (editing) => {
3139
+ if (!editing) setDraft(null);
3140
+ },
3105
3141
  textClassName: "text-body-base"
3106
3142
  }
3107
- ) : /* @__PURE__ */ jsx48("p", { className: "w-full text-body-base [word-break:break-word]", children: caption })
3143
+ ) : /* @__PURE__ */ jsx48("p", { className: "w-full text-body-base [word-break:break-word]", children: caption }),
3144
+ onCaptionChange && showCounter && maxLength !== void 0 && /* @__PURE__ */ jsxs34("p", { className: "text-ui-caption text-secondary-muted-foreground", children: [
3145
+ count,
3146
+ "/",
3147
+ maxLength,
3148
+ " characters"
3149
+ ] })
3108
3150
  ] })
3109
3151
  ] }),
3110
3152
  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 }) })