twico-ui 1.36.1 → 1.37.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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to Twico UI are documented here (newest first). This file is generated automatically on each release by [semantic-release](https://semantic-release.gitbook.io/) from [Conventional Commits](https://www.conventionalcommits.org/) — do not edit it by hand. The same notes appear on each [GitHub Release](https://github.com/EncryptionL/twico-ui/releases).
4
4
 
5
+ # [1.37.0](https://github.com/EncryptionL/twico-ui/compare/v1.36.1...v1.37.0) (2026-09-11)
6
+
7
+
8
+ ### Features
9
+
10
+ * **overlay:** safer Dialog/Drawer defaults + Popover height clamp ([#371](https://github.com/EncryptionL/twico-ui/issues/371), [#372](https://github.com/EncryptionL/twico-ui/issues/372), [#373](https://github.com/EncryptionL/twico-ui/issues/373)) ([0f7a386](https://github.com/EncryptionL/twico-ui/commit/0f7a38656956bb9ef7cdd0331d2b5e6776aa7d7c))
11
+
5
12
  ## [1.36.1](https://github.com/EncryptionL/twico-ui/compare/v1.36.0...v1.36.1) (2026-09-09)
6
13
 
7
14
 
package/dist/index.cjs CHANGED
@@ -15433,6 +15433,10 @@ var DIALOG_CSS = `
15433
15433
  .twc-dialog__close svg { width: 18px; height: 18px; }
15434
15434
  .twc-dialog__body { padding: var(--space-4) var(--space-5); font-size: var(--text-sm); color: var(--color-text-muted); line-height: var(--leading-normal); }
15435
15435
  .twc-dialog__footer { display: flex; gap: var(--space-2); justify-content: flex-end; padding: 0 var(--space-5) var(--space-5); }
15436
+ /* #372: header/body and body/footer rules (on by default). Per-region data-divider so the rule is
15437
+ suppressed when the region is empty (no footer; a header with only a close button and no title/description). */
15438
+ .twc-dialog__header[data-divider="true"] { padding-bottom: var(--space-4); border-bottom: var(--border-thin) solid var(--color-divider); }
15439
+ .twc-dialog__footer[data-divider="true"] { padding-top: var(--space-4); border-top: var(--border-thin) solid var(--color-divider); }
15436
15440
  `;
15437
15441
  function Dialog({
15438
15442
  open,
@@ -15442,7 +15446,8 @@ function Dialog({
15442
15446
  footer,
15443
15447
  children,
15444
15448
  size = "md",
15445
- scrollBody = false,
15449
+ scrollBody = true,
15450
+ dividers = true,
15446
15451
  closeOnBackdrop = true,
15447
15452
  className = "",
15448
15453
  ...rest
@@ -15483,7 +15488,7 @@ function Dialog({
15483
15488
  }, children: [
15484
15489
  __twcStyles,
15485
15490
  /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: dialogRef, className: `twc-dialog ${className}`, "data-state": state, "data-size": size, "data-scroll-body": scrollBody ? "true" : void 0, role: "dialog", "aria-modal": "true", tabIndex: -1, "aria-labelledby": title ? titleId : void 0, "aria-label": !title ? "Dialog" : void 0, "aria-describedby": description ? descId : void 0, ...rest, children: [
15486
- title || description || onClose ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dialog__header", children: [
15491
+ title || description || onClose ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dialog__header", "data-divider": dividers && (title || description) && (children || footer) ? "true" : void 0, children: [
15487
15492
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dialog__titles", children: [
15488
15493
  title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__title", id: titleId, children: title }) : null,
15489
15494
  description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__desc", id: descId, children: description }) : null
@@ -15491,7 +15496,7 @@ function Dialog({
15491
15496
  onClose ? /* @__PURE__ */ jsxRuntime.jsx("button", { className: "twc-dialog__close", onClick: onClose, "aria-label": "Close", type: "button", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
15492
15497
  ] }) : null,
15493
15498
  children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__body", children }) : null,
15494
- footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__footer", children: footer }) : null
15499
+ footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__footer", "data-divider": dividers && children ? "true" : void 0, children: footer }) : null
15495
15500
  ] })
15496
15501
  ] });
15497
15502
  return renderPortal(overlay);
@@ -15591,7 +15596,11 @@ var DRAWER_CSS = `
15591
15596
  .twc-drawer__close:hover { background: var(--color-surface-sunken); color: var(--color-text); }
15592
15597
  .twc-drawer__close svg { width: 18px; height: 18px; }
15593
15598
  .twc-drawer__body { flex: 1; overflow-y: auto; padding: var(--space-2) var(--space-5) var(--space-5); font-size: var(--text-sm); color: var(--color-text-muted); line-height: var(--leading-normal); }
15594
- .twc-drawer__footer { display: flex; gap: var(--space-2); justify-content: flex-end; padding: var(--space-4) var(--space-5); border-top: var(--border-thin) solid var(--color-divider); }
15599
+ .twc-drawer__footer { display: flex; gap: var(--space-2); justify-content: flex-end; padding: var(--space-4) var(--space-5); }
15600
+ /* #372: header/footer rules (on by default), consistent with Dialog. Per-region data-divider so an empty
15601
+ region grows no stray line (no footer; a header with only a close button and no title/description). */
15602
+ .twc-drawer__header[data-divider="true"] { border-bottom: var(--border-thin) solid var(--color-divider); }
15603
+ .twc-drawer__footer[data-divider="true"] { border-top: var(--border-thin) solid var(--color-divider); }
15595
15604
  @media (prefers-reduced-motion: reduce) {
15596
15605
  .twc-drawer__overlay[data-state], .twc-drawer[data-state] { animation-duration: 1ms; }
15597
15606
  }
@@ -15607,6 +15616,7 @@ function Drawer({
15607
15616
  width,
15608
15617
  height,
15609
15618
  closeOnBackdrop = true,
15619
+ dividers = true,
15610
15620
  children,
15611
15621
  className = "",
15612
15622
  style,
@@ -15652,7 +15662,7 @@ function Drawer({
15652
15662
  }, children: [
15653
15663
  __twcStyles,
15654
15664
  /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: panelRef, className: `twc-drawer ${className}`, "data-side": side, "data-state": state, role: "dialog", "aria-modal": "true", tabIndex: -1, "aria-labelledby": title ? titleId : void 0, "aria-label": !title ? "Drawer" : void 0, "aria-describedby": description ? descId : void 0, style: { ...dim ? sizeVar : null, ...style }, ...rest, children: [
15655
- title || description || onClose ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drawer__header", children: [
15665
+ title || description || onClose ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drawer__header", "data-divider": dividers && (title || description) ? "true" : void 0, children: [
15656
15666
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drawer__titles", children: [
15657
15667
  title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__title", id: titleId, children: title }) : null,
15658
15668
  description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__desc", id: descId, children: description }) : null
@@ -15660,7 +15670,7 @@ function Drawer({
15660
15670
  onClose ? /* @__PURE__ */ jsxRuntime.jsx("button", { className: "twc-drawer__close", onClick: onClose, "aria-label": "Close", type: "button", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
15661
15671
  ] }) : null,
15662
15672
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__body", children }),
15663
- footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__footer", children: footer }) : null
15673
+ footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__footer", "data-divider": dividers ? "true" : void 0, children: footer }) : null
15664
15674
  ] })
15665
15675
  ] });
15666
15676
  return renderPortal(overlay);
@@ -16361,7 +16371,9 @@ var POPOVER_CSS = `
16361
16371
  .twc-popover__arrow { position: absolute; width: 11px; height: 11px; background: var(--color-surface-raised);
16362
16372
  border-left: var(--border-thin) solid var(--color-border); border-top: var(--border-thin) solid var(--color-border);
16363
16373
  transform: rotate(45deg); }
16364
- .twc-popover__inner { padding: var(--space-4); font-size: var(--text-sm); color: var(--color-text-muted); line-height: var(--leading-normal); }
16374
+ /* #373: cap the panel to the room place() measured and scroll INSIDE the inner (not the panel \u2014 the panel
16375
+ must keep overflow visible so the absolutely-positioned arrow isn't clipped) so a tall panel's footer stays reachable. */
16376
+ .twc-popover__inner { padding: var(--space-4); font-size: var(--text-sm); color: var(--color-text-muted); line-height: var(--leading-normal); overflow-y: auto; }
16365
16377
  .twc-popover__title { font-size: var(--text-sm); font-weight: var(--font-bold); color: var(--color-text); margin-bottom: 4px; }
16366
16378
  `;
16367
16379
  function Popover({
@@ -16423,7 +16435,7 @@ function Popover({
16423
16435
  l = Math.max(M, Math.min(l, vw - w - M));
16424
16436
  left = l;
16425
16437
  arrow = { left: Math.min(Math.max(r2.left + r2.width / 2 - l - 5.5, 10), w - 20), [flip ? "bottom" : "top"]: -6 };
16426
- return setPos({ top, bottom, left, width: w, flip, arrow });
16438
+ return setPos({ top, bottom, left, width: w, flip, arrow, maxHeight: flip ? spaceAbove : spaceBelow });
16427
16439
  }
16428
16440
  const ph = popRef.current ? popRef.current.offsetHeight : 120;
16429
16441
  let onRight = placement === "right";
@@ -16435,7 +16447,7 @@ function Popover({
16435
16447
  const cy = r2.top + r2.height / 2;
16436
16448
  top = Math.max(M, Math.min(cy - ph / 2, vh - ph - M));
16437
16449
  arrow = { top: Math.min(Math.max(cy - top - 5.5, 8), ph - 18), [onRight ? "left" : "right"]: -6 };
16438
- setPos({ top, left, width: w, flip: false, arrow });
16450
+ setPos({ top, left, width: w, flip: false, arrow, maxHeight: vh - 2 * M });
16439
16451
  }, [placement, align, width]);
16440
16452
  React51__namespace.default.useEffect(() => {
16441
16453
  if (!open) return;
@@ -16501,7 +16513,7 @@ function Popover({
16501
16513
  children: [
16502
16514
  __twcStyles,
16503
16515
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-popover__arrow", style: pos.arrow, "aria-hidden": "true" }),
16504
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-popover__inner", children: [
16516
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-popover__inner", style: { maxHeight: pos.maxHeight }, children: [
16505
16517
  title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-popover__title", id: `${popId}-title`, children: title }) : null,
16506
16518
  children
16507
16519
  ] })