@discuss-ds/ui 0.2.0 → 0.3.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
@@ -338,6 +338,17 @@ interface InlineEditProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'on
338
338
  disabled?: boolean;
339
339
  /** Called when editing starts or ends. */
340
340
  onEditingChange?: (editing: boolean) => void;
341
+ /** Called on every keystroke while editing (and with the saved value when editing ends), e.g. for a live character counter. */
342
+ onDraftChange?: (value: string) => void;
343
+ /**
344
+ * `default` is a rounded field. A short single-line label shows the pencil and hugs its text; `multiline` (longer
345
+ * text) hides the pencil and fills the width. `question` is for question text: it fills the width with no pencil,
346
+ * has square corners and shows a bottom border on hover and focus (in the input border colour) that turns accent while `selected`. Enter saves; Shift+Enter adds a
347
+ * line break when `multiline`.
348
+ */
349
+ variant?: 'default' | 'question';
350
+ /** `question` variant: keeps the bottom border while the owning question is selected. */
351
+ selected?: boolean;
341
352
  /** Classes for the displayed/edited text, e.g. a heading text style. The field itself keeps the same box. */
342
353
  textClassName?: string;
343
354
  }
package/dist/index.d.ts CHANGED
@@ -338,6 +338,17 @@ interface InlineEditProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'on
338
338
  disabled?: boolean;
339
339
  /** Called when editing starts or ends. */
340
340
  onEditingChange?: (editing: boolean) => void;
341
+ /** Called on every keystroke while editing (and with the saved value when editing ends), e.g. for a live character counter. */
342
+ onDraftChange?: (value: string) => void;
343
+ /**
344
+ * `default` is a rounded field. A short single-line label shows the pencil and hugs its text; `multiline` (longer
345
+ * text) hides the pencil and fills the width. `question` is for question text: it fills the width with no pencil,
346
+ * has square corners and shows a bottom border on hover and focus (in the input border colour) that turns accent while `selected`. Enter saves; Shift+Enter adds a
347
+ * line break when `multiline`.
348
+ */
349
+ variant?: 'default' | 'question';
350
+ /** `question` variant: keeps the bottom border while the owning question is selected. */
351
+ selected?: boolean;
341
352
  /** Classes for the displayed/edited text, e.g. a heading text style. The field itself keeps the same box. */
342
353
  textClassName?: string;
343
354
  }
package/dist/index.js CHANGED
@@ -1378,6 +1378,9 @@ var InlineEdit = React24.forwardRef(
1378
1378
  maxLength,
1379
1379
  disabled,
1380
1380
  onEditingChange,
1381
+ onDraftChange,
1382
+ variant = "default",
1383
+ selected,
1381
1384
  "aria-label": ariaLabel,
1382
1385
  ...props
1383
1386
  }, ref) => {
@@ -1413,26 +1416,32 @@ var InlineEdit = React24.forwardRef(
1413
1416
  }
1414
1417
  setEditing(false);
1415
1418
  onEditingChange?.(false);
1419
+ onDraftChange?.(commit ? draft : value);
1416
1420
  };
1417
1421
  const textClasses = cn("text-ui-input-value text-surface-foreground", textClassName);
1418
- return /* @__PURE__ */ jsx25("div", { ref, className: cn("inline-flex min-w-0 max-w-full", className), ...props, children: editing ? React24.createElement(multiline ? "textarea" : "input", {
1422
+ const question = variant === "question";
1423
+ const fill = multiline || question;
1424
+ return /* @__PURE__ */ jsx25("div", { ref, className: cn(fill ? "flex w-full min-w-0" : "inline-flex min-w-0 max-w-full", className), ...props, children: editing ? React24.createElement(multiline ? "textarea" : "input", {
1419
1425
  ref: fieldRef,
1420
1426
  value: draft,
1421
1427
  maxLength,
1422
1428
  "aria-label": ariaLabel,
1423
1429
  ...multiline ? { rows: 1 } : { type: "text" },
1424
1430
  className: cn(
1425
- "w-full rounded-md px-3 py-1 outline-none focus-visible:border-input-accent",
1431
+ "w-full px-3 py-1 outline-none",
1426
1432
  multiline && "field-sizing-content resize-none",
1427
- fieldSurfaceClasses("default"),
1433
+ question ? "rounded-none border-0 border-b-2 border-accent-foreground bg-transparent" : cn("rounded-md focus-visible:border-input-accent", fieldSurfaceClasses("default")),
1428
1434
  textClasses
1429
1435
  ),
1430
- onChange: (event) => setDraft(event.target.value),
1436
+ onChange: (event) => {
1437
+ setDraft(event.target.value);
1438
+ onDraftChange?.(event.target.value);
1439
+ },
1431
1440
  onKeyDown: (event) => {
1432
1441
  if (event.key === "Escape") {
1433
1442
  cancelled.current = true;
1434
1443
  finish(false, true);
1435
- } else if (event.key === "Enter" && (!multiline || event.metaKey || event.ctrlKey)) {
1444
+ } else if (event.key === "Enter" && (!multiline || event.metaKey || event.ctrlKey || question && !event.shiftKey)) {
1436
1445
  event.preventDefault();
1437
1446
  finish(true, true);
1438
1447
  }
@@ -1449,13 +1458,18 @@ var InlineEdit = React24.forwardRef(
1449
1458
  "aria-label": `${ariaLabel}: ${value || placeholder}. Edit`,
1450
1459
  onClick: start,
1451
1460
  className: cn(
1452
- "group inline-flex min-w-0 max-w-full items-center gap-2 rounded-md border border-transparent px-3 py-1 text-left outline-none",
1453
- "enabled:hover:bg-secondary enabled:focus-visible:border-input-accent disabled:cursor-not-allowed disabled:opacity-50",
1461
+ "group inline-flex min-w-0 max-w-full items-center gap-2 px-3 py-1 text-left outline-none",
1462
+ fill && "w-full",
1463
+ question ? (
1464
+ // Hover and focus use the input border colour; only a selected question takes the accent.
1465
+ cn("rounded-none border-b-2", selected ? "border-accent-foreground" : "border-transparent enabled:hover:border-input enabled:focus-visible:border-input")
1466
+ ) : "rounded-md border border-transparent enabled:focus-visible:border-input-accent",
1467
+ "enabled:hover:bg-secondary disabled:cursor-not-allowed disabled:opacity-50",
1454
1468
  textClasses
1455
1469
  ),
1456
1470
  children: [
1457
- /* @__PURE__ */ jsx25("span", { className: cn("min-w-0 truncate", multiline && "whitespace-pre-wrap", !value && "text-muted-foreground"), children: value || placeholder }),
1458
- /* @__PURE__ */ jsx25(
1471
+ /* @__PURE__ */ jsx25("span", { className: cn("min-w-0", multiline ? "whitespace-pre-wrap" : "truncate", fill && "flex-1", !value && "text-muted-foreground"), children: value || placeholder }),
1472
+ !fill && /* @__PURE__ */ jsx25(
1459
1473
  PencilSimple,
1460
1474
  {
1461
1475
  size: 16,
@@ -2489,10 +2503,12 @@ import { jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
2489
2503
  var eyebrow = "text-ui-caption uppercase text-secondary-muted-foreground";
2490
2504
  var QuestionHeader = React40.forwardRef(
2491
2505
  ({ className, number, title, required, error, onDismissError, editor, onTitleChange, actions, maxLength = 2e3, active, showCounter = true, ...props }, ref) => {
2506
+ const [draft, setDraft] = React40.useState(null);
2492
2507
  const label = String(number).padStart(2, "0");
2493
2508
  const showError = error !== void 0 && error !== false;
2494
2509
  if (editor) {
2495
2510
  const text = typeof title === "string" ? title : "";
2511
+ const count = (draft ?? text).length;
2496
2512
  return /* @__PURE__ */ jsxs28("div", { ref, className: cn("group/header flex w-full flex-col gap-2", className), ...props, children: [
2497
2513
  /* @__PURE__ */ jsxs28("div", { className: "flex items-center justify-between", children: [
2498
2514
  /* @__PURE__ */ jsxs28(
@@ -2512,18 +2528,26 @@ var QuestionHeader = React40.forwardRef(
2512
2528
  actions && /* @__PURE__ */ jsx41("div", { className: "flex items-center gap-2 opacity-0 transition-opacity group-focus-within/header:opacity-100 group-hover/header:opacity-100", children: actions })
2513
2529
  ] }),
2514
2530
  /* @__PURE__ */ jsx41(
2515
- "textarea",
2531
+ InlineEdit,
2516
2532
  {
2517
- rows: 1,
2533
+ variant: "question",
2534
+ multiline: true,
2535
+ selected: active,
2536
+ className: "-mx-3 w-auto",
2518
2537
  "aria-label": "Question title",
2538
+ placeholder: "Click to write the question text",
2519
2539
  value: text,
2520
2540
  maxLength,
2521
- onChange: (event) => onTitleChange?.(event.target.value),
2522
- className: "field-sizing-content w-full resize-none bg-transparent text-heading-h4 text-surface-foreground outline-none"
2541
+ onValueChange: onTitleChange,
2542
+ onDraftChange: setDraft,
2543
+ onEditingChange: (editing) => {
2544
+ if (!editing) setDraft(null);
2545
+ },
2546
+ textClassName: "text-heading-h4"
2523
2547
  }
2524
2548
  ),
2525
2549
  showCounter && /* @__PURE__ */ jsxs28("p", { className: "text-ui-caption text-secondary-muted-foreground", children: [
2526
- text.length,
2550
+ count,
2527
2551
  "/",
2528
2552
  maxLength,
2529
2553
  " characters"