trotl-filter 1.0.42 → 1.0.44

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
@@ -7224,12 +7224,12 @@ const MultiSelectDropdown = ({
7224
7224
  maxHeight: 34,
7225
7225
  minHeight: 34,
7226
7226
  height: "auto",
7227
- backgroundColor: theme === "dark" ? "#1e1e1e" : "#fff",
7227
+ background: disabled ? '#f5f5f5' : theme === "dark" ? "#1e1e1e" : "#fff",
7228
7228
  borderColor: theme === "dark" ? "#555" : "#ccc",
7229
7229
  color: theme === "dark" ? "#eee" : "#333",
7230
7230
  boxShadow: "none",
7231
7231
  borderRadius: 2,
7232
- cursor: "pointer",
7232
+ cursor: disabled ? 'not-allowed' : "pointer",
7233
7233
  paddingRight: showSpinner ? 36 : base.paddingRight
7234
7234
  }),
7235
7235
  menu: base => ({
@@ -7293,7 +7293,8 @@ const MultiSelectDropdown = ({
7293
7293
  position: 'relative',
7294
7294
  ...style,
7295
7295
  ...(disabled ? {
7296
- opacity: 0.6
7296
+ opacity: 0.6,
7297
+ background: '#f5f5f5'
7297
7298
  } : {})
7298
7299
  },
7299
7300
  ref: containerRef,
@@ -8329,6 +8330,327 @@ function requirePropTypes () {
8329
8330
  var propTypesExports = /*@__PURE__*/ requirePropTypes();
8330
8331
  var PropTypes = /*@__PURE__*/getDefaultExportFromCjs(propTypesExports);
8331
8332
 
8333
+ function MultiSelect({
8334
+ isMulti = false,
8335
+ options = [],
8336
+ selected = [],
8337
+ onChange,
8338
+ placeholder = 'Select...',
8339
+ allowClear = false,
8340
+ disabled = false,
8341
+ addItem,
8342
+ pushUrlParamObj = false,
8343
+ loading = false,
8344
+ controlStyle = {},
8345
+ className = ''
8346
+ }) {
8347
+ const containerRef = React.useRef(null);
8348
+ const [open, setOpen] = React.useState(false);
8349
+ const [inputValue, setInputValue] = React.useState('');
8350
+ const [maxVisible, setMaxVisible] = React.useState(3);
8351
+ const [isFocused, setIsFocused] = React.useState(false);
8352
+ React.useEffect(() => {
8353
+ if (!containerRef.current) return;
8354
+ const calc = () => {
8355
+ const w = containerRef.current.offsetWidth || 200;
8356
+ const avg = 90;
8357
+ setMaxVisible(Math.max(1, Math.floor((w - 40) / avg)));
8358
+ };
8359
+ calc();
8360
+ window.addEventListener('resize', calc);
8361
+ return () => window.removeEventListener('resize', calc);
8362
+ }, []);
8363
+
8364
+ // URL sync helpers
8365
+ const setUrlParam = value => {
8366
+ if (!pushUrlParamObj) return;
8367
+ const url = new URL(window.location);
8368
+ if (!value || Array.isArray(value) && value.length === 0) url.searchParams.delete(pushUrlParamObj);else url.searchParams.set(pushUrlParamObj, Array.isArray(value) ? value.join(',') : value);
8369
+ window.history.replaceState({}, '', url);
8370
+ };
8371
+ React.useEffect(() => {
8372
+ if (!pushUrlParamObj || !options || options.length === 0) return;
8373
+ const read = () => {
8374
+ const params = new URLSearchParams(window.location.search);
8375
+ const val = params.get(pushUrlParamObj);
8376
+ if (!val) return;
8377
+ if (isMulti) {
8378
+ const arr = val.split(',').filter(Boolean);
8379
+ if (JSON.stringify(arr) !== JSON.stringify(selected)) onChange?.(arr);
8380
+ } else {
8381
+ if (!selected || selected[0] !== val) onChange?.([val]);
8382
+ }
8383
+ };
8384
+ read();
8385
+ window.addEventListener('popstate', read);
8386
+ return () => window.removeEventListener('popstate', read);
8387
+ }, [pushUrlParamObj, options, isMulti, selected, onChange]);
8388
+ const selectedOptions = React.useMemo(() => {
8389
+ if (isMulti) return options.filter(o => (selected || []).includes(o.value));
8390
+ return options.find(o => o.value === (selected && selected[0])) || null;
8391
+ }, [options, selected, isMulti]);
8392
+ const inputExists = React.useMemo(() => {
8393
+ return options.some(opt => String(opt.label).toLowerCase() === inputValue.trim().toLowerCase());
8394
+ }, [inputValue, options]);
8395
+ const menuOptions = React.useMemo(() => {
8396
+ if (inputValue && !inputExists && typeof addItem === 'function') {
8397
+ return [...options, {
8398
+ label: `+ Add "${inputValue.trim()}"`,
8399
+ value: '__add_new__',
8400
+ __isAddNew: true
8401
+ }];
8402
+ }
8403
+ return options;
8404
+ }, [options, inputValue, inputExists, addItem]);
8405
+
8406
+ // Labels for items hidden behind the "overflow" indicator
8407
+ const hiddenLabels = React.useMemo(() => {
8408
+ if (!isMulti) return "";
8409
+ const arr = (selectedOptions || []).slice(maxVisible).map(s => s.label);
8410
+ return arr.join(', ');
8411
+ }, [isMulti, selectedOptions, maxVisible]);
8412
+ const toggleValue = val => {
8413
+ if (disabled) return;
8414
+ if (isMulti) {
8415
+ const prev = Array.isArray(selected) ? [...selected] : [];
8416
+ const idx = prev.findIndex(x => String(x) === String(val));
8417
+ if (idx === -1) prev.push(val);else prev.splice(idx, 1);
8418
+ onChange?.(prev);
8419
+ if (pushUrlParamObj) setUrlParam(prev);
8420
+ } else {
8421
+ const out = [val];
8422
+ onChange?.(out);
8423
+ if (pushUrlParamObj) setUrlParam(val);
8424
+ setOpen(false);
8425
+ }
8426
+ };
8427
+ const handleAddNew = () => {
8428
+ if (disabled) return;
8429
+ const label = inputValue.trim();
8430
+ if (!label) return;
8431
+ const exists = options.some(opt => String(opt.value).toLowerCase() === label.toLowerCase() || String(opt.label).toLowerCase() === label.toLowerCase());
8432
+ if (exists) {
8433
+ setInputValue('');
8434
+ return;
8435
+ }
8436
+ const newOpt = {
8437
+ label,
8438
+ value: label
8439
+ };
8440
+ if (typeof addItem === 'function') addItem(newOpt);
8441
+ if (isMulti) {
8442
+ const next = [...(selected || []), newOpt.value];
8443
+ onChange?.(next);
8444
+ if (pushUrlParamObj) setUrlParam(next);
8445
+ } else {
8446
+ onChange?.([newOpt.value]);
8447
+ if (pushUrlParamObj) setUrlParam(newOpt.value);
8448
+ setOpen(false);
8449
+ }
8450
+ setInputValue('');
8451
+ };
8452
+ const clearSelection = () => {
8453
+ if (disabled) return;
8454
+ onChange?.([]);
8455
+ if (pushUrlParamObj) setUrlParam([]);
8456
+ };
8457
+ React.useEffect(() => {
8458
+ const onDocClick = e => {
8459
+ if (!containerRef.current) return;
8460
+ if (!containerRef.current.contains(e.target)) setOpen(false);
8461
+ };
8462
+ document.addEventListener('mousedown', onDocClick);
8463
+ return () => document.removeEventListener('mousedown', onDocClick);
8464
+ }, []);
8465
+ return /*#__PURE__*/React.createElement("div", {
8466
+ ref: containerRef,
8467
+ className: className,
8468
+ style: {
8469
+ position: 'relative',
8470
+ width: '100%',
8471
+ ...controlStyle,
8472
+ ...(disabled ? {
8473
+ background: '#f5f5f5',
8474
+ opacity: 0.9
8475
+ } : {})
8476
+ }
8477
+ }, /*#__PURE__*/React.createElement("div", {
8478
+ role: "button",
8479
+ tabIndex: 0,
8480
+ onClick: () => !disabled && setOpen(s => !s),
8481
+ onKeyDown: e => {
8482
+ if (e.key === 'Enter' && !disabled) setOpen(s => !s);
8483
+ },
8484
+ onFocus: () => setIsFocused(true),
8485
+ onBlur: () => setIsFocused(false),
8486
+ style: {
8487
+ minHeight: 36,
8488
+ display: 'flex',
8489
+ alignItems: 'center',
8490
+ gap: 8,
8491
+ padding: '6px 8px',
8492
+ border: disabled ? '1px solid #eee' : isFocused && (isMulti ? (selected || []).length > 0 : selected && selected[0] != null) ? '1px solid transparent' : '1px solid #ccc',
8493
+ borderRadius: 2,
8494
+ cursor: disabled ? 'not-allowed' : 'pointer',
8495
+ background: disabled ? '#f5f5f5' : '#fff',
8496
+ outline: 'none',
8497
+ ...controlStyle
8498
+ }
8499
+ }, /*#__PURE__*/React.createElement("div", {
8500
+ style: {
8501
+ display: 'flex',
8502
+ gap: 6,
8503
+ alignItems: 'center',
8504
+ flexWrap: 'nowrap',
8505
+ flex: 1,
8506
+ overflow: 'hidden',
8507
+ minWidth: 0
8508
+ }
8509
+ }, isMulti ? (selectedOptions || []).slice(0, maxVisible).map(s => /*#__PURE__*/React.createElement("div", {
8510
+ key: s.value,
8511
+ onClick: e => e.stopPropagation(),
8512
+ style: {
8513
+ display: 'inline-flex',
8514
+ alignItems: 'center',
8515
+ gap: 6,
8516
+ padding: '4px 8px',
8517
+ background: '#e6f4ff',
8518
+ borderRadius: 2,
8519
+ fontSize: 13,
8520
+ whiteSpace: 'nowrap',
8521
+ overflow: 'hidden',
8522
+ textOverflow: 'ellipsis',
8523
+ maxWidth: 200,
8524
+ minWidth: 0
8525
+ }
8526
+ }, /*#__PURE__*/React.createElement("span", {
8527
+ style: {
8528
+ overflow: 'hidden',
8529
+ textOverflow: 'ellipsis',
8530
+ whiteSpace: 'nowrap',
8531
+ display: 'inline-block'
8532
+ }
8533
+ }, s.label), /*#__PURE__*/React.createElement("button", {
8534
+ "aria-label": `Remove ${s.label}`,
8535
+ onClick: e => {
8536
+ e.stopPropagation();
8537
+ if (!disabled) toggleValue(s.value);
8538
+ },
8539
+ style: {
8540
+ border: 'none',
8541
+ background: 'transparent',
8542
+ cursor: disabled ? 'not-allowed' : 'pointer',
8543
+ padding: 0,
8544
+ margin: 0,
8545
+ fontSize: 12
8546
+ }
8547
+ }, "\u2716"))) : /*#__PURE__*/React.createElement("div", {
8548
+ style: {
8549
+ fontSize: 14,
8550
+ color: selectedOptions ? '#000' : '#666',
8551
+ overflow: 'hidden',
8552
+ textOverflow: 'ellipsis',
8553
+ whiteSpace: 'nowrap'
8554
+ }
8555
+ }, selectedOptions ? selectedOptions.label : placeholder), isMulti && (selectedOptions || []).length > maxVisible && /*#__PURE__*/React.createElement("div", {
8556
+ title: hiddenLabels,
8557
+ style: {
8558
+ fontSize: 13,
8559
+ color: '#666',
8560
+ cursor: 'default',
8561
+ flex: '0 0 auto',
8562
+ paddingLeft: 4
8563
+ }
8564
+ }, "\u2026")), /*#__PURE__*/React.createElement("div", {
8565
+ style: {
8566
+ display: 'flex',
8567
+ alignItems: 'center',
8568
+ gap: 8
8569
+ }
8570
+ }, allowClear && selected && selected.length > 0 && /*#__PURE__*/React.createElement("span", {
8571
+ onClick: e => {
8572
+ e.stopPropagation();
8573
+ clearSelection();
8574
+ },
8575
+ disabled: disabled,
8576
+ style: {
8577
+ cursor: disabled ? 'not-allowed' : 'pointer'
8578
+ }
8579
+ }, "\u2716"), loading && !disabled && /*#__PURE__*/React.createElement("div", {
8580
+ style: {
8581
+ width: 16,
8582
+ height: 16,
8583
+ borderRadius: 2,
8584
+ border: '2px solid #ccc',
8585
+ borderTopColor: '#444',
8586
+ animation: 'spin 0.9s linear infinite'
8587
+ }
8588
+ }))), open && /*#__PURE__*/React.createElement("div", {
8589
+ style: {
8590
+ position: 'absolute',
8591
+ zIndex: 50,
8592
+ marginTop: 6,
8593
+ width: '100%',
8594
+ background: '#fff',
8595
+ border: '1px solid #ddd',
8596
+ borderRadius: 2,
8597
+ boxShadow: '0 4px 12px rgba(0,0,0,0.08)',
8598
+ maxHeight: 260,
8599
+ overflow: 'auto'
8600
+ }
8601
+ }, /*#__PURE__*/React.createElement("div", {
8602
+ style: {
8603
+ padding: 8,
8604
+ borderBottom: '1px solid #f1f1f1'
8605
+ }
8606
+ }, /*#__PURE__*/React.createElement("input", {
8607
+ autoFocus: true,
8608
+ value: inputValue,
8609
+ onChange: e => setInputValue(e.target.value),
8610
+ onKeyDown: e => {
8611
+ if (e.key === 'Enter' && inputValue && typeof addItem === 'function') {
8612
+ handleAddNew();
8613
+ }
8614
+ },
8615
+ placeholder: "Type to search...",
8616
+ disabled: disabled,
8617
+ style: {
8618
+ padding: '6px 8px',
8619
+ borderRadius: 2,
8620
+ border: '1px solid #ffffff'
8621
+ }
8622
+ })), /*#__PURE__*/React.createElement("div", null, menuOptions.filter(opt => String(opt.label).toLowerCase().includes(inputValue.trim().toLowerCase())).map(opt => /*#__PURE__*/React.createElement("div", {
8623
+ key: opt.value,
8624
+ onClick: e => {
8625
+ e.stopPropagation();
8626
+ if (opt.__isAddNew) {
8627
+ handleAddNew();
8628
+ } else {
8629
+ toggleValue(opt.value);
8630
+ }
8631
+ },
8632
+ style: {
8633
+ padding: '8px 12px',
8634
+ cursor: disabled ? 'not-allowed' : 'pointer',
8635
+ background: (isMulti ? (selected || []).includes(opt.value) : selected && selected[0] === opt.value) ? '#eef6ff' : '#fff'
8636
+ }
8637
+ }, opt.label)))), /*#__PURE__*/React.createElement("style", null, `@keyframes spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }`));
8638
+ }
8639
+ MultiSelect.propTypes = {
8640
+ isMulti: PropTypes.bool,
8641
+ options: PropTypes.array,
8642
+ selected: PropTypes.array,
8643
+ onChange: PropTypes.func,
8644
+ placeholder: PropTypes.string,
8645
+ allowClear: PropTypes.bool,
8646
+ disabled: PropTypes.bool,
8647
+ addItem: PropTypes.func,
8648
+ pushUrlParamObj: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
8649
+ loading: PropTypes.bool,
8650
+ controlStyle: PropTypes.object,
8651
+ className: PropTypes.string
8652
+ };
8653
+
8332
8654
  // import "src/style/DebounceSelect.css";
8333
8655
 
8334
8656
  const DebounceSelect = ({
@@ -9242,23 +9564,32 @@ function DateTimeInput({
9242
9564
  return new Date(d.getFullYear(), d.getMonth(), 1, 0, 0, 0, 0);
9243
9565
  });
9244
9566
  // Keep monthCursor in sync when selected date changes externally
9567
+ // Guard updates so changing references (e.g. PREDEFINED array identity) doesn't force state
9245
9568
  React.useEffect(() => {
9246
9569
  if (selectedDate) {
9247
- setMonthCursor(new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1));
9248
- }
9249
- // Match predefined
9250
- if (selectedDate) {
9570
+ const newMonth = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1);
9571
+ // only update monthCursor when month/year actually differ
9572
+ setMonthCursor(prev => {
9573
+ if (!prev || prev.getFullYear() !== newMonth.getFullYear() || prev.getMonth() !== newMonth.getMonth()) {
9574
+ return newMonth;
9575
+ }
9576
+ return prev;
9577
+ });
9578
+
9579
+ // Match predefined: set only when value actually changes
9251
9580
  const ts = selectedDate.getTime();
9581
+ let matched = false;
9252
9582
  for (let i = 0; i < PREDEFINED.length; i++) {
9253
9583
  const d = PREDEFINED[i].getDate();
9254
9584
  if (d.getTime() === ts) {
9255
- setSelectedPredefined(String(i));
9256
- return;
9585
+ setSelectedPredefined(prev => prev !== String(i) ? String(i) : prev);
9586
+ matched = true;
9587
+ break;
9257
9588
  }
9258
9589
  }
9259
- setSelectedPredefined("");
9590
+ if (!matched) setSelectedPredefined(prev => prev !== "" ? "" : prev);
9260
9591
  } else {
9261
- setSelectedPredefined("");
9592
+ setSelectedPredefined(prev => prev !== "" ? "" : prev);
9262
9593
  }
9263
9594
  }, [value, selectedDate, PREDEFINED]);
9264
9595
 
@@ -11117,6 +11448,7 @@ exports.DateTimeInput = DateTimeInput;
11117
11448
  exports.DebounceSelect = DebounceSelect;
11118
11449
  exports.IconInput = IconInput;
11119
11450
  exports.LineDivider = LineDivider;
11451
+ exports.MultiSelect = MultiSelect;
11120
11452
  exports.MultiSelectDropdown = MultiSelectDropdown;
11121
11453
  exports.RangePicker = RangePicker;
11122
11454
  exports.SearchInput = SearchInput;