@sustaina/shared-ui 1.51.1 → 1.52.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.
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ import { twMerge } from 'tailwind-merge';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
9
9
  import useEmblaCarousel from 'embla-carousel-react';
10
- import { ChevronLeft, ChevronRight, CircleX, CircleHelp, Clock, ChevronDown, X, Undo, Redo, Check, Pencil, BaselineIcon, Bold, Italic, Underline, Strikethrough, Code, Pilcrow, Heading1, Heading2, Heading3, List, ListOrdered, QuoteIcon, CodeSquare, LinkIcon, Link2Off, ImageIcon, AlignLeft, AlignCenter, AlignRight, SearchIcon as SearchIcon$1, ExternalLink, XIcon, CheckIcon, Triangle, CalendarIcon, Search, ChevronUp, PanelLeftIcon, Minimize2, Maximize2, Plus, MoreVertical, Bug, GripVertical, Info, CircleMinus, Minus } from 'lucide-react';
10
+ import { ChevronLeft, ChevronRight, CircleX, CircleHelp, Clock, X, ChevronDown, Undo, Redo, Check, Pencil, BaselineIcon, Bold, Italic, Underline, Strikethrough, Code, Pilcrow, Heading1, Heading2, Heading3, List, ListOrdered, QuoteIcon, CodeSquare, LinkIcon, Link2Off, ImageIcon, AlignLeft, AlignCenter, AlignRight, SearchIcon as SearchIcon$1, ExternalLink, XIcon, CheckIcon, Triangle, CalendarIcon, Search, ChevronUp, PanelLeftIcon, Minimize2, Maximize2, Plus, MoreVertical, Bug, GripVertical, Info, CircleMinus, Minus } from 'lucide-react';
11
11
  import Autoplay from 'embla-carousel-autoplay';
12
12
  import { Slot } from '@radix-ui/react-slot';
13
13
  import { cva } from 'class-variance-authority';
@@ -15,7 +15,7 @@ import { createPortal } from 'react-dom';
15
15
  import * as SelectPrimitive from '@radix-ui/react-select';
16
16
  import { useForm, FormProvider, Controller, useFormContext, useFormState, useFieldArray, useWatch } from 'react-hook-form';
17
17
  import * as LabelPrimitive from '@radix-ui/react-label';
18
- import { format, isValid, parseISO, isAfter, compareAsc, parse } from 'date-fns';
18
+ import { format, isValid, parse, parseISO, isAfter, compareAsc } from 'date-fns';
19
19
  import * as PopoverPrimitive from '@radix-ui/react-popover';
20
20
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
21
21
  import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
@@ -40,7 +40,7 @@ import { CSS } from '@dnd-kit/utilities';
40
40
  import { useSensors, useSensor, PointerSensor, DndContext, closestCenter } from '@dnd-kit/core';
41
41
  import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers';
42
42
  import { z } from 'zod';
43
- import { createCommand, TextNode, $applyNodeReplacement, $getRoot, DecoratorNode, $getSelection, $isRangeSelection, CAN_UNDO_COMMAND, COMMAND_PRIORITY_NORMAL, CAN_REDO_COMMAND, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_CRITICAL, UNDO_COMMAND, REDO_COMMAND, $insertNodes, COMMAND_PRIORITY_EDITOR, $isTextNode, CLICK_COMMAND, COMMAND_PRIORITY_LOW, FORMAT_TEXT_COMMAND, $createParagraphNode, $getNodeByKey, $createTextNode, FORMAT_ELEMENT_COMMAND } from 'lexical';
43
+ import { createCommand, TextNode, $applyNodeReplacement, $getRoot, DecoratorNode, $getSelection, $isRangeSelection, CAN_UNDO_COMMAND, COMMAND_PRIORITY_NORMAL, CAN_REDO_COMMAND, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_CRITICAL, UNDO_COMMAND, REDO_COMMAND, $insertNodes, COMMAND_PRIORITY_EDITOR, $isTextNode, PASTE_COMMAND, $createTextNode, COMMAND_PRIORITY_LOW, RootNode, CLICK_COMMAND, FORMAT_TEXT_COMMAND, $createParagraphNode, $getNodeByKey, FORMAT_ELEMENT_COMMAND } from 'lexical';
44
44
  import { HeadingNode, QuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode } from '@lexical/rich-text';
45
45
  import { ListNode, ListItemNode, $isListNode, REMOVE_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list';
46
46
  import { LinkNode, $isLinkNode, $createLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
@@ -59,7 +59,7 @@ import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
59
59
  import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
60
60
  import { ContentEditable } from '@lexical/react/LexicalContentEditable';
61
61
  import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
62
- import { $getSelectionStyleValueForProperty, $patchStyleText, $setBlocksType } from '@lexical/selection';
62
+ import { $getSelectionStyleValueForProperty, trimTextContentFromAnchor, $patchStyleText, $setBlocksType } from '@lexical/selection';
63
63
  import { LexicalTypeaheadMenuPlugin, MenuOption } from '@lexical/react/LexicalTypeaheadMenuPlugin';
64
64
  import Cropper from 'react-easy-crop';
65
65
  import { NumericFormat } from 'react-number-format';
@@ -194,7 +194,7 @@ function stripNullishObject(value) {
194
194
 
195
195
  // src/utils/date.ts
196
196
  var pad = (num) => String(num).padStart(2, "0");
197
- function formatISODate(isoDate, format5 = "d/m/Y H:i") {
197
+ function formatISODate(isoDate, format6 = "d/m/Y H:i") {
198
198
  const date = new Date(isoDate);
199
199
  if (isNaN(date.getTime())) {
200
200
  return "Invalid Date";
@@ -225,7 +225,7 @@ function formatISODate(isoDate, format5 = "d/m/Y H:i") {
225
225
  // Second
226
226
  s: "s"
227
227
  };
228
- return format5.replace(/YYYY|Y|MM|m|DD|d|H|i|s/g, (match) => {
228
+ return format6.replace(/YYYY|Y|MM|m|DD|d|H|i|s/g, (match) => {
229
229
  const partKey = tokenMap[match];
230
230
  return partKey ? parts[partKey] : match;
231
231
  });
@@ -11625,8 +11625,8 @@ var GridSettingsModal = ({
11625
11625
  }
11626
11626
  }, [isOpen, currentColumns, form]);
11627
11627
  const addColumn = async () => {
11628
- const isValid6 = await trigger("columns");
11629
- if (isValid6) {
11628
+ const isValid7 = await trigger("columns");
11629
+ if (isValid7) {
11630
11630
  append({ id: "" });
11631
11631
  requestAnimationFrame(() => {
11632
11632
  const container = scrollRef.current;
@@ -13505,12 +13505,12 @@ function LinkPreviewPopover({
13505
13505
  function FormatPlugin({ disabled, state }) {
13506
13506
  const [editor] = useLexicalComposerContext();
13507
13507
  const toggleFormat = useCallback(
13508
- (format5) => {
13508
+ (format6) => {
13509
13509
  if (disabled) {
13510
13510
  return;
13511
13511
  }
13512
13512
  editor.focus();
13513
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, format5);
13513
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, format6);
13514
13514
  },
13515
13515
  [disabled, editor]
13516
13516
  );
@@ -13855,12 +13855,12 @@ function BlockPlugin({ disabled, state }) {
13855
13855
  function AlignPlugin({ disabled, state }) {
13856
13856
  const [editor] = useLexicalComposerContext();
13857
13857
  const applyElementFormat = useCallback(
13858
- (format5) => {
13858
+ (format6) => {
13859
13859
  if (disabled) {
13860
13860
  return;
13861
13861
  }
13862
13862
  editor.focus();
13863
- editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, format5);
13863
+ editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, format6);
13864
13864
  },
13865
13865
  [disabled, editor]
13866
13866
  );
@@ -14240,6 +14240,65 @@ function MentionsPlugin({
14240
14240
  });
14241
14241
  });
14242
14242
  }, [editor, mentions, mentionKeyMap, mentionConfig]);
14243
+ useEffect(() => {
14244
+ if (mentions.length === 0) return;
14245
+ return editor.registerCommand(
14246
+ PASTE_COMMAND,
14247
+ (event) => {
14248
+ const clipboardData = event.clipboardData;
14249
+ if (!clipboardData) return false;
14250
+ const pastedText = clipboardData.getData("text/plain");
14251
+ if (!pastedText) return false;
14252
+ const variableMatches = [...pastedText.matchAll(COMPLETE_VARIABLE_REGEX)];
14253
+ const hasKnownVariable = variableMatches.some((match) => {
14254
+ const variableName = match[1];
14255
+ const fullMatch = match[0];
14256
+ return mentionKeyMap.has(variableName.toUpperCase()) || mentionKeyMap.has(fullMatch.toUpperCase());
14257
+ });
14258
+ if (!hasKnownVariable) return false;
14259
+ event.preventDefault();
14260
+ editor.update(
14261
+ () => {
14262
+ const selection = $getSelection();
14263
+ if (!$isRangeSelection(selection)) return;
14264
+ let lastIndex = 0;
14265
+ const nodesToInsert = [];
14266
+ for (const match of variableMatches) {
14267
+ const fullMatch = match[0];
14268
+ const variableName = match[1];
14269
+ const matchIndex = match.index;
14270
+ if (matchIndex > lastIndex) {
14271
+ const textBefore = pastedText.slice(lastIndex, matchIndex);
14272
+ nodesToInsert.push($createTextNode(textBefore));
14273
+ }
14274
+ const mentionItem = mentionKeyMap.get(variableName.toUpperCase()) || mentionKeyMap.get(fullMatch.toUpperCase());
14275
+ if (mentionItem) {
14276
+ nodesToInsert.push(
14277
+ $createMentionNode(
14278
+ mentionConfig.getName(mentionItem),
14279
+ mentionConfig.getLabel(mentionItem)
14280
+ )
14281
+ );
14282
+ nodesToInsert.push($createTextNode(" "));
14283
+ } else {
14284
+ nodesToInsert.push($createTextNode(fullMatch));
14285
+ }
14286
+ lastIndex = matchIndex + fullMatch.length;
14287
+ }
14288
+ if (lastIndex < pastedText.length) {
14289
+ nodesToInsert.push($createTextNode(pastedText.slice(lastIndex)));
14290
+ }
14291
+ for (const node of nodesToInsert) {
14292
+ selection.insertNodes([node]);
14293
+ }
14294
+ },
14295
+ { tag: "paste-mention-conversion" }
14296
+ );
14297
+ return true;
14298
+ },
14299
+ COMMAND_PRIORITY_LOW
14300
+ );
14301
+ }, [editor, mentions, mentionKeyMap, mentionConfig]);
14243
14302
  const options = useMemo(() => {
14244
14303
  if (!queryString) {
14245
14304
  return mentions.map(
@@ -14380,6 +14439,41 @@ function OnChangePluginFiltered({
14380
14439
  }, [editor, onChange, ignoreSelectionChange]);
14381
14440
  return null;
14382
14441
  }
14442
+ function MaxLengthPlugin({ maxLength }) {
14443
+ const [editor] = useLexicalComposerContext();
14444
+ useEffect(() => {
14445
+ if (!maxLength) return;
14446
+ let lastRestoredTextLength = 0;
14447
+ return editor.registerNodeTransform(RootNode, (rootNode) => {
14448
+ const selection = $getSelection();
14449
+ if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
14450
+ return;
14451
+ }
14452
+ const prevTextLength = lastRestoredTextLength;
14453
+ const textLength = rootNode.getTextContentSize();
14454
+ if (prevTextLength !== textLength) {
14455
+ const delCount = textLength - maxLength;
14456
+ const anchor = selection.anchor;
14457
+ if (delCount > 0) {
14458
+ if (prevTextLength === maxLength && lastRestoredTextLength !== maxLength) {
14459
+ lastRestoredTextLength = maxLength;
14460
+ $restoreTextContent(anchor);
14461
+ } else {
14462
+ trimTextContentFromAnchor(editor, anchor, delCount);
14463
+ }
14464
+ }
14465
+ }
14466
+ });
14467
+ }, [editor, maxLength]);
14468
+ return null;
14469
+ }
14470
+ function $restoreTextContent(anchor) {
14471
+ const selection = $getSelection();
14472
+ if ($isRangeSelection(selection)) {
14473
+ selection.anchor.set(anchor.key, anchor.offset, "text");
14474
+ selection.focus.set(anchor.key, anchor.offset, "text");
14475
+ }
14476
+ }
14383
14477
  var theme = {
14384
14478
  paragraph: "mb-2 leading-normal text-sm text-foreground",
14385
14479
  placeholder: "pointer-events-none absolute left-0 top-0 px-3 py-2 text-sm text-muted-foreground select-none",
@@ -14435,6 +14529,7 @@ function RichTextInner({
14435
14529
  onImageDialogUploadError,
14436
14530
  acceptImageMimeTypes = "image/*",
14437
14531
  allowImageUrlInsert = true,
14532
+ maxLength,
14438
14533
  mentions,
14439
14534
  mentionConfig,
14440
14535
  mentionTrigger,
@@ -14554,7 +14649,8 @@ function RichTextInner({
14554
14649
  autoFocus ? /* @__PURE__ */ jsx(AutoFocusPlugin, {}) : null,
14555
14650
  /* @__PURE__ */ jsx(EditableStatePlugin, { editable }),
14556
14651
  /* @__PURE__ */ jsx(ControlledValuePlugin, { value, trackAppliedValue: appliedValueRef }),
14557
- /* @__PURE__ */ jsx(OnChangePluginFiltered, { onChange: handleChange, ignoreSelectionChange: true })
14652
+ /* @__PURE__ */ jsx(OnChangePluginFiltered, { onChange: handleChange, ignoreSelectionChange: true }),
14653
+ maxLength && /* @__PURE__ */ jsx(MaxLengthPlugin, { maxLength })
14558
14654
  ] })
14559
14655
  ]
14560
14656
  }
@@ -14650,58 +14746,81 @@ var RightPanelContainer = ({
14650
14746
  );
14651
14747
  };
14652
14748
  var RightPanelContainer_default = RightPanelContainer;
14749
+ var defaultDisplayFormatter3 = (date, use24Hour) => use24Hour ? format(date, "HH:mm") : format(date, "hh:mm a");
14750
+ var defaultValueFormatter3 = (date) => format(date, "HH:mm");
14751
+ var defaultValueParser3 = (value) => {
14752
+ let parsed = parse(value, "HH:mm", /* @__PURE__ */ new Date());
14753
+ if (isValid(parsed)) return parsed;
14754
+ parsed = parse(value, "HH:mm:ss", /* @__PURE__ */ new Date());
14755
+ if (isValid(parsed)) return parsed;
14756
+ parsed = parse(value, "hh:mm a", /* @__PURE__ */ new Date());
14757
+ if (isValid(parsed)) return parsed;
14758
+ return void 0;
14759
+ };
14653
14760
  var TimePicker = React.forwardRef(
14654
14761
  ({
14655
- value = "",
14762
+ value,
14656
14763
  onChange,
14764
+ onValueChange,
14657
14765
  disabled = false,
14658
14766
  className,
14767
+ buttonClassName,
14768
+ wrapperClassName,
14659
14769
  placeholder: placeholder2,
14660
14770
  use24Hour = false,
14661
14771
  iconPosition = "start",
14662
- icon
14772
+ icon,
14773
+ allowClear = true,
14774
+ clearAriaLabel = "Clear time",
14775
+ displayFormatter,
14776
+ valueFormatter,
14777
+ valueParser,
14778
+ invalid = false
14663
14779
  }, ref) => {
14664
14780
  const [open, setOpen] = React.useState(false);
14665
14781
  const [hours, setHours] = React.useState("12");
14666
14782
  const [minutes, setMinutes] = React.useState("00");
14667
14783
  const [period, setPeriod] = React.useState("AM");
14784
+ const parser = React.useMemo(() => valueParser ?? defaultValueParser3, [valueParser]);
14785
+ const outputFormatter = React.useMemo(
14786
+ () => valueFormatter ?? defaultValueFormatter3,
14787
+ [valueFormatter]
14788
+ );
14789
+ const labelFormatter = React.useMemo(
14790
+ () => displayFormatter ?? defaultDisplayFormatter3,
14791
+ [displayFormatter]
14792
+ );
14793
+ const parseInput = React.useCallback(
14794
+ (input) => {
14795
+ if (input === null || input === void 0) return void 0;
14796
+ if (input instanceof Date) return isValid(input) ? input : void 0;
14797
+ const parsed = parser(input);
14798
+ return parsed && isValid(parsed) ? parsed : void 0;
14799
+ },
14800
+ [parser]
14801
+ );
14802
+ const parsedValue = React.useMemo(() => parseInput(value), [parseInput, value]);
14803
+ const hasValue = parsedValue !== void 0;
14668
14804
  React.useEffect(() => {
14669
- if (value) {
14670
- let h, m;
14671
- if (value.includes("Z") || value.match(/^\d{2}:\d{2}:\d{2}/)) {
14672
- const [hour, min] = value.split(":");
14673
- const hour24 = parseInt(hour);
14674
- const minute = parseInt(min);
14675
- if (use24Hour) {
14676
- h = String(hour24);
14677
- m = String(minute);
14678
- } else {
14679
- const newPeriod = hour24 >= 12 ? "PM" : "AM";
14680
- const hour12 = hour24 === 0 ? 12 : hour24 > 12 ? hour24 - 12 : hour24;
14681
- h = String(hour12);
14682
- m = String(minute);
14683
- setPeriod(newPeriod);
14684
- }
14805
+ if (parsedValue) {
14806
+ const hour24 = parsedValue.getHours();
14807
+ const minute = parsedValue.getMinutes();
14808
+ if (use24Hour) {
14809
+ setHours(String(hour24).padStart(2, "0"));
14685
14810
  } else {
14686
- const [time, meridiem] = value.split(" ");
14687
- const [hour, min] = time.split(":");
14688
- h = hour;
14689
- m = min;
14690
- if (meridiem) {
14691
- setPeriod(meridiem);
14692
- }
14811
+ const newPeriod = hour24 >= 12 ? "PM" : "AM";
14812
+ const hour12 = hour24 === 0 ? 12 : hour24 > 12 ? hour24 - 12 : hour24;
14813
+ setHours(String(hour12).padStart(2, "0"));
14814
+ setPeriod(newPeriod);
14693
14815
  }
14694
- setHours(h.padStart(2, "0"));
14695
- setMinutes(m.padStart(2, "0"));
14696
- }
14697
- }, [value, use24Hour]);
14698
- const formatTime = (h, m, p) => {
14699
- if (use24Hour) {
14700
- return `${h.padStart(2, "0")}:${m.padStart(2, "0")}`;
14816
+ setMinutes(String(minute).padStart(2, "0"));
14817
+ } else {
14818
+ setHours(use24Hour ? "00" : "12");
14819
+ setMinutes("00");
14820
+ setPeriod("AM");
14701
14821
  }
14702
- return `${h.padStart(2, "0")}:${m.padStart(2, "0")} ${p}`;
14703
- };
14704
- const toISOTime = (h, m, p) => {
14822
+ }, [parsedValue, use24Hour]);
14823
+ const createTimeDate = (h, m, p) => {
14705
14824
  let hour = parseInt(h) || 0;
14706
14825
  const minute = parseInt(m) || 0;
14707
14826
  if (!use24Hour) {
@@ -14711,28 +14830,37 @@ var TimePicker = React.forwardRef(
14711
14830
  hour = 0;
14712
14831
  }
14713
14832
  }
14714
- return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}:00.000Z`;
14833
+ const today = /* @__PURE__ */ new Date();
14834
+ return new Date(today.getFullYear(), today.getMonth(), today.getDate(), hour, minute, 0, 0);
14835
+ };
14836
+ const emitChange = (date) => {
14837
+ onChange?.(date);
14838
+ onValueChange?.(outputFormatter(date));
14839
+ };
14840
+ const handleClear = () => {
14841
+ onChange?.(void 0);
14842
+ onValueChange?.(void 0);
14715
14843
  };
14716
14844
  const handleHourChange = (newHour) => {
14717
14845
  const hour = parseInt(newHour) || 0;
14718
14846
  const maxHour = use24Hour ? 23 : 12;
14719
14847
  const minHour = use24Hour ? 0 : 1;
14720
14848
  if (hour <= maxHour && hour >= minHour) {
14721
- setHours(newHour);
14722
- onChange?.(toISOTime(newHour, minutes, period));
14849
+ setHours(newHour.padStart(2, "0"));
14850
+ emitChange(createTimeDate(newHour, minutes, period));
14723
14851
  }
14724
14852
  };
14725
14853
  const handleMinuteChange = (newMinute) => {
14726
14854
  const minute = parseInt(newMinute) || 0;
14727
14855
  if (minute <= 59 && minute >= 0) {
14728
- setMinutes(newMinute);
14729
- onChange?.(toISOTime(hours, newMinute, period));
14856
+ setMinutes(newMinute.padStart(2, "0"));
14857
+ emitChange(createTimeDate(hours, newMinute, period));
14730
14858
  }
14731
14859
  };
14732
14860
  const handlePeriodToggle = () => {
14733
14861
  const newPeriod = period === "AM" ? "PM" : "AM";
14734
14862
  setPeriod(newPeriod);
14735
- onChange?.(toISOTime(hours, minutes, newPeriod));
14863
+ emitChange(createTimeDate(hours, minutes, newPeriod));
14736
14864
  };
14737
14865
  const incrementHour = () => {
14738
14866
  const hour = parseInt(hours) || 0;
@@ -14758,193 +14886,207 @@ var TimePicker = React.forwardRef(
14758
14886
  const newMinute = minute <= 0 ? 59 : minute - 1;
14759
14887
  handleMinuteChange(String(newMinute));
14760
14888
  };
14761
- const displayValue = formatTime(hours, minutes, period);
14889
+ const displayValue = parsedValue ? labelFormatter(parsedValue, use24Hour) : placeholder2;
14762
14890
  const displayIcon = icon || /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4" });
14763
- return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
14764
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
14765
- Button,
14766
- {
14767
- variant: "outline",
14768
- disabled,
14769
- className: cn(
14770
- "w-full justify-between text-left font-normal",
14771
- !value && "text-muted-foreground",
14772
- className
14773
- ),
14774
- children: [
14775
- iconPosition === "start" && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: displayIcon }),
14891
+ const showClear = allowClear && hasValue && !disabled;
14892
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative inline-flex w-full", wrapperClassName), children: [
14893
+ /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
14894
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
14895
+ Button,
14896
+ {
14897
+ variant: "outline",
14898
+ disabled,
14899
+ className: cn(
14900
+ "w-full justify-between text-left font-normal",
14901
+ !hasValue && "text-muted-foreground",
14902
+ invalid && "border-destructive focus-visible:ring-destructive",
14903
+ showClear && "pr-8",
14904
+ buttonClassName,
14905
+ className
14906
+ ),
14907
+ children: [
14908
+ iconPosition === "start" && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: displayIcon }),
14909
+ /* @__PURE__ */ jsx(
14910
+ "span",
14911
+ {
14912
+ className: cn(
14913
+ "flex-1 truncate",
14914
+ iconPosition === "start" && "ml-2",
14915
+ iconPosition === "end" && "mr-2"
14916
+ ),
14917
+ children: displayValue || placeholder2 || "Select time"
14918
+ }
14919
+ ),
14920
+ iconPosition === "end" && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: displayIcon })
14921
+ ]
14922
+ }
14923
+ ) }),
14924
+ /* @__PURE__ */ jsx(PopoverContent, { className: "w-auto p-4", align: "start", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
14925
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
14776
14926
  /* @__PURE__ */ jsx(
14777
- "span",
14927
+ Button,
14778
14928
  {
14779
- className: cn(
14780
- "flex-1",
14781
- iconPosition === "start" && "ml-2",
14782
- iconPosition === "end" && "mr-2"
14783
- ),
14784
- children: value ? displayValue : placeholder2 || displayValue
14929
+ variant: "ghost",
14930
+ size: "icon",
14931
+ className: "h-8 w-8",
14932
+ onClick: incrementHour,
14933
+ type: "button",
14934
+ children: /* @__PURE__ */ jsx(
14935
+ "svg",
14936
+ {
14937
+ xmlns: "http://www.w3.org/2000/svg",
14938
+ width: "16",
14939
+ height: "16",
14940
+ viewBox: "0 0 24 24",
14941
+ fill: "none",
14942
+ stroke: "currentColor",
14943
+ strokeWidth: "2",
14944
+ strokeLinecap: "round",
14945
+ strokeLinejoin: "round",
14946
+ children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" })
14947
+ }
14948
+ )
14785
14949
  }
14786
14950
  ),
14787
- iconPosition === "end" && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: displayIcon })
14788
- ]
14789
- }
14790
- ) }),
14791
- /* @__PURE__ */ jsx(PopoverContent, { className: "w-auto p-4", align: "start", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
14792
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
14793
- /* @__PURE__ */ jsx(
14794
- Button,
14795
- {
14796
- variant: "ghost",
14797
- size: "icon",
14798
- className: "h-8 w-8",
14799
- onClick: incrementHour,
14800
- type: "button",
14801
- children: /* @__PURE__ */ jsx(
14802
- "svg",
14803
- {
14804
- xmlns: "http://www.w3.org/2000/svg",
14805
- width: "16",
14806
- height: "16",
14807
- viewBox: "0 0 24 24",
14808
- fill: "none",
14809
- stroke: "currentColor",
14810
- strokeWidth: "2",
14811
- strokeLinecap: "round",
14812
- strokeLinejoin: "round",
14813
- children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" })
14814
- }
14815
- )
14816
- }
14817
- ),
14818
- /* @__PURE__ */ jsx(
14819
- Input,
14820
- {
14821
- ref,
14822
- type: "text",
14823
- inputMode: "numeric",
14824
- value: hours,
14825
- onChange: (e) => handleHourChange(e.target.value),
14826
- className: "w-16 text-center",
14827
- maxLength: 2
14828
- }
14829
- ),
14830
- /* @__PURE__ */ jsx(
14831
- Button,
14832
- {
14833
- variant: "ghost",
14834
- size: "icon",
14835
- className: "h-8 w-8",
14836
- onClick: decrementHour,
14837
- type: "button",
14838
- children: /* @__PURE__ */ jsx(
14839
- "svg",
14840
- {
14841
- xmlns: "http://www.w3.org/2000/svg",
14842
- width: "16",
14843
- height: "16",
14844
- viewBox: "0 0 24 24",
14845
- fill: "none",
14846
- stroke: "currentColor",
14847
- strokeWidth: "2",
14848
- strokeLinecap: "round",
14849
- strokeLinejoin: "round",
14850
- children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
14851
- }
14852
- )
14853
- }
14854
- )
14855
- ] }),
14856
- /* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold", children: ":" }),
14857
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
14858
- /* @__PURE__ */ jsx(
14859
- Button,
14860
- {
14861
- variant: "ghost",
14862
- size: "icon",
14863
- className: "h-8 w-8",
14864
- onClick: incrementMinute,
14865
- type: "button",
14866
- children: /* @__PURE__ */ jsx(
14867
- "svg",
14868
- {
14869
- xmlns: "http://www.w3.org/2000/svg",
14870
- width: "16",
14871
- height: "16",
14872
- viewBox: "0 0 24 24",
14873
- fill: "none",
14874
- stroke: "currentColor",
14875
- strokeWidth: "2",
14876
- strokeLinecap: "round",
14877
- strokeLinejoin: "round",
14878
- children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" })
14879
- }
14880
- )
14881
- }
14882
- ),
14883
- /* @__PURE__ */ jsx(
14884
- Input,
14885
- {
14886
- type: "text",
14887
- inputMode: "numeric",
14888
- value: minutes,
14889
- onChange: (e) => handleMinuteChange(e.target.value),
14890
- className: "w-16 text-center",
14891
- maxLength: 2
14892
- }
14893
- ),
14894
- /* @__PURE__ */ jsx(
14895
- Button,
14896
- {
14897
- variant: "ghost",
14898
- size: "icon",
14899
- className: "h-8 w-8",
14900
- onClick: decrementMinute,
14901
- type: "button",
14902
- children: /* @__PURE__ */ jsx(
14903
- "svg",
14904
- {
14905
- xmlns: "http://www.w3.org/2000/svg",
14906
- width: "16",
14907
- height: "16",
14908
- viewBox: "0 0 24 24",
14909
- fill: "none",
14910
- stroke: "currentColor",
14911
- strokeWidth: "2",
14912
- strokeLinecap: "round",
14913
- strokeLinejoin: "round",
14914
- children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
14915
- }
14916
- )
14917
- }
14918
- )
14919
- ] }),
14920
- !use24Hour && /* @__PURE__ */ jsxs(Fragment, { children: [
14921
- /* @__PURE__ */ jsx("div", { className: "w-px h-16 bg-border mx-1" }),
14951
+ /* @__PURE__ */ jsx(
14952
+ Input,
14953
+ {
14954
+ ref,
14955
+ type: "text",
14956
+ inputMode: "numeric",
14957
+ value: hours,
14958
+ onChange: (e) => handleHourChange(e.target.value),
14959
+ className: "w-16 text-center",
14960
+ maxLength: 2
14961
+ }
14962
+ ),
14963
+ /* @__PURE__ */ jsx(
14964
+ Button,
14965
+ {
14966
+ variant: "ghost",
14967
+ size: "icon",
14968
+ className: "h-8 w-8",
14969
+ onClick: decrementHour,
14970
+ type: "button",
14971
+ children: /* @__PURE__ */ jsx(
14972
+ "svg",
14973
+ {
14974
+ xmlns: "http://www.w3.org/2000/svg",
14975
+ width: "16",
14976
+ height: "16",
14977
+ viewBox: "0 0 24 24",
14978
+ fill: "none",
14979
+ stroke: "currentColor",
14980
+ strokeWidth: "2",
14981
+ strokeLinecap: "round",
14982
+ strokeLinejoin: "round",
14983
+ children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
14984
+ }
14985
+ )
14986
+ }
14987
+ )
14988
+ ] }),
14989
+ /* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold", children: ":" }),
14922
14990
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
14923
14991
  /* @__PURE__ */ jsx(
14924
14992
  Button,
14925
14993
  {
14926
- variant: period === "AM" ? "default" : "outline",
14927
- size: "sm",
14928
- className: "w-14 h-12",
14929
- onClick: handlePeriodToggle,
14994
+ variant: "ghost",
14995
+ size: "icon",
14996
+ className: "h-8 w-8",
14997
+ onClick: incrementMinute,
14930
14998
  type: "button",
14931
- children: "AM"
14999
+ children: /* @__PURE__ */ jsx(
15000
+ "svg",
15001
+ {
15002
+ xmlns: "http://www.w3.org/2000/svg",
15003
+ width: "16",
15004
+ height: "16",
15005
+ viewBox: "0 0 24 24",
15006
+ fill: "none",
15007
+ stroke: "currentColor",
15008
+ strokeWidth: "2",
15009
+ strokeLinecap: "round",
15010
+ strokeLinejoin: "round",
15011
+ children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" })
15012
+ }
15013
+ )
15014
+ }
15015
+ ),
15016
+ /* @__PURE__ */ jsx(
15017
+ Input,
15018
+ {
15019
+ type: "text",
15020
+ inputMode: "numeric",
15021
+ value: minutes,
15022
+ onChange: (e) => handleMinuteChange(e.target.value),
15023
+ className: "w-16 text-center",
15024
+ maxLength: 2
14932
15025
  }
14933
15026
  ),
14934
15027
  /* @__PURE__ */ jsx(
14935
15028
  Button,
14936
15029
  {
14937
- variant: period === "PM" ? "default" : "outline",
14938
- size: "sm",
14939
- className: "w-14 h-12",
14940
- onClick: handlePeriodToggle,
15030
+ variant: "ghost",
15031
+ size: "icon",
15032
+ className: "h-8 w-8",
15033
+ onClick: decrementMinute,
14941
15034
  type: "button",
14942
- children: "PM"
15035
+ children: /* @__PURE__ */ jsx(
15036
+ "svg",
15037
+ {
15038
+ xmlns: "http://www.w3.org/2000/svg",
15039
+ width: "16",
15040
+ height: "16",
15041
+ viewBox: "0 0 24 24",
15042
+ fill: "none",
15043
+ stroke: "currentColor",
15044
+ strokeWidth: "2",
15045
+ strokeLinecap: "round",
15046
+ strokeLinejoin: "round",
15047
+ children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
15048
+ }
15049
+ )
14943
15050
  }
14944
15051
  )
15052
+ ] }),
15053
+ !use24Hour && /* @__PURE__ */ jsxs(Fragment, { children: [
15054
+ /* @__PURE__ */ jsx("div", { className: "w-px h-16 bg-border mx-1" }),
15055
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
15056
+ /* @__PURE__ */ jsx(
15057
+ Button,
15058
+ {
15059
+ variant: period === "AM" ? "default" : "outline",
15060
+ size: "sm",
15061
+ className: "w-14 h-12",
15062
+ onClick: handlePeriodToggle,
15063
+ type: "button",
15064
+ children: "AM"
15065
+ }
15066
+ ),
15067
+ /* @__PURE__ */ jsx(
15068
+ Button,
15069
+ {
15070
+ variant: period === "PM" ? "default" : "outline",
15071
+ size: "sm",
15072
+ className: "w-14 h-12",
15073
+ onClick: handlePeriodToggle,
15074
+ type: "button",
15075
+ children: "PM"
15076
+ }
15077
+ )
15078
+ ] })
14945
15079
  ] })
14946
- ] })
14947
- ] }) })
15080
+ ] }) })
15081
+ ] }),
15082
+ showClear && /* @__PURE__ */ jsx(
15083
+ ClearButton,
15084
+ {
15085
+ onClick: handleClear,
15086
+ ariaLabel: clearAriaLabel,
15087
+ className: "absolute right-2 top-1/2 -translate-y-1/2"
15088
+ }
15089
+ )
14948
15090
  ] });
14949
15091
  }
14950
15092
  );
@@ -16131,7 +16273,7 @@ var useFieldNames = ({
16131
16273
  };
16132
16274
  var useFieldNames_default = useFieldNames;
16133
16275
  var ROW_HEIGHT = 32;
16134
- var MIN_HEIGHT_EMPTY = 76;
16276
+ var MIN_HEIGHT_EMPTY = 68;
16135
16277
  var VirtualizedCommand = ({
16136
16278
  name,
16137
16279
  height = 292,
@@ -16263,7 +16405,7 @@ var VirtualizedCommand = ({
16263
16405
  onMouseMove: () => setIsKeyboardNavActive(false),
16264
16406
  children: [
16265
16407
  /* @__PURE__ */ jsx(CommandEmpty, { "data-testid": `command-item-empty-${name}`, children: internalOptions.length === 0 ? emptyContent : notFoundContent }),
16266
- /* @__PURE__ */ jsx(
16408
+ filteredOptions.length > 0 && /* @__PURE__ */ jsx(
16267
16409
  CommandGroup,
16268
16410
  {
16269
16411
  style: {
@@ -16367,7 +16509,7 @@ var NonVirtualizedCommand = ({
16367
16509
  },
16368
16510
  children: [
16369
16511
  /* @__PURE__ */ jsx(CommandEmpty, { "data-testid": `command-item-empty-${name}`, children: internalOptions.length === 0 ? emptyContent : notFoundContent }),
16370
- /* @__PURE__ */ jsx(CommandGroup, { children: filteredOptions.map((option, index) => {
16512
+ filteredOptions.length > 0 && /* @__PURE__ */ jsx(CommandGroup, { children: filteredOptions.map((option, index) => {
16371
16513
  const optionLabel = getLabelField(option);
16372
16514
  const optionValue = getValueField(option);
16373
16515
  const labelRendered = labelRender ? labelRender(option) : optionLabel;
@@ -16430,6 +16572,8 @@ var ComboboxInner = ({
16430
16572
  virtual = true,
16431
16573
  isLoading,
16432
16574
  loadingContent,
16575
+ popoverProps,
16576
+ popoverContentProps,
16433
16577
  ...props
16434
16578
  }, ref) => {
16435
16579
  const { getLabelField, getValueField } = useFieldNames_default({ fieldNames });
@@ -16501,7 +16645,7 @@ var ComboboxInner = ({
16501
16645
  },
16502
16646
  [currentSelectedOption, onClear, selectedValue, setSelectedValue]
16503
16647
  );
16504
- return /* @__PURE__ */ jsxs(Popover, { open: openPopover, onOpenChange: handleOpenPopover, children: [
16648
+ return /* @__PURE__ */ jsxs(Popover, { open: openPopover, onOpenChange: handleOpenPopover, ...popoverProps, children: [
16505
16649
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
16506
16650
  "button",
16507
16651
  {
@@ -16556,9 +16700,17 @@ var ComboboxInner = ({
16556
16700
  /* @__PURE__ */ jsx(
16557
16701
  PopoverContent,
16558
16702
  {
16559
- className: "p-0 w-(--radix-popper-anchor-width) min-w-(--radix-popper-anchor-width) max-w-(--radix-popper-available-width) rounded-md",
16560
16703
  align: "start",
16561
16704
  sideOffset: 4,
16705
+ ...popoverContentProps,
16706
+ onWheel: (e) => {
16707
+ e.stopPropagation();
16708
+ popoverContentProps?.onWheel?.(e);
16709
+ },
16710
+ className: cn(
16711
+ "p-0 w-(--radix-popper-anchor-width) min-w-(--radix-popper-anchor-width) max-w-(--radix-popper-available-width) rounded-md",
16712
+ popoverContentProps?.className
16713
+ ),
16562
16714
  children: virtual ? /* @__PURE__ */ jsx(
16563
16715
  VirtualizedCommand_default,
16564
16716
  {
@@ -17124,6 +17276,65 @@ function MentionsPlugin2({
17124
17276
  });
17125
17277
  });
17126
17278
  }, [editor, mentions, mentionKeyMap, mentionConfig]);
17279
+ useEffect(() => {
17280
+ if (mentions.length === 0) return;
17281
+ return editor.registerCommand(
17282
+ PASTE_COMMAND,
17283
+ (event) => {
17284
+ const clipboardData = event.clipboardData;
17285
+ if (!clipboardData) return false;
17286
+ const pastedText = clipboardData.getData("text/plain");
17287
+ if (!pastedText) return false;
17288
+ const variableMatches = [...pastedText.matchAll(COMPLETE_VARIABLE_REGEX2)];
17289
+ const hasKnownVariable = variableMatches.some((match) => {
17290
+ const variableName = match[1];
17291
+ const fullMatch = match[0];
17292
+ return mentionKeyMap.has(variableName.toUpperCase()) || mentionKeyMap.has(fullMatch.toUpperCase());
17293
+ });
17294
+ if (!hasKnownVariable) return false;
17295
+ event.preventDefault();
17296
+ editor.update(
17297
+ () => {
17298
+ const selection = $getSelection();
17299
+ if (!$isRangeSelection(selection)) return;
17300
+ let lastIndex = 0;
17301
+ const nodesToInsert = [];
17302
+ for (const match of variableMatches) {
17303
+ const fullMatch = match[0];
17304
+ const variableName = match[1];
17305
+ const matchIndex = match.index;
17306
+ if (matchIndex > lastIndex) {
17307
+ const textBefore = pastedText.slice(lastIndex, matchIndex);
17308
+ nodesToInsert.push($createTextNode(textBefore));
17309
+ }
17310
+ const mentionItem = mentionKeyMap.get(variableName.toUpperCase()) || mentionKeyMap.get(fullMatch.toUpperCase());
17311
+ if (mentionItem) {
17312
+ nodesToInsert.push(
17313
+ $createMentionNode2(
17314
+ mentionConfig.getName(mentionItem),
17315
+ mentionConfig.getLabel(mentionItem)
17316
+ )
17317
+ );
17318
+ nodesToInsert.push($createTextNode(" "));
17319
+ } else {
17320
+ nodesToInsert.push($createTextNode(fullMatch));
17321
+ }
17322
+ lastIndex = matchIndex + fullMatch.length;
17323
+ }
17324
+ if (lastIndex < pastedText.length) {
17325
+ nodesToInsert.push($createTextNode(pastedText.slice(lastIndex)));
17326
+ }
17327
+ for (const node of nodesToInsert) {
17328
+ selection.insertNodes([node]);
17329
+ }
17330
+ },
17331
+ { tag: "paste-mention-conversion" }
17332
+ );
17333
+ return true;
17334
+ },
17335
+ COMMAND_PRIORITY_LOW
17336
+ );
17337
+ }, [editor, mentions, mentionKeyMap, mentionConfig]);
17127
17338
  const options = useMemo(() => {
17128
17339
  if (!queryString) {
17129
17340
  return mentions.map(
@@ -17267,6 +17478,41 @@ function OnChangePluginFiltered2({
17267
17478
  }, [editor, onChange, ignoreSelectionChange]);
17268
17479
  return null;
17269
17480
  }
17481
+ function MaxLengthPlugin2({ maxLength }) {
17482
+ const [editor] = useLexicalComposerContext();
17483
+ useEffect(() => {
17484
+ if (!maxLength) return;
17485
+ let lastRestoredTextLength = 0;
17486
+ return editor.registerNodeTransform(RootNode, (rootNode) => {
17487
+ const selection = $getSelection();
17488
+ if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
17489
+ return;
17490
+ }
17491
+ const prevTextLength = lastRestoredTextLength;
17492
+ const textLength = rootNode.getTextContentSize();
17493
+ if (prevTextLength !== textLength) {
17494
+ const delCount = textLength - maxLength;
17495
+ const anchor = selection.anchor;
17496
+ if (delCount > 0) {
17497
+ if (prevTextLength === maxLength && lastRestoredTextLength !== maxLength) {
17498
+ lastRestoredTextLength = maxLength;
17499
+ $restoreTextContent2(anchor);
17500
+ } else {
17501
+ trimTextContentFromAnchor(editor, anchor, delCount);
17502
+ }
17503
+ }
17504
+ }
17505
+ });
17506
+ }, [editor, maxLength]);
17507
+ return null;
17508
+ }
17509
+ function $restoreTextContent2(anchor) {
17510
+ const selection = $getSelection();
17511
+ if ($isRangeSelection(selection)) {
17512
+ selection.anchor.set(anchor.key, anchor.offset, "text");
17513
+ selection.focus.set(anchor.key, anchor.offset, "text");
17514
+ }
17515
+ }
17270
17516
  var theme2 = {
17271
17517
  paragraph: "leading-6 text-sm text-foreground",
17272
17518
  placeholder: "pointer-events-none absolute left-0 top-0 px-3 py-2 text-sm text-muted-foreground select-none"
@@ -17280,6 +17526,7 @@ function InputMentionInner({
17280
17526
  disabled,
17281
17527
  editorClassName,
17282
17528
  autoFocus,
17529
+ maxLength,
17283
17530
  mentions,
17284
17531
  mentionConfig,
17285
17532
  mentionTrigger,
@@ -17376,7 +17623,8 @@ function InputMentionInner({
17376
17623
  mentionConfig: finalMentionConfig
17377
17624
  }
17378
17625
  ),
17379
- /* @__PURE__ */ jsx(OnChangePluginFiltered2, { onChange: handleChange, ignoreSelectionChange: true })
17626
+ /* @__PURE__ */ jsx(OnChangePluginFiltered2, { onChange: handleChange, ignoreSelectionChange: true }),
17627
+ maxLength && /* @__PURE__ */ jsx(MaxLengthPlugin2, { maxLength })
17380
17628
  ]
17381
17629
  }
17382
17630
  ) });