twico-ui 1.37.0 → 1.37.2

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
@@ -122,7 +122,9 @@ interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, "al
122
122
  src?: string;
123
123
  /** Alternative text (required for accessibility; pass "" for decorative images). */
124
124
  alt: string;
125
- /** object-fit value. */
125
+ /** object-fit value. When set, the image fills its container by default (`width`/`height: 100%`); pass an
126
+ * explicit `width`/`height` (or size the container / `style`) to give it a fixed box that `fit` crops within
127
+ * — an explicit `width`/`height` now wins over the fill (#378). */
126
128
  fit?: "cover" | "contain" | "fill" | "none" | "scale-down";
127
129
  /** Border radius token (e.g. "md", "lg", "full"). */
128
130
  radius?: string;
package/dist/index.d.ts CHANGED
@@ -122,7 +122,9 @@ interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, "al
122
122
  src?: string;
123
123
  /** Alternative text (required for accessibility; pass "" for decorative images). */
124
124
  alt: string;
125
- /** object-fit value. */
125
+ /** object-fit value. When set, the image fills its container by default (`width`/`height: 100%`); pass an
126
+ * explicit `width`/`height` (or size the container / `style`) to give it a fixed box that `fit` crops within
127
+ * — an explicit `width`/`height` now wins over the fill (#378). */
126
128
  fit?: "cover" | "contain" | "fill" | "none" | "scale-down";
127
129
  /** Border radius token (e.g. "md", "lg", "full"). */
128
130
  radius?: string;
package/dist/index.mjs CHANGED
@@ -304,6 +304,8 @@ var Image = React51__default.forwardRef(function Image2({
304
304
  className = "",
305
305
  style,
306
306
  onError,
307
+ width,
308
+ height,
307
309
  ...rest
308
310
  }, ref) {
309
311
  const __twcStyles = useScopedStyles("twc-image-styles", IMAGE_CSS);
@@ -313,6 +315,14 @@ var Image = React51__default.forwardRef(function Image2({
313
315
  objectFit: fit,
314
316
  borderRadius: radius ? `var(--radius-${radius})` : void 0,
315
317
  aspectRatio,
318
+ // #378: only when `fit` is set, also emit explicit width/height as inline style — that's the sole case
319
+ // where the `[data-fit] { width/height: 100% }` rule overrides the width/height ATTRIBUTES (a
320
+ // presentational attr loses to a real CSS rule), silently sizing `<Image width height fit>` to the
321
+ // container/intrinsic size. Inline style beats that rule so the props mean what they say (object-fit
322
+ // still crops within the box), placed before `...style` so a caller's own `style` still wins. Gated on
323
+ // `fit` so the no-fit path stays attribute-only — a consumer's own CSS/className can still override it.
324
+ ...fit && width != null ? { width } : null,
325
+ ...fit && height != null ? { height } : null,
316
326
  ...style
317
327
  };
318
328
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -326,6 +336,8 @@ var Image = React51__default.forwardRef(function Image2({
326
336
  loading,
327
337
  className: `twc-image ${className}`.trim(),
328
338
  "data-fit": fit || void 0,
339
+ width,
340
+ height,
329
341
  style: s,
330
342
  onError: (e) => {
331
343
  onError?.(e);
@@ -10634,8 +10646,10 @@ function Datatable({
10634
10646
  return;
10635
10647
  }
10636
10648
  if (k === "v") {
10637
- e.preventDefault();
10638
- pasteSelection();
10649
+ if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.readText) {
10650
+ e.preventDefault();
10651
+ pasteSelection();
10652
+ }
10639
10653
  return;
10640
10654
  }
10641
10655
  }
@@ -10855,19 +10869,19 @@ function Datatable({
10855
10869
  const copyTypeOf = (col) => col && col.copyType != null ? col.copyType : col && col.type === "number" ? "number" : "text";
10856
10870
  const writeClipboard = (text) => {
10857
10871
  const execFallback = () => {
10872
+ if (typeof document === "undefined") return;
10873
+ const ta = document.createElement("textarea");
10874
+ ta.value = text;
10875
+ ta.style.position = "fixed";
10876
+ ta.style.opacity = "0";
10877
+ document.body.appendChild(ta);
10878
+ ta.focus();
10879
+ ta.select();
10858
10880
  try {
10859
- if (typeof document !== "undefined") {
10860
- const ta = document.createElement("textarea");
10861
- ta.value = text;
10862
- ta.style.position = "fixed";
10863
- ta.style.opacity = "0";
10864
- document.body.appendChild(ta);
10865
- ta.focus();
10866
- ta.select();
10867
- document.execCommand("copy");
10868
- document.body.removeChild(ta);
10869
- }
10881
+ document.execCommand("copy");
10870
10882
  } catch {
10883
+ } finally {
10884
+ if (ta.parentNode) ta.parentNode.removeChild(ta);
10871
10885
  }
10872
10886
  };
10873
10887
  try {
@@ -10938,19 +10952,8 @@ function Datatable({
10938
10952
  announceClip(`Copied ${n} cell${n === 1 ? "" : "s"}`, cellRect);
10939
10953
  }
10940
10954
  };
10941
- const pasteSelection = async () => {
10942
- if (!cellRect) return;
10943
- let text = null;
10944
- try {
10945
- if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.readText) text = await navigator.clipboard.readText();
10946
- } catch {
10947
- setClipboardMsg("Paste unavailable \u2014 clipboard permission denied");
10948
- return;
10949
- }
10950
- if (text == null) {
10951
- setClipboardMsg("Nothing to paste");
10952
- return;
10953
- }
10955
+ const applyPastedText = (text) => {
10956
+ if (text == null || !cellRect) return;
10954
10957
  const inApp = clipboardRef.current && clipboardRef.current.tsv === text ? clipboardRef.current : null;
10955
10958
  let matrix;
10956
10959
  if (inApp) matrix = inApp.matrix;
@@ -10995,6 +10998,31 @@ function Datatable({
10995
10998
  const pasteRect = written ? { r0: cellRect.r0, c0: cellRect.c0, r1: Math.min(cellRect.r0 + matrix.length - 1, leafRows.length - 1), c1: Math.min(cellRect.c0 + maxCols - 1, ordered.length - 1) } : null;
10996
10999
  announceClip(`Pasted ${written} cell${written === 1 ? "" : "s"}${skipped ? `, ${skipped} skipped (incompatible column)` : ""}`, pasteRect);
10997
11000
  };
11001
+ const pasteSelection = async () => {
11002
+ if (!cellRect) return;
11003
+ let text = null;
11004
+ try {
11005
+ if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.readText) text = await navigator.clipboard.readText();
11006
+ } catch {
11007
+ setClipboardMsg("Paste unavailable \u2014 clipboard permission denied");
11008
+ return;
11009
+ }
11010
+ if (text == null) {
11011
+ setClipboardMsg("Nothing to paste");
11012
+ return;
11013
+ }
11014
+ applyPastedText(text);
11015
+ };
11016
+ const onGridPaste = (e) => {
11017
+ if (e.target.closest("input, textarea, select, [contenteditable='true']") || editing) return;
11018
+ if (!e.target.closest(".twc-dt__td[data-r]")) return;
11019
+ if (!(enableClipboard && selectionMode === "cell") || !cellRect) return;
11020
+ if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.readText) return;
11021
+ const text = e.clipboardData ? e.clipboardData.getData("text/plain") : null;
11022
+ if (text == null || text === "") return;
11023
+ e.preventDefault();
11024
+ applyPastedText(text);
11025
+ };
10998
11026
  React51__default.useEffect(() => {
10999
11027
  if (!editing) return;
11000
11028
  const onDown = (e) => {
@@ -12010,6 +12038,7 @@ function Datatable({
12010
12038
  "aria-activedescendant": activeCellId,
12011
12039
  "aria-busy": loading || void 0,
12012
12040
  onKeyDown: onGridKeyDown,
12041
+ onPaste: onGridPaste,
12013
12042
  children: [
12014
12043
  /* @__PURE__ */ jsx("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
12015
12044
  hasExpandCol ? /* @__PURE__ */ jsx("th", { className: "twc-dt__th twc-dt__expand-cell", role: "columnheader", "aria-label": "Expand", "data-pin": "left", "data-pin-edge": checkboxSelection || showRowNum || visLeft.length ? void 0 : "left", style: { insetInlineStart: 0, width: EXP_W, minWidth: EXP_W } }) : null,
@@ -19084,14 +19113,36 @@ function Code({
19084
19113
  const timerRef = React51__default.useRef(null);
19085
19114
  React51__default.useEffect(() => () => clearTimeout(timerRef.current), []);
19086
19115
  const doCopy = () => {
19087
- if (typeof navigator === "undefined" || !navigator.clipboard) return;
19088
19116
  const text = typeof children === "string" ? children : rootRef.current?.textContent ?? "";
19089
- navigator.clipboard.writeText(text).then(() => {
19117
+ const flash = () => {
19090
19118
  setCopied(true);
19091
19119
  clearTimeout(timerRef.current);
19092
19120
  timerRef.current = setTimeout(() => setCopied(false), 2e3);
19093
- }).catch(() => {
19094
- });
19121
+ };
19122
+ const execFallback = () => {
19123
+ if (typeof document === "undefined") return;
19124
+ const ta = document.createElement("textarea");
19125
+ ta.value = text;
19126
+ ta.style.position = "fixed";
19127
+ ta.style.opacity = "0";
19128
+ document.body.appendChild(ta);
19129
+ ta.focus();
19130
+ ta.select();
19131
+ try {
19132
+ if (document.execCommand("copy")) flash();
19133
+ } catch {
19134
+ } finally {
19135
+ if (ta.parentNode) ta.parentNode.removeChild(ta);
19136
+ }
19137
+ };
19138
+ try {
19139
+ if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
19140
+ navigator.clipboard.writeText(text).then(flash).catch(execFallback);
19141
+ return;
19142
+ }
19143
+ } catch {
19144
+ }
19145
+ execFallback();
19095
19146
  };
19096
19147
  return /* @__PURE__ */ jsxs(Tag2, { ref: rootRef, className: `twc-code ${className}`.trim(), "data-block": block || void 0, ...rest, children: [
19097
19148
  __twcStyles,