@natoe/colab 0.1.17 → 0.1.19

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.mts CHANGED
@@ -286,6 +286,15 @@ interface CollabPanelProps {
286
286
  * MessageInput, PinnedMessagesBar, etc.) inherits them via cascade.
287
287
  */
288
288
  themeMode?: 'light' | 'dark';
289
+ /**
290
+ * Controlled channel-settings overlay. When both `showSettings` and
291
+ * `onSettingsChange` are provided, the parent (e.g. CollabPopup
292
+ * rendering a settings button in its title bar) owns the open/closed
293
+ * state. When omitted, CollabPanel falls back to its own internal
294
+ * state and surfaces a button via PatientHeader.
295
+ */
296
+ showSettings?: boolean;
297
+ onSettingsChange?: (open: boolean) => void;
289
298
  /** Custom class name for the outer container */
290
299
  className?: string;
291
300
  /** Custom inline styles for the outer container */
@@ -295,7 +304,7 @@ interface CollabPanelProps {
295
304
  * Full collaboration panel: patient header + pinned bar + message thread + input.
296
305
  * Supports reply, pin/unpin, and (optionally) seen-by indicators.
297
306
  */
298
- declare function CollabPanel({ orderId, patientData, participantIds, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
307
+ declare function CollabPanel({ orderId, patientData, participantIds, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, showSettings: controlledShowSettings, onSettingsChange, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
299
308
 
300
309
  interface CollabPopupProps {
301
310
  /** Order ID for this conversation */
package/dist/index.d.ts CHANGED
@@ -286,6 +286,15 @@ interface CollabPanelProps {
286
286
  * MessageInput, PinnedMessagesBar, etc.) inherits them via cascade.
287
287
  */
288
288
  themeMode?: 'light' | 'dark';
289
+ /**
290
+ * Controlled channel-settings overlay. When both `showSettings` and
291
+ * `onSettingsChange` are provided, the parent (e.g. CollabPopup
292
+ * rendering a settings button in its title bar) owns the open/closed
293
+ * state. When omitted, CollabPanel falls back to its own internal
294
+ * state and surfaces a button via PatientHeader.
295
+ */
296
+ showSettings?: boolean;
297
+ onSettingsChange?: (open: boolean) => void;
289
298
  /** Custom class name for the outer container */
290
299
  className?: string;
291
300
  /** Custom inline styles for the outer container */
@@ -295,7 +304,7 @@ interface CollabPanelProps {
295
304
  * Full collaboration panel: patient header + pinned bar + message thread + input.
296
305
  * Supports reply, pin/unpin, and (optionally) seen-by indicators.
297
306
  */
298
- declare function CollabPanel({ orderId, patientData, participantIds, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
307
+ declare function CollabPanel({ orderId, patientData, participantIds, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, showSettings: controlledShowSettings, onSettingsChange, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
299
308
 
300
309
  interface CollabPopupProps {
301
310
  /** Order ID for this conversation */
package/dist/index.js CHANGED
@@ -1540,8 +1540,8 @@ function PatientHeader({
1540
1540
  if (patientData.bodyParts && patientData.bodyParts.length > 0) {
1541
1541
  metaParts.push(patientData.bodyParts.join(", "));
1542
1542
  }
1543
- const hasActions = hasDicom && onOpenDicom || onOpenCase || onOpenSettings;
1544
- const actions = hasActions ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.actions, children: [
1543
+ const hasPrimaryActions = hasDicom && onOpenDicom || onOpenCase;
1544
+ const primaryActions = hasPrimaryActions ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.actions, children: [
1545
1545
  hasDicom && onOpenDicom && /* @__PURE__ */ jsxRuntime.jsxs(
1546
1546
  "button",
1547
1547
  {
@@ -1568,22 +1568,22 @@ function PatientHeader({
1568
1568
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Open" })
1569
1569
  ]
1570
1570
  }
1571
- ),
1572
- onOpenSettings && /* @__PURE__ */ jsxRuntime.jsxs(
1573
- "button",
1574
- {
1575
- onClick: onOpenSettings,
1576
- style: styles.settingsIconButton,
1577
- type: "button",
1578
- "aria-label": `Open channel settings (${participants.length} participants)`,
1579
- title: "Channel participants",
1580
- children: [
1581
- /* @__PURE__ */ jsxRuntime.jsx(PeopleIcon, { size: 18, color: COLOR.neutral600 }),
1582
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.participantCount, children: participants.length })
1583
- ]
1584
- }
1585
1571
  )
1586
1572
  ] }) : null;
1573
+ const settingsButton = onOpenSettings ? /* @__PURE__ */ jsxRuntime.jsxs(
1574
+ "button",
1575
+ {
1576
+ onClick: onOpenSettings,
1577
+ style: styles.settingsIconButton,
1578
+ type: "button",
1579
+ "aria-label": `Open channel settings (${participants.length} participants)`,
1580
+ title: "Channel participants",
1581
+ children: [
1582
+ /* @__PURE__ */ jsxRuntime.jsx(PeopleIcon, { size: 18, color: COLOR.neutral600 }),
1583
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.participantCount, children: participants.length })
1584
+ ]
1585
+ }
1586
+ ) : null;
1587
1587
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: styles.container, children: [
1588
1588
  !hideName && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.nameRow, children: [
1589
1589
  onBack && /* @__PURE__ */ jsxRuntime.jsx(
@@ -1598,10 +1598,16 @@ function PatientHeader({
1598
1598
  }
1599
1599
  ),
1600
1600
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.nameText, children: resolvedName }),
1601
- actions
1601
+ primaryActions
1602
+ ] }),
1603
+ !hideName && (metaParts.length > 0 || settingsButton) && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.metaRow, children: [
1604
+ metaParts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.metaText, children: metaParts.join(" \xB7 ") }),
1605
+ settingsButton && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.metaRowEnd, children: settingsButton })
1602
1606
  ] }),
1603
- !hideName && metaParts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.metaRow, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.metaText, children: metaParts.join(" \xB7 ") }) }),
1604
- hideName && actions && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.actionsOnlyRow, children: actions })
1607
+ hideName && (primaryActions || settingsButton) && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.actionsOnlyRow, children: [
1608
+ primaryActions,
1609
+ settingsButton
1610
+ ] })
1605
1611
  ] });
1606
1612
  }
1607
1613
  var styles = {
@@ -1645,15 +1651,21 @@ var styles = {
1645
1651
  textOverflow: "ellipsis",
1646
1652
  whiteSpace: "nowrap"
1647
1653
  },
1648
- // Sub-row beneath the title — small muted meta. Aligned under the
1649
- // name (past the back button) so it visually anchors to the title
1650
- // rather than the panel edge.
1654
+ // Sub-row beneath the title — small muted meta on the left, the
1655
+ // channel-settings pill on the right when present. Meta text can
1656
+ // ellipsis on narrow widths; the settings pill stays its content
1657
+ // width and hugs the trailing edge via `metaRowEnd`.
1651
1658
  metaRow: {
1652
1659
  display: "flex",
1653
1660
  alignItems: "center",
1661
+ gap: SPACE.S2,
1654
1662
  paddingLeft: `calc(${SIZE.controlIcon} + ${SPACE.S2} - ${SPACE.S2})`,
1655
1663
  minWidth: 0
1656
1664
  },
1665
+ metaRowEnd: {
1666
+ marginLeft: "auto",
1667
+ flexShrink: 0
1668
+ },
1657
1669
  // When hideName=true (popup mode), only the actions render. The
1658
1670
  // popup's own title bar carries the name + meta above this row, so
1659
1671
  // the actions land flush-left here — pinning them to the right
@@ -1854,7 +1866,6 @@ var styles3 = {
1854
1866
  display: "flex",
1855
1867
  alignItems: "center",
1856
1868
  gap: SPACE.S1,
1857
- marginTop: "2px",
1858
1869
  fontSize: FONT_SIZE.xs,
1859
1870
  opacity: 0.85
1860
1871
  },
@@ -2562,7 +2573,7 @@ var styles5 = {
2562
2573
  minWidth: 0
2563
2574
  },
2564
2575
  bubble: {
2565
- padding: `${SPACE.S3} ${SPACE.S4}`,
2576
+ padding: `${SPACE.S2} ${SPACE.S4}`,
2566
2577
  borderRadius: "18px",
2567
2578
  wordBreak: "break-word",
2568
2579
  border: "1px solid transparent"
@@ -2609,7 +2620,7 @@ var styles5 = {
2609
2620
  alignItems: "center",
2610
2621
  justifyContent: "flex-end",
2611
2622
  gap: SPACE.S2,
2612
- marginTop: SPACE.S2,
2623
+ marginTop: SPACE.S1,
2613
2624
  fontSize: "11px",
2614
2625
  opacity: 0.85
2615
2626
  },
@@ -4214,11 +4225,16 @@ function CollabPanel({
4214
4225
  hideOpenCase = false,
4215
4226
  onConversationChange,
4216
4227
  themeMode = "light",
4228
+ showSettings: controlledShowSettings,
4229
+ onSettingsChange,
4217
4230
  className,
4218
4231
  style
4219
4232
  }) {
4220
4233
  const { config } = useCollab();
4221
- const [showSettings, setShowSettings] = React4.useState(false);
4234
+ const isSettingsControlled = controlledShowSettings !== void 0 && onSettingsChange !== void 0;
4235
+ const [internalShowSettings, setInternalShowSettings] = React4.useState(false);
4236
+ const showSettings = isSettingsControlled ? controlledShowSettings : internalShowSettings;
4237
+ const updateShowSettings = isSettingsControlled ? onSettingsChange : setInternalShowSettings;
4222
4238
  const [toast, setToast] = React4.useState(null);
4223
4239
  const messageListRef = React4.useRef(null);
4224
4240
  const {
@@ -4321,7 +4337,7 @@ function CollabPanel({
4321
4337
  onRemoveUser: channelSettings.removeUser,
4322
4338
  onLeaveChannel: channelSettings.leaveChannel,
4323
4339
  onDeleteChannel: channelSettings.deleteChannel,
4324
- onClose: () => setShowSettings(false)
4340
+ onClose: () => updateShowSettings(false)
4325
4341
  }
4326
4342
  ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4327
4343
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4331,7 +4347,7 @@ function CollabPanel({
4331
4347
  participants,
4332
4348
  onOpenDicom: handleOpenDicom,
4333
4349
  onOpenCase: handleOpenCase,
4334
- onOpenSettings: () => setShowSettings(true),
4350
+ onOpenSettings: hidePatientName ? void 0 : () => updateShowSettings(true),
4335
4351
  onBack,
4336
4352
  hideName: hidePatientName,
4337
4353
  displayName: conversation?.name
@@ -4474,6 +4490,7 @@ function CollabPopup({
4474
4490
  );
4475
4491
  const [isDragging, setIsDragging] = React4.useState(false);
4476
4492
  const [isMinimized, setIsMinimized] = React4.useState(false);
4493
+ const [showSettings, setShowSettings] = React4.useState(false);
4477
4494
  const [loadedConversation, setLoadedConversation] = React4.useState(null);
4478
4495
  const dragOffset = React4.useRef({ x: 0, y: 0 });
4479
4496
  const containerRef = React4.useRef(null);
@@ -4597,6 +4614,20 @@ function CollabPopup({
4597
4614
  })()
4598
4615
  ] }),
4599
4616
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles13.titleActions, children: [
4617
+ loadedConversation && /* @__PURE__ */ jsxRuntime.jsxs(
4618
+ "button",
4619
+ {
4620
+ type: "button",
4621
+ onClick: () => setShowSettings(true),
4622
+ style: styles13.titleSettingsPill,
4623
+ "aria-label": `Open channel settings (${loadedConversation.participants.length} participants)`,
4624
+ title: "Channel participants",
4625
+ children: [
4626
+ /* @__PURE__ */ jsxRuntime.jsx(PeopleIcon, { size: 18, color: COLOR.neutral600 }),
4627
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles13.titleParticipantCount, children: loadedConversation.participants.length })
4628
+ ]
4629
+ }
4630
+ ),
4600
4631
  /* @__PURE__ */ jsxRuntime.jsx(
4601
4632
  "button",
4602
4633
  {
@@ -4631,7 +4662,9 @@ function CollabPopup({
4631
4662
  patientData,
4632
4663
  participantIds,
4633
4664
  hidePatientName: true,
4634
- onConversationChange: setLoadedConversation
4665
+ onConversationChange: setLoadedConversation,
4666
+ showSettings,
4667
+ onSettingsChange: setShowSettings
4635
4668
  }
4636
4669
  ) })
4637
4670
  ]
@@ -4713,6 +4746,29 @@ var styles13 = {
4713
4746
  color: COLOR.neutral700,
4714
4747
  flexShrink: 0
4715
4748
  },
4749
+ // People-icon + participant-count pill in the title-actions row.
4750
+ // Matches the in-panel settings button's shape (icon + count) so a
4751
+ // user moving between popup mode and standalone mode (CollabInbox
4752
+ // compact) sees the same control language.
4753
+ titleSettingsPill: {
4754
+ height: "36px",
4755
+ display: "inline-flex",
4756
+ alignItems: "center",
4757
+ gap: SPACE.S2,
4758
+ padding: `0 ${SPACE.S3}`,
4759
+ backgroundColor: COLOR.neutral100,
4760
+ border: `1px solid ${COLOR.neutral200}`,
4761
+ borderRadius: RADIUS.md,
4762
+ color: COLOR.neutral600,
4763
+ cursor: "pointer",
4764
+ flexShrink: 0
4765
+ },
4766
+ titleParticipantCount: {
4767
+ fontSize: FONT_SIZE.sm,
4768
+ fontWeight: FONT_WEIGHT.semibold,
4769
+ color: COLOR.neutral700,
4770
+ fontVariantNumeric: "tabular-nums"
4771
+ },
4716
4772
  panelWrapper: {
4717
4773
  flex: 1,
4718
4774
  overflow: "hidden"
@@ -4882,6 +4938,7 @@ function useInlineCollab({
4882
4938
  }
4883
4939
  try {
4884
4940
  await socket.sendMessage(preview.conversationId, payload);
4941
+ setUnreadCount(0);
4885
4942
  } catch (err) {
4886
4943
  setError(err instanceof Error ? err.message : "Failed to send message");
4887
4944
  config.onError?.({
@@ -5213,8 +5270,6 @@ function InlineInputBar({
5213
5270
  }
5214
5271
  }
5215
5272
  ),
5216
- unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles14.unreadBadge, title: `${unreadCount} unread`, children: unreadCount > 99 ? "99+" : unreadCount }),
5217
- isLive && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles14.liveDot, title: "Live updates active" }),
5218
5273
  text.trim() && /* @__PURE__ */ jsxRuntime.jsx(
5219
5274
  "button",
5220
5275
  {
@@ -5226,6 +5281,8 @@ function InlineInputBar({
5226
5281
  children: "\u27A4"
5227
5282
  }
5228
5283
  ),
5284
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles14.unreadBadge, title: `${unreadCount} unread`, children: unreadCount > 99 ? "99+" : unreadCount }),
5285
+ isLive && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles14.liveDot, title: "Live updates active" }),
5229
5286
  showExpand && onExpand && /* @__PURE__ */ jsxRuntime.jsx(
5230
5287
  "button",
5231
5288
  {