trotl-filter 1.0.23 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -7688,8 +7688,16 @@ const DebounceSelect = ({
7688
7688
  }, "No results")));
7689
7689
  };
7690
7690
 
7691
+ /**
7692
+ * SearchInput Component
7693
+ * @param {string} pushUrlParamObj - URL parameter key (if null, URL params are not updated)
7694
+ * @param {string} height - Input height (e.g., "40px", "3rem")
7695
+ * @param {object} style - Custom inline styles
7696
+ */
7691
7697
  function SearchInput({
7692
7698
  pushUrlParamObj = null,
7699
+ height,
7700
+ style = {},
7693
7701
  ...props
7694
7702
  }) {
7695
7703
  const key = pushUrlParamObj || "search";
@@ -7697,6 +7705,7 @@ function SearchInput({
7697
7705
 
7698
7706
  // On mount, read the URL param and set value
7699
7707
  React.useEffect(() => {
7708
+ if (pushUrlParamObj === null) return;
7700
7709
  const params = new URLSearchParams(window.location.search);
7701
7710
  const urlValue = params.get(key) || "";
7702
7711
  setValue(urlValue);
@@ -7708,8 +7717,9 @@ function SearchInput({
7708
7717
  };
7709
7718
  window.addEventListener("popstate", onPopState);
7710
7719
  return () => window.removeEventListener("popstate", onPopState);
7711
- }, [key]);
7720
+ }, [key, pushUrlParamObj]);
7712
7721
  const setUrlParam = val => {
7722
+ if (pushUrlParamObj === null) return;
7713
7723
  const params = new URLSearchParams(window.location.search);
7714
7724
  if (val && val.length > 0) {
7715
7725
  params.set(key, val);
@@ -7728,14 +7738,18 @@ function SearchInput({
7728
7738
  setValue("");
7729
7739
  setUrlParam("");
7730
7740
  };
7741
+
7742
+ // Apply height to container if provided and style doesn't have height defined
7743
+ const containerStyle = {
7744
+ position: "relative",
7745
+ display: "inline-block",
7746
+ height: height || "34px",
7747
+ verticalAlign: "middle",
7748
+ ...style
7749
+ };
7731
7750
  return /*#__PURE__*/React.createElement("div", {
7732
7751
  title: "tooltip",
7733
- style: {
7734
- position: "relative",
7735
- display: "inline-block",
7736
- height: "34px",
7737
- verticalAlign: "middle"
7738
- }
7752
+ style: containerStyle
7739
7753
  }, /*#__PURE__*/React.createElement("input", _extends$1({
7740
7754
  className: "basic-input",
7741
7755
  value: value,
@@ -10616,6 +10630,7 @@ const Button = ({
10616
10630
  style = {},
10617
10631
  className = "",
10618
10632
  height,
10633
+ float = null,
10619
10634
  ...rest
10620
10635
  }) => {
10621
10636
  // Determine button class based on type
@@ -10639,11 +10654,76 @@ const Button = ({
10639
10654
  } : {}),
10640
10655
  ...style
10641
10656
  };
10657
+
10658
+ // If `float` prop is provided, compute fixed positioning styles.
10659
+ let floatClass = "";
10660
+ const floatStyle = {};
10661
+ if (float) {
10662
+ floatClass = "button--float";
10663
+ // Accept boolean true (use bottom-right default) or object
10664
+ const cfg = typeof float === "object" ? float : {
10665
+ position: "bottom-right"
10666
+ };
10667
+
10668
+ // Default spacing if not provided
10669
+ const gap = cfg.gap || cfg.offset || 16;
10670
+
10671
+ // If explicit top/left/right/bottom provided, use them.
10672
+ if (cfg.top !== undefined) floatStyle.top = cfg.top;
10673
+ if (cfg.left !== undefined) floatStyle.left = cfg.left;
10674
+ if (cfg.right !== undefined) floatStyle.right = cfg.right;
10675
+ if (cfg.bottom !== undefined) floatStyle.bottom = cfg.bottom;
10676
+
10677
+ // If position string is provided, map to offsets unless explicit coords set
10678
+ if (cfg.position && !(cfg.top || cfg.left || cfg.right || cfg.bottom)) {
10679
+ const pos = String(cfg.position).toLowerCase();
10680
+ // Accept common typo 'botton' as 'bottom'
10681
+ const posNormalized = pos.replace(/botton/g, 'bottom');
10682
+
10683
+ // If user requested an edge position like 'bottom-left', place at exact edge (0px)
10684
+ if (posNormalized.includes("bottom") && posNormalized.includes("left")) {
10685
+ floatStyle.bottom = '0px';
10686
+ floatStyle.left = '0px';
10687
+ } else if (posNormalized.includes("bottom") && posNormalized.includes("right")) {
10688
+ floatStyle.bottom = '0px';
10689
+ floatStyle.right = '0px';
10690
+ } else if (posNormalized.includes("top") && posNormalized.includes("left")) {
10691
+ floatStyle.top = '0px';
10692
+ floatStyle.left = '0px';
10693
+ } else if (posNormalized.includes("top") && posNormalized.includes("right")) {
10694
+ floatStyle.top = '0px';
10695
+ floatStyle.right = '0px';
10696
+ } else {
10697
+ // fallback: apply gap offsets
10698
+ if (posNormalized.includes("bottom")) floatStyle.bottom = typeof gap === "number" ? `${gap}px` : gap;
10699
+ if (posNormalized.includes("top")) floatStyle.top = typeof gap === "number" ? `${gap}px` : gap;
10700
+ if (posNormalized.includes("left")) floatStyle.left = typeof gap === "number" ? `${gap}px` : gap;
10701
+ if (posNormalized.includes("right")) floatStyle.right = typeof gap === "number" ? `${gap}px` : gap;
10702
+ }
10703
+
10704
+ // center handling (horizontal or vertical centering)
10705
+ if (posNormalized.startsWith("center-")) {
10706
+ const parts = posNormalized.split("-");
10707
+ const dir = parts[1];
10708
+ if (dir === "bottom" || dir === "top") {
10709
+ floatStyle.left = "50%";
10710
+ floatStyle.transform = (floatStyle.transform ? floatStyle.transform + " " : "") + "translateX(-50%)";
10711
+ }
10712
+ if (dir === "left" || dir === "right") {
10713
+ floatStyle.top = "50%";
10714
+ floatStyle.transform = (floatStyle.transform ? floatStyle.transform + " " : "") + "translateY(-50%)";
10715
+ }
10716
+ }
10717
+ }
10718
+ }
10642
10719
  return /*#__PURE__*/React.createElement("button", _extends$1({
10643
- className: `button ${getButtonClass()} ${className}`,
10720
+ className: `button ${getButtonClass()} ${className} ${floatClass}`,
10644
10721
  onClick: onClick,
10645
10722
  disabled: disabled,
10646
- style: buttonStyle
10723
+ style: {
10724
+ ...buttonStyle,
10725
+ ...floatStyle
10726
+ }
10647
10727
  }, rest), children);
10648
10728
  };
10649
10729
 
@@ -10699,6 +10779,137 @@ const Switch = ({
10699
10779
  }, label));
10700
10780
  };
10701
10781
 
10782
+ const formatBytes = bytes => {
10783
+ if (!bytes) return "0 B";
10784
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
10785
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
10786
+ return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`;
10787
+ };
10788
+ function Upload({
10789
+ onChange,
10790
+ multiple = false,
10791
+ accept,
10792
+ acceptFiles,
10793
+ maxFiles = null,
10794
+ maxFileSize = null,
10795
+ customPreview = null,
10796
+ buttonLabel = "Browse...",
10797
+ className = "",
10798
+ style = {},
10799
+ value = undefined
10800
+ }) {
10801
+ const inputRef = React.useRef(null);
10802
+ const [dragOver, setDragOver] = React.useState(false);
10803
+ const [files, setFiles] = React.useState([]);
10804
+ const handleFiles = React.useCallback((fileList, event = null) => {
10805
+ const incoming = Array.from(fileList || []);
10806
+ setFiles(prev => {
10807
+ let arr = multiple ? [...prev, ...incoming] : incoming;
10808
+ if (maxFileSize) {
10809
+ arr = arr.filter(f => f.size <= maxFileSize);
10810
+ }
10811
+ if (maxFiles && Number.isFinite(maxFiles)) {
10812
+ arr = arr.slice(0, maxFiles);
10813
+ }
10814
+ if (onChange) {
10815
+ // Call with (files, event) for consistency; provide single file when not multiple
10816
+ onChange(multiple ? arr : arr[0] || null, event || null);
10817
+ }
10818
+ return arr;
10819
+ });
10820
+ }, [onChange, multiple, maxFileSize, maxFiles]);
10821
+
10822
+ // Determine which files to display: prefer `value` prop when provided (controlled),
10823
+ // otherwise use internal `files` state.
10824
+ const displayFiles = (() => {
10825
+ if (value !== undefined && value !== null) {
10826
+ if (Array.isArray(value)) return value;
10827
+ return [value];
10828
+ }
10829
+ return files;
10830
+ })();
10831
+ const onInputChange = e => {
10832
+ handleFiles(e.target.files, e);
10833
+ };
10834
+ const openFileDialog = () => {
10835
+ if (inputRef.current) inputRef.current.click();
10836
+ };
10837
+ const onDrop = e => {
10838
+ e.preventDefault();
10839
+ setDragOver(false);
10840
+ handleFiles(e.dataTransfer.files, e);
10841
+ };
10842
+ const onDragOver = e => {
10843
+ e.preventDefault();
10844
+ setDragOver(true);
10845
+ };
10846
+ const onDragLeave = () => setDragOver(false);
10847
+ return /*#__PURE__*/React.createElement("div", {
10848
+ className: `trotl-upload ${className}`,
10849
+ style: style
10850
+ }, /*#__PURE__*/React.createElement("input", {
10851
+ ref: inputRef,
10852
+ type: "file",
10853
+ style: {
10854
+ display: "none"
10855
+ },
10856
+ onChange: onInputChange,
10857
+ multiple: multiple,
10858
+ accept: acceptFiles || accept
10859
+ }), /*#__PURE__*/React.createElement("div", {
10860
+ className: `upload-dropzone ${dragOver ? "drag-over" : ""}`,
10861
+ onClick: openFileDialog,
10862
+ onDrop: onDrop,
10863
+ onDragOver: onDragOver,
10864
+ onDragLeave: onDragLeave,
10865
+ role: "button",
10866
+ tabIndex: 0,
10867
+ onKeyDown: e => {
10868
+ if (e.key === "Enter") openFileDialog();
10869
+ }
10870
+ }, /*#__PURE__*/React.createElement("div", {
10871
+ className: "upload-inner"
10872
+ }, /*#__PURE__*/React.createElement("div", {
10873
+ className: "upload-icon"
10874
+ }, "\u2B06\uFE0F"), /*#__PURE__*/React.createElement("div", {
10875
+ className: "upload-text"
10876
+ }, "Drag & drop files here or"), /*#__PURE__*/React.createElement("button", {
10877
+ type: "button",
10878
+ className: "upload-browse",
10879
+ onClick: e => {
10880
+ e.stopPropagation();
10881
+ openFileDialog();
10882
+ }
10883
+ }, buttonLabel))), displayFiles && displayFiles.length > 0 && (customPreview ? (
10884
+ /*#__PURE__*/
10885
+ // If customPreview is a component, render it with `files` prop
10886
+ React.createElement(customPreview, {
10887
+ files: displayFiles
10888
+ })) : /*#__PURE__*/React.createElement("ul", {
10889
+ className: "upload-list"
10890
+ }, displayFiles.map((f, i) => /*#__PURE__*/React.createElement("li", {
10891
+ key: `${f && f.name || f || i}-${i}`,
10892
+ className: "upload-item"
10893
+ }, /*#__PURE__*/React.createElement("span", {
10894
+ className: "upload-name"
10895
+ }, f && f.name || String(f)), /*#__PURE__*/React.createElement("span", {
10896
+ className: "upload-size"
10897
+ }, f && f.size ? formatBytes(f.size) : ""))))));
10898
+ }
10899
+ Upload.propTypes = {
10900
+ onChange: PropTypes.func,
10901
+ multiple: PropTypes.bool,
10902
+ accept: PropTypes.string,
10903
+ acceptFiles: PropTypes.string,
10904
+ maxFiles: PropTypes.number,
10905
+ maxFileSize: PropTypes.number,
10906
+ customPreview: PropTypes.oneOfType([PropTypes.elementType, PropTypes.func, PropTypes.node]),
10907
+ value: PropTypes.oneOfType([PropTypes.array, PropTypes.object, PropTypes.string]),
10908
+ buttonLabel: PropTypes.string,
10909
+ className: PropTypes.string,
10910
+ style: PropTypes.object
10911
+ };
10912
+
10702
10913
  exports.Button = Button;
10703
10914
  exports.CalendarRangePicker = CalendarRangePicker;
10704
10915
  exports.DateTimeInput = DateTimeInput;
@@ -10709,4 +10920,5 @@ exports.MultiSelectDropdown = MultiSelectDropdown;
10709
10920
  exports.RangePicker = RangePicker;
10710
10921
  exports.SearchInput = SearchInput;
10711
10922
  exports.Switch = Switch;
10923
+ exports.Upload = Upload;
10712
10924
  //# sourceMappingURL=index.cjs.js.map