@bioturing/components 0.23.0 → 0.24.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.
@@ -26,7 +26,13 @@ const F = ({
26
26
  disabled: n,
27
27
  onDragEnd: g,
28
28
  onDragStart: E
29
- }), t = c === !0 ? /* @__PURE__ */ p(C, { className: m("draggable-indicator") }) : c, y = [
29
+ }), t = c === !0 ? /* @__PURE__ */ p(
30
+ C,
31
+ {
32
+ className: m("draggable-indicator")
33
+ },
34
+ "indicator-icon" + e
35
+ ) : c, y = [
30
36
  N(t) ? l ? R(t, {
31
37
  ...a,
32
38
  ...f,
@@ -1 +1 @@
1
- {"version":3,"file":"draggable.js","sources":["../../../src/components/drag-drop/draggable.tsx"],"sourcesContent":["\"use client\";\n\nimport { useDraggable } from \"./hooks\";\nimport { clsx, useCls } from \"../utils\";\nimport { useRender } from \"@base-ui-components/react/use-render\";\nimport type { Data, DragDropDraggableProps } from \"./types\";\nimport { DotsSixVerticalIcon } from \"@bioturing/assets\";\nimport { cloneElement, isValidElement, HTMLProps } from \"react\";\n\n/**\n * A draggable item component that can be used within the Drag context\n */\nexport const Draggable = <T extends Data = Data>({\n id,\n children,\n className,\n disabled = false,\n data,\n style,\n render,\n onDragEnd,\n onDragStart,\n indicator = true,\n onlyIndicatorDraggable = false,\n ...rest\n}: DragDropDraggableProps<T>) => {\n const cls = useCls();\n\n const { attributes, listeners, isDragging, setNodeRef } = useDraggable({\n id,\n data,\n disabled,\n onDragEnd,\n onDragStart,\n });\n\n const indicatorIcon =\n indicator === true ? (\n <DotsSixVerticalIcon className={cls(\"draggable-indicator\")} />\n ) : (\n indicator\n );\n const indicatorElement = isValidElement(indicatorIcon)\n ? onlyIndicatorDraggable\n ? cloneElement(indicatorIcon, {\n ...attributes,\n ...listeners,\n key: \"indicator\" + id,\n })\n : indicatorIcon\n : null;\n\n const combinedChildren = [\n indicatorElement,\n ...(Array.isArray(children) ? children : [children]),\n ];\n\n const renderedElement = useRender({\n render: render ? (\n render\n ) : (\n <div className={clsx(cls(\"draggable\"), className)} />\n ),\n ref: setNodeRef,\n props: {\n children: combinedChildren,\n className,\n style: {\n ...style,\n touchAction: \"none\", // Prevents scrolling on touch devices while dragging\n cursor: disabled ? \"default\" : \"grab\",\n opacity: isDragging ? 0.5 : 1, // Visual feedback when dragging\n },\n ...rest,\n ...attributes,\n ...(!onlyIndicatorDraggable ? listeners : {}),\n },\n state: {\n isDragging,\n id,\n data,\n },\n });\n\n return renderedElement;\n};\n"],"names":["Draggable","id","children","className","disabled","data","style","render","onDragEnd","onDragStart","indicator","onlyIndicatorDraggable","rest","cls","useCls","attributes","listeners","isDragging","setNodeRef","useDraggable","indicatorIcon","jsx","DotsSixVerticalIcon","combinedChildren","isValidElement","cloneElement","useRender","clsx"],"mappings":";;;;;;;;AAYO,MAAMA,IAAY,CAAwB;AAAA,EAC/C,IAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,wBAAAC,IAAyB;AAAA,EACzB,GAAGC;AACL,MAAiC;AAC/B,QAAMC,IAAMC,EAAO,GAEb,EAAE,YAAAC,GAAY,WAAAC,GAAW,YAAAC,GAAY,YAAAC,EAAA,IAAeC,EAAa;AAAA,IACrE,IAAAlB;AAAA,IACA,MAAAI;AAAA,IACA,UAAAD;AAAA,IACA,WAAAI;AAAA,IACA,aAAAC;AAAA,EAAA,CACD,GAEKW,IACJV,MAAc,KACZ,gBAAAW,EAACC,KAAoB,WAAWT,EAAI,qBAAqB,EAAA,CAAG,IAE5DH,GAYEa,IAAmB;AAAA,IAVAC,EAAeJ,CAAa,IACjDT,IACEc,EAAaL,GAAe;AAAA,MAC1B,GAAGL;AAAA,MACH,GAAGC;AAAA,MACH,KAAK,cAAcf;AAAA,IAAA,CACpB,IACDmB,IACF;AAAA,IAIF,GAAI,MAAM,QAAQlB,CAAQ,IAAIA,IAAW,CAACA,CAAQ;AAAA,EACpD;AA6BO,SA3BiBwB,EAAU;AAAA,IAChC,QAAQnB,KAGL,gBAAAc,EAAA,OAAA,EAAI,WAAWM,EAAKd,EAAI,WAAW,GAAGV,CAAS,EAAG,CAAA;AAAA,IAErD,KAAKe;AAAA,IACL,OAAO;AAAA,MACL,UAAUK;AAAA,MACV,WAAApB;AAAA,MACA,OAAO;AAAA,QACL,GAAGG;AAAA,QACH,aAAa;AAAA;AAAA,QACb,QAAQF,IAAW,YAAY;AAAA,QAC/B,SAASa,IAAa,MAAM;AAAA;AAAA,MAC9B;AAAA,MACA,GAAGL;AAAA,MACH,GAAGG;AAAA,MACH,GAAKJ,IAAqC,CAAA,IAAZK;AAAA,IAChC;AAAA,IACA,OAAO;AAAA,MACL,YAAAC;AAAA,MACA,IAAAhB;AAAA,MACA,MAAAI;AAAA,IAAA;AAAA,EACF,CACD;AAGH;"}
1
+ {"version":3,"file":"draggable.js","sources":["../../../src/components/drag-drop/draggable.tsx"],"sourcesContent":["\"use client\";\n\nimport { useDraggable } from \"./hooks\";\nimport { clsx, useCls } from \"../utils\";\nimport { useRender } from \"@base-ui-components/react/use-render\";\nimport type { Data, DragDropDraggableProps } from \"./types\";\nimport { DotsSixVerticalIcon } from \"@bioturing/assets\";\nimport { cloneElement, isValidElement, HTMLProps } from \"react\";\n\n/**\n * A draggable item component that can be used within the Drag context\n */\nexport const Draggable = <T extends Data = Data>({\n id,\n children,\n className,\n disabled = false,\n data,\n style,\n render,\n onDragEnd,\n onDragStart,\n indicator = true,\n onlyIndicatorDraggable = false,\n ...rest\n}: DragDropDraggableProps<T>) => {\n const cls = useCls();\n\n const { attributes, listeners, isDragging, setNodeRef } = useDraggable({\n id,\n data,\n disabled,\n onDragEnd,\n onDragStart,\n });\n\n const indicatorIcon =\n indicator === true ? (\n <DotsSixVerticalIcon\n className={cls(\"draggable-indicator\")}\n key={\"indicator-icon\" + id}\n />\n ) : (\n indicator\n );\n const indicatorElement = isValidElement(indicatorIcon)\n ? onlyIndicatorDraggable\n ? cloneElement(indicatorIcon, {\n ...attributes,\n ...listeners,\n key: \"indicator\" + id,\n })\n : indicatorIcon\n : null;\n\n const combinedChildren = [\n indicatorElement,\n ...(Array.isArray(children) ? children : [children]),\n ];\n\n const renderedElement = useRender({\n render: render ? (\n render\n ) : (\n <div className={clsx(cls(\"draggable\"), className)} />\n ),\n ref: setNodeRef,\n props: {\n children: combinedChildren,\n className,\n style: {\n ...style,\n touchAction: \"none\", // Prevents scrolling on touch devices while dragging\n cursor: disabled ? \"default\" : \"grab\",\n opacity: isDragging ? 0.5 : 1, // Visual feedback when dragging\n },\n ...rest,\n ...attributes,\n ...(!onlyIndicatorDraggable ? listeners : {}),\n },\n state: {\n isDragging,\n id,\n data,\n },\n });\n\n return renderedElement;\n};\n"],"names":["Draggable","id","children","className","disabled","data","style","render","onDragEnd","onDragStart","indicator","onlyIndicatorDraggable","rest","cls","useCls","attributes","listeners","isDragging","setNodeRef","useDraggable","indicatorIcon","jsx","DotsSixVerticalIcon","combinedChildren","isValidElement","cloneElement","useRender","clsx"],"mappings":";;;;;;;;AAYO,MAAMA,IAAY,CAAwB;AAAA,EAC/C,IAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,wBAAAC,IAAyB;AAAA,EACzB,GAAGC;AACL,MAAiC;AAC/B,QAAMC,IAAMC,EAAO,GAEb,EAAE,YAAAC,GAAY,WAAAC,GAAW,YAAAC,GAAY,YAAAC,EAAA,IAAeC,EAAa;AAAA,IACrE,IAAAlB;AAAA,IACA,MAAAI;AAAA,IACA,UAAAD;AAAA,IACA,WAAAI;AAAA,IACA,aAAAC;AAAA,EAAA,CACD,GAEKW,IACJV,MAAc,KACZ,gBAAAW;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,WAAWT,EAAI,qBAAqB;AAAA,IAAA;AAAA,IAC/B,mBAAmBZ;AAAA,EAAA,IAG1BS,GAYEa,IAAmB;AAAA,IAVAC,EAAeJ,CAAa,IACjDT,IACEc,EAAaL,GAAe;AAAA,MAC1B,GAAGL;AAAA,MACH,GAAGC;AAAA,MACH,KAAK,cAAcf;AAAA,IAAA,CACpB,IACDmB,IACF;AAAA,IAIF,GAAI,MAAM,QAAQlB,CAAQ,IAAIA,IAAW,CAACA,CAAQ;AAAA,EACpD;AA6BO,SA3BiBwB,EAAU;AAAA,IAChC,QAAQnB,KAGL,gBAAAc,EAAA,OAAA,EAAI,WAAWM,EAAKd,EAAI,WAAW,GAAGV,CAAS,EAAG,CAAA;AAAA,IAErD,KAAKe;AAAA,IACL,OAAO;AAAA,MACL,UAAUK;AAAA,MACV,WAAApB;AAAA,MACA,OAAO;AAAA,QACL,GAAGG;AAAA,QACH,aAAa;AAAA;AAAA,QACb,QAAQF,IAAW,YAAY;AAAA,QAC/B,SAASa,IAAa,MAAM;AAAA;AAAA,MAC9B;AAAA,MACA,GAAGL;AAAA,MACH,GAAGG;AAAA,MACH,GAAKJ,IAAqC,CAAA,IAAZK;AAAA,IAChC;AAAA,IACA,OAAO;AAAA,MACL,YAAAC;AAAA,MACA,IAAAhB;AAAA,MACA,MAAAI;AAAA,IAAA;AAAA,EACF,CACD;AAGH;"}
@@ -1,80 +1,88 @@
1
1
  "use client";
2
- import { jsxs as P, jsx as n } from "react/jsx-runtime";
3
- import { memo as W, useCallback as x, useMemo as b } from "react";
4
- import { useDroppable as h } from "./hooks.js";
5
- import { ArrowsOutCardinalIcon as y } from "@bioturing/assets";
6
- import { useRender as B } from "@base-ui-components/react/use-render";
7
- import { Value as F } from "./value.js";
8
- import { uniqWith as G, isEqual as H } from "es-toolkit";
9
- import { useCls as J } from "../utils/antdUtils.js";
10
- import { clsx as K } from "../utils/cn.js";
11
- const Q = ({
2
+ import { jsxs as h, jsx as n } from "react/jsx-runtime";
3
+ import { memo as y, useCallback as x, useMemo as D } from "react";
4
+ import { useDroppable as B } from "./hooks.js";
5
+ import { ArrowsOutCardinalIcon as F } from "@bioturing/assets";
6
+ import { useRender as G } from "@base-ui-components/react/use-render";
7
+ import { Value as H } from "./value.js";
8
+ import { uniqWith as I, isEqual as J } from "es-toolkit";
9
+ import { useCls as K } from "../utils/antdUtils.js";
10
+ import { clsx as Q } from "../utils/cn.js";
11
+ const S = ({
12
12
  id: d,
13
- children: m,
14
- className: D,
15
- disabled: E = !1,
16
- placeholder: i = "Drop items here",
17
- icon: f = /* @__PURE__ */ n(y, { size: 24 }),
18
- render: O,
13
+ children: i,
14
+ className: E,
15
+ disabled: O = !1,
16
+ placeholder: m = "Drop items here",
17
+ icon: f = /* @__PURE__ */ n(F, {}),
18
+ render: R,
19
19
  onDrop: c,
20
- validate: R,
20
+ validate: j,
21
21
  multiple: s,
22
22
  value: r,
23
23
  onChange: o,
24
- renderValueLabel: p,
25
- renderValueIcon: a,
26
- ...j
24
+ maxItems: p,
25
+ renderValueLabel: a,
26
+ renderValueIcon: N,
27
+ ...q
27
28
  }) => {
28
- const e = J(), q = x(
29
+ const e = K(), k = x(
29
30
  (t) => {
30
31
  c && c(t), o && o(
31
- s ? G([...r, t.data], H) : t.data
32
+ s ? I([...r, t.data], J) : t.data
32
33
  );
33
34
  },
34
35
  [c, o, s, r]
35
- ), { isOver: k, setNodeRef: w, attributes: A, listeners: I } = h({
36
+ ), { isOver: w, setNodeRef: A, attributes: M, listeners: P } = B({
36
37
  id: d,
37
- disabled: E,
38
- onDrop: q,
39
- validate: R
40
- }), z = b(
41
- () => ({ value: r, onChange: o, multiple: s, renderValueLabel: p, renderValueIcon: a }),
42
- [r, o, s, p, a]
43
- ), N = b(
44
- () => m || /* @__PURE__ */ P("div", { className: e("drop-zone-content"), children: [
38
+ disabled: O,
39
+ onDrop: k,
40
+ validate: j
41
+ }), l = D(
42
+ () => ({
43
+ value: r,
44
+ onChange: o,
45
+ multiple: s,
46
+ renderValueLabel: a,
47
+ renderValueIcon: N,
48
+ maxItems: p
49
+ }),
50
+ [r, o, s, a, N, p]
51
+ ), z = D(
52
+ () => i || /* @__PURE__ */ h("div", { className: e("drop-zone-content"), children: [
45
53
  /* @__PURE__ */ n("div", { className: e("drop-zone-icon"), children: f }),
46
- /* @__PURE__ */ n("div", { className: e("drop-zone-text"), children: i })
54
+ /* @__PURE__ */ n("div", { className: e("drop-zone-text"), children: m })
47
55
  ] }),
48
- [m, e, f, i]
49
- ), M = x(
50
- ({ className: t, ...l }) => !s && r || s && r.length ? /* @__PURE__ */ n(
51
- F,
56
+ [i, e, f, m]
57
+ ), W = x(
58
+ ({ className: t, ...b }) => !s && r || s && r.length ? /* @__PURE__ */ n(
59
+ H,
52
60
  {
53
- ...z,
61
+ ...l,
54
62
  className: t,
55
- ...l
63
+ ...b
56
64
  }
57
- ) : /* @__PURE__ */ n("div", { className: K(e("drop-zone"), t), ...l, children: N }),
58
- [r, s, z, N, e]
65
+ ) : /* @__PURE__ */ n("div", { className: Q(e("drop-zone"), t), ...b, children: z }),
66
+ [r, s, l, z, e]
59
67
  );
60
- return B({
61
- render: O ?? M,
62
- ref: w,
68
+ return G({
69
+ render: R ?? W,
70
+ ref: A,
63
71
  props: {
64
- className: D,
65
- ...A,
66
- ...I,
67
- ...j
72
+ className: E,
73
+ ...M,
74
+ ...P,
75
+ ...q
68
76
  },
69
77
  state: {
70
- isOver: k,
78
+ isOver: w,
71
79
  value: r,
72
80
  onChange: o,
73
81
  id: d
74
82
  }
75
83
  });
76
- }, L = W(Q);
84
+ }, V = y(S);
77
85
  export {
78
- L as Droppable
86
+ V as Droppable
79
87
  };
80
88
  //# sourceMappingURL=droppable.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"droppable.js","sources":["../../../src/components/drag-drop/droppable.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useCallback, useMemo, memo } from \"react\";\nimport { useDroppable } from \"./hooks\";\nimport { clsx, useCls } from \"../utils\";\nimport { ArrowsOutCardinalIcon } from \"@bioturing/assets\";\nimport { useRender } from \"@base-ui-components/react/use-render\";\nimport type { Data, DragDropDroppableProps, DraggableInfo } from \"./types\";\nimport { Value } from \"./value\";\nimport { uniqWith, isEqual } from \"es-toolkit\";\n\n/**\n * A drop zone component that can receive draggable items\n * Styled similarly to the Upload component's droppable area\n */\nconst DroppableInner = <T extends Data = Data, M extends boolean = false>({\n id,\n children,\n className,\n disabled = false,\n placeholder = \"Drop items here\",\n icon = <ArrowsOutCardinalIcon size={24} />,\n render,\n onDrop,\n validate,\n multiple,\n value,\n onChange,\n renderValueLabel,\n renderValueIcon,\n ...rest\n}: DragDropDroppableProps<T, M>) => {\n const cls = useCls();\n const handleOnDrop = useCallback(\n (item: DraggableInfo<T>) => {\n if (onDrop) {\n onDrop(item);\n }\n if (onChange) {\n if (multiple) {\n (onChange as (value: T[]) => void)(\n uniqWith([...(value as T[]), item.data], isEqual)\n );\n } else {\n (onChange as (value: T) => void)(item.data);\n }\n }\n },\n [onDrop, onChange, multiple, value]\n );\n const { isOver, setNodeRef, attributes, listeners } = useDroppable({\n id,\n disabled,\n onDrop: handleOnDrop,\n validate,\n });\n\n // memoize value props to avoid re-renders\n const valueProps = useMemo(\n () => ({ value, onChange, multiple, renderValueLabel, renderValueIcon }),\n [value, onChange, multiple, renderValueLabel, renderValueIcon]\n );\n\n // memoize dropzone content\n const dropzoneContent = useMemo(\n () =>\n children || (\n <div className={cls(\"drop-zone-content\")}>\n <div className={cls(\"drop-zone-icon\")}>{icon}</div>\n <div className={cls(\"drop-zone-text\")}>{placeholder}</div>\n </div>\n ),\n [children, cls, icon, placeholder]\n );\n\n // memoize default render tree\n const defaultRender = useCallback<\n (props: Record<string, unknown>) => React.ReactElement\n >(\n ({ className, ...props }) =>\n (!multiple && value) || (multiple && (value as T[]).length) ? (\n <Value<T, M>\n {...valueProps}\n className={className as string}\n {...props}\n />\n ) : (\n <div className={clsx(cls(\"drop-zone\"), className as string)} {...props}>\n {dropzoneContent}\n </div>\n ),\n [value, multiple, valueProps, dropzoneContent, cls]\n );\n\n const renderedElement = useRender({\n render: render ?? defaultRender,\n ref: setNodeRef,\n props: {\n className: className,\n ...attributes,\n ...listeners,\n ...rest,\n },\n state: {\n isOver,\n value,\n onChange: onChange as (value: M extends true ? T[] : T) => void,\n id,\n },\n });\n\n return renderedElement;\n};\n\nexport const Droppable = memo(DroppableInner) as typeof DroppableInner;\n"],"names":["DroppableInner","id","children","className","disabled","placeholder","icon","jsx","ArrowsOutCardinalIcon","render","onDrop","validate","multiple","value","onChange","renderValueLabel","renderValueIcon","rest","cls","useCls","handleOnDrop","useCallback","item","uniqWith","isEqual","isOver","setNodeRef","attributes","listeners","useDroppable","valueProps","useMemo","dropzoneContent","jsxs","defaultRender","props","Value","clsx","useRender","Droppable","memo"],"mappings":";;;;;;;;;;AAeA,MAAMA,IAAiB,CAAmD;AAAA,EACxE,IAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,aAAAC,IAAc;AAAA,EACd,MAAAC,IAAO,gBAAAC,EAACC,GAAsB,EAAA,MAAM,GAAI,CAAA;AAAA,EACxC,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,iBAAAC;AAAA,EACA,GAAGC;AACL,MAAoC;AAClC,QAAMC,IAAMC,EAAO,GACbC,IAAeC;AAAA,IACnB,CAACC,MAA2B;AAC1B,MAAIZ,KACFA,EAAOY,CAAI,GAETR,KAECA;AAAA,QADCF,IAEAW,EAAS,CAAC,GAAIV,GAAeS,EAAK,IAAI,GAAGE,CAAO,IAGjBF,EAAK;AAAA,MAFtC;AAAA,IAKN;AAAA,IACA,CAACZ,GAAQI,GAAUF,GAAUC,CAAK;AAAA,EACpC,GACM,EAAE,QAAAY,GAAQ,YAAAC,GAAY,YAAAC,GAAY,WAAAC,EAAA,IAAcC,EAAa;AAAA,IACjE,IAAA5B;AAAA,IACA,UAAAG;AAAA,IACA,QAAQgB;AAAA,IACR,UAAAT;AAAA,EAAA,CACD,GAGKmB,IAAaC;AAAA,IACjB,OAAO,EAAE,OAAAlB,GAAO,UAAAC,GAAU,UAAAF,GAAU,kBAAAG,GAAkB,iBAAAC,EAAgB;AAAA,IACtE,CAACH,GAAOC,GAAUF,GAAUG,GAAkBC,CAAe;AAAA,EAC/D,GAGMgB,IAAkBD;AAAA,IACtB,MACE7B,KACE,gBAAA+B,EAAC,SAAI,WAAWf,EAAI,mBAAmB,GACrC,UAAA;AAAA,MAAA,gBAAAX,EAAC,OAAI,EAAA,WAAWW,EAAI,gBAAgB,GAAI,UAAKZ,GAAA;AAAA,wBAC5C,OAAI,EAAA,WAAWY,EAAI,gBAAgB,GAAI,UAAYb,EAAA,CAAA;AAAA,IAAA,GACtD;AAAA,IAEJ,CAACH,GAAUgB,GAAKZ,GAAMD,CAAW;AAAA,EACnC,GAGM6B,IAAgBb;AAAA,IAGpB,CAAC,EAAE,WAAAlB,GAAW,GAAGgC,EACd,MAAA,CAACvB,KAAYC,KAAWD,KAAaC,EAAc,SAClD,gBAAAN;AAAA,MAAC6B;AAAA,MAAA;AAAA,QACE,GAAGN;AAAA,QACJ,WAAW3B;AAAAA,QACV,GAAGgC;AAAA,MAAA;AAAA,IAGN,IAAA,gBAAA5B,EAAC,OAAI,EAAA,WAAW8B,EAAKnB,EAAI,WAAW,GAAGf,CAAmB,GAAI,GAAGgC,GAC9D,UACHH,EAAA,CAAA;AAAA,IAEJ,CAACnB,GAAOD,GAAUkB,GAAYE,GAAiBd,CAAG;AAAA,EACpD;AAmBO,SAjBiBoB,EAAU;AAAA,IAChC,QAAQ7B,KAAUyB;AAAA,IAClB,KAAKR;AAAA,IACL,OAAO;AAAA,MACL,WAAAvB;AAAA,MACA,GAAGwB;AAAA,MACH,GAAGC;AAAA,MACH,GAAGX;AAAA,IACL;AAAA,IACA,OAAO;AAAA,MACL,QAAAQ;AAAA,MACA,OAAAZ;AAAA,MACA,UAAAC;AAAA,MACA,IAAAb;AAAA,IAAA;AAAA,EACF,CACD;AAGH,GAEasC,IAAYC,EAAKxC,CAAc;"}
1
+ {"version":3,"file":"droppable.js","sources":["../../../src/components/drag-drop/droppable.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useCallback, useMemo, memo } from \"react\";\nimport { useDroppable } from \"./hooks\";\nimport { clsx, useCls } from \"../utils\";\nimport { ArrowsOutCardinalIcon } from \"@bioturing/assets\";\nimport { useRender } from \"@base-ui-components/react/use-render\";\nimport type { Data, DragDropDroppableProps, DraggableInfo } from \"./types\";\nimport { Value } from \"./value\";\nimport { uniqWith, isEqual } from \"es-toolkit\";\n\n/**\n * A drop zone component that can receive draggable items\n * Styled similarly to the Upload component's droppable area\n */\nconst DroppableInner = <T extends Data = Data, M extends boolean = false>({\n id,\n children,\n className,\n disabled = false,\n placeholder = \"Drop items here\",\n icon = <ArrowsOutCardinalIcon />,\n render,\n onDrop,\n validate,\n multiple,\n value,\n onChange,\n maxItems,\n renderValueLabel,\n renderValueIcon,\n ...rest\n}: DragDropDroppableProps<T, M>) => {\n const cls = useCls();\n const handleOnDrop = useCallback(\n (item: DraggableInfo<T>) => {\n if (onDrop) {\n onDrop(item);\n }\n if (onChange) {\n if (multiple) {\n (onChange as (value: T[]) => void)(\n uniqWith([...(value as T[]), item.data], isEqual)\n );\n } else {\n (onChange as (value: T) => void)(item.data);\n }\n }\n },\n [onDrop, onChange, multiple, value]\n );\n const { isOver, setNodeRef, attributes, listeners } = useDroppable({\n id,\n disabled,\n onDrop: handleOnDrop,\n validate,\n });\n\n // memoize value props to avoid re-renders\n const valueProps = useMemo(\n () => ({\n value,\n onChange,\n multiple,\n renderValueLabel,\n renderValueIcon,\n maxItems,\n }),\n [value, onChange, multiple, renderValueLabel, renderValueIcon, maxItems]\n );\n\n // memoize dropzone content\n const dropzoneContent = useMemo(\n () =>\n children || (\n <div className={cls(\"drop-zone-content\")}>\n <div className={cls(\"drop-zone-icon\")}>{icon}</div>\n <div className={cls(\"drop-zone-text\")}>{placeholder}</div>\n </div>\n ),\n [children, cls, icon, placeholder]\n );\n\n // memoize default render tree\n const defaultRender = useCallback<\n (props: Record<string, unknown>) => React.ReactElement\n >(\n ({ className, ...props }) =>\n (!multiple && value) || (multiple && (value as T[]).length) ? (\n <Value<T, M>\n {...valueProps}\n className={className as string}\n {...props}\n />\n ) : (\n <div className={clsx(cls(\"drop-zone\"), className as string)} {...props}>\n {dropzoneContent}\n </div>\n ),\n [value, multiple, valueProps, dropzoneContent, cls]\n );\n\n const renderedElement = useRender({\n render: render ?? defaultRender,\n ref: setNodeRef,\n props: {\n className: className,\n ...attributes,\n ...listeners,\n ...rest,\n },\n state: {\n isOver,\n value,\n onChange: onChange as (value: M extends true ? T[] : T) => void,\n id,\n },\n });\n\n return renderedElement;\n};\n\nexport const Droppable = memo(DroppableInner) as typeof DroppableInner;\n"],"names":["DroppableInner","id","children","className","disabled","placeholder","icon","ArrowsOutCardinalIcon","render","onDrop","validate","multiple","value","onChange","maxItems","renderValueLabel","renderValueIcon","rest","cls","useCls","handleOnDrop","useCallback","item","uniqWith","isEqual","isOver","setNodeRef","attributes","listeners","useDroppable","valueProps","useMemo","dropzoneContent","jsxs","jsx","defaultRender","props","Value","clsx","useRender","Droppable","memo"],"mappings":";;;;;;;;;;AAeA,MAAMA,IAAiB,CAAmD;AAAA,EACxE,IAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,aAAAC,IAAc;AAAA,EACd,MAAAC,sBAAQC,GAAsB,EAAA;AAAA,EAC9B,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,iBAAAC;AAAA,EACA,GAAGC;AACL,MAAoC;AAClC,QAAMC,IAAMC,EAAO,GACbC,IAAeC;AAAA,IACnB,CAACC,MAA2B;AAC1B,MAAIb,KACFA,EAAOa,CAAI,GAETT,KAECA;AAAA,QADCF,IAEAY,EAAS,CAAC,GAAIX,GAAeU,EAAK,IAAI,GAAGE,CAAO,IAGjBF,EAAK;AAAA,MAFtC;AAAA,IAKN;AAAA,IACA,CAACb,GAAQI,GAAUF,GAAUC,CAAK;AAAA,EACpC,GACM,EAAE,QAAAa,GAAQ,YAAAC,GAAY,YAAAC,GAAY,WAAAC,EAAA,IAAcC,EAAa;AAAA,IACjE,IAAA5B;AAAA,IACA,UAAAG;AAAA,IACA,QAAQgB;AAAA,IACR,UAAAV;AAAA,EAAA,CACD,GAGKoB,IAAaC;AAAA,IACjB,OAAO;AAAA,MACL,OAAAnB;AAAA,MACA,UAAAC;AAAA,MACA,UAAAF;AAAA,MACA,kBAAAI;AAAA,MACA,iBAAAC;AAAA,MACA,UAAAF;AAAA,IAAA;AAAA,IAEF,CAACF,GAAOC,GAAUF,GAAUI,GAAkBC,GAAiBF,CAAQ;AAAA,EACzE,GAGMkB,IAAkBD;AAAA,IACtB,MACE7B,KACE,gBAAA+B,EAAC,SAAI,WAAWf,EAAI,mBAAmB,GACrC,UAAA;AAAA,MAAA,gBAAAgB,EAAC,OAAI,EAAA,WAAWhB,EAAI,gBAAgB,GAAI,UAAKZ,GAAA;AAAA,wBAC5C,OAAI,EAAA,WAAWY,EAAI,gBAAgB,GAAI,UAAYb,EAAA,CAAA;AAAA,IAAA,GACtD;AAAA,IAEJ,CAACH,GAAUgB,GAAKZ,GAAMD,CAAW;AAAA,EACnC,GAGM8B,IAAgBd;AAAA,IAGpB,CAAC,EAAE,WAAAlB,GAAW,GAAGiC,EACd,MAAA,CAACzB,KAAYC,KAAWD,KAAaC,EAAc,SAClD,gBAAAsB;AAAA,MAACG;AAAA,MAAA;AAAA,QACE,GAAGP;AAAA,QACJ,WAAW3B;AAAAA,QACV,GAAGiC;AAAA,MAAA;AAAA,IAGN,IAAA,gBAAAF,EAAC,OAAI,EAAA,WAAWI,EAAKpB,EAAI,WAAW,GAAGf,CAAmB,GAAI,GAAGiC,GAC9D,UACHJ,EAAA,CAAA;AAAA,IAEJ,CAACpB,GAAOD,GAAUmB,GAAYE,GAAiBd,CAAG;AAAA,EACpD;AAmBO,SAjBiBqB,EAAU;AAAA,IAChC,QAAQ/B,KAAU2B;AAAA,IAClB,KAAKT;AAAA,IACL,OAAO;AAAA,MACL,WAAAvB;AAAA,MACA,GAAGwB;AAAA,MACH,GAAGC;AAAA,MACH,GAAGX;AAAA,IACL;AAAA,IACA,OAAO;AAAA,MACL,QAAAQ;AAAA,MACA,OAAAb;AAAA,MACA,UAAAC;AAAA,MACA,IAAAZ;AAAA,IAAA;AAAA,EACF,CACD;AAGH,GAEauC,IAAYC,EAAKzC,CAAc;"}
@@ -1 +1 @@
1
- .ds-drag-container{position:relative}.ds-draggable{position:relative;cursor:grab;-webkit-user-select:none;user-select:none;background-color:var(--ds-color-bg-container);border:1px solid var(--ds-color-border);border-radius:var(--ds-border-radius);padding:.5rem;margin-bottom:.5rem;display:flex;max-width:100%;gap:.5rem;transition:background-color .2s,border-color .2s,box-shadow .2s}.ds-draggable[data-dragging]{opacity:.5;box-shadow:0 2px 8px #00000026;z-index:1}.ds-draggable[data-disabled]{cursor:not-allowed;opacity:.5;background-color:var(--ds-color-bg-disabled)}.ds-draggable:hover{border-color:var(--ds-color-primary)}.ds-draggable .ds-draggable-indicator{font-size:1.25rem;color:var(--ds-color-icon)}.ds-drop-zone{position:relative;width:100%;background-color:var(--ds-color-fill-alter);border:1px dashed var(--ds-color-border);border-radius:var(--ds-border-radius);padding:.75rem;display:flex;align-items:center;justify-content:center;transition:border-color .1s,background-color .1s}.ds-drop-zone[data-disabled=true]{cursor:not-allowed;background-color:var(--ds-color-bg-disabled);border-color:var(--ds-color-border)}.ds-drop-zone[data-dropping][data-valid],.ds-drop-zone[data-over][data-valid]{border-color:var(--ds-color-primary);background-color:var(--ds-color-primary-bg)}.ds-drop-zone[data-dragging][data-valid]{border-color:var(--ds-color-primary)}.ds-drop-zone .ds-drop-zone-content{display:flex;align-items:center;justify-content:center;text-align:center;gap:.5rem}.ds-drop-zone .ds-drop-zone-icon{font-size:1.25rem;color:var(--ds-color-icon)}.ds-drop-zone .ds-drop-zone-text{color:var(--ds-color-text-description);font-size:var(--ds-font-size)}.ds-dropzone-value{padding:.75rem;border-radius:var(--ds-border-radius);border-width:1px;border-style:solid;border-color:var(--ds-color-border)}.ds-dropzone-value[data-dragging][data-valid]{border-color:var(--ds-color-primary);border-style:dashed}.ds-dropzone-value[data-dropping][data-valid],.ds-dropzone-value[data-over][data-valid]{border-color:var(--ds-color-primary);background-color:var(--ds-color-primary-bg)}.ds-dropzone-value-icon{color:var(--ds-color-icon);font-size:1.5rem}.ds-dropzone-value-text{color:var(--ds-color-text-secondary)}.ds-dropzone-value-multiple .ds-tag{max-width:50%;margin:0}
1
+ .ds-drag-container{position:relative}.ds-draggable{position:relative;cursor:grab;-webkit-user-select:none;user-select:none;background-color:var(--ds-color-bg-container);border:1px solid var(--ds-color-border);border-radius:var(--ds-border-radius);padding:.5rem;margin-bottom:.5rem;display:flex;max-width:100%;gap:.5rem;transition:background-color .2s,border-color .2s,box-shadow .2s}.ds-draggable[data-dragging]{opacity:.5;box-shadow:0 2px 8px #00000026;z-index:1}.ds-draggable[data-disabled]{cursor:not-allowed;opacity:.5;background-color:var(--ds-color-bg-disabled)}.ds-draggable:hover{border-color:var(--ds-color-primary)}.ds-draggable .ds-draggable-indicator{font-size:1.25rem;color:var(--ds-color-icon)}.ds-drop-zone{position:relative;width:100%;background-color:var(--ds-color-fill-alter);border:1px dashed var(--ds-color-border);border-radius:var(--ds-border-radius);padding:.75rem;display:flex;align-items:center;justify-content:center;transition:border-color .1s,background-color .1s}.ds-drop-zone[data-disabled=true]{cursor:not-allowed;background-color:var(--ds-color-bg-disabled);border-color:var(--ds-color-border)}.ds-drop-zone[data-dropping][data-valid],.ds-drop-zone[data-over][data-valid]{border-color:var(--ds-color-primary);background-color:var(--ds-color-primary-bg)}.ds-drop-zone[data-dragging][data-valid]{border-color:var(--ds-color-primary)}.ds-drop-zone .ds-drop-zone-content{display:flex;align-items:center;justify-content:center;text-align:center;gap:.5rem}.ds-drop-zone .ds-drop-zone-icon{font-size:1.25rem;color:var(--ds-color-icon)}.ds-drop-zone .ds-drop-zone-text{color:var(--ds-color-text-description);font-size:var(--ds-font-size)}.ds-dropzone-value{padding:.75rem;border-radius:var(--ds-border-radius);border-width:1px;border-style:solid;border-color:var(--ds-color-border)}.ds-dropzone-value[data-dragging][data-valid]{border-color:var(--ds-color-primary);border-style:dashed}.ds-dropzone-value[data-dropping][data-valid],.ds-dropzone-value[data-over][data-valid]{border-color:var(--ds-color-primary);background-color:var(--ds-color-primary-bg)}.ds-dropzone-value.ds-dropzone-value-multiple{padding:.625rem}.ds-dropzone-value.ds-dropzone-value-multiple .ds-tag{max-width:100%;flex-shrink:0;margin:0}:is(.ds-dropzone-value.ds-dropzone-value-multiple .ds-tag) .ds-tag-close-icon{flex-shrink:0}.ds-dropzone-value-icon{color:var(--ds-color-icon);font-size:1.5rem;flex-shrink:0}.ds-dropzone-value-text{color:var(--ds-color-text-secondary)}
@@ -1,84 +1,108 @@
1
1
  "use client";
2
- import { jsx as e, Fragment as u, jsxs as S } from "react/jsx-runtime";
3
- import { FileIcon as h, XIcon as j } from "@bioturing/assets";
4
- import { isEqual as w } from "es-toolkit";
5
- import { memo as E, forwardRef as F, useCallback as c, cloneElement as M } from "react";
6
- import { Stack as T } from "../stack/index.js";
7
- import { Tag as q } from "../tag/component.js";
8
- import { useCls as y } from "../utils/antdUtils.js";
9
- import { Truncate as B } from "../truncate/component.js";
10
- import { IconButton as X } from "../icon-button/component.js";
11
- function A({
12
- value: o,
13
- onChange: l,
14
- multiple: s,
15
- renderValueLabel: a,
16
- renderValueIcon: p,
17
- className: b,
18
- style: z,
19
- ...I
20
- }, N) {
21
- const n = y(), i = c(
2
+ import { jsx as o, jsxs as T, Fragment as R } from "react/jsx-runtime";
3
+ import { FileIcon as h, XIcon as q } from "@bioturing/assets";
4
+ import { isEqual as x } from "es-toolkit";
5
+ import { memo as y, forwardRef as B, useState as X, useCallback as t, cloneElement as v } from "react";
6
+ import { Stack as A } from "../stack/index.js";
7
+ import D from "merge-refs";
8
+ import { Tag as G } from "../tag/component.js";
9
+ import { useCls as H } from "../utils/antdUtils.js";
10
+ import { Truncate as J } from "../truncate/component.js";
11
+ import { IconButton as K } from "../icon-button/component.js";
12
+ function L({
13
+ value: s,
14
+ onChange: n,
15
+ multiple: l,
16
+ renderValueLabel: d,
17
+ renderValueIcon: f,
18
+ className: E,
19
+ style: N,
20
+ maxItems: m,
21
+ ...S
22
+ }, k) {
23
+ const c = H(), [u, j] = X(null), w = t((r) => {
24
+ j(r);
25
+ }, []), i = t(
22
26
  (r) => {
23
- const t = a(r);
24
- return typeof t == "string" ? /* @__PURE__ */ e(B, { position: "middle", className: n("dropzone-value-label"), children: t }) : /* @__PURE__ */ e("span", { className: n("dropzone-value-label"), children: t });
27
+ const e = d(r);
28
+ return typeof e == "string" ? /* @__PURE__ */ o(
29
+ J,
30
+ {
31
+ position: "middle",
32
+ className: c("dropzone-value-label"),
33
+ container: u,
34
+ children: e
35
+ }
36
+ ) : /* @__PURE__ */ o("span", { className: c("dropzone-value-label"), children: e });
25
37
  },
26
- [n, a]
27
- ), m = c(
28
- (r) => M(p(r) || /* @__PURE__ */ e(h, {}), {
29
- className: n("dropzone-value-icon")
38
+ [c, u, d]
39
+ ), a = t(
40
+ (r) => v(f(r) || /* @__PURE__ */ o(h, {}), {
41
+ className: c("dropzone-value-icon")
30
42
  }),
31
- [n, p]
32
- ), d = c(
43
+ [c, f]
44
+ ), b = t(
33
45
  (r) => {
34
- l(
35
- o.filter((t) => !w(t, r))
46
+ n(
47
+ s.filter((e) => !x(e, r))
36
48
  );
37
49
  },
38
- [l, o]
39
- ), k = c(() => /* @__PURE__ */ e(u, { children: (o || []).map((r, t) => {
40
- const R = i(r);
41
- return /* @__PURE__ */ e(
42
- q,
43
- {
44
- icon: m(r),
45
- size: "large",
46
- closable: !0,
47
- onClose: () => d(r),
48
- children: R
49
- },
50
- t
51
- );
52
- }) }), [i, o, m, d]), f = c(() => {
53
- l == null || l(null);
54
- }, [l]), x = c(() => {
55
- const r = i(o);
56
- return /* @__PURE__ */ S(u, { children: [
57
- m(o),
58
- r,
59
- /* @__PURE__ */ e(X, { size: "small", onClick: f, children: /* @__PURE__ */ e(j, {}) })
60
- ] });
61
- }, [o, i, m, f]);
62
- return /* @__PURE__ */ e(
63
- T,
50
+ [n, s]
51
+ ), z = t(() => {
52
+ l ? n([]) : n == null || n(null);
53
+ }, [n, l]), p = t(
54
+ (r) => {
55
+ const e = i(r);
56
+ return /* @__PURE__ */ T(R, { children: [
57
+ a(r),
58
+ e,
59
+ /* @__PURE__ */ o(K, { size: "small", onClick: z, children: /* @__PURE__ */ o(q, {}) })
60
+ ] });
61
+ },
62
+ [i, a, z]
63
+ ), F = t(
64
+ (r) => m == 1 ? p(r[0]) : /* @__PURE__ */ o(R, { children: (r || []).map((e, I) => {
65
+ const M = i(e);
66
+ return /* @__PURE__ */ o(
67
+ G,
68
+ {
69
+ icon: a(e),
70
+ size: "large",
71
+ closable: !0,
72
+ onClose: () => b(e),
73
+ children: M
74
+ },
75
+ I
76
+ );
77
+ }) }),
78
+ [
79
+ i,
80
+ a,
81
+ m,
82
+ p,
83
+ b
84
+ ]
85
+ );
86
+ return /* @__PURE__ */ o(
87
+ A,
64
88
  {
65
- ref: N,
66
- gap: s ? 4 : 8,
89
+ ref: D(k, w),
90
+ gap: l ? 4 : 8,
67
91
  align: "center",
68
- wrap: s,
69
- className: n(
92
+ wrap: l && m !== 1,
93
+ className: c(
70
94
  "dropzone-value",
71
- s && "dropzone-value-multiple",
72
- b
95
+ l && "dropzone-value-multiple",
96
+ E
73
97
  ),
74
- style: z,
75
- ...I,
76
- children: s ? k() : x()
98
+ style: N,
99
+ ...S,
100
+ children: l ? F(s) : p(s)
77
101
  }
78
102
  );
79
103
  }
80
- const U = E(F(A));
104
+ const g = y(B(L));
81
105
  export {
82
- U as Value
106
+ g as Value
83
107
  };
84
108
  //# sourceMappingURL=value.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"value.js","sources":["../../../src/components/drag-drop/value.tsx"],"sourcesContent":["\"use client\";\nimport { FileIcon, XIcon } from \"@bioturing/assets\";\nimport { isEqual } from \"es-toolkit\";\nimport React, {\n cloneElement,\n forwardRef,\n memo,\n useCallback,\n type ComponentPropsWithoutRef,\n type ForwardedRef,\n} from \"react\";\nimport { IconButton } from \"../icon-button\";\nimport { Stack } from \"../stack\";\nimport { Tag } from \"../tag\";\nimport { Truncate } from \"../truncate\";\nimport { useCls } from \"../utils\";\nimport { DragDropValueProps, type Data } from \"./types\";\n\nfunction ValueInner<T extends Data, M extends boolean>(\n {\n value,\n onChange,\n multiple,\n renderValueLabel,\n renderValueIcon,\n className,\n style,\n ...rest\n }: DragDropValueProps<T, M> & ComponentPropsWithoutRef<\"div\">,\n ref: ForwardedRef<HTMLDivElement>\n) {\n const cls = useCls();\n\n const renderLabel = useCallback(\n (value: T) => {\n const label = renderValueLabel(value);\n return typeof label === \"string\" ? (\n <Truncate position=\"middle\" className={cls(\"dropzone-value-label\")}>\n {label}\n </Truncate>\n ) : (\n <span className={cls(\"dropzone-value-label\")}>{label}</span>\n );\n },\n [cls, renderValueLabel]\n );\n\n const renderedValueIcon = useCallback(\n (value: T) => {\n return cloneElement(renderValueIcon(value) || <FileIcon />, {\n className: cls(\"dropzone-value-icon\"),\n });\n },\n [cls, renderValueIcon]\n );\n\n const handleResetMultiple = useCallback(\n (singleValue: T) => {\n (onChange as (value: T[]) => void)(\n (value as T[]).filter((v) => !isEqual(v, singleValue))\n );\n },\n [onChange, value]\n );\n\n const renderMultiple = useCallback(() => {\n return (\n <>\n {((value as T[]) || []).map((singleValue, index) => {\n const label = renderLabel(singleValue);\n return (\n <Tag\n key={index}\n icon={renderedValueIcon(singleValue)}\n size=\"large\"\n closable\n onClose={() => handleResetMultiple(singleValue)}\n >\n {label}\n </Tag>\n );\n })}\n </>\n );\n }, [renderLabel, value, renderedValueIcon, handleResetMultiple]);\n\n const handleResetSingle = useCallback(() => {\n onChange?.(null);\n }, [onChange]);\n\n const renderSingle = useCallback(() => {\n const label = renderLabel(value as T);\n return (\n <>\n {renderedValueIcon(value as T)}\n {label}\n <IconButton size=\"small\" onClick={handleResetSingle}>\n <XIcon />\n </IconButton>\n </>\n );\n }, [value, renderLabel, renderedValueIcon, handleResetSingle]);\n\n return (\n <Stack\n ref={ref}\n gap={multiple ? 4 : 8}\n align=\"center\"\n wrap={multiple}\n className={cls(\n \"dropzone-value\",\n multiple && \"dropzone-value-multiple\",\n className\n )}\n style={style}\n {...rest}\n >\n {multiple ? renderMultiple() : renderSingle()}\n </Stack>\n );\n}\n\nexport const Value = memo(forwardRef(ValueInner)) as <\n T extends Data,\n M extends boolean\n>(\n props: DragDropValueProps<T, M> & { ref?: ForwardedRef<HTMLDivElement> }\n) => ReturnType<typeof ValueInner>;\n"],"names":["ValueInner","value","onChange","multiple","renderValueLabel","renderValueIcon","className","style","rest","ref","cls","useCls","renderLabel","useCallback","label","Truncate","jsx","renderedValueIcon","cloneElement","FileIcon","handleResetMultiple","singleValue","v","isEqual","renderMultiple","index","Tag","handleResetSingle","renderSingle","jsxs","Fragment","IconButton","XIcon","Stack","Value","memo","forwardRef"],"mappings":";;;;;;;;;;AAkBA,SAASA,EACP;AAAA,EACE,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,iBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAMC,EAAO,GAEbC,IAAcC;AAAA,IAClB,CAACZ,MAAa;AACN,YAAAa,IAAQV,EAAiBH,CAAK;AACpC,aAAO,OAAOa,KAAU,6BACrBC,GAAS,EAAA,UAAS,UAAS,WAAWL,EAAI,sBAAsB,GAC9D,UAAAI,GACH,IAEC,gBAAAE,EAAA,QAAA,EAAK,WAAWN,EAAI,sBAAsB,GAAI,UAAMI,GAAA;AAAA,IAEzD;AAAA,IACA,CAACJ,GAAKN,CAAgB;AAAA,EACxB,GAEMa,IAAoBJ;AAAA,IACxB,CAACZ,MACQiB,EAAab,EAAgBJ,CAAK,KAAK,gBAAAe,EAACG,KAAS,GAAI;AAAA,MAC1D,WAAWT,EAAI,qBAAqB;AAAA,IAAA,CACrC;AAAA,IAEH,CAACA,GAAKL,CAAe;AAAA,EACvB,GAEMe,IAAsBP;AAAA,IAC1B,CAACQ,MAAmB;AACjB,MAAAnB;AAAA,QACED,EAAc,OAAO,CAACqB,MAAM,CAACC,EAAQD,GAAGD,CAAW,CAAC;AAAA,MACvD;AAAA,IACF;AAAA,IACA,CAACnB,GAAUD,CAAK;AAAA,EAClB,GAEMuB,IAAiBX,EAAY,6BAG1B,WAAiBZ,KAAA,CAAA,GAAI,IAAI,CAACoB,GAAaI,MAAU;AAC5C,UAAAX,IAAQF,EAAYS,CAAW;AAEnC,WAAA,gBAAAL;AAAA,MAACU;AAAA,MAAA;AAAA,QAEC,MAAMT,EAAkBI,CAAW;AAAA,QACnC,MAAK;AAAA,QACL,UAAQ;AAAA,QACR,SAAS,MAAMD,EAAoBC,CAAW;AAAA,QAE7C,UAAAP;AAAA,MAAA;AAAA,MANIW;AAAA,IAOP;AAAA,EAEH,CAAA,GACH,GAED,CAACb,GAAaX,GAAOgB,GAAmBG,CAAmB,CAAC,GAEzDO,IAAoBd,EAAY,MAAM;AAC1C,IAAAX,KAAA,QAAAA,EAAW;AAAA,EAAI,GACd,CAACA,CAAQ,CAAC,GAEP0B,IAAef,EAAY,MAAM;AAC/B,UAAAC,IAAQF,EAAYX,CAAU;AACpC,WAEK,gBAAA4B,EAAAC,GAAA,EAAA,UAAA;AAAA,MAAAb,EAAkBhB,CAAU;AAAA,MAC5Ba;AAAA,MACD,gBAAAE,EAACe,KAAW,MAAK,SAAQ,SAASJ,GAChC,UAAA,gBAAAX,EAACgB,IAAM,CAAA,EACT,CAAA;AAAA,IAAA,GACF;AAAA,KAED,CAAC/B,GAAOW,GAAaK,GAAmBU,CAAiB,CAAC;AAG3D,SAAA,gBAAAX;AAAA,IAACiB;AAAA,IAAA;AAAA,MACC,KAAAxB;AAAA,MACA,KAAKN,IAAW,IAAI;AAAA,MACpB,OAAM;AAAA,MACN,MAAMA;AAAA,MACN,WAAWO;AAAA,QACT;AAAA,QACAP,KAAY;AAAA,QACZG;AAAA,MACF;AAAA,MACA,OAAAC;AAAA,MACC,GAAGC;AAAA,MAEH,UAAAL,IAAWqB,EAAe,IAAII,EAAa;AAAA,IAAA;AAAA,EAC9C;AAEJ;AAEO,MAAMM,IAAQC,EAAKC,EAAWpC,CAAU,CAAC;"}
1
+ {"version":3,"file":"value.js","sources":["../../../src/components/drag-drop/value.tsx"],"sourcesContent":["\"use client\";\nimport { FileIcon, XIcon } from \"@bioturing/assets\";\nimport { isEqual } from \"es-toolkit\";\nimport {\n cloneElement,\n forwardRef,\n memo,\n useCallback,\n type ForwardedRef,\n useState,\n} from \"react\";\nimport { IconButton } from \"../icon-button\";\nimport { Stack } from \"../stack\";\nimport { Tag } from \"../tag\";\nimport { Truncate } from \"../truncate\";\nimport { useCls } from \"../utils\";\nimport { DragDropValueProps, type Data } from \"./types\";\nimport mergeRefs from \"merge-refs\";\n\nfunction ValueInner<T extends Data, M extends boolean>(\n {\n value,\n onChange,\n multiple,\n renderValueLabel,\n renderValueIcon,\n className,\n style,\n maxItems,\n ...rest\n }: DragDropValueProps<T, M>,\n ref: ForwardedRef<HTMLDivElement>\n) {\n const cls = useCls();\n\n // todo: make this as reusable hook\n const [containerEl, setContainerEl] = useState<HTMLDivElement>(null);\n const containerRef = useCallback((el: HTMLDivElement | null) => {\n setContainerEl(el);\n }, []);\n\n const renderLabel = useCallback(\n (value: T) => {\n const label = renderValueLabel(value);\n return typeof label === \"string\" ? (\n <Truncate\n position=\"middle\"\n className={cls(\"dropzone-value-label\")}\n container={containerEl}\n >\n {label}\n </Truncate>\n ) : (\n <span className={cls(\"dropzone-value-label\")}>{label}</span>\n );\n },\n [cls, containerEl, renderValueLabel]\n );\n\n const renderedValueIcon = useCallback(\n (value: T) => {\n return cloneElement(renderValueIcon(value) || <FileIcon />, {\n className: cls(\"dropzone-value-icon\"),\n });\n },\n [cls, renderValueIcon]\n );\n\n const handleResetMultiple = useCallback(\n (singleValue: T) => {\n (onChange as (value: T[]) => void)(\n (value as T[]).filter((v) => !isEqual(v, singleValue))\n );\n },\n [onChange, value]\n );\n\n const handleResetSingle = useCallback(() => {\n if (multiple) {\n (onChange as (value: T[]) => void)([] as T[]);\n } else {\n onChange?.(null);\n }\n }, [onChange, multiple]);\n\n const renderSingle = useCallback(\n (value: T) => {\n const label = renderLabel(value as T);\n return (\n <>\n {renderedValueIcon(value as T)}\n {label}\n <IconButton size=\"small\" onClick={handleResetSingle}>\n <XIcon />\n </IconButton>\n </>\n );\n },\n [renderLabel, renderedValueIcon, handleResetSingle]\n );\n\n const renderMultiple = useCallback(\n (value: T[]) => {\n if (maxItems == 1) {\n return renderSingle(value[0]);\n }\n return (\n <>\n {((value as T[]) || []).map((singleValue, index) => {\n const label = renderLabel(singleValue);\n return (\n <Tag\n key={index}\n icon={renderedValueIcon(singleValue)}\n size=\"large\"\n closable\n onClose={() => handleResetMultiple(singleValue)}\n >\n {label}\n </Tag>\n );\n })}\n </>\n );\n },\n [\n renderLabel,\n renderedValueIcon,\n maxItems,\n renderSingle,\n handleResetMultiple,\n ]\n );\n\n return (\n <Stack\n ref={mergeRefs(ref, containerRef)}\n gap={multiple ? 4 : 8}\n align=\"center\"\n wrap={multiple && maxItems !== 1}\n className={cls(\n \"dropzone-value\",\n multiple && \"dropzone-value-multiple\",\n className\n )}\n style={style}\n {...rest}\n >\n {multiple ? renderMultiple(value as T[]) : renderSingle(value as T)}\n </Stack>\n );\n}\n\nexport const Value = memo(forwardRef(ValueInner)) as <\n T extends Data,\n M extends boolean\n>(\n props: DragDropValueProps<T, M> & { ref?: ForwardedRef<HTMLDivElement> }\n) => ReturnType<typeof ValueInner>;\n"],"names":["ValueInner","value","onChange","multiple","renderValueLabel","renderValueIcon","className","style","maxItems","rest","ref","cls","useCls","containerEl","setContainerEl","useState","containerRef","useCallback","el","renderLabel","label","jsx","Truncate","renderedValueIcon","cloneElement","FileIcon","handleResetMultiple","singleValue","v","isEqual","handleResetSingle","renderSingle","jsxs","Fragment","IconButton","XIcon","renderMultiple","index","Tag","Stack","mergeRefs","Value","memo","forwardRef"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","sources":["../../../src/components/tag/component.tsx"],"sourcesContent":["\"use client\";\nimport {\n forwardRef,\n cloneElement,\n isValidElement,\n ReactElement,\n HTMLAttributes,\n} from \"react\";\nimport { XIcon } from \"@bioturing/assets\";\nimport AntTag, {\n type TagProps as AntTagProps,\n type TagType,\n} from \"antd/es/tag\";\nimport AntCheckableTag, {\n type CheckableTagProps as AntCheckableTagProps,\n} from \"antd/es/tag/CheckableTag\";\nimport { clsx, useCls } from \"../utils\";\n\n// Import component-specific styles\nimport \"./style.css\";\n\nexport { TagType };\n\n// Define props interface extending Ant Design's TagProps\nexport interface TagProps extends AntTagProps {\n /**\n * Indicates if the tag is active\n * @default false\n */\n active?: boolean;\n /**\n * Size of the tag\n * @default \"small\"\n */\n size?: \"small\" | \"medium\" | \"large\";\n}\n\nexport interface CheckableTagProps extends AntCheckableTagProps {\n size?: \"small\" | \"medium\" | \"large\";\n}\n\n// Create Tag component\nconst InternalTag = forwardRef<HTMLSpanElement, TagProps>(\n (\n {\n className,\n active = false,\n color,\n style,\n closeIcon,\n closable,\n size = \"small\",\n children,\n icon,\n ...rest\n },\n ref\n ) => {\n const renderedCloseIcon = <XIcon weight=\"bold\" />;\n const cls = useCls();\n const renderedIcon = isValidElement(icon)\n ? cloneElement<HTMLAttributes<SVGElement>>(icon, {\n className: clsx(\n cls(\"tag-icon\"),\n icon.props &&\n typeof icon.props === \"object\" &&\n \"className\" in icon.props &&\n typeof icon.props.className === \"string\"\n ? icon.props.className\n : \"\"\n ),\n })\n : icon;\n return (\n <AntTag\n ref={ref}\n className={clsx(\n cls(\"tag\"),\n cls(`tag-${size}`),\n !children && cls(\"tag-empty\"),\n className\n )}\n color={color}\n style={style}\n closable={closable}\n closeIcon={closable ? closeIcon || renderedCloseIcon : undefined}\n icon={renderedIcon}\n {...(active ? { \"data-active\": \"true\" } : {})}\n {...rest}\n >\n {children}\n </AntTag>\n );\n }\n);\n\nconst CheckableTag = forwardRef<HTMLSpanElement, CheckableTagProps>(\n ({ className, size = \"small\", children, ...rest }, ref) => {\n const cls = useCls();\n return (\n <AntCheckableTag\n ref={ref}\n className={clsx(\n cls(\"tag\"),\n cls(`tag-${size}`),\n !children && cls(\"tag-empty\"),\n className\n )}\n {...rest}\n >\n {children}\n </AntCheckableTag>\n );\n }\n);\n\nexport const Tag = Object.assign(InternalTag, {\n CheckableTag,\n});\n"],"names":["InternalTag","forwardRef","className","active","color","style","closeIcon","closable","size","children","icon","rest","ref","renderedCloseIcon","jsx","XIcon","cls","useCls","renderedIcon","isValidElement","cloneElement","clsx","AntTag","CheckableTag","AntCheckableTag","Tag"],"mappings":";;;;;;;;;AA0CA,MAAMA,IAAcC;AAAA,EAClB,CACE;AAAA,IACE,WAAAC;AAAA,IACA,QAAAC,IAAS;AAAA,IACT,OAAAC;AAAA,IACA,OAAAC;AAAA,IACA,WAAAC;AAAA,IACA,UAAAC;AAAA,IACA,MAAAC,IAAO;AAAA,IACP,UAAAC;AAAA,IACA,MAAAC;AAAA,IACA,GAAGC;AAAA,KAELC,MACG;AACH,UAAMC,IAAoB,gBAAAC,EAACC,GAAM,EAAA,QAAO,OAAO,CAAA,GACzCC,IAAMC,EAAO,GACbC,IAAeC,EAAeT,CAAI,IACpCU,EAAyCV,GAAM;AAAA,MAC7C,WAAWW;AAAA,QACTL,EAAI,UAAU;AAAA,QACdN,EAAK,SACH,OAAOA,EAAK,SAAU,YACtB,eAAeA,EAAK,SACpB,OAAOA,EAAK,MAAM,aAAc,WAC9BA,EAAK,MAAM,YACX;AAAA,MAAA;AAAA,IAEP,CAAA,IACDA;AAEF,WAAA,gBAAAI;AAAA,MAACQ;AAAA,MAAA;AAAA,QACC,KAAAV;AAAA,QACA,WAAWS;AAAA,UACTL,EAAI,KAAK;AAAA,UACTA,EAAI,OAAOR,CAAI,EAAE;AAAA,UACjB,CAACC,KAAYO,EAAI,WAAW;AAAA,UAC5Bd;AAAA,QACF;AAAA,QACA,OAAAE;AAAA,QACA,OAAAC;AAAA,QACA,UAAAE;AAAA,QACA,WAAWA,IAAWD,KAAaO,IAAoB;AAAA,QACvD,MAAMK;AAAA,QACL,GAAIf,IAAS,EAAE,eAAe,WAAW,CAAC;AAAA,QAC1C,GAAGQ;AAAA,QAEH,UAAAF;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,GAEMc,IAAetB;AAAA,EACnB,CAAC,EAAE,WAAAC,GAAW,MAAAM,IAAO,SAAS,UAAAC,GAAU,GAAGE,EAAK,GAAGC,MAAQ;AACzD,UAAMI,IAAMC,EAAO;AAEjB,WAAA,gBAAAH;AAAA,MAACU;AAAA,MAAA;AAAA,QACC,KAAAZ;AAAA,QACA,WAAWS;AAAA,UACTL,EAAI,KAAK;AAAA,UACTA,EAAI,OAAOR,CAAI,EAAE;AAAA,UACjB,CAACC,KAAYO,EAAI,WAAW;AAAA,UAC5Bd;AAAA,QACF;AAAA,QACC,GAAGS;AAAA,QAEH,UAAAF;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,GAEagB,IAAM,OAAO,OAAOzB,GAAa;AAAA,EAC5C,cAAAuB;AACF,CAAC;"}
1
+ {"version":3,"file":"component.js","sources":["../../../src/components/tag/component.tsx"],"sourcesContent":["\"use client\";\nimport {\n forwardRef,\n cloneElement,\n isValidElement,\n HTMLAttributes,\n} from \"react\";\nimport { XIcon } from \"@bioturing/assets\";\nimport AntTag, {\n type TagProps as AntTagProps,\n type TagType,\n} from \"antd/es/tag\";\nimport AntCheckableTag, {\n type CheckableTagProps as AntCheckableTagProps,\n} from \"antd/es/tag/CheckableTag\";\nimport { clsx, useCls } from \"../utils\";\n\n// Import component-specific styles\nimport \"./style.css\";\n\nexport { TagType };\n\n// Define props interface extending Ant Design's TagProps\nexport interface TagProps extends AntTagProps {\n /**\n * Indicates if the tag is active\n * @default false\n */\n active?: boolean;\n /**\n * Size of the tag\n * @default \"small\"\n */\n size?: \"small\" | \"medium\" | \"large\";\n}\n\nexport interface CheckableTagProps extends AntCheckableTagProps {\n size?: \"small\" | \"medium\" | \"large\";\n}\n\n// Create Tag component\nconst InternalTag = forwardRef<HTMLSpanElement, TagProps>(\n (\n {\n className,\n active = false,\n color,\n style,\n closeIcon,\n closable,\n size = \"small\",\n children,\n icon,\n ...rest\n },\n ref,\n ) => {\n const renderedCloseIcon = <XIcon weight=\"bold\" />;\n const cls = useCls();\n const renderedIcon = isValidElement(icon)\n ? cloneElement<HTMLAttributes<SVGElement>>(icon, {\n className: clsx(\n cls(\"tag-icon\"),\n icon.props &&\n typeof icon.props === \"object\" &&\n \"className\" in icon.props &&\n typeof icon.props.className === \"string\"\n ? icon.props.className\n : \"\",\n ),\n })\n : icon;\n return (\n <AntTag\n ref={ref}\n className={clsx(\n cls(\"tag\"),\n cls(`tag-${size}`),\n !children && cls(\"tag-empty\"),\n className,\n )}\n color={color}\n style={style}\n closable={closable}\n closeIcon={closable ? closeIcon || renderedCloseIcon : undefined}\n icon={renderedIcon}\n {...(active ? { \"data-active\": \"true\" } : {})}\n {...rest}\n >\n {children}\n </AntTag>\n );\n },\n);\n\nconst CheckableTag = forwardRef<HTMLSpanElement, CheckableTagProps>(\n ({ className, size = \"small\", children, ...rest }, ref) => {\n const cls = useCls();\n return (\n <AntCheckableTag\n ref={ref}\n className={clsx(\n cls(\"tag\"),\n cls(`tag-${size}`),\n !children && cls(\"tag-empty\"),\n className,\n )}\n {...rest}\n >\n {children}\n </AntCheckableTag>\n );\n },\n);\n\nexport const Tag = Object.assign(InternalTag, {\n CheckableTag,\n});\n"],"names":["InternalTag","forwardRef","className","active","color","style","closeIcon","closable","size","children","icon","rest","ref","renderedCloseIcon","jsx","XIcon","cls","useCls","renderedIcon","isValidElement","cloneElement","clsx","AntTag","CheckableTag","AntCheckableTag","Tag"],"mappings":";;;;;;;;;AAyCA,MAAMA,IAAcC;AAAA,EAClB,CACE;AAAA,IACE,WAAAC;AAAA,IACA,QAAAC,IAAS;AAAA,IACT,OAAAC;AAAA,IACA,OAAAC;AAAA,IACA,WAAAC;AAAA,IACA,UAAAC;AAAA,IACA,MAAAC,IAAO;AAAA,IACP,UAAAC;AAAA,IACA,MAAAC;AAAA,IACA,GAAGC;AAAA,KAELC,MACG;AACH,UAAMC,IAAoB,gBAAAC,EAACC,GAAM,EAAA,QAAO,OAAO,CAAA,GACzCC,IAAMC,EAAO,GACbC,IAAeC,EAAeT,CAAI,IACpCU,EAAyCV,GAAM;AAAA,MAC7C,WAAWW;AAAA,QACTL,EAAI,UAAU;AAAA,QACdN,EAAK,SACH,OAAOA,EAAK,SAAU,YACtB,eAAeA,EAAK,SACpB,OAAOA,EAAK,MAAM,aAAc,WAC9BA,EAAK,MAAM,YACX;AAAA,MAAA;AAAA,IAEP,CAAA,IACDA;AAEF,WAAA,gBAAAI;AAAA,MAACQ;AAAA,MAAA;AAAA,QACC,KAAAV;AAAA,QACA,WAAWS;AAAA,UACTL,EAAI,KAAK;AAAA,UACTA,EAAI,OAAOR,CAAI,EAAE;AAAA,UACjB,CAACC,KAAYO,EAAI,WAAW;AAAA,UAC5Bd;AAAA,QACF;AAAA,QACA,OAAAE;AAAA,QACA,OAAAC;AAAA,QACA,UAAAE;AAAA,QACA,WAAWA,IAAWD,KAAaO,IAAoB;AAAA,QACvD,MAAMK;AAAA,QACL,GAAIf,IAAS,EAAE,eAAe,WAAW,CAAC;AAAA,QAC1C,GAAGQ;AAAA,QAEH,UAAAF;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,GAEMc,IAAetB;AAAA,EACnB,CAAC,EAAE,WAAAC,GAAW,MAAAM,IAAO,SAAS,UAAAC,GAAU,GAAGE,EAAK,GAAGC,MAAQ;AACzD,UAAMI,IAAMC,EAAO;AAEjB,WAAA,gBAAAH;AAAA,MAACU;AAAA,MAAA;AAAA,QACC,KAAAZ;AAAA,QACA,WAAWS;AAAA,UACTL,EAAI,KAAK;AAAA,UACTA,EAAI,OAAOR,CAAI,EAAE;AAAA,UACjB,CAACC,KAAYO,EAAI,WAAW;AAAA,UAC5Bd;AAAA,QACF;AAAA,QACC,GAAGS;AAAA,QAEH,UAAAF;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,GAEagB,IAAM,OAAO,OAAOzB,GAAa;AAAA,EAC5C,cAAAuB;AACF,CAAC;"}
@@ -1 +1 @@
1
- @layer components{.ds-tag{display:inline-flex;align-items:center;font-size:var(--ds-font-size-sm);padding:.0625rem .4375rem}.ds-tag[data-active]{background-color:var(--ds-color-primary-bg);border-color:var(--ds-color-primary);color:var(--ds-color-primary)}.ds-tag .ds-tag-close-icon{color:var(--ds-color-icon)}.ds-tag.ds-tag-medium{padding:.1875rem .4375rem}.ds-tag.ds-tag-large{font-size:var(--ds-font-size);padding:.1875rem .4375rem}.ds-tag:is(.ds-tag-magenta,.ds-tag-red,.ds-tag-volcano,.ds-tag-orange,.ds-tag-gold,.ds-tag-lime,.ds-tag-green,.ds-tag-cyan,.ds-tag-blue,.ds-tag-geekblue,.ds-tag-purple) .ds-tag-close-icon{color:currentColor}.ds-tag.ds-tag-empty .ds-tag-close-icon{margin-left:0}.ds-tag .ds-tag-icon{margin-right:.25rem}}
1
+ @layer components{.ds-tag{display:inline-flex;align-items:center;font-size:var(--ds-font-size-sm);padding:.0625rem .4375rem}.ds-tag[data-active]{background-color:var(--ds-color-primary-bg);border-color:var(--ds-color-primary);color:var(--ds-color-primary)}.ds-tag .ds-tag-close-icon{color:var(--ds-color-icon)}.ds-tag.ds-tag-medium{padding:.1875rem .4375rem}.ds-tag.ds-tag-large{font-size:var(--ds-font-size);padding:.1875rem .4375rem}.ds-tag:is(.ds-tag-magenta,.ds-tag-red,.ds-tag-volcano,.ds-tag-orange,.ds-tag-gold,.ds-tag-lime,.ds-tag-green,.ds-tag-cyan,.ds-tag-blue,.ds-tag-geekblue,.ds-tag-purple) .ds-tag-close-icon{color:currentColor}.ds-tag.ds-tag-empty .ds-tag-close-icon{margin-left:0}.ds-tag .ds-tag-icon{margin-right:.25rem}.ds-tag>span:not(.ds-tag-icon,.ds-tag-close-icon){flex-shrink:1;min-height:0;min-width:0;flex-grow:1;flex-basis:max-content}}
@@ -1,94 +1,109 @@
1
1
  "use client";
2
- import { jsx as W } from "react/jsx-runtime";
3
- import { useRef as N, useState as z, useCallback as P, useLayoutEffect as D } from "react";
4
- import { getMiddleTruncatedString as G } from "./helpers.js";
2
+ import { jsx as y } from "react/jsx-runtime";
3
+ import { useRef as k, useState as M, useCallback as g, useLayoutEffect as z } from "react";
4
+ import { calculateAvailableHeight as A, checkIfTextTruncated as b, getMiddleTruncatedString as F } from "./helpers.js";
5
5
  import './style.css';/* empty css */
6
- import { useCls as J } from "../utils/antdUtils.js";
7
- import { clsx as K } from "../utils/cn.js";
8
- const ee = ({
9
- children: h,
10
- position: a = "end",
6
+ import { useCls as O } from "../utils/antdUtils.js";
7
+ import { clsx as R } from "../utils/cn.js";
8
+ const D = ({
9
+ children: x,
10
+ position: f = "end",
11
11
  lines: r = 1,
12
- className: E,
13
- style: f,
14
- ...O
12
+ className: C,
13
+ style: m,
14
+ container: l,
15
+ ...E
15
16
  }) => {
16
- const l = J(), y = N(null), [R, p] = z(!1), [g, d] = z(h), n = h, k = P(() => r !== "auto" ? {
17
- ...f,
17
+ const d = O(), h = k(null), [S, T] = M(!1), [p, v] = M(x), c = x, H = g(() => r !== "auto" ? {
18
+ ...m,
18
19
  ...r > 1 ? { "--ds-line-clamp": r } : {}
19
20
  } : {
20
- ...f,
21
+ ...m,
21
22
  // Set a temporary high value to ensure it fits the parent
22
23
  "--ds-line-clamp": 999
23
- }, [r, f]);
24
- return D(() => {
25
- var v, T, H, x, C, S;
26
- const e = y.current;
24
+ }, [r, m]), w = g(() => {
25
+ const e = h.current;
27
26
  if (!e) return;
28
- if (r === "auto") {
29
- d(n);
30
- const i = () => {
31
- const t = e.parentElement;
32
- if (!t) return;
33
- const s = window.getComputedStyle(e), q = parseFloat(s.fontSize), B = s.lineHeight === "normal" ? q * 1.2 : parseFloat(s.lineHeight), m = window.getComputedStyle(t), A = parseFloat(m.paddingTop) || 0, L = parseFloat(m.paddingBottom) || 0, M = parseFloat(m.borderTopWidth) || 0, I = parseFloat(m.borderBottomWidth) || 0, F = t.clientHeight - A - L - M - I, $ = Math.max(1, Math.floor(F / B));
34
- e && e.style.setProperty("--ds-line-clamp", String($));
35
- const o = document.createElement("span");
36
- o.style.visibility = "hidden", o.style.position = "absolute", o.style.width = `${e.clientWidth}px`, o.style.fontSize = s.fontSize, o.style.fontFamily = s.fontFamily, o.style.lineHeight = s.lineHeight, o.textContent = n, document.body.appendChild(o);
37
- const j = o.scrollHeight > F;
38
- document.body.removeChild(o), p(j);
39
- };
40
- (T = (v = window.document.fonts) == null ? void 0 : v.ready) == null || T.then(i);
41
- const c = new ResizeObserver(() => {
42
- window.requestAnimationFrame(i);
27
+ const t = window.getComputedStyle(e), u = parseFloat(t.fontSize), n = t.lineHeight === "normal" ? u * 1.2 : parseFloat(t.lineHeight), a = A(e), o = Math.max(1, Math.floor(a / n));
28
+ e.style.setProperty("--ds-line-clamp", String(o));
29
+ const i = b(c, e, a);
30
+ T(i);
31
+ }, [c]), s = g(
32
+ (e, t, u) => {
33
+ var o, i;
34
+ const n = e.filter(Boolean);
35
+ if (n.length === 0) return;
36
+ (i = (o = window.document.fonts) == null ? void 0 : o.ready) == null || i.then(t);
37
+ const a = new ResizeObserver(() => {
38
+ window.requestAnimationFrame(t);
43
39
  });
44
- return c.observe(e), e.parentElement && c.observe(e.parentElement), () => c.disconnect();
45
- }
46
- if (typeof r == "number" && r > 1) {
47
- d(n);
48
- const i = () => {
49
- const t = document.createElement("span");
50
- t.style.visibility = "hidden", t.style.position = "absolute", t.style.width = `${e.clientWidth}px`, t.textContent = n, document.body.appendChild(t);
51
- const s = t.scrollHeight > (e.clientHeight || e.offsetHeight);
52
- document.body.removeChild(t), p(s);
40
+ return n.forEach((L) => a.observe(L)), () => {
41
+ a.disconnect(), u && u();
53
42
  };
54
- (x = (H = window.document.fonts) == null ? void 0 : H.ready) == null || x.then(i);
55
- const c = new ResizeObserver(() => {
56
- window.requestAnimationFrame(i);
43
+ },
44
+ []
45
+ );
46
+ return z(() => {
47
+ const e = h.current;
48
+ let t = l instanceof HTMLElement ? l : l && "current" in l ? l.current : null;
49
+ if (!e || (t || (t = e.closest("[data-truncate-container]")), typeof l < "u" && !t)) return;
50
+ if (v(c), r === "auto")
51
+ return s(
52
+ [e, e.parentElement],
53
+ w
54
+ );
55
+ if (typeof r == "number" && r > 1)
56
+ return s([e], () => {
57
+ const a = b(c, e);
58
+ T(a);
57
59
  });
58
- return c.observe(e), () => c.disconnect();
60
+ if (f == "middle") {
61
+ let n = { cancelled: !1 };
62
+ return s(
63
+ [t || e],
64
+ () => {
65
+ n && (n.cancelled = !0);
66
+ const o = { cancelled: !1 };
67
+ n = o;
68
+ const i = F(c, "…", e);
69
+ o.cancelled || v(i);
70
+ },
71
+ () => {
72
+ n.cancelled = !0;
73
+ }
74
+ );
59
75
  }
60
- let u = { cancelled: !1 };
61
- const b = () => {
62
- u && (u.cancelled = !0);
63
- const i = { cancelled: !1 };
64
- u = i;
65
- const c = G(n, "…", e);
66
- i.cancelled || d(c);
67
- };
68
- a == "middle" && ((S = (C = window.document.fonts) == null ? void 0 : C.ready) == null || S.then(b)), a == "end" && d(n);
69
- const w = new ResizeObserver(() => {
70
- a == "middle" && window.requestAnimationFrame(b), a == "end" && e.scrollWidth > e.clientWidth && p(!0);
71
- });
72
- return w.observe(e), () => {
73
- u.cancelled = !0, w.disconnect();
74
- };
75
- }, [n, a, r]), /* @__PURE__ */ W(
76
+ return s(
77
+ [t || e],
78
+ () => {
79
+ e.scrollWidth > e.clientWidth && T(!0);
80
+ }
81
+ );
82
+ }, [
83
+ c,
84
+ f,
85
+ r,
86
+ l,
87
+ w,
88
+ s
89
+ ]), /* @__PURE__ */ y(
76
90
  "span",
77
91
  {
78
- ref: y,
79
- style: k(),
80
- className: K(
81
- l(r === 1 ? "truncate" : "truncate-multiline"),
82
- l(a === "middle" ? "truncate-middle" : "truncate-end"),
83
- E
92
+ ref: h,
93
+ style: H(),
94
+ className: R(
95
+ d(r === 1 ? "truncate" : "truncate-multiline"),
96
+ d(f === "middle" ? "truncate-middle" : "truncate-end"),
97
+ C
84
98
  ),
85
- title: R ? n : void 0,
86
- ...O,
87
- children: n === g ? n : /* @__PURE__ */ W("span", { children: g })
99
+ title: S ? c : void 0,
100
+ "data-truncate-text-node": !0,
101
+ ...E,
102
+ children: c === p ? c : /* @__PURE__ */ y("span", { children: p })
88
103
  }
89
104
  );
90
105
  };
91
106
  export {
92
- ee as Truncate
107
+ D as Truncate
93
108
  };
94
109
  //# sourceMappingURL=component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","sources":["../../../src/components/truncate/component.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useLayoutEffect, useRef, useState } from \"react\";\nimport { clsx, useCls } from \"../utils\";\nimport { getMiddleTruncatedString } from \"./helpers\";\n\nimport \"./style.css\";\n\nexport type TruncatePosition = \"middle\" | \"end\";\n\nexport type Props = {\n text: string;\n} & React.CSSProperties;\n\nexport interface TruncateProps\n extends Omit<React.ComponentPropsWithoutRef<\"span\">, \"title\"> {\n /** Text content to be truncated */\n children: string;\n /** Where to truncate the text: start, middle, or end */\n position?: TruncatePosition;\n /** Number of lines to display before truncating (default: 1, 'auto' to fit parent height) */\n lines?: number | \"auto\";\n}\n\nexport const Truncate: React.FC<TruncateProps> = ({\n children,\n position = \"end\",\n lines = 1,\n className,\n style,\n ...rest\n}) => {\n const cls = useCls();\n const containerRef = useRef<HTMLElement>(null);\n const [isTruncated, setIsTruncated] = useState(false);\n const [truncatedText, setTruncatedText] = useState(children);\n\n const text = children;\n\n // Get appropriate container styles based on position and lines\n const getContainerStyles = useCallback(() => {\n // For explicit line numbers or single line\n if (lines !== \"auto\") {\n return {\n ...style,\n ...(lines > 1 ? { \"--ds-line-clamp\": lines } : {}),\n } as React.CSSProperties;\n }\n\n // For auto line calculation, we'll let the useLayoutEffect handle it\n return {\n ...style,\n // Set a temporary high value to ensure it fits the parent\n \"--ds-line-clamp\": 999,\n } as React.CSSProperties;\n }, [lines, style]);\n\n useLayoutEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n // Handle auto-height calculation\n if (lines === \"auto\") {\n setTruncatedText(text);\n\n const calculateLines = () => {\n // Get the parent element's height\n const parent = container.parentElement;\n if (!parent) return;\n\n // Calculate the font size and line height\n const styles = window.getComputedStyle(container);\n const fontSize = parseFloat(styles.fontSize);\n const lineHeight =\n styles.lineHeight === \"normal\"\n ? fontSize * 1.2 // Approximate for \"normal\"\n : parseFloat(styles.lineHeight);\n\n // Get parent's padding and border to subtract from available height\n const parentStyles = window.getComputedStyle(parent);\n const paddingTop = parseFloat(parentStyles.paddingTop) || 0;\n const paddingBottom = parseFloat(parentStyles.paddingBottom) || 0;\n const borderTopWidth = parseFloat(parentStyles.borderTopWidth) || 0;\n const borderBottomWidth =\n parseFloat(parentStyles.borderBottomWidth) || 0;\n\n // Calculate how many lines can fit in the available space\n const parentHeight = parent.clientHeight;\n const availableHeight =\n parentHeight -\n paddingTop -\n paddingBottom -\n borderTopWidth -\n borderBottomWidth;\n const maxLines = Math.max(1, Math.floor(availableHeight / lineHeight));\n\n // Apply the calculated line-clamp\n if (container) {\n container.style.setProperty(\"--ds-line-clamp\", String(maxLines));\n }\n\n // Check if the text is truncated\n const clone = document.createElement(\"span\");\n clone.style.visibility = \"hidden\";\n clone.style.position = \"absolute\";\n clone.style.width = `${container.clientWidth}px`;\n clone.style.fontSize = styles.fontSize;\n clone.style.fontFamily = styles.fontFamily;\n clone.style.lineHeight = styles.lineHeight;\n clone.textContent = text;\n document.body.appendChild(clone);\n\n const isTruncated = clone.scrollHeight > availableHeight;\n document.body.removeChild(clone);\n\n setIsTruncated(isTruncated);\n };\n\n window.document.fonts?.ready?.then(calculateLines);\n const observer = new ResizeObserver(() => {\n window.requestAnimationFrame(calculateLines);\n });\n\n observer.observe(container);\n if (container.parentElement) {\n observer.observe(container.parentElement);\n }\n\n return () => observer.disconnect();\n }\n\n // Skip custom truncation logic for multi-line mode\n if (typeof lines === \"number\" && lines > 1) {\n setTruncatedText(text);\n // Check if text is truncated by comparing heights\n const checkIfTruncated = () => {\n const clone = document.createElement(\"span\");\n clone.style.visibility = \"hidden\";\n clone.style.position = \"absolute\";\n clone.style.width = `${container.clientWidth}px`;\n clone.textContent = text;\n document.body.appendChild(clone);\n const isTruncated =\n clone.scrollHeight >\n (container.clientHeight || container.offsetHeight);\n document.body.removeChild(clone);\n setIsTruncated(isTruncated);\n };\n\n window.document.fonts?.ready?.then(checkIfTruncated);\n const observer = new ResizeObserver(() => {\n window.requestAnimationFrame(checkIfTruncated);\n });\n\n observer.observe(container);\n return () => observer.disconnect();\n }\n\n // Original single-line truncation logic\n let cancellationToken = { cancelled: false };\n\n const calculateTruncatedString = () => {\n if (cancellationToken) cancellationToken.cancelled = true;\n\n const requestCancellationToken = { cancelled: false };\n cancellationToken = requestCancellationToken;\n\n const truncated = getMiddleTruncatedString(text, \"\\u2026\", container);\n\n if (requestCancellationToken.cancelled) return;\n\n setTruncatedText(truncated);\n };\n\n if (position == \"middle\") {\n window.document.fonts?.ready?.then(calculateTruncatedString);\n }\n\n if (position == \"end\") {\n setTruncatedText(text);\n }\n\n const observer = new ResizeObserver(() => {\n if (position == \"middle\")\n window.requestAnimationFrame(calculateTruncatedString);\n if (position == \"end\") {\n const isOverflowing = container.scrollWidth > container.clientWidth;\n if (isOverflowing) {\n setIsTruncated(true);\n }\n }\n });\n\n observer.observe(container);\n\n return () => {\n cancellationToken.cancelled = true;\n observer.disconnect();\n };\n }, [text, position, lines]);\n\n return (\n <span\n ref={containerRef}\n style={getContainerStyles()}\n className={clsx(\n lines === 1 ? cls(\"truncate\") : cls(\"truncate-multiline\"),\n position === \"middle\" ? cls(\"truncate-middle\") : cls(\"truncate-end\"),\n className\n )}\n title={isTruncated ? text : undefined}\n {...rest}\n >\n {text === truncatedText ? text : <span>{truncatedText}</span>}\n </span>\n );\n};\n"],"names":["Truncate","children","position","lines","className","style","rest","cls","useCls","containerRef","useRef","isTruncated","setIsTruncated","useState","truncatedText","setTruncatedText","text","getContainerStyles","useCallback","useLayoutEffect","container","calculateLines","parent","styles","fontSize","lineHeight","parentStyles","paddingTop","paddingBottom","borderTopWidth","borderBottomWidth","availableHeight","maxLines","clone","_b","_a","observer","checkIfTruncated","_d","_c","cancellationToken","calculateTruncatedString","requestCancellationToken","truncated","getMiddleTruncatedString","_f","_e","jsx","clsx"],"mappings":";;;;;;;AAuBO,MAAMA,KAAoC,CAAC;AAAA,EAChD,UAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,OAAAC,IAAQ;AAAA,EACR,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,GAAGC;AACL,MAAM;AACJ,QAAMC,IAAMC,EAAO,GACbC,IAAeC,EAAoB,IAAI,GACvC,CAACC,GAAaC,CAAc,IAAIC,EAAS,EAAK,GAC9C,CAACC,GAAeC,CAAgB,IAAIF,EAASZ,CAAQ,GAErDe,IAAOf,GAGPgB,IAAqBC,EAAY,MAEjCf,MAAU,SACL;AAAA,IACL,GAAGE;AAAA,IACH,GAAIF,IAAQ,IAAI,EAAE,mBAAmBA,EAAA,IAAU,CAAA;AAAA,EACjD,IAIK;AAAA,IACL,GAAGE;AAAA;AAAA,IAEH,mBAAmB;AAAA,EACrB,GACC,CAACF,GAAOE,CAAK,CAAC;AAEjB,SAAAc,EAAgB,MAAM;;AACpB,UAAMC,IAAYX,EAAa;AAC/B,QAAI,CAACW,EAAW;AAGhB,QAAIjB,MAAU,QAAQ;AACpB,MAAAY,EAAiBC,CAAI;AAErB,YAAMK,IAAiB,MAAM;AAE3B,cAAMC,IAASF,EAAU;AACzB,YAAI,CAACE,EAAQ;AAGP,cAAAC,IAAS,OAAO,iBAAiBH,CAAS,GAC1CI,IAAW,WAAWD,EAAO,QAAQ,GACrCE,IACJF,EAAO,eAAe,WAClBC,IAAW,MACX,WAAWD,EAAO,UAAU,GAG5BG,IAAe,OAAO,iBAAiBJ,CAAM,GAC7CK,IAAa,WAAWD,EAAa,UAAU,KAAK,GACpDE,IAAgB,WAAWF,EAAa,aAAa,KAAK,GAC1DG,IAAiB,WAAWH,EAAa,cAAc,KAAK,GAC5DI,IACJ,WAAWJ,EAAa,iBAAiB,KAAK,GAI1CK,IADeT,EAAO,eAG1BK,IACAC,IACAC,IACAC,GACIE,IAAW,KAAK,IAAI,GAAG,KAAK,MAAMD,IAAkBN,CAAU,CAAC;AAGrE,QAAIL,KACFA,EAAU,MAAM,YAAY,mBAAmB,OAAOY,CAAQ,CAAC;AAI3D,cAAAC,IAAQ,SAAS,cAAc,MAAM;AAC3C,QAAAA,EAAM,MAAM,aAAa,UACzBA,EAAM,MAAM,WAAW,YACvBA,EAAM,MAAM,QAAQ,GAAGb,EAAU,WAAW,MACtCa,EAAA,MAAM,WAAWV,EAAO,UACxBU,EAAA,MAAM,aAAaV,EAAO,YAC1BU,EAAA,MAAM,aAAaV,EAAO,YAChCU,EAAM,cAAcjB,GACX,SAAA,KAAK,YAAYiB,CAAK;AAEzBtB,cAAAA,IAAcsB,EAAM,eAAeF;AAChC,iBAAA,KAAK,YAAYE,CAAK,GAE/BrB,EAAeD,CAAW;AAAA,MAC5B;AAEA,OAAAuB,KAAAC,IAAA,OAAO,SAAS,UAAhB,gBAAAA,EAAuB,UAAvB,QAAAD,EAA8B,KAAKb;AAC7Be,YAAAA,IAAW,IAAI,eAAe,MAAM;AACxC,eAAO,sBAAsBf,CAAc;AAAA,MAAA,CAC5C;AAEDe,aAAAA,EAAS,QAAQhB,CAAS,GACtBA,EAAU,iBACZgB,EAAS,QAAQhB,EAAU,aAAa,GAGnC,MAAMgB,EAAS,WAAW;AAAA,IAAA;AAInC,QAAI,OAAOjC,KAAU,YAAYA,IAAQ,GAAG;AAC1C,MAAAY,EAAiBC,CAAI;AAErB,YAAMqB,IAAmB,MAAM;AACvB,cAAAJ,IAAQ,SAAS,cAAc,MAAM;AAC3C,QAAAA,EAAM,MAAM,aAAa,UACzBA,EAAM,MAAM,WAAW,YACvBA,EAAM,MAAM,QAAQ,GAAGb,EAAU,WAAW,MAC5Ca,EAAM,cAAcjB,GACX,SAAA,KAAK,YAAYiB,CAAK;AAC/B,cAAMtB,IACJsB,EAAM,gBACLb,EAAU,gBAAgBA,EAAU;AAC9B,iBAAA,KAAK,YAAYa,CAAK,GAC/BrB,EAAeD,CAAW;AAAA,MAC5B;AAEA,OAAA2B,KAAAC,IAAA,OAAO,SAAS,UAAhB,gBAAAA,EAAuB,UAAvB,QAAAD,EAA8B,KAAKD;AAC7BD,YAAAA,IAAW,IAAI,eAAe,MAAM;AACxC,eAAO,sBAAsBC,CAAgB;AAAA,MAAA,CAC9C;AAEDD,aAAAA,EAAS,QAAQhB,CAAS,GACnB,MAAMgB,EAAS,WAAW;AAAA,IAAA;AAI/B,QAAAI,IAAoB,EAAE,WAAW,GAAM;AAE3C,UAAMC,IAA2B,MAAM;AACjC,MAAAD,QAAqC,YAAY;AAE/C,YAAAE,IAA2B,EAAE,WAAW,GAAM;AAChC,MAAAF,IAAAE;AAEpB,YAAMC,IAAYC,EAAyB5B,GAAM,KAAUI,CAAS;AAEpE,MAAIsB,EAAyB,aAE7B3B,EAAiB4B,CAAS;AAAA,IAC5B;AAEA,IAAIzC,KAAY,cACd2C,KAAAC,IAAA,OAAO,SAAS,UAAhB,gBAAAA,EAAuB,UAAvB,QAAAD,EAA8B,KAAKJ,KAGjCvC,KAAY,SACda,EAAiBC,CAAI;AAGjB,UAAAoB,IAAW,IAAI,eAAe,MAAM;AACxC,MAAIlC,KAAY,YACd,OAAO,sBAAsBuC,CAAwB,GACnDvC,KAAY,SACQkB,EAAU,cAAcA,EAAU,eAEtDR,EAAe,EAAI;AAAA,IAEvB,CACD;AAED,WAAAwB,EAAS,QAAQhB,CAAS,GAEnB,MAAM;AACX,MAAAoB,EAAkB,YAAY,IAC9BJ,EAAS,WAAW;AAAA,IACtB;AAAA,EACC,GAAA,CAACpB,GAAMd,GAAUC,CAAK,CAAC,GAGxB,gBAAA4C;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAKtC;AAAA,MACL,OAAOQ,EAAmB;AAAA,MAC1B,WAAW+B;AAAA,QACKzC,EAAdJ,MAAU,IAAQ,aAAkB,oBAAR;AAAA,QACJI,EAAxBL,MAAa,WAAe,oBAAyB,cAAR;AAAA,QAC7CE;AAAA,MACF;AAAA,MACA,OAAOO,IAAcK,IAAO;AAAA,MAC3B,GAAGV;AAAA,MAEH,UAASU,MAAAF,IAAgBE,IAAO,gBAAA+B,EAAC,UAAM,UAAcjC,EAAA,CAAA;AAAA,IAAA;AAAA,EACxD;AAEJ;"}
1
+ {"version":3,"file":"component.js","sources":["../../../src/components/truncate/component.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useLayoutEffect, useRef, useState } from \"react\";\nimport { clsx, useCls } from \"../utils\";\nimport {\n calculateAvailableHeight,\n checkIfTextTruncated,\n getMiddleTruncatedString,\n} from \"./helpers\";\n\nimport \"./style.css\";\n\nexport type TruncatePosition = \"middle\" | \"end\";\n\nexport interface TruncateProps\n extends Omit<React.ComponentPropsWithoutRef<\"span\">, \"title\"> {\n /** Text content to be truncated */\n children: string;\n /** Position where the truncation should occur */\n position?: TruncatePosition;\n /**\n * Number of lines to display before truncating\n * @default 1\n * @remarks Use 'auto' to fit parent height\n */\n lines?: number | \"auto\";\n /*\n * Ref to the container element of the truncated text. It will have resize observer attached to it.\n */\n container?: HTMLElement | React.RefObject<HTMLElement>;\n}\n\nexport const Truncate: React.FC<TruncateProps> = ({\n children,\n position = \"end\",\n lines = 1,\n className,\n style,\n container: containerProp,\n ...rest\n}) => {\n const cls = useCls();\n const containerRef = useRef<HTMLElement>(null);\n const [isTruncated, setIsTruncated] = useState(false);\n const [truncatedText, setTruncatedText] = useState(children);\n\n const text = children;\n\n // Get appropriate container styles based on position and lines\n const getContainerStyles = useCallback(() => {\n // For explicit line numbers or single line\n if (lines !== \"auto\") {\n return {\n ...style,\n ...(lines > 1 ? { \"--ds-line-clamp\": lines } : {}),\n } as React.CSSProperties;\n }\n\n // For auto line calculation, we'll let the useLayoutEffect handle it\n return {\n ...style,\n // Set a temporary high value to ensure it fits the parent\n \"--ds-line-clamp\": 999,\n } as React.CSSProperties;\n }, [lines, style]);\n\n // Handle auto line calculation\n const handleAutoLineCalculation = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const styles = window.getComputedStyle(container);\n const fontSize = parseFloat(styles.fontSize);\n const lineHeight =\n styles.lineHeight === \"normal\"\n ? fontSize * 1.2\n : parseFloat(styles.lineHeight);\n\n const availableHeight = calculateAvailableHeight(container);\n const maxLines = Math.max(1, Math.floor(availableHeight / lineHeight));\n\n container.style.setProperty(\"--ds-line-clamp\", String(maxLines));\n const isTruncated = checkIfTextTruncated(text, container, availableHeight);\n setIsTruncated(isTruncated);\n }, [text]);\n\n const observe = useCallback(\n (\n elements: (HTMLElement | null)[],\n callback: () => void,\n cleanup?: () => void\n ) => {\n const validElements = elements.filter(Boolean) as HTMLElement[];\n if (validElements.length === 0) return;\n\n window.document.fonts?.ready?.then(callback);\n\n const observer = new ResizeObserver(() => {\n window.requestAnimationFrame(callback);\n });\n\n validElements.forEach((el) => observer.observe(el));\n return () => {\n observer.disconnect();\n if (cleanup) cleanup();\n };\n },\n []\n );\n\n useLayoutEffect(() => {\n const container = containerRef.current;\n let flexContainer =\n containerProp instanceof HTMLElement\n ? containerProp\n : containerProp && \"current\" in containerProp\n ? containerProp.current\n : null;\n if (!container) return;\n if (!flexContainer) {\n flexContainer = container.closest(\"[data-truncate-container]\");\n }\n if (typeof containerProp !== \"undefined\" && !flexContainer) return;\n\n // reset\n setTruncatedText(text);\n\n // Handle auto-height calculation\n if (lines === \"auto\") {\n return observe(\n [container, container.parentElement],\n handleAutoLineCalculation\n );\n }\n\n // Skip custom truncation logic for multi-line mode\n if (typeof lines === \"number\" && lines > 1) {\n // Check if text is truncated by comparing heights\n const handleMultilineTruncation = () => {\n const isTruncated = checkIfTextTruncated(text, container);\n setIsTruncated(isTruncated);\n };\n\n return observe([container], handleMultilineTruncation);\n }\n\n if (position == \"middle\") {\n // Middle truncate\n let cancellationToken = { cancelled: false };\n\n const handleMiddleTruncate = () => {\n if (cancellationToken) cancellationToken.cancelled = true;\n\n const requestCancellationToken = { cancelled: false };\n cancellationToken = requestCancellationToken;\n\n const truncated = getMiddleTruncatedString(text, \"\\u2026\", container);\n\n if (requestCancellationToken.cancelled) return;\n\n setTruncatedText(truncated);\n };\n // to do: optimize resize observer here to avoid re-calculate\n return observe(\n [flexContainer ? flexContainer : container],\n handleMiddleTruncate,\n () => {\n cancellationToken.cancelled = true;\n }\n );\n }\n\n // End truncate\n const handleEndTruncate = () => {\n const isOverflowing = container.scrollWidth > container.clientWidth;\n if (isOverflowing) {\n setIsTruncated(true);\n }\n };\n\n return observe(\n [flexContainer ? flexContainer : container],\n handleEndTruncate\n );\n }, [\n text,\n position,\n lines,\n containerProp,\n handleAutoLineCalculation,\n observe,\n ]);\n\n return (\n <span\n ref={containerRef}\n style={getContainerStyles()}\n className={clsx(\n lines === 1 ? cls(\"truncate\") : cls(\"truncate-multiline\"),\n position === \"middle\" ? cls(\"truncate-middle\") : cls(\"truncate-end\"),\n className\n )}\n title={isTruncated ? text : undefined}\n data-truncate-text-node\n {...rest}\n >\n {text === truncatedText ? text : <span>{truncatedText}</span>}\n </span>\n );\n};\n"],"names":["Truncate","children","position","lines","className","style","containerProp","rest","cls","useCls","containerRef","useRef","isTruncated","setIsTruncated","useState","truncatedText","setTruncatedText","text","getContainerStyles","useCallback","handleAutoLineCalculation","container","styles","fontSize","lineHeight","availableHeight","calculateAvailableHeight","maxLines","checkIfTextTruncated","observe","elements","callback","cleanup","validElements","_b","_a","observer","el","useLayoutEffect","flexContainer","cancellationToken","requestCancellationToken","truncated","getMiddleTruncatedString","jsx","clsx"],"mappings":"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"}
@@ -1,36 +1,56 @@
1
- const b = (e) => {
2
- const t = document.createElement("span");
3
- return t.style.opacity = "0", t.style.position = "absolute", t.style.top = "-1000px", t.style.left = "-1000px", t.style.whiteSpace = "nowrap", t.style.pointerEvents = "none", e.appendChild(t), {
4
- measure: (o) => (t.innerText = o, t.clientWidth),
1
+ const g = (n) => {
2
+ var l;
3
+ const e = document.createElement("span");
4
+ e.style.opacity = "0", e.style.position = "absolute", e.style.top = "-1000px", e.style.left = "-1000px", e.style.whiteSpace = "nowrap", e.style.pointerEvents = "none";
5
+ const o = n.getBoundingClientRect(), t = n.cloneNode(!0);
6
+ return t.style.maxWidth = `${o.width}px`, t.style.position = "absolute", t.style.pointerEvents = "none", t.style.top = "-99px", t.style.left = "-99px", t.style.zIndex = "-1", (l = n.parentElement) == null || l.appendChild(t), t.appendChild(e), {
7
+ measure: (i) => (e.innerText = i, e.clientWidth),
5
8
  destroy: () => {
6
- e.removeChild(t);
9
+ var i;
10
+ t.removeChild(e), (i = n.parentElement) == null || i.removeChild(t);
7
11
  }
8
12
  };
9
- }, g = (e, t, o) => {
10
- var h;
11
- if (!e) return e;
12
- const { measure: i, destroy: l } = b(o), d = i(e), s = o.clientWidth, f = Math.floor(s / d * e.length);
13
- if (d <= s)
14
- return l(), e;
15
- let n = f;
16
- const r = {}, p = 20, m = 10;
17
- for (; Object.values(r).length <= p && !r[n]; ) {
18
- if (n <= 1) {
19
- r[0] = [0, t];
13
+ }, C = (n, e, o) => {
14
+ var p;
15
+ if (!n) return n;
16
+ const { measure: t, destroy: l } = g(o), i = t(n), r = o.clientWidth, u = Math.floor(
17
+ r / i * n.length
18
+ );
19
+ if (i <= r)
20
+ return l(), n;
21
+ let s = u;
22
+ const d = {}, m = 20, y = 4;
23
+ for (; Object.values(d).length <= m && !d[s]; ) {
24
+ if (s <= 1) {
25
+ d[0] = [0, e];
20
26
  break;
21
27
  }
22
- const a = e.slice(0, Math.ceil((n - t.length) / 2 - 1)).trimEnd(), y = e.slice(Math.floor((n - t.length) / 2) - n).trimStart(), u = a + t + y, c = i(u);
23
- if (r[n] = [c, u], c >= s)
24
- n = n - 2;
28
+ const c = n.slice(0, Math.ceil((s - e.length) / 2 - 1)).trimEnd(), f = n.slice(Math.floor((s - e.length) / 2) - s).trimStart(), h = c + e + f, a = t(h);
29
+ if (d[s] = [a, h], a >= r)
30
+ s = s - 2;
25
31
  else {
26
- if (s - c < m) break;
27
- n = n + 2;
32
+ if (r - a < y) break;
33
+ s = s + 2;
28
34
  }
29
35
  }
30
- return l(), ((h = Object.values(r).reverse().find(([a]) => a < s)) == null ? void 0 : h[1]) ?? Object.values(r)[0][1];
36
+ return l(), ((p = Object.values(d).reverse().find(([c]) => c < r)) == null ? void 0 : p[1]) ?? Object.values(d)[0][1];
37
+ }, b = (n, e) => {
38
+ const o = window.getComputedStyle(e), t = document.createElement("span");
39
+ return t.style.visibility = "hidden", t.style.position = "absolute", t.style.width = `${e.clientWidth}px`, t.style.fontSize = o.fontSize, t.style.fontFamily = o.fontFamily, t.style.lineHeight = o.lineHeight, t.textContent = n, document.body.appendChild(t), t;
40
+ }, T = (n, e, o) => {
41
+ const t = b(n, e), l = o ? t.scrollHeight > o : t.scrollHeight > (e.clientHeight || e.offsetHeight);
42
+ return document.body.removeChild(t), l;
43
+ }, W = (n) => {
44
+ const e = n.parentElement;
45
+ if (!e) return 0;
46
+ const o = window.getComputedStyle(e), t = parseFloat(o.paddingTop) || 0, l = parseFloat(o.paddingBottom) || 0, i = parseFloat(o.borderTopWidth) || 0, r = parseFloat(o.borderBottomWidth) || 0;
47
+ return e.clientHeight - t - l - i - r;
31
48
  };
32
49
  export {
33
- g as getMiddleTruncatedString,
34
- b as measureText
50
+ W as calculateAvailableHeight,
51
+ T as checkIfTextTruncated,
52
+ b as createMeasurementClone,
53
+ C as getMiddleTruncatedString,
54
+ g as measureText
35
55
  };
36
56
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sources":["../../../src/components/truncate/helpers.ts"],"sourcesContent":["export const measureText = (container: HTMLElement) => {\n const span = document.createElement(\"span\");\n span.style.opacity = \"0\";\n span.style.position = \"absolute\";\n span.style.top = \"-1000px\";\n span.style.left = \"-1000px\";\n span.style.whiteSpace = \"nowrap\";\n span.style.pointerEvents = \"none\";\n container.appendChild(span);\n\n return {\n measure: (text: string) => {\n span.innerText = text;\n return span.clientWidth;\n },\n destroy: () => {\n container.removeChild(span);\n },\n };\n};\n\nexport const getMiddleTruncatedString = (\n text: string,\n ellipsis: string,\n container: HTMLElement\n): string => {\n if (!text) return text;\n\n const { measure: getTextWidth, destroy: destroyMeasure } =\n measureText(container);\n\n const textWidth = getTextWidth(text);\n const containerWidth = container.clientWidth;\n const initialOffset = Math.floor((containerWidth / textWidth) * text.length);\n\n if (textWidth <= containerWidth) {\n destroyMeasure();\n return text;\n }\n\n let offset = initialOffset;\n const attempts: Record<number, [number, string]> = {};\n const maxAttempts = 20;\n const buffer = 10;\n\n while (Object.values(attempts).length <= maxAttempts) {\n // If we have already tried this offset, stop\n if (attempts[offset]) break;\n\n // If we are at the beginning of the string, just return the ellipsis\n if (offset <= 1) {\n attempts[0] = [0, ellipsis];\n break;\n }\n\n const start = text\n .slice(0, Math.ceil((offset - ellipsis.length) / 2 - 1))\n .trimEnd();\n const end = text\n .slice(Math.floor((offset - ellipsis.length) / 2) - offset)\n .trimStart();\n const truncatedStr = start + ellipsis + end;\n const width = getTextWidth(truncatedStr);\n\n attempts[offset] = [width, truncatedStr];\n\n if (width >= containerWidth) {\n offset = offset - 2;\n } else {\n // If we are close to the container width, stop\n if (containerWidth - width < buffer) break;\n offset = offset + 2;\n }\n }\n\n // Remove the span element used for measuring text\n destroyMeasure();\n\n // Find the closest attempt that is smaller than the container width\n return (\n Object.values(attempts)\n .reverse()\n .find(([width]) => width < containerWidth)?.[1] ??\n Object.values(attempts)[0][1]\n );\n};\n"],"names":["measureText","container","span","text","getMiddleTruncatedString","ellipsis","_a","getTextWidth","destroyMeasure","textWidth","containerWidth","initialOffset","offset","attempts","maxAttempts","buffer","start","end","truncatedStr","width"],"mappings":"AAAa,MAAAA,IAAc,CAACC,MAA2B;AAC/C,QAAAC,IAAO,SAAS,cAAc,MAAM;AAC1C,SAAAA,EAAK,MAAM,UAAU,KACrBA,EAAK,MAAM,WAAW,YACtBA,EAAK,MAAM,MAAM,WACjBA,EAAK,MAAM,OAAO,WAClBA,EAAK,MAAM,aAAa,UACxBA,EAAK,MAAM,gBAAgB,QAC3BD,EAAU,YAAYC,CAAI,GAEnB;AAAA,IACL,SAAS,CAACC,OACRD,EAAK,YAAYC,GACVD,EAAK;AAAA,IAEd,SAAS,MAAM;AACb,MAAAD,EAAU,YAAYC,CAAI;AAAA,IAAA;AAAA,EAE9B;AACF,GAEaE,IAA2B,CACtCD,GACAE,GACAJ,MACW;AAzBA,MAAAK;AA0BP,MAAA,CAACH,EAAa,QAAAA;AAElB,QAAM,EAAE,SAASI,GAAc,SAASC,EAAe,IACrDR,EAAYC,CAAS,GAEjBQ,IAAYF,EAAaJ,CAAI,GAC7BO,IAAiBT,EAAU,aAC3BU,IAAgB,KAAK,MAAOD,IAAiBD,IAAaN,EAAK,MAAM;AAE3E,MAAIM,KAAaC;AACA,WAAAF,EAAA,GACRL;AAGT,MAAIS,IAASD;AACb,QAAME,IAA6C,CAAC,GAC9CC,IAAc,IACdC,IAAS;AAEf,SAAO,OAAO,OAAOF,CAAQ,EAAE,UAAUC,KAEnC,CAAAD,EAASD,CAAM,KAFiC;AAKpD,QAAIA,KAAU,GAAG;AACf,MAAAC,EAAS,CAAC,IAAI,CAAC,GAAGR,CAAQ;AAC1B;AAAA,IAAA;AAGF,UAAMW,IAAQb,EACX,MAAM,GAAG,KAAK,MAAMS,IAASP,EAAS,UAAU,IAAI,CAAC,CAAC,EACtD,QAAQ,GACLY,IAAMd,EACT,MAAM,KAAK,OAAOS,IAASP,EAAS,UAAU,CAAC,IAAIO,CAAM,EACzD,UAAU,GACPM,IAAeF,IAAQX,IAAWY,GAClCE,IAAQZ,EAAaW,CAAY;AAIvC,QAFAL,EAASD,CAAM,IAAI,CAACO,GAAOD,CAAY,GAEnCC,KAAST;AACX,MAAAE,IAASA,IAAS;AAAA,SACb;AAED,UAAAF,IAAiBS,IAAQJ,EAAQ;AACrC,MAAAH,IAASA,IAAS;AAAA,IAAA;AAAA,EACpB;AAIa,SAAAJ,EAAA,KAIbF,IAAA,OAAO,OAAOO,CAAQ,EACnB,UACA,KAAK,CAAC,CAACM,CAAK,MAAMA,IAAQT,CAAc,MAF3C,gBAAAJ,EAE+C,OAC/C,OAAO,OAAOO,CAAQ,EAAE,CAAC,EAAE,CAAC;AAEhC;"}
1
+ {"version":3,"file":"helpers.js","sources":["../../../src/components/truncate/helpers.ts"],"sourcesContent":["/**\n * Finds the closest parent element that has inline flex display\n * @param element - The starting HTMLElement\n * @returns The closest inline flex parent or null if not found\n */\nexport function findClosestInlineFlexParent(\n element: HTMLElement\n): HTMLElement | null {\n if (!element) return null;\n\n let currentElement = element.parentElement;\n let attempts = 0;\n const maxAttempts = 10;\n\n while (currentElement && attempts < maxAttempts) {\n const computedStyle = window.getComputedStyle(currentElement);\n\n // Check if display is inline-flex\n if (computedStyle.display === \"inline-flex\") {\n return currentElement;\n }\n\n // Move to the next parent\n currentElement = currentElement.parentElement;\n attempts++;\n }\n\n return null;\n}\n\nexport function getFlexRemainingSpace(\n flexContainer: HTMLElement,\n ignoreElement: HTMLElement | ((e: HTMLElement) => boolean) = null\n) {\n const style = window.getComputedStyle(flexContainer);\n const isRow =\n style.flexDirection === \"row\" || style.flexDirection === \"row-reverse\";\n const containerSize = isRow\n ? flexContainer.offsetWidth\n : flexContainer.offsetHeight;\n const gap = parseFloat(style.gap) || 0;\n const paddingStart =\n parseFloat(isRow ? style.paddingLeft : style.paddingTop) || 0;\n const paddingEnd =\n parseFloat(isRow ? style.paddingRight : style.paddingBottom) || 0;\n\n let totalItemSize = 0;\n const items: HTMLElement[] = (\n [...flexContainer.children] as HTMLElement[]\n ).filter(\n (child) =>\n child instanceof HTMLElement &&\n (typeof ignoreElement === \"function\"\n ? !ignoreElement(child)\n : child !== ignoreElement)\n );\n for (let i = 0; i < items.length; i++) {\n const itemStyle = window.getComputedStyle(items[i]);\n const marginStart =\n parseFloat(isRow ? itemStyle.marginLeft : itemStyle.marginTop) || 0;\n const marginEnd =\n parseFloat(isRow ? itemStyle.marginRight : itemStyle.marginBottom) || 0;\n totalItemSize +=\n (isRow ? items[i].offsetWidth : items[i].offsetHeight) +\n marginStart +\n marginEnd;\n }\n\n // Add gaps (n-1 gaps for n items)\n totalItemSize += gap * (items.length - 1);\n\n // Calculate remaining space\n const remainingSpace =\n containerSize - totalItemSize - paddingStart - paddingEnd;\n return Math.max(0, remainingSpace);\n}\n\nexport const measureText = (container: HTMLElement) => {\n const span = document.createElement(\"span\");\n span.style.opacity = \"0\";\n span.style.position = \"absolute\";\n span.style.top = \"-1000px\";\n span.style.left = \"-1000px\";\n span.style.whiteSpace = \"nowrap\";\n span.style.pointerEvents = \"none\";\n\n const nodeRect = container.getBoundingClientRect();\n\n const containerClone = container.cloneNode(true) as HTMLElement;\n containerClone.style.maxWidth = `${nodeRect.width}px`;\n containerClone.style.position = \"absolute\";\n containerClone.style.pointerEvents = \"none\";\n containerClone.style.top = \"-99px\";\n containerClone.style.left = \"-99px\";\n containerClone.style.zIndex = \"-1\";\n\n container.parentElement?.appendChild(containerClone);\n containerClone.appendChild(span);\n\n return {\n measure: (text: string) => {\n span.innerText = text;\n return span.clientWidth;\n },\n destroy: () => {\n containerClone.removeChild(span);\n container.parentElement?.removeChild(containerClone);\n },\n };\n};\n\nexport const getMiddleTruncatedString = (\n text: string,\n ellipsis: string,\n container: HTMLElement\n): string => {\n if (!text) return text;\n\n const { measure: getTextWidth, destroy: destroyMeasure } =\n measureText(container);\n\n const textWidth = getTextWidth(text);\n\n const containerWidthToCompare = container.clientWidth;\n\n const initialOffset = Math.floor(\n (containerWidthToCompare / textWidth) * text.length\n );\n\n if (textWidth <= containerWidthToCompare) {\n destroyMeasure();\n return text;\n }\n\n let offset = initialOffset;\n const attempts: Record<number, [number, string]> = {};\n const maxAttempts = 20;\n const buffer = 4;\n\n while (Object.values(attempts).length <= maxAttempts) {\n // If we have already tried this offset, stop\n if (attempts[offset]) break;\n\n // If we are at the beginning of the string, just return the ellipsis\n if (offset <= 1) {\n attempts[0] = [0, ellipsis];\n break;\n }\n\n const start = text\n .slice(0, Math.ceil((offset - ellipsis.length) / 2 - 1))\n .trimEnd();\n const end = text\n .slice(Math.floor((offset - ellipsis.length) / 2) - offset)\n .trimStart();\n const truncatedStr = start + ellipsis + end;\n const width = getTextWidth(truncatedStr);\n\n attempts[offset] = [width, truncatedStr];\n\n if (width >= containerWidthToCompare) {\n offset = offset - 2;\n } else {\n // If we are close to the container width, stop\n if (containerWidthToCompare - width < buffer) break;\n offset = offset + 2;\n }\n }\n\n // Remove the span element used for measuring text\n destroyMeasure();\n\n // Find the closest attempt that is smaller than the container width\n return (\n Object.values(attempts)\n .reverse()\n .find(([width]) => width < containerWidthToCompare)?.[1] ??\n Object.values(attempts)[0][1]\n );\n};\n\n// Utility functions for measurements\nexport const createMeasurementClone = (\n text: string,\n container: HTMLElement\n) => {\n const styles = window.getComputedStyle(container);\n const clone = document.createElement(\"span\");\n clone.style.visibility = \"hidden\";\n clone.style.position = \"absolute\";\n clone.style.width = `${container.clientWidth}px`;\n clone.style.fontSize = styles.fontSize;\n clone.style.fontFamily = styles.fontFamily;\n clone.style.lineHeight = styles.lineHeight;\n clone.textContent = text;\n document.body.appendChild(clone);\n return clone;\n};\n\nexport const checkIfTextTruncated = (\n text: string,\n container: HTMLElement,\n availableHeight?: number\n) => {\n const clone = createMeasurementClone(text, container);\n const isTruncated = availableHeight\n ? clone.scrollHeight > availableHeight\n : clone.scrollHeight > (container.clientHeight || container.offsetHeight);\n document.body.removeChild(clone);\n return isTruncated;\n};\n\nexport const calculateAvailableHeight = (container: HTMLElement) => {\n const parent = container.parentElement;\n if (!parent) return 0;\n\n const parentStyles = window.getComputedStyle(parent);\n const paddingTop = parseFloat(parentStyles.paddingTop) || 0;\n const paddingBottom = parseFloat(parentStyles.paddingBottom) || 0;\n const borderTopWidth = parseFloat(parentStyles.borderTopWidth) || 0;\n const borderBottomWidth = parseFloat(parentStyles.borderBottomWidth) || 0;\n\n return (\n parent.clientHeight -\n paddingTop -\n paddingBottom -\n borderTopWidth -\n borderBottomWidth\n );\n};\n\nexport const calculateAvailableWidth = (container: HTMLElement) => {\n const parent = container.parentElement;\n if (!parent) return 0;\n\n const parentStyles = window.getComputedStyle(parent);\n const paddingLeft = parseFloat(parentStyles.paddingLeft) || 0;\n const paddingRight = parseFloat(parentStyles.paddingRight) || 0;\n const borderLeftWidth = parseFloat(parentStyles.borderLeftWidth) || 0;\n const borderRightWidth = parseFloat(parentStyles.borderRightWidth) || 0;\n\n return (\n parent.clientWidth -\n paddingLeft -\n paddingRight -\n borderLeftWidth -\n borderRightWidth\n );\n};\n"],"names":["measureText","container","_a","span","nodeRect","containerClone","text","getMiddleTruncatedString","ellipsis","getTextWidth","destroyMeasure","textWidth","containerWidthToCompare","initialOffset","offset","attempts","maxAttempts","buffer","start","end","truncatedStr","width","createMeasurementClone","styles","clone","checkIfTextTruncated","availableHeight","isTruncated","calculateAvailableHeight","parent","parentStyles","paddingTop","paddingBottom","borderTopWidth","borderBottomWidth"],"mappings":"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"}
@@ -1 +1 @@
1
- @layer components{.ds-truncate{white-space:nowrap;overflow:hidden;flex-grow:1;min-width:0;max-width:100%;display:block}.ds-truncate-end{text-overflow:ellipsis}.ds-truncate-middle{text-overflow:clip}.ds-truncate-multiline{display:-webkit-box;-webkit-line-clamp:var(--ds-line-clamp, 2);line-clamp:var(--ds-line-clamp, 2);-webkit-box-orient:vertical;overflow:hidden;white-space:normal;text-overflow:ellipsis;word-break:break-word}}
1
+ @layer components{.ds-truncate{white-space:nowrap;overflow:hidden;flex-grow:1;min-width:0;max-width:100%;display:block}.ds-truncate-end{text-overflow:ellipsis}.ds-truncate-middle{text-overflow:clip;width:100%;flex-grow:0}.ds-truncate-multiline{display:-webkit-box;-webkit-line-clamp:var(--ds-line-clamp, 2);line-clamp:var(--ds-line-clamp, 2);-webkit-box-orient:vertical;overflow:hidden;white-space:normal;text-overflow:ellipsis;word-break:break-word}}
package/dist/index.d.ts CHANGED
@@ -1382,13 +1382,13 @@ export declare const DragDrop: typeof DragDropRoot & {
1382
1382
  /**
1383
1383
  * Zone where items can be dropped
1384
1384
  */
1385
- Droppable: <T extends DragData = unknown, M extends boolean = false>({ id, children, className, disabled, placeholder, icon, render, onDrop, validate, multiple, value, onChange, renderValueLabel, renderValueIcon, ...rest }: DragDropDroppableProps<T, M>) => null;
1385
+ Droppable: <T extends DragData = unknown, M extends boolean = false>({ id, children, className, disabled, placeholder, icon, render, onDrop, validate, multiple, value, onChange, maxItems, renderValueLabel, renderValueIcon, ...rest }: DragDropDroppableProps<T, M>) => null;
1386
1386
  /**
1387
1387
  * Value component to display the value of the dragged item
1388
1388
  */
1389
1389
  Value: <T extends DragData, M extends boolean>(props: DragDropValueProps_2<T, M> & {
1390
1390
  ref?: ForwardedRef<HTMLDivElement>;
1391
- }) => ReturnType<(<T_1 extends DragData, M_1 extends boolean>({ value, onChange, multiple, renderValueLabel, renderValueIcon, className, style, ...rest }: DragDropValueProps_2<T_1, M_1> & ComponentPropsWithoutRef<"div">, ref: ForwardedRef<HTMLDivElement>) => JSX.Element)>;
1391
+ }) => ReturnType<(<T_1 extends DragData, M_1 extends boolean>({ value, onChange, multiple, renderValueLabel, renderValueIcon, className, style, maxItems, ...rest }: DragDropValueProps_2<T_1, M_1>, ref: ForwardedRef<HTMLDivElement>) => JSX.Element)>;
1392
1392
  };
1393
1393
 
1394
1394
  /**
@@ -1478,10 +1478,6 @@ export declare interface DragDropDroppableProps<T extends DragData = DragData, M
1478
1478
  * Validate if the item can be dropped
1479
1479
  */
1480
1480
  validate?: (item: DraggableInfo<T>) => boolean;
1481
- /**
1482
- * Maximum number of items that can be dropped
1483
- */
1484
- maxItems?: number;
1485
1481
  }
1486
1482
 
1487
1483
  /**
@@ -1521,11 +1517,31 @@ export declare interface DragDropProps<T extends DragData = DragData> {
1521
1517
  export declare function DragDropRoot<T extends DragData = DragData>({ children, onDragEnd: _onDragEnd, onDragStart: _onDragStart, onDrop: _onDrop, className, }: DragDropProps<T>): JSX.Element;
1522
1518
 
1523
1519
  declare interface DragDropValueProps<T extends DragData = DragData, M extends boolean = false> extends Omit<ComponentPropsWithoutRef<"div">, "onDragEnter" | "onDragOver" | "onDragLeave" | "onDrop" | "onChange"> {
1520
+ /**
1521
+ * Whether to allow multiple values
1522
+ * @default false
1523
+ */
1524
1524
  multiple?: M;
1525
+ /**
1526
+ * The current value(s) of the drop zone
1527
+ */
1525
1528
  value?: M extends true ? T[] : T;
1529
+ /**
1530
+ * Callback fired when the value changes
1531
+ */
1526
1532
  onChange?: M extends true ? (value: T[]) => void : (value: T) => void;
1533
+ /**
1534
+ * Function to render the label for a single value
1535
+ */
1527
1536
  renderValueLabel?: (singleValue: T) => ReactElement | string;
1537
+ /**
1538
+ * Function to render the icon for a single value
1539
+ */
1528
1540
  renderValueIcon?: (singleValue: T) => ReactElement;
1541
+ /**
1542
+ * Maximum number of items that can be dropped
1543
+ */
1544
+ maxItems?: number;
1529
1545
  }
1530
1546
 
1531
1547
  export declare interface DragEndEvent<T extends DragData = DragData> {
@@ -2395,10 +2411,6 @@ export { Progress }
2395
2411
 
2396
2412
  export { ProgressProps }
2397
2413
 
2398
- export declare type Props = {
2399
- text: string;
2400
- } & default_2.CSSProperties;
2401
-
2402
2414
  export { QRCode }
2403
2415
 
2404
2416
  export { QRCodeProps }
@@ -3337,10 +3349,15 @@ export declare type TruncatePosition = "middle" | "end";
3337
3349
  export declare interface TruncateProps extends Omit<default_2.ComponentPropsWithoutRef<"span">, "title"> {
3338
3350
  /** Text content to be truncated */
3339
3351
  children: string;
3340
- /** Where to truncate the text: start, middle, or end */
3352
+ /** Position where the truncation should occur */
3341
3353
  position?: TruncatePosition;
3342
- /** Number of lines to display before truncating (default: 1, 'auto' to fit parent height) */
3354
+ /**
3355
+ * Number of lines to display before truncating
3356
+ * @default 1
3357
+ * @remarks Use 'auto' to fit parent height
3358
+ */
3343
3359
  lines?: number | "auto";
3360
+ container?: HTMLElement | default_2.RefObject<HTMLElement>;
3344
3361
  }
3345
3362
 
3346
3363
  export { Typography }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bioturing/components",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "type": "module",
5
5
  "module": "./dist/index.js",
6
6
  "main": "./dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "rc-tree": "^5.13.1",
29
29
  "react-use-resizable": "^0.2.0",
30
30
  "tailwind-merge": "^3.3.1",
31
- "@bioturing/assets": "0.18.0"
31
+ "@bioturing/assets": "0.19.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "antd": "^5.25.0",