@lessly/ui 0.15.0 → 0.16.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.ts CHANGED
@@ -1381,9 +1381,8 @@ interface NotificationBellProps {
1381
1381
  /** Rows shown in the popover. Default 5. */
1382
1382
  maxItems?: number;
1383
1383
  /**
1384
- * Reported when the consumer's own row affordance marks a single item read.
1385
- * The default row surfaces the item through `onItemClick`; this exists so a
1386
- * host that renders its own control has the callback the model expects.
1384
+ * Marks a single item read. Supplying it puts a quiet check control on every
1385
+ * unread row "Read all" and clicking through are no longer the only ways.
1387
1386
  */
1388
1387
  onMarkRead?(id: string): void;
1389
1388
  onMarkAllRead?(): void;
@@ -1415,6 +1414,11 @@ interface NotificationCenterProps {
1415
1414
  onDeepLinkClick?(item: NotificationItem): void;
1416
1415
  /** "Read all". Rendered only when supplied. */
1417
1416
  onMarkAllRead?(): void;
1417
+ /**
1418
+ * Marks a single item read. Supplying it puts a quiet check control on every
1419
+ * unread row — "Read all" and clicking through are no longer the only ways.
1420
+ */
1421
+ onMarkRead?(id: string): void;
1418
1422
  /** Override the default relative-age label. */
1419
1423
  formatAge?(createdAt: Date): string;
1420
1424
  className?: string;
package/dist/index.js CHANGED
@@ -4306,19 +4306,33 @@ function isNotificationSettled(item) {
4306
4306
 
4307
4307
  // src/components/notification-bell.tsx
4308
4308
  import * as React61 from "react";
4309
- import { BellRing, Settings } from "lucide-react";
4309
+ import { BellRing, Check as Check7, Settings } from "lucide-react";
4310
4310
  import { jsx as jsx74, jsxs as jsxs41 } from "react/jsx-runtime";
4311
+ function MarkReadButton({ title, onClick }) {
4312
+ return /* @__PURE__ */ jsx74(
4313
+ "button",
4314
+ {
4315
+ type: "button",
4316
+ "aria-label": `Mark "${title}" as read`,
4317
+ onClick,
4318
+ className: "flex size-5 shrink-0 items-center justify-center rounded text-text-tertiary opacity-0 transition-opacity hover:text-text-primary focus-visible:opacity-100 group-hover:opacity-100",
4319
+ children: /* @__PURE__ */ jsx74(Check7, { className: "size-3.5", "aria-hidden": "true" })
4320
+ }
4321
+ );
4322
+ }
4311
4323
  function BellRow({
4312
4324
  item,
4313
4325
  onItemClick,
4314
4326
  onActionClick,
4327
+ onMarkRead,
4315
4328
  formatAge
4316
4329
  }) {
4317
4330
  const settled = isNotificationSettled(item);
4318
4331
  const unread = !settled && !item.read;
4332
+ const markable = Boolean(onMarkRead) && unread && item.cls !== "ephemeral";
4319
4333
  const actions = settled ? [] : item.actions ?? [];
4320
4334
  const age = item.createdAt ? formatAge(item.createdAt instanceof Date ? item.createdAt : new Date(item.createdAt)) : null;
4321
- return /* @__PURE__ */ jsxs41("div", { className: cn("rounded-lg bg-bg-surface px-3 py-2.5", settled && "opacity-45"), children: [
4335
+ return /* @__PURE__ */ jsxs41("div", { className: cn("group rounded-lg bg-bg-surface px-3 py-2.5", settled && "opacity-45"), children: [
4322
4336
  /* @__PURE__ */ jsxs41("div", { className: "flex items-start gap-2.5", children: [
4323
4337
  /* @__PURE__ */ jsxs41("div", { className: "min-w-0 flex-1", children: [
4324
4338
  /* @__PURE__ */ jsx74(
@@ -4338,7 +4352,10 @@ function BellRow({
4338
4352
  item.receipt ? `, ${item.receipt}` : ""
4339
4353
  ] })
4340
4354
  ] }),
4341
- age && /* @__PURE__ */ jsx74("span", { className: "mt-px shrink-0 text-xs text-text-tertiary", children: age })
4355
+ (age || markable) && /* @__PURE__ */ jsxs41("div", { className: "mt-px flex shrink-0 items-center gap-1", children: [
4356
+ age && /* @__PURE__ */ jsx74("span", { className: "text-xs text-text-tertiary", children: age }),
4357
+ markable && /* @__PURE__ */ jsx74(MarkReadButton, { title: item.title, onClick: () => onMarkRead(item.id) })
4358
+ ] })
4342
4359
  ] }),
4343
4360
  (actions.length > 0 || item.deepLink) && /* @__PURE__ */ jsxs41("div", { className: "mt-2 flex flex-wrap items-center gap-x-4 gap-y-2", children: [
4344
4361
  actions.map((action) => {
@@ -4375,6 +4392,7 @@ var NotificationBell = React61.forwardRef(
4375
4392
  items,
4376
4393
  unreadCount,
4377
4394
  maxItems = 5,
4395
+ onMarkRead,
4378
4396
  onMarkAllRead,
4379
4397
  onItemClick,
4380
4398
  onActionClick,
@@ -4443,6 +4461,7 @@ var NotificationBell = React61.forwardRef(
4443
4461
  item,
4444
4462
  onItemClick,
4445
4463
  onActionClick,
4464
+ onMarkRead,
4446
4465
  formatAge: age
4447
4466
  },
4448
4467
  item.id
@@ -4464,24 +4483,41 @@ NotificationBell.displayName = "NotificationBell";
4464
4483
 
4465
4484
  // src/components/notification-center.tsx
4466
4485
  import * as React62 from "react";
4467
- import { ArrowUpRight, Check as Check7, Search as Search2 } from "lucide-react";
4486
+ import { ArrowUpRight, Check as Check8, Search as Search2 } from "lucide-react";
4468
4487
  import { jsx as jsx75, jsxs as jsxs42 } from "react/jsx-runtime";
4469
4488
  var chipBase = "rounded-lg px-3 py-1.5 text-sm font-medium transition-colors";
4470
4489
  var chipOn = "bg-bg-brand-subtle text-text-brand";
4471
4490
  var chipOff = "bg-bg-elevated text-text-secondary hover:text-text-primary";
4491
+ function MarkReadButton2({ title, onClick }) {
4492
+ return /* @__PURE__ */ jsx75(
4493
+ "button",
4494
+ {
4495
+ type: "button",
4496
+ "aria-label": `Mark "${title}" as read`,
4497
+ onClick,
4498
+ className: "flex size-5 shrink-0 items-center justify-center rounded text-text-tertiary opacity-0 transition-opacity hover:text-text-primary focus-visible:opacity-100 group-hover:opacity-100",
4499
+ children: /* @__PURE__ */ jsx75(Check8, { className: "size-3.5", "aria-hidden": "true" })
4500
+ }
4501
+ );
4502
+ }
4472
4503
  function CenterRow({
4473
4504
  item,
4474
4505
  onItemClick,
4475
4506
  onDeepLinkClick,
4507
+ onMarkRead,
4476
4508
  formatAge
4477
4509
  }) {
4478
4510
  const unread = !item.read;
4511
+ const markable = Boolean(onMarkRead) && unread && item.cls !== "ephemeral";
4479
4512
  const age = item.createdAt ? formatAge(item.createdAt instanceof Date ? item.createdAt : new Date(item.createdAt)) : null;
4480
- return /* @__PURE__ */ jsxs42("article", { className: "rounded-lg bg-bg-elevated px-4 py-3", children: [
4513
+ return /* @__PURE__ */ jsxs42("article", { className: "group rounded-lg bg-bg-elevated px-4 py-3", children: [
4481
4514
  /* @__PURE__ */ jsxs42("div", { className: "flex items-center gap-2", children: [
4482
4515
  /* @__PURE__ */ jsx75("span", { className: "text-xs font-semibold uppercase tracking-[0.09em] text-text-secondary", children: item.source }),
4483
4516
  /* @__PURE__ */ jsx75(Badge, { variant: severityBadgeVariant(item.severity), children: severityLabels[item.severity] }),
4484
- age && /* @__PURE__ */ jsx75("span", { className: "ml-auto shrink-0 text-xs tabular-nums text-text-tertiary", children: age })
4517
+ (age || markable) && /* @__PURE__ */ jsxs42("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
4518
+ age && /* @__PURE__ */ jsx75("span", { className: "text-xs tabular-nums text-text-tertiary", children: age }),
4519
+ markable && /* @__PURE__ */ jsx75(MarkReadButton2, { title: item.title, onClick: () => onMarkRead(item.id) })
4520
+ ] })
4485
4521
  ] }),
4486
4522
  /* @__PURE__ */ jsx75(
4487
4523
  "button",
@@ -4498,7 +4534,7 @@ function CenterRow({
4498
4534
  item.body && /* @__PURE__ */ jsx75("p", { className: "mt-0.5 text-sm leading-snug text-text-secondary", children: item.body }),
4499
4535
  (item.receipt || item.deepLink) && /* @__PURE__ */ jsxs42("div", { className: "mt-2 flex flex-wrap items-center gap-x-4 gap-y-1.5", children: [
4500
4536
  item.receipt && /* @__PURE__ */ jsxs42("span", { className: "inline-flex items-center gap-1.5 text-sm text-text-tertiary", children: [
4501
- /* @__PURE__ */ jsx75(Check7, { className: "size-3 shrink-0 text-text-success", "aria-hidden": "true" }),
4537
+ /* @__PURE__ */ jsx75(Check8, { className: "size-3 shrink-0 text-text-success", "aria-hidden": "true" }),
4502
4538
  item.receipt
4503
4539
  ] }),
4504
4540
  item.deepLink && /* @__PURE__ */ jsxs42(
@@ -4528,6 +4564,7 @@ var NotificationCenter = React62.forwardRef(
4528
4564
  onItemClick,
4529
4565
  onDeepLinkClick,
4530
4566
  onMarkAllRead,
4567
+ onMarkRead,
4531
4568
  formatAge,
4532
4569
  className
4533
4570
  }, ref) => {
@@ -4649,6 +4686,7 @@ var NotificationCenter = React62.forwardRef(
4649
4686
  item,
4650
4687
  onItemClick,
4651
4688
  onDeepLinkClick,
4689
+ onMarkRead,
4652
4690
  formatAge: age
4653
4691
  },
4654
4692
  item.id
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessly/ui",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Lessly design system — shared UI primitives, tokens, and theme",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.30.3",