@papernote/ui 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2546,9 +2546,9 @@ TimezoneSelector.displayName = 'TimezoneSelector';
2546
2546
  * />
2547
2547
  * ```
2548
2548
  */
2549
- const Combobox = React.forwardRef(({ value = '', onChange, options, onSearch, onCreateOption, label, placeholder = 'Search or select...', allowCustomValue = false, loading = false, validationState, validationMessage, helperText, required = false, disabled = false, className = '', size = 'md', }, ref) => {
2549
+ const Combobox = React.forwardRef(({ value = "", onChange, options, onSearch, onCreateOption, label, placeholder = "Search or select...", allowCustomValue = false, loading = false, validationState, validationMessage, helperText, required = false, disabled = false, className = "", size = "md", }, ref) => {
2550
2550
  const [isOpen, setIsOpen] = React.useState(false);
2551
- const [searchQuery, setSearchQuery] = React.useState('');
2551
+ const [searchQuery, setSearchQuery] = React.useState("");
2552
2552
  const [highlightedIndex, setHighlightedIndex] = React.useState(0);
2553
2553
  const containerRef = React.useRef(null);
2554
2554
  const inputRef = React.useRef(null);
@@ -2565,23 +2565,26 @@ const Combobox = React.forwardRef(({ value = '', onChange, options, onSearch, on
2565
2565
  close: () => setIsOpen(false),
2566
2566
  }));
2567
2567
  // Filter options based on search query
2568
- const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchQuery.toLowerCase()));
2568
+ const filteredOptions = options.filter((option) => option.label.toLowerCase().includes(searchQuery.toLowerCase()));
2569
2569
  // Get display value
2570
- const selectedOption = options.find(opt => opt.value === value);
2571
- const displayValue = isOpen ? searchQuery : (selectedOption?.label || value || '');
2570
+ const selectedOption = options.find((opt) => opt.value === value);
2571
+ const displayValue = isOpen
2572
+ ? searchQuery
2573
+ : selectedOption?.label || value || "";
2572
2574
  // Close on click outside
2573
2575
  React.useEffect(() => {
2574
2576
  const handleClickOutside = (event) => {
2575
- if (containerRef.current && !containerRef.current.contains(event.target)) {
2577
+ if (containerRef.current &&
2578
+ !containerRef.current.contains(event.target)) {
2576
2579
  setIsOpen(false);
2577
- setSearchQuery('');
2580
+ setSearchQuery("");
2578
2581
  }
2579
2582
  };
2580
2583
  if (isOpen) {
2581
- document.addEventListener('mousedown', handleClickOutside);
2584
+ document.addEventListener("mousedown", handleClickOutside);
2582
2585
  }
2583
2586
  return () => {
2584
- document.removeEventListener('mousedown', handleClickOutside);
2587
+ document.removeEventListener("mousedown", handleClickOutside);
2585
2588
  };
2586
2589
  }, [isOpen]);
2587
2590
  // Scroll highlighted option into view
@@ -2589,7 +2592,10 @@ const Combobox = React.forwardRef(({ value = '', onChange, options, onSearch, on
2589
2592
  if (isOpen && listRef.current) {
2590
2593
  const highlightedElement = listRef.current.children[highlightedIndex];
2591
2594
  if (highlightedElement) {
2592
- highlightedElement.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
2595
+ highlightedElement.scrollIntoView({
2596
+ block: "nearest",
2597
+ behavior: "smooth",
2598
+ });
2593
2599
  }
2594
2600
  }
2595
2601
  }, [highlightedIndex, isOpen]);
@@ -2613,7 +2619,7 @@ const Combobox = React.forwardRef(({ value = '', onChange, options, onSearch, on
2613
2619
  if (option.disabled)
2614
2620
  return;
2615
2621
  onChange?.(option.value);
2616
- setSearchQuery('');
2622
+ setSearchQuery("");
2617
2623
  setIsOpen(false);
2618
2624
  inputRef.current?.blur();
2619
2625
  };
@@ -2621,14 +2627,14 @@ const Combobox = React.forwardRef(({ value = '', onChange, options, onSearch, on
2621
2627
  const handleCreateOption = () => {
2622
2628
  if (searchQuery.trim() && onCreateOption) {
2623
2629
  onCreateOption(searchQuery.trim());
2624
- setSearchQuery('');
2630
+ setSearchQuery("");
2625
2631
  setIsOpen(false);
2626
2632
  }
2627
2633
  };
2628
2634
  // Handle clear
2629
2635
  const handleClear = () => {
2630
- onChange?.('');
2631
- setSearchQuery('');
2636
+ onChange?.("");
2637
+ setSearchQuery("");
2632
2638
  inputRef.current?.focus();
2633
2639
  };
2634
2640
  // Keyboard navigation
@@ -2636,22 +2642,22 @@ const Combobox = React.forwardRef(({ value = '', onChange, options, onSearch, on
2636
2642
  if (disabled)
2637
2643
  return;
2638
2644
  switch (e.key) {
2639
- case 'ArrowDown':
2645
+ case "ArrowDown":
2640
2646
  e.preventDefault();
2641
2647
  if (!isOpen) {
2642
2648
  setIsOpen(true);
2643
2649
  }
2644
2650
  else {
2645
- setHighlightedIndex(prev => prev < filteredOptions.length - 1 ? prev + 1 : prev);
2651
+ setHighlightedIndex((prev) => prev < filteredOptions.length - 1 ? prev + 1 : prev);
2646
2652
  }
2647
2653
  break;
2648
- case 'ArrowUp':
2654
+ case "ArrowUp":
2649
2655
  e.preventDefault();
2650
2656
  if (isOpen) {
2651
- setHighlightedIndex(prev => (prev > 0 ? prev - 1 : 0));
2657
+ setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : 0));
2652
2658
  }
2653
2659
  break;
2654
- case 'Enter':
2660
+ case "Enter":
2655
2661
  e.preventDefault();
2656
2662
  if (isOpen && filteredOptions.length > 0) {
2657
2663
  handleSelectOption(filteredOptions[highlightedIndex]);
@@ -2661,64 +2667,83 @@ const Combobox = React.forwardRef(({ value = '', onChange, options, onSearch, on
2661
2667
  setIsOpen(false);
2662
2668
  }
2663
2669
  break;
2664
- case 'Escape':
2670
+ case "Escape":
2665
2671
  e.preventDefault();
2666
2672
  setIsOpen(false);
2667
- setSearchQuery('');
2673
+ setSearchQuery("");
2668
2674
  break;
2669
- case 'Tab':
2675
+ case "Tab":
2670
2676
  setIsOpen(false);
2671
- setSearchQuery('');
2677
+ setSearchQuery("");
2672
2678
  break;
2673
2679
  }
2674
2680
  };
2675
2681
  // Size classes
2676
2682
  const sizeClasses = {
2677
- sm: 'text-sm py-1.5 px-3',
2678
- md: 'text-sm py-2 px-3',
2679
- lg: 'text-base py-2.5 px-4',
2683
+ sm: "text-sm py-1.5 px-3",
2684
+ md: "text-sm py-2 px-3",
2685
+ lg: "text-base py-2.5 px-4",
2680
2686
  };
2681
2687
  const iconSizeClasses = {
2682
- sm: 'h-4 w-4',
2683
- md: 'h-4 w-4',
2684
- lg: 'h-5 w-5',
2688
+ sm: "h-4 w-4",
2689
+ md: "h-4 w-4",
2690
+ lg: "h-5 w-5",
2685
2691
  };
2686
2692
  // Validation classes
2687
2693
  const validationClasses = {
2688
- error: 'border-error-500 focus:ring-error-500 focus:border-error-500',
2689
- success: 'border-success-500 focus:ring-success-500 focus:border-success-500',
2690
- warning: 'border-warning-500 focus:ring-warning-500 focus:border-warning-500',
2694
+ error: "border-error-500 focus:ring-error-500 focus:border-error-500",
2695
+ success: "border-success-500 focus:ring-success-500 focus:border-success-500",
2696
+ warning: "border-warning-500 focus:ring-warning-500 focus:border-warning-500",
2691
2697
  };
2692
2698
  const validationMessageColors = {
2693
- error: 'text-error-600',
2694
- success: 'text-success-600',
2695
- warning: 'text-warning-600',
2699
+ error: "text-error-600",
2700
+ success: "text-success-600",
2701
+ warning: "text-warning-600",
2696
2702
  };
2697
2703
  // Check if can create custom option
2698
2704
  const canCreateOption = onCreateOption &&
2699
2705
  searchQuery.trim() &&
2700
- !filteredOptions.some(opt => opt.label.toLowerCase() === searchQuery.toLowerCase());
2701
- return (jsxRuntime.jsxs("div", { className: `relative ${className}`, ref: containerRef, children: [label && (jsxRuntime.jsxs("label", { id: labelId, className: "block text-sm font-medium text-ink-700 mb-1", children: [label, required && jsxRuntime.jsx("span", { className: "text-error-500 ml-1", children: "*" })] })), jsxRuntime.jsx("div", { className: "relative", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: displayValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: () => setIsOpen(true), placeholder: placeholder, disabled: disabled, className: `
2706
+ !filteredOptions.some((opt) => opt.label.toLowerCase() === searchQuery.toLowerCase());
2707
+ return (jsxRuntime.jsxs("div", { className: `relative ${className}`, ref: containerRef, children: [label && (jsxRuntime.jsxs("label", { id: labelId, className: "block text-sm font-medium text-ink-700 mb-1", children: [label, required && jsxRuntime.jsx("span", { className: "text-error-500 ml-1", children: "*" })] })), jsxRuntime.jsx("div", { className: "relative", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: displayValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: () => setIsOpen(true), onMouseDown: (e) => {
2708
+ // Toggle close on click when the input is already focused + open.
2709
+ // Without this, the second click is a no-op because onFocus only
2710
+ // fires on focus transition.
2711
+ if (isOpen && document.activeElement === inputRef.current) {
2712
+ e.preventDefault();
2713
+ setIsOpen(false);
2714
+ }
2715
+ }, placeholder: placeholder, disabled: disabled, className: `
2702
2716
  w-full rounded-md border bg-white
2703
2717
  ${sizeClasses[size]}
2704
- ${validationState ? validationClasses[validationState] : 'border-paper-300 focus:ring-primary-500 focus:border-primary-500'}
2705
- ${disabled ? 'bg-paper-100 text-ink-400 cursor-not-allowed' : ''}
2718
+ ${validationState ? validationClasses[validationState] : "border-paper-300 focus:ring-primary-500 focus:border-primary-500"}
2719
+ ${disabled ? "bg-paper-100 text-ink-400 cursor-not-allowed" : ""}
2706
2720
  focus:outline-none focus:ring-2
2707
2721
  pr-20
2708
- `, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Combobox' : undefined, "aria-expanded": isOpen, "aria-autocomplete": "list", "aria-controls": listboxId, "aria-activedescendant": isOpen && filteredOptions.length > 0 ? `option-${highlightedIndex}` : undefined, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : undefined, "aria-required": required, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-2 gap-1", children: [loading && (jsxRuntime.jsx("div", { className: "animate-spin", children: jsxRuntime.jsx(lucideReact.Search, { className: `${iconSizeClasses[size]} text-ink-400` }) })), !loading && value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-0.5 text-ink-400 hover:text-ink-600 focus:outline-none", "aria-label": "Clear", tabIndex: -1, children: jsxRuntime.jsx(lucideReact.X, { className: iconSizeClasses[size] }) })), !loading && (jsxRuntime.jsx(lucideReact.ChevronDown, { className: `${iconSizeClasses[size]} text-ink-400 transition-transform ${isOpen ? 'rotate-180' : ''}` }))] })] }) }), validationMessage && (jsxRuntime.jsx("p", { id: descriptionId, className: `mt-1 text-xs ${validationState ? validationMessageColors[validationState] : 'text-ink-500'}`, role: "alert", "aria-live": "polite", children: validationMessage })), helperText && !validationMessage && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-ink-500", children: helperText })), isOpen && (jsxRuntime.jsx("div", { className: "absolute z-50 mt-1 w-full bg-white rounded-md shadow-lg border border-paper-200 max-h-60 overflow-auto", role: "listbox", id: listboxId, "aria-label": "Available options", children: loading ? (jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-ink-500 text-sm", role: "status", "aria-live": "polite", children: "Loading..." })) : filteredOptions.length === 0 && !canCreateOption ? (jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-ink-500 text-sm", role: "status", "aria-live": "polite", children: "No options found" })) : (jsxRuntime.jsxs("ul", { ref: listRef, children: [filteredOptions.map((option, index) => {
2722
+ `, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? "Combobox" : undefined, "aria-expanded": isOpen, "aria-autocomplete": "list", "aria-controls": listboxId, "aria-activedescendant": isOpen && filteredOptions.length > 0
2723
+ ? `option-${highlightedIndex}`
2724
+ : undefined, "aria-invalid": validationState === "error" ? "true" : undefined, "aria-describedby": validationMessage ? descriptionId : undefined, "aria-required": required, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-2 gap-1", children: [loading && (jsxRuntime.jsx("div", { className: "animate-spin", children: jsxRuntime.jsx(lucideReact.Search, { className: `${iconSizeClasses[size]} text-ink-400` }) })), !loading && value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-0.5 text-ink-400 hover:text-ink-600 focus:outline-none", "aria-label": "Clear", tabIndex: -1, children: jsxRuntime.jsx(lucideReact.X, { className: iconSizeClasses[size] }) })), !loading && !disabled && (jsxRuntime.jsx("button", { type: "button", onMouseDown: (e) => {
2725
+ // preventDefault keeps the input from losing focus on
2726
+ // mousedown so the toggle stays smooth. Manually re-focus
2727
+ // when opening so keyboard nav works immediately.
2728
+ e.preventDefault();
2729
+ setIsOpen((o) => !o);
2730
+ if (!isOpen) {
2731
+ inputRef.current?.focus();
2732
+ }
2733
+ }, className: "p-0.5 text-ink-400 hover:text-ink-600 focus:outline-none", "aria-label": isOpen ? "Close options" : "Open options", tabIndex: -1, children: jsxRuntime.jsx(lucideReact.ChevronDown, { className: `${iconSizeClasses[size]} transition-transform ${isOpen ? "rotate-180" : ""}` }) })), !loading && disabled && (jsxRuntime.jsx(lucideReact.ChevronDown, { className: `${iconSizeClasses[size]} text-ink-400 transition-transform` }))] })] }) }), validationMessage && (jsxRuntime.jsx("p", { id: descriptionId, className: `mt-1 text-xs ${validationState ? validationMessageColors[validationState] : "text-ink-500"}`, role: "alert", "aria-live": "polite", children: validationMessage })), helperText && !validationMessage && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-ink-500", children: helperText })), isOpen && (jsxRuntime.jsx("div", { className: "absolute z-50 mt-1 w-full bg-white rounded-md shadow-lg border border-paper-200 max-h-60 overflow-auto", role: "listbox", id: listboxId, "aria-label": "Available options", children: loading ? (jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-ink-500 text-sm", role: "status", "aria-live": "polite", children: "Loading..." })) : filteredOptions.length === 0 && !canCreateOption ? (jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-ink-500 text-sm", role: "status", "aria-live": "polite", children: "No options found" })) : (jsxRuntime.jsxs("ul", { ref: listRef, children: [filteredOptions.map((option, index) => {
2709
2734
  const Icon = option.icon;
2710
2735
  const isSelected = option.value === value;
2711
2736
  const isHighlighted = index === highlightedIndex;
2712
2737
  return (jsxRuntime.jsxs("li", { id: `option-${index}`, role: "option", "aria-selected": isSelected, "aria-disabled": option.disabled, onClick: () => handleSelectOption(option), onMouseEnter: () => setHighlightedIndex(index), className: `
2713
2738
  px-3 py-2 cursor-pointer flex items-center justify-between gap-2
2714
- ${option.disabled ? 'opacity-50 cursor-not-allowed' : ''}
2715
- ${isHighlighted ? 'bg-primary-50' : ''}
2716
- ${isSelected ? 'bg-primary-100 font-medium' : ''}
2739
+ ${option.disabled ? "opacity-50 cursor-not-allowed" : ""}
2740
+ ${isHighlighted ? "bg-primary-50" : ""}
2741
+ ${isSelected ? "bg-primary-100 font-medium" : ""}
2717
2742
  hover:bg-primary-50
2718
- `, children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [Icon && jsxRuntime.jsx(Icon, { className: `${iconSizeClasses[size]} flex-shrink-0 text-ink-600` }), jsxRuntime.jsx("span", { className: "truncate text-sm text-ink-900", children: option.label })] }), isSelected && (jsxRuntime.jsx(lucideReact.Check, { className: `${iconSizeClasses[size]} flex-shrink-0 text-primary-600` }))] }, option.value));
2743
+ `, children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [Icon && (jsxRuntime.jsx(Icon, { className: `${iconSizeClasses[size]} flex-shrink-0 text-ink-600` })), jsxRuntime.jsx("span", { className: "truncate text-sm text-ink-900", children: option.label })] }), isSelected && (jsxRuntime.jsx(lucideReact.Check, { className: `${iconSizeClasses[size]} flex-shrink-0 text-primary-600` }))] }, option.value));
2719
2744
  }), canCreateOption && (jsxRuntime.jsxs("li", { role: "option", onClick: handleCreateOption, className: "px-3 py-2 cursor-pointer flex items-center gap-2 border-t border-paper-200 hover:bg-primary-50 bg-success-50", children: [jsxRuntime.jsx(lucideReact.Plus, { className: `${iconSizeClasses[size]} text-success-600` }), jsxRuntime.jsxs("span", { className: "text-sm text-success-700 font-medium", children: ["Create \"", searchQuery, "\""] })] }))] })) }))] }));
2720
2745
  });
2721
- Combobox.displayName = 'Combobox';
2746
+ Combobox.displayName = "Combobox";
2722
2747
 
2723
2748
  /**
2724
2749
  * FormControl wrapper component for consistent form field layout.
@@ -15816,52 +15841,44 @@ function getAugmentedNamespace(n) {
15816
15841
  * (A1, A1:C5, ...)
15817
15842
  */
15818
15843
 
15819
- var collection;
15820
- var hasRequiredCollection;
15821
-
15822
- function requireCollection () {
15823
- if (hasRequiredCollection) return collection;
15824
- hasRequiredCollection = 1;
15825
- class Collection {
15844
+ let Collection$3 = class Collection {
15826
15845
 
15827
- constructor(data, refs) {
15828
- if (data == null && refs == null) {
15829
- this._data = [];
15830
- this._refs = [];
15831
- } else {
15832
- if (data.length !== refs.length)
15833
- throw Error('Collection: data length should match references length.');
15834
- this._data = data;
15835
- this._refs = refs;
15836
- }
15837
- }
15846
+ constructor(data, refs) {
15847
+ if (data == null && refs == null) {
15848
+ this._data = [];
15849
+ this._refs = [];
15850
+ } else {
15851
+ if (data.length !== refs.length)
15852
+ throw Error('Collection: data length should match references length.');
15853
+ this._data = data;
15854
+ this._refs = refs;
15855
+ }
15856
+ }
15838
15857
 
15839
- get data() {
15840
- return this._data;
15841
- }
15858
+ get data() {
15859
+ return this._data;
15860
+ }
15842
15861
 
15843
- get refs() {
15844
- return this._refs;
15845
- }
15862
+ get refs() {
15863
+ return this._refs;
15864
+ }
15846
15865
 
15847
- get length() {
15848
- return this._data.length;
15849
- }
15866
+ get length() {
15867
+ return this._data.length;
15868
+ }
15850
15869
 
15851
- /**
15852
- * Add data and references to this collection.
15853
- * @param {{}} obj - data
15854
- * @param {{}} ref - reference
15855
- */
15856
- add(obj, ref) {
15857
- this._data.push(obj);
15858
- this._refs.push(ref);
15859
- }
15860
- }
15870
+ /**
15871
+ * Add data and references to this collection.
15872
+ * @param {{}} obj - data
15873
+ * @param {{}} ref - reference
15874
+ */
15875
+ add(obj, ref) {
15876
+ this._data.push(obj);
15877
+ this._refs.push(ref);
15878
+ }
15879
+ };
15861
15880
 
15862
- collection = Collection;
15863
- return collection;
15864
- }
15881
+ var collection = Collection$3;
15865
15882
 
15866
15883
  var helpers;
15867
15884
  var hasRequiredHelpers;
@@ -15870,7 +15887,7 @@ function requireHelpers () {
15870
15887
  if (hasRequiredHelpers) return helpers;
15871
15888
  hasRequiredHelpers = 1;
15872
15889
  const FormulaError = requireError();
15873
- const Collection = requireCollection();
15890
+ const Collection = collection;
15874
15891
 
15875
15892
  const Types = {
15876
15893
  NUMBER: 0,
@@ -25524,7 +25541,7 @@ var engineering = EngineeringFunctions;
25524
25541
 
25525
25542
  const FormulaError$b = requireError();
25526
25543
  const {FormulaHelpers: FormulaHelpers$8, Types: Types$6, WildCard, Address: Address$3} = requireHelpers();
25527
- const Collection$2 = requireCollection();
25544
+ const Collection$2 = collection;
25528
25545
  const H$5 = FormulaHelpers$8;
25529
25546
 
25530
25547
  const ReferenceFunctions$1 = {
@@ -37152,7 +37169,7 @@ var parsing = {
37152
37169
  const FormulaError$4 = requireError();
37153
37170
  const {Address: Address$1} = requireHelpers();
37154
37171
  const {Prefix: Prefix$1, Postfix: Postfix$1, Infix: Infix$1, Operators: Operators$1} = operators;
37155
- const Collection$1 = requireCollection();
37172
+ const Collection$1 = collection;
37156
37173
  const MAX_ROW$1 = 1048576, MAX_COLUMN$1 = 16384;
37157
37174
  const {NotAllInputParsedException} = require$$4;
37158
37175
 
@@ -37914,7 +37931,7 @@ var hooks$1 = {
37914
37931
  const FormulaError$2 = requireError();
37915
37932
  const {FormulaHelpers: FormulaHelpers$1, Types, Address} = requireHelpers();
37916
37933
  const {Prefix, Postfix, Infix, Operators} = operators;
37917
- const Collection = requireCollection();
37934
+ const Collection = collection;
37918
37935
  const MAX_ROW = 1048576, MAX_COLUMN = 16384;
37919
37936
 
37920
37937
  let Utils$1 = class Utils {