twico-ui 1.36.1 → 1.37.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/CHANGELOG.md +14 -0
- package/dist/index.cjs +34 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.mjs +34 -10
- package/dist/index.mjs.map +1 -1
- package/llms.txt +3 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
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.1](https://github.com/EncryptionL/twico-ui/compare/v1.37.0...v1.37.1) (2026-09-14)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **image:** let explicit width/height win over fit's container fill ([#378](https://github.com/EncryptionL/twico-ui/issues/378)) ([770823d](https://github.com/EncryptionL/twico-ui/commit/770823ddced42288e623bb00e8f92886bd01d373))
|
|
11
|
+
|
|
12
|
+
# [1.37.0](https://github.com/EncryptionL/twico-ui/compare/v1.36.1...v1.37.0) (2026-09-11)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **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))
|
|
18
|
+
|
|
5
19
|
## [1.36.1](https://github.com/EncryptionL/twico-ui/compare/v1.36.0...v1.36.1) (2026-09-09)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.cjs
CHANGED
|
@@ -325,6 +325,8 @@ var Image = React51__namespace.default.forwardRef(function Image2({
|
|
|
325
325
|
className = "",
|
|
326
326
|
style,
|
|
327
327
|
onError,
|
|
328
|
+
width,
|
|
329
|
+
height,
|
|
328
330
|
...rest
|
|
329
331
|
}, ref) {
|
|
330
332
|
const __twcStyles = useScopedStyles("twc-image-styles", IMAGE_CSS);
|
|
@@ -334,6 +336,14 @@ var Image = React51__namespace.default.forwardRef(function Image2({
|
|
|
334
336
|
objectFit: fit,
|
|
335
337
|
borderRadius: radius ? `var(--radius-${radius})` : void 0,
|
|
336
338
|
aspectRatio,
|
|
339
|
+
// #378: only when `fit` is set, also emit explicit width/height as inline style — that's the sole case
|
|
340
|
+
// where the `[data-fit] { width/height: 100% }` rule overrides the width/height ATTRIBUTES (a
|
|
341
|
+
// presentational attr loses to a real CSS rule), silently sizing `<Image width height fit>` to the
|
|
342
|
+
// container/intrinsic size. Inline style beats that rule so the props mean what they say (object-fit
|
|
343
|
+
// still crops within the box), placed before `...style` so a caller's own `style` still wins. Gated on
|
|
344
|
+
// `fit` so the no-fit path stays attribute-only — a consumer's own CSS/className can still override it.
|
|
345
|
+
...fit && width != null ? { width } : null,
|
|
346
|
+
...fit && height != null ? { height } : null,
|
|
337
347
|
...style
|
|
338
348
|
};
|
|
339
349
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -347,6 +357,8 @@ var Image = React51__namespace.default.forwardRef(function Image2({
|
|
|
347
357
|
loading,
|
|
348
358
|
className: `twc-image ${className}`.trim(),
|
|
349
359
|
"data-fit": fit || void 0,
|
|
360
|
+
width,
|
|
361
|
+
height,
|
|
350
362
|
style: s,
|
|
351
363
|
onError: (e) => {
|
|
352
364
|
onError?.(e);
|
|
@@ -15433,6 +15445,10 @@ var DIALOG_CSS = `
|
|
|
15433
15445
|
.twc-dialog__close svg { width: 18px; height: 18px; }
|
|
15434
15446
|
.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
15447
|
.twc-dialog__footer { display: flex; gap: var(--space-2); justify-content: flex-end; padding: 0 var(--space-5) var(--space-5); }
|
|
15448
|
+
/* #372: header/body and body/footer rules (on by default). Per-region data-divider so the rule is
|
|
15449
|
+
suppressed when the region is empty (no footer; a header with only a close button and no title/description). */
|
|
15450
|
+
.twc-dialog__header[data-divider="true"] { padding-bottom: var(--space-4); border-bottom: var(--border-thin) solid var(--color-divider); }
|
|
15451
|
+
.twc-dialog__footer[data-divider="true"] { padding-top: var(--space-4); border-top: var(--border-thin) solid var(--color-divider); }
|
|
15436
15452
|
`;
|
|
15437
15453
|
function Dialog({
|
|
15438
15454
|
open,
|
|
@@ -15442,7 +15458,8 @@ function Dialog({
|
|
|
15442
15458
|
footer,
|
|
15443
15459
|
children,
|
|
15444
15460
|
size = "md",
|
|
15445
|
-
scrollBody =
|
|
15461
|
+
scrollBody = true,
|
|
15462
|
+
dividers = true,
|
|
15446
15463
|
closeOnBackdrop = true,
|
|
15447
15464
|
className = "",
|
|
15448
15465
|
...rest
|
|
@@ -15483,7 +15500,7 @@ function Dialog({
|
|
|
15483
15500
|
}, children: [
|
|
15484
15501
|
__twcStyles,
|
|
15485
15502
|
/* @__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: [
|
|
15503
|
+
title || description || onClose ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dialog__header", "data-divider": dividers && (title || description) && (children || footer) ? "true" : void 0, children: [
|
|
15487
15504
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dialog__titles", children: [
|
|
15488
15505
|
title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__title", id: titleId, children: title }) : null,
|
|
15489
15506
|
description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__desc", id: descId, children: description }) : null
|
|
@@ -15491,7 +15508,7 @@ function Dialog({
|
|
|
15491
15508
|
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
15509
|
] }) : null,
|
|
15493
15510
|
children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__body", children }) : null,
|
|
15494
|
-
footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__footer", children: footer }) : null
|
|
15511
|
+
footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dialog__footer", "data-divider": dividers && children ? "true" : void 0, children: footer }) : null
|
|
15495
15512
|
] })
|
|
15496
15513
|
] });
|
|
15497
15514
|
return renderPortal(overlay);
|
|
@@ -15591,7 +15608,11 @@ var DRAWER_CSS = `
|
|
|
15591
15608
|
.twc-drawer__close:hover { background: var(--color-surface-sunken); color: var(--color-text); }
|
|
15592
15609
|
.twc-drawer__close svg { width: 18px; height: 18px; }
|
|
15593
15610
|
.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);
|
|
15611
|
+
.twc-drawer__footer { display: flex; gap: var(--space-2); justify-content: flex-end; padding: var(--space-4) var(--space-5); }
|
|
15612
|
+
/* #372: header/footer rules (on by default), consistent with Dialog. Per-region data-divider so an empty
|
|
15613
|
+
region grows no stray line (no footer; a header with only a close button and no title/description). */
|
|
15614
|
+
.twc-drawer__header[data-divider="true"] { border-bottom: var(--border-thin) solid var(--color-divider); }
|
|
15615
|
+
.twc-drawer__footer[data-divider="true"] { border-top: var(--border-thin) solid var(--color-divider); }
|
|
15595
15616
|
@media (prefers-reduced-motion: reduce) {
|
|
15596
15617
|
.twc-drawer__overlay[data-state], .twc-drawer[data-state] { animation-duration: 1ms; }
|
|
15597
15618
|
}
|
|
@@ -15607,6 +15628,7 @@ function Drawer({
|
|
|
15607
15628
|
width,
|
|
15608
15629
|
height,
|
|
15609
15630
|
closeOnBackdrop = true,
|
|
15631
|
+
dividers = true,
|
|
15610
15632
|
children,
|
|
15611
15633
|
className = "",
|
|
15612
15634
|
style,
|
|
@@ -15652,7 +15674,7 @@ function Drawer({
|
|
|
15652
15674
|
}, children: [
|
|
15653
15675
|
__twcStyles,
|
|
15654
15676
|
/* @__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: [
|
|
15677
|
+
title || description || onClose ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drawer__header", "data-divider": dividers && (title || description) ? "true" : void 0, children: [
|
|
15656
15678
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drawer__titles", children: [
|
|
15657
15679
|
title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__title", id: titleId, children: title }) : null,
|
|
15658
15680
|
description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__desc", id: descId, children: description }) : null
|
|
@@ -15660,7 +15682,7 @@ function Drawer({
|
|
|
15660
15682
|
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
15683
|
] }) : null,
|
|
15662
15684
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__body", children }),
|
|
15663
|
-
footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__footer", children: footer }) : null
|
|
15685
|
+
footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drawer__footer", "data-divider": dividers ? "true" : void 0, children: footer }) : null
|
|
15664
15686
|
] })
|
|
15665
15687
|
] });
|
|
15666
15688
|
return renderPortal(overlay);
|
|
@@ -16361,7 +16383,9 @@ var POPOVER_CSS = `
|
|
|
16361
16383
|
.twc-popover__arrow { position: absolute; width: 11px; height: 11px; background: var(--color-surface-raised);
|
|
16362
16384
|
border-left: var(--border-thin) solid var(--color-border); border-top: var(--border-thin) solid var(--color-border);
|
|
16363
16385
|
transform: rotate(45deg); }
|
|
16364
|
-
|
|
16386
|
+
/* #373: cap the panel to the room place() measured and scroll INSIDE the inner (not the panel \u2014 the panel
|
|
16387
|
+
must keep overflow visible so the absolutely-positioned arrow isn't clipped) so a tall panel's footer stays reachable. */
|
|
16388
|
+
.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
16389
|
.twc-popover__title { font-size: var(--text-sm); font-weight: var(--font-bold); color: var(--color-text); margin-bottom: 4px; }
|
|
16366
16390
|
`;
|
|
16367
16391
|
function Popover({
|
|
@@ -16423,7 +16447,7 @@ function Popover({
|
|
|
16423
16447
|
l = Math.max(M, Math.min(l, vw - w - M));
|
|
16424
16448
|
left = l;
|
|
16425
16449
|
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 });
|
|
16450
|
+
return setPos({ top, bottom, left, width: w, flip, arrow, maxHeight: flip ? spaceAbove : spaceBelow });
|
|
16427
16451
|
}
|
|
16428
16452
|
const ph = popRef.current ? popRef.current.offsetHeight : 120;
|
|
16429
16453
|
let onRight = placement === "right";
|
|
@@ -16435,7 +16459,7 @@ function Popover({
|
|
|
16435
16459
|
const cy = r2.top + r2.height / 2;
|
|
16436
16460
|
top = Math.max(M, Math.min(cy - ph / 2, vh - ph - M));
|
|
16437
16461
|
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 });
|
|
16462
|
+
setPos({ top, left, width: w, flip: false, arrow, maxHeight: vh - 2 * M });
|
|
16439
16463
|
}, [placement, align, width]);
|
|
16440
16464
|
React51__namespace.default.useEffect(() => {
|
|
16441
16465
|
if (!open) return;
|
|
@@ -16501,7 +16525,7 @@ function Popover({
|
|
|
16501
16525
|
children: [
|
|
16502
16526
|
__twcStyles,
|
|
16503
16527
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-popover__arrow", style: pos.arrow, "aria-hidden": "true" }),
|
|
16504
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-popover__inner", children: [
|
|
16528
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-popover__inner", style: { maxHeight: pos.maxHeight }, children: [
|
|
16505
16529
|
title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-popover__title", id: `${popId}-title`, children: title }) : null,
|
|
16506
16530
|
children
|
|
16507
16531
|
] })
|