@sustaina/shared-ui 1.51.0 → 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.js CHANGED
@@ -236,7 +236,7 @@ function stripNullishObject(value) {
236
236
 
237
237
  // src/utils/date.ts
238
238
  var pad = (num) => String(num).padStart(2, "0");
239
- function formatISODate(isoDate, format5 = "d/m/Y H:i") {
239
+ function formatISODate(isoDate, format6 = "d/m/Y H:i") {
240
240
  const date = new Date(isoDate);
241
241
  if (isNaN(date.getTime())) {
242
242
  return "Invalid Date";
@@ -267,7 +267,7 @@ function formatISODate(isoDate, format5 = "d/m/Y H:i") {
267
267
  // Second
268
268
  s: "s"
269
269
  };
270
- return format5.replace(/YYYY|Y|MM|m|DD|d|H|i|s/g, (match) => {
270
+ return format6.replace(/YYYY|Y|MM|m|DD|d|H|i|s/g, (match) => {
271
271
  const partKey = tokenMap[match];
272
272
  return partKey ? parts[partKey] : match;
273
273
  });
@@ -11667,8 +11667,8 @@ var GridSettingsModal = ({
11667
11667
  }
11668
11668
  }, [isOpen, currentColumns, form]);
11669
11669
  const addColumn = async () => {
11670
- const isValid6 = await trigger("columns");
11671
- if (isValid6) {
11670
+ const isValid7 = await trigger("columns");
11671
+ if (isValid7) {
11672
11672
  append({ id: "" });
11673
11673
  requestAnimationFrame(() => {
11674
11674
  const container = scrollRef.current;
@@ -13547,12 +13547,12 @@ function LinkPreviewPopover({
13547
13547
  function FormatPlugin({ disabled, state }) {
13548
13548
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
13549
13549
  const toggleFormat = React.useCallback(
13550
- (format5) => {
13550
+ (format6) => {
13551
13551
  if (disabled) {
13552
13552
  return;
13553
13553
  }
13554
13554
  editor.focus();
13555
- editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, format5);
13555
+ editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, format6);
13556
13556
  },
13557
13557
  [disabled, editor]
13558
13558
  );
@@ -13897,12 +13897,12 @@ function BlockPlugin({ disabled, state }) {
13897
13897
  function AlignPlugin({ disabled, state }) {
13898
13898
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
13899
13899
  const applyElementFormat = React.useCallback(
13900
- (format5) => {
13900
+ (format6) => {
13901
13901
  if (disabled) {
13902
13902
  return;
13903
13903
  }
13904
13904
  editor.focus();
13905
- editor.dispatchCommand(lexical.FORMAT_ELEMENT_COMMAND, format5);
13905
+ editor.dispatchCommand(lexical.FORMAT_ELEMENT_COMMAND, format6);
13906
13906
  },
13907
13907
  [disabled, editor]
13908
13908
  );
@@ -14282,6 +14282,65 @@ function MentionsPlugin({
14282
14282
  });
14283
14283
  });
14284
14284
  }, [editor, mentions, mentionKeyMap, mentionConfig]);
14285
+ React.useEffect(() => {
14286
+ if (mentions.length === 0) return;
14287
+ return editor.registerCommand(
14288
+ lexical.PASTE_COMMAND,
14289
+ (event) => {
14290
+ const clipboardData = event.clipboardData;
14291
+ if (!clipboardData) return false;
14292
+ const pastedText = clipboardData.getData("text/plain");
14293
+ if (!pastedText) return false;
14294
+ const variableMatches = [...pastedText.matchAll(COMPLETE_VARIABLE_REGEX)];
14295
+ const hasKnownVariable = variableMatches.some((match) => {
14296
+ const variableName = match[1];
14297
+ const fullMatch = match[0];
14298
+ return mentionKeyMap.has(variableName.toUpperCase()) || mentionKeyMap.has(fullMatch.toUpperCase());
14299
+ });
14300
+ if (!hasKnownVariable) return false;
14301
+ event.preventDefault();
14302
+ editor.update(
14303
+ () => {
14304
+ const selection = lexical.$getSelection();
14305
+ if (!lexical.$isRangeSelection(selection)) return;
14306
+ let lastIndex = 0;
14307
+ const nodesToInsert = [];
14308
+ for (const match of variableMatches) {
14309
+ const fullMatch = match[0];
14310
+ const variableName = match[1];
14311
+ const matchIndex = match.index;
14312
+ if (matchIndex > lastIndex) {
14313
+ const textBefore = pastedText.slice(lastIndex, matchIndex);
14314
+ nodesToInsert.push(lexical.$createTextNode(textBefore));
14315
+ }
14316
+ const mentionItem = mentionKeyMap.get(variableName.toUpperCase()) || mentionKeyMap.get(fullMatch.toUpperCase());
14317
+ if (mentionItem) {
14318
+ nodesToInsert.push(
14319
+ $createMentionNode(
14320
+ mentionConfig.getName(mentionItem),
14321
+ mentionConfig.getLabel(mentionItem)
14322
+ )
14323
+ );
14324
+ nodesToInsert.push(lexical.$createTextNode(" "));
14325
+ } else {
14326
+ nodesToInsert.push(lexical.$createTextNode(fullMatch));
14327
+ }
14328
+ lastIndex = matchIndex + fullMatch.length;
14329
+ }
14330
+ if (lastIndex < pastedText.length) {
14331
+ nodesToInsert.push(lexical.$createTextNode(pastedText.slice(lastIndex)));
14332
+ }
14333
+ for (const node of nodesToInsert) {
14334
+ selection.insertNodes([node]);
14335
+ }
14336
+ },
14337
+ { tag: "paste-mention-conversion" }
14338
+ );
14339
+ return true;
14340
+ },
14341
+ lexical.COMMAND_PRIORITY_LOW
14342
+ );
14343
+ }, [editor, mentions, mentionKeyMap, mentionConfig]);
14285
14344
  const options = React.useMemo(() => {
14286
14345
  if (!queryString) {
14287
14346
  return mentions.map(
@@ -14422,6 +14481,41 @@ function OnChangePluginFiltered({
14422
14481
  }, [editor, onChange, ignoreSelectionChange]);
14423
14482
  return null;
14424
14483
  }
14484
+ function MaxLengthPlugin({ maxLength }) {
14485
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
14486
+ React.useEffect(() => {
14487
+ if (!maxLength) return;
14488
+ let lastRestoredTextLength = 0;
14489
+ return editor.registerNodeTransform(lexical.RootNode, (rootNode) => {
14490
+ const selection$1 = lexical.$getSelection();
14491
+ if (!lexical.$isRangeSelection(selection$1) || !selection$1.isCollapsed()) {
14492
+ return;
14493
+ }
14494
+ const prevTextLength = lastRestoredTextLength;
14495
+ const textLength = rootNode.getTextContentSize();
14496
+ if (prevTextLength !== textLength) {
14497
+ const delCount = textLength - maxLength;
14498
+ const anchor = selection$1.anchor;
14499
+ if (delCount > 0) {
14500
+ if (prevTextLength === maxLength && lastRestoredTextLength !== maxLength) {
14501
+ lastRestoredTextLength = maxLength;
14502
+ $restoreTextContent(anchor);
14503
+ } else {
14504
+ selection.trimTextContentFromAnchor(editor, anchor, delCount);
14505
+ }
14506
+ }
14507
+ }
14508
+ });
14509
+ }, [editor, maxLength]);
14510
+ return null;
14511
+ }
14512
+ function $restoreTextContent(anchor) {
14513
+ const selection = lexical.$getSelection();
14514
+ if (lexical.$isRangeSelection(selection)) {
14515
+ selection.anchor.set(anchor.key, anchor.offset, "text");
14516
+ selection.focus.set(anchor.key, anchor.offset, "text");
14517
+ }
14518
+ }
14425
14519
  var theme = {
14426
14520
  paragraph: "mb-2 leading-normal text-sm text-foreground",
14427
14521
  placeholder: "pointer-events-none absolute left-0 top-0 px-3 py-2 text-sm text-muted-foreground select-none",
@@ -14477,6 +14571,7 @@ function RichTextInner({
14477
14571
  onImageDialogUploadError,
14478
14572
  acceptImageMimeTypes = "image/*",
14479
14573
  allowImageUrlInsert = true,
14574
+ maxLength,
14480
14575
  mentions,
14481
14576
  mentionConfig,
14482
14577
  mentionTrigger,
@@ -14596,7 +14691,8 @@ function RichTextInner({
14596
14691
  autoFocus ? /* @__PURE__ */ jsxRuntime.jsx(LexicalAutoFocusPlugin.AutoFocusPlugin, {}) : null,
14597
14692
  /* @__PURE__ */ jsxRuntime.jsx(EditableStatePlugin, { editable }),
14598
14693
  /* @__PURE__ */ jsxRuntime.jsx(ControlledValuePlugin, { value, trackAppliedValue: appliedValueRef }),
14599
- /* @__PURE__ */ jsxRuntime.jsx(OnChangePluginFiltered, { onChange: handleChange, ignoreSelectionChange: true })
14694
+ /* @__PURE__ */ jsxRuntime.jsx(OnChangePluginFiltered, { onChange: handleChange, ignoreSelectionChange: true }),
14695
+ maxLength && /* @__PURE__ */ jsxRuntime.jsx(MaxLengthPlugin, { maxLength })
14600
14696
  ] })
14601
14697
  ]
14602
14698
  }
@@ -14692,58 +14788,81 @@ var RightPanelContainer = ({
14692
14788
  );
14693
14789
  };
14694
14790
  var RightPanelContainer_default = RightPanelContainer;
14791
+ var defaultDisplayFormatter3 = (date, use24Hour) => use24Hour ? dateFns.format(date, "HH:mm") : dateFns.format(date, "hh:mm a");
14792
+ var defaultValueFormatter3 = (date) => dateFns.format(date, "HH:mm");
14793
+ var defaultValueParser3 = (value) => {
14794
+ let parsed = dateFns.parse(value, "HH:mm", /* @__PURE__ */ new Date());
14795
+ if (dateFns.isValid(parsed)) return parsed;
14796
+ parsed = dateFns.parse(value, "HH:mm:ss", /* @__PURE__ */ new Date());
14797
+ if (dateFns.isValid(parsed)) return parsed;
14798
+ parsed = dateFns.parse(value, "hh:mm a", /* @__PURE__ */ new Date());
14799
+ if (dateFns.isValid(parsed)) return parsed;
14800
+ return void 0;
14801
+ };
14695
14802
  var TimePicker = React__namespace.forwardRef(
14696
14803
  ({
14697
- value = "",
14804
+ value,
14698
14805
  onChange,
14806
+ onValueChange,
14699
14807
  disabled = false,
14700
14808
  className,
14809
+ buttonClassName,
14810
+ wrapperClassName,
14701
14811
  placeholder: placeholder2,
14702
14812
  use24Hour = false,
14703
14813
  iconPosition = "start",
14704
- icon
14814
+ icon,
14815
+ allowClear = true,
14816
+ clearAriaLabel = "Clear time",
14817
+ displayFormatter,
14818
+ valueFormatter,
14819
+ valueParser,
14820
+ invalid = false
14705
14821
  }, ref) => {
14706
14822
  const [open, setOpen] = React__namespace.useState(false);
14707
14823
  const [hours, setHours] = React__namespace.useState("12");
14708
14824
  const [minutes, setMinutes] = React__namespace.useState("00");
14709
14825
  const [period, setPeriod] = React__namespace.useState("AM");
14826
+ const parser = React__namespace.useMemo(() => valueParser ?? defaultValueParser3, [valueParser]);
14827
+ const outputFormatter = React__namespace.useMemo(
14828
+ () => valueFormatter ?? defaultValueFormatter3,
14829
+ [valueFormatter]
14830
+ );
14831
+ const labelFormatter = React__namespace.useMemo(
14832
+ () => displayFormatter ?? defaultDisplayFormatter3,
14833
+ [displayFormatter]
14834
+ );
14835
+ const parseInput = React__namespace.useCallback(
14836
+ (input) => {
14837
+ if (input === null || input === void 0) return void 0;
14838
+ if (input instanceof Date) return dateFns.isValid(input) ? input : void 0;
14839
+ const parsed = parser(input);
14840
+ return parsed && dateFns.isValid(parsed) ? parsed : void 0;
14841
+ },
14842
+ [parser]
14843
+ );
14844
+ const parsedValue = React__namespace.useMemo(() => parseInput(value), [parseInput, value]);
14845
+ const hasValue = parsedValue !== void 0;
14710
14846
  React__namespace.useEffect(() => {
14711
- if (value) {
14712
- let h, m;
14713
- if (value.includes("Z") || value.match(/^\d{2}:\d{2}:\d{2}/)) {
14714
- const [hour, min] = value.split(":");
14715
- const hour24 = parseInt(hour);
14716
- const minute = parseInt(min);
14717
- if (use24Hour) {
14718
- h = String(hour24);
14719
- m = String(minute);
14720
- } else {
14721
- const newPeriod = hour24 >= 12 ? "PM" : "AM";
14722
- const hour12 = hour24 === 0 ? 12 : hour24 > 12 ? hour24 - 12 : hour24;
14723
- h = String(hour12);
14724
- m = String(minute);
14725
- setPeriod(newPeriod);
14726
- }
14847
+ if (parsedValue) {
14848
+ const hour24 = parsedValue.getHours();
14849
+ const minute = parsedValue.getMinutes();
14850
+ if (use24Hour) {
14851
+ setHours(String(hour24).padStart(2, "0"));
14727
14852
  } else {
14728
- const [time, meridiem] = value.split(" ");
14729
- const [hour, min] = time.split(":");
14730
- h = hour;
14731
- m = min;
14732
- if (meridiem) {
14733
- setPeriod(meridiem);
14734
- }
14853
+ const newPeriod = hour24 >= 12 ? "PM" : "AM";
14854
+ const hour12 = hour24 === 0 ? 12 : hour24 > 12 ? hour24 - 12 : hour24;
14855
+ setHours(String(hour12).padStart(2, "0"));
14856
+ setPeriod(newPeriod);
14735
14857
  }
14736
- setHours(h.padStart(2, "0"));
14737
- setMinutes(m.padStart(2, "0"));
14738
- }
14739
- }, [value, use24Hour]);
14740
- const formatTime = (h, m, p) => {
14741
- if (use24Hour) {
14742
- return `${h.padStart(2, "0")}:${m.padStart(2, "0")}`;
14858
+ setMinutes(String(minute).padStart(2, "0"));
14859
+ } else {
14860
+ setHours(use24Hour ? "00" : "12");
14861
+ setMinutes("00");
14862
+ setPeriod("AM");
14743
14863
  }
14744
- return `${h.padStart(2, "0")}:${m.padStart(2, "0")} ${p}`;
14745
- };
14746
- const toISOTime = (h, m, p) => {
14864
+ }, [parsedValue, use24Hour]);
14865
+ const createTimeDate = (h, m, p) => {
14747
14866
  let hour = parseInt(h) || 0;
14748
14867
  const minute = parseInt(m) || 0;
14749
14868
  if (!use24Hour) {
@@ -14753,28 +14872,37 @@ var TimePicker = React__namespace.forwardRef(
14753
14872
  hour = 0;
14754
14873
  }
14755
14874
  }
14756
- return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}:00.000Z`;
14875
+ const today = /* @__PURE__ */ new Date();
14876
+ return new Date(today.getFullYear(), today.getMonth(), today.getDate(), hour, minute, 0, 0);
14877
+ };
14878
+ const emitChange = (date) => {
14879
+ onChange?.(date);
14880
+ onValueChange?.(outputFormatter(date));
14881
+ };
14882
+ const handleClear = () => {
14883
+ onChange?.(void 0);
14884
+ onValueChange?.(void 0);
14757
14885
  };
14758
14886
  const handleHourChange = (newHour) => {
14759
14887
  const hour = parseInt(newHour) || 0;
14760
14888
  const maxHour = use24Hour ? 23 : 12;
14761
14889
  const minHour = use24Hour ? 0 : 1;
14762
14890
  if (hour <= maxHour && hour >= minHour) {
14763
- setHours(newHour);
14764
- onChange?.(toISOTime(newHour, minutes, period));
14891
+ setHours(newHour.padStart(2, "0"));
14892
+ emitChange(createTimeDate(newHour, minutes, period));
14765
14893
  }
14766
14894
  };
14767
14895
  const handleMinuteChange = (newMinute) => {
14768
14896
  const minute = parseInt(newMinute) || 0;
14769
14897
  if (minute <= 59 && minute >= 0) {
14770
- setMinutes(newMinute);
14771
- onChange?.(toISOTime(hours, newMinute, period));
14898
+ setMinutes(newMinute.padStart(2, "0"));
14899
+ emitChange(createTimeDate(hours, newMinute, period));
14772
14900
  }
14773
14901
  };
14774
14902
  const handlePeriodToggle = () => {
14775
14903
  const newPeriod = period === "AM" ? "PM" : "AM";
14776
14904
  setPeriod(newPeriod);
14777
- onChange?.(toISOTime(hours, minutes, newPeriod));
14905
+ emitChange(createTimeDate(hours, minutes, newPeriod));
14778
14906
  };
14779
14907
  const incrementHour = () => {
14780
14908
  const hour = parseInt(hours) || 0;
@@ -14800,193 +14928,207 @@ var TimePicker = React__namespace.forwardRef(
14800
14928
  const newMinute = minute <= 0 ? 59 : minute - 1;
14801
14929
  handleMinuteChange(String(newMinute));
14802
14930
  };
14803
- const displayValue = formatTime(hours, minutes, period);
14931
+ const displayValue = parsedValue ? labelFormatter(parsedValue, use24Hour) : placeholder2;
14804
14932
  const displayIcon = icon || /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-4 w-4" });
14805
- return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
14806
- /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
14807
- Button,
14808
- {
14809
- variant: "outline",
14810
- disabled,
14811
- className: cn(
14812
- "w-full justify-between text-left font-normal",
14813
- !value && "text-muted-foreground",
14814
- className
14815
- ),
14816
- children: [
14817
- iconPosition === "start" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: displayIcon }),
14933
+ const showClear = allowClear && hasValue && !disabled;
14934
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative inline-flex w-full", wrapperClassName), children: [
14935
+ /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
14936
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
14937
+ Button,
14938
+ {
14939
+ variant: "outline",
14940
+ disabled,
14941
+ className: cn(
14942
+ "w-full justify-between text-left font-normal",
14943
+ !hasValue && "text-muted-foreground",
14944
+ invalid && "border-destructive focus-visible:ring-destructive",
14945
+ showClear && "pr-8",
14946
+ buttonClassName,
14947
+ className
14948
+ ),
14949
+ children: [
14950
+ iconPosition === "start" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: displayIcon }),
14951
+ /* @__PURE__ */ jsxRuntime.jsx(
14952
+ "span",
14953
+ {
14954
+ className: cn(
14955
+ "flex-1 truncate",
14956
+ iconPosition === "start" && "ml-2",
14957
+ iconPosition === "end" && "mr-2"
14958
+ ),
14959
+ children: displayValue || placeholder2 || "Select time"
14960
+ }
14961
+ ),
14962
+ iconPosition === "end" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: displayIcon })
14963
+ ]
14964
+ }
14965
+ ) }),
14966
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-auto p-4", align: "start", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
14967
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
14818
14968
  /* @__PURE__ */ jsxRuntime.jsx(
14819
- "span",
14969
+ Button,
14820
14970
  {
14821
- className: cn(
14822
- "flex-1",
14823
- iconPosition === "start" && "ml-2",
14824
- iconPosition === "end" && "mr-2"
14825
- ),
14826
- children: value ? displayValue : placeholder2 || displayValue
14971
+ variant: "ghost",
14972
+ size: "icon",
14973
+ className: "h-8 w-8",
14974
+ onClick: incrementHour,
14975
+ type: "button",
14976
+ children: /* @__PURE__ */ jsxRuntime.jsx(
14977
+ "svg",
14978
+ {
14979
+ xmlns: "http://www.w3.org/2000/svg",
14980
+ width: "16",
14981
+ height: "16",
14982
+ viewBox: "0 0 24 24",
14983
+ fill: "none",
14984
+ stroke: "currentColor",
14985
+ strokeWidth: "2",
14986
+ strokeLinecap: "round",
14987
+ strokeLinejoin: "round",
14988
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" })
14989
+ }
14990
+ )
14827
14991
  }
14828
14992
  ),
14829
- iconPosition === "end" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: displayIcon })
14830
- ]
14831
- }
14832
- ) }),
14833
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { className: "w-auto p-4", align: "start", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
14834
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
14835
- /* @__PURE__ */ jsxRuntime.jsx(
14836
- Button,
14837
- {
14838
- variant: "ghost",
14839
- size: "icon",
14840
- className: "h-8 w-8",
14841
- onClick: incrementHour,
14842
- type: "button",
14843
- children: /* @__PURE__ */ jsxRuntime.jsx(
14844
- "svg",
14845
- {
14846
- xmlns: "http://www.w3.org/2000/svg",
14847
- width: "16",
14848
- height: "16",
14849
- viewBox: "0 0 24 24",
14850
- fill: "none",
14851
- stroke: "currentColor",
14852
- strokeWidth: "2",
14853
- strokeLinecap: "round",
14854
- strokeLinejoin: "round",
14855
- children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" })
14856
- }
14857
- )
14858
- }
14859
- ),
14860
- /* @__PURE__ */ jsxRuntime.jsx(
14861
- Input,
14862
- {
14863
- ref,
14864
- type: "text",
14865
- inputMode: "numeric",
14866
- value: hours,
14867
- onChange: (e) => handleHourChange(e.target.value),
14868
- className: "w-16 text-center",
14869
- maxLength: 2
14870
- }
14871
- ),
14872
- /* @__PURE__ */ jsxRuntime.jsx(
14873
- Button,
14874
- {
14875
- variant: "ghost",
14876
- size: "icon",
14877
- className: "h-8 w-8",
14878
- onClick: decrementHour,
14879
- type: "button",
14880
- children: /* @__PURE__ */ jsxRuntime.jsx(
14881
- "svg",
14882
- {
14883
- xmlns: "http://www.w3.org/2000/svg",
14884
- width: "16",
14885
- height: "16",
14886
- viewBox: "0 0 24 24",
14887
- fill: "none",
14888
- stroke: "currentColor",
14889
- strokeWidth: "2",
14890
- strokeLinecap: "round",
14891
- strokeLinejoin: "round",
14892
- children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" })
14893
- }
14894
- )
14895
- }
14896
- )
14897
- ] }),
14898
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl font-semibold", children: ":" }),
14899
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
14900
- /* @__PURE__ */ jsxRuntime.jsx(
14901
- Button,
14902
- {
14903
- variant: "ghost",
14904
- size: "icon",
14905
- className: "h-8 w-8",
14906
- onClick: incrementMinute,
14907
- type: "button",
14908
- children: /* @__PURE__ */ jsxRuntime.jsx(
14909
- "svg",
14910
- {
14911
- xmlns: "http://www.w3.org/2000/svg",
14912
- width: "16",
14913
- height: "16",
14914
- viewBox: "0 0 24 24",
14915
- fill: "none",
14916
- stroke: "currentColor",
14917
- strokeWidth: "2",
14918
- strokeLinecap: "round",
14919
- strokeLinejoin: "round",
14920
- children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" })
14921
- }
14922
- )
14923
- }
14924
- ),
14925
- /* @__PURE__ */ jsxRuntime.jsx(
14926
- Input,
14927
- {
14928
- type: "text",
14929
- inputMode: "numeric",
14930
- value: minutes,
14931
- onChange: (e) => handleMinuteChange(e.target.value),
14932
- className: "w-16 text-center",
14933
- maxLength: 2
14934
- }
14935
- ),
14936
- /* @__PURE__ */ jsxRuntime.jsx(
14937
- Button,
14938
- {
14939
- variant: "ghost",
14940
- size: "icon",
14941
- className: "h-8 w-8",
14942
- onClick: decrementMinute,
14943
- type: "button",
14944
- children: /* @__PURE__ */ jsxRuntime.jsx(
14945
- "svg",
14946
- {
14947
- xmlns: "http://www.w3.org/2000/svg",
14948
- width: "16",
14949
- height: "16",
14950
- viewBox: "0 0 24 24",
14951
- fill: "none",
14952
- stroke: "currentColor",
14953
- strokeWidth: "2",
14954
- strokeLinecap: "round",
14955
- strokeLinejoin: "round",
14956
- children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" })
14957
- }
14958
- )
14959
- }
14960
- )
14961
- ] }),
14962
- !use24Hour && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
14963
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-16 bg-border mx-1" }),
14993
+ /* @__PURE__ */ jsxRuntime.jsx(
14994
+ Input,
14995
+ {
14996
+ ref,
14997
+ type: "text",
14998
+ inputMode: "numeric",
14999
+ value: hours,
15000
+ onChange: (e) => handleHourChange(e.target.value),
15001
+ className: "w-16 text-center",
15002
+ maxLength: 2
15003
+ }
15004
+ ),
15005
+ /* @__PURE__ */ jsxRuntime.jsx(
15006
+ Button,
15007
+ {
15008
+ variant: "ghost",
15009
+ size: "icon",
15010
+ className: "h-8 w-8",
15011
+ onClick: decrementHour,
15012
+ type: "button",
15013
+ children: /* @__PURE__ */ jsxRuntime.jsx(
15014
+ "svg",
15015
+ {
15016
+ xmlns: "http://www.w3.org/2000/svg",
15017
+ width: "16",
15018
+ height: "16",
15019
+ viewBox: "0 0 24 24",
15020
+ fill: "none",
15021
+ stroke: "currentColor",
15022
+ strokeWidth: "2",
15023
+ strokeLinecap: "round",
15024
+ strokeLinejoin: "round",
15025
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" })
15026
+ }
15027
+ )
15028
+ }
15029
+ )
15030
+ ] }),
15031
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl font-semibold", children: ":" }),
14964
15032
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
14965
15033
  /* @__PURE__ */ jsxRuntime.jsx(
14966
15034
  Button,
14967
15035
  {
14968
- variant: period === "AM" ? "default" : "outline",
14969
- size: "sm",
14970
- className: "w-14 h-12",
14971
- onClick: handlePeriodToggle,
15036
+ variant: "ghost",
15037
+ size: "icon",
15038
+ className: "h-8 w-8",
15039
+ onClick: incrementMinute,
14972
15040
  type: "button",
14973
- children: "AM"
15041
+ children: /* @__PURE__ */ jsxRuntime.jsx(
15042
+ "svg",
15043
+ {
15044
+ xmlns: "http://www.w3.org/2000/svg",
15045
+ width: "16",
15046
+ height: "16",
15047
+ viewBox: "0 0 24 24",
15048
+ fill: "none",
15049
+ stroke: "currentColor",
15050
+ strokeWidth: "2",
15051
+ strokeLinecap: "round",
15052
+ strokeLinejoin: "round",
15053
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" })
15054
+ }
15055
+ )
15056
+ }
15057
+ ),
15058
+ /* @__PURE__ */ jsxRuntime.jsx(
15059
+ Input,
15060
+ {
15061
+ type: "text",
15062
+ inputMode: "numeric",
15063
+ value: minutes,
15064
+ onChange: (e) => handleMinuteChange(e.target.value),
15065
+ className: "w-16 text-center",
15066
+ maxLength: 2
14974
15067
  }
14975
15068
  ),
14976
15069
  /* @__PURE__ */ jsxRuntime.jsx(
14977
15070
  Button,
14978
15071
  {
14979
- variant: period === "PM" ? "default" : "outline",
14980
- size: "sm",
14981
- className: "w-14 h-12",
14982
- onClick: handlePeriodToggle,
15072
+ variant: "ghost",
15073
+ size: "icon",
15074
+ className: "h-8 w-8",
15075
+ onClick: decrementMinute,
14983
15076
  type: "button",
14984
- children: "PM"
15077
+ children: /* @__PURE__ */ jsxRuntime.jsx(
15078
+ "svg",
15079
+ {
15080
+ xmlns: "http://www.w3.org/2000/svg",
15081
+ width: "16",
15082
+ height: "16",
15083
+ viewBox: "0 0 24 24",
15084
+ fill: "none",
15085
+ stroke: "currentColor",
15086
+ strokeWidth: "2",
15087
+ strokeLinecap: "round",
15088
+ strokeLinejoin: "round",
15089
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" })
15090
+ }
15091
+ )
14985
15092
  }
14986
15093
  )
15094
+ ] }),
15095
+ !use24Hour && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15096
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-16 bg-border mx-1" }),
15097
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
15098
+ /* @__PURE__ */ jsxRuntime.jsx(
15099
+ Button,
15100
+ {
15101
+ variant: period === "AM" ? "default" : "outline",
15102
+ size: "sm",
15103
+ className: "w-14 h-12",
15104
+ onClick: handlePeriodToggle,
15105
+ type: "button",
15106
+ children: "AM"
15107
+ }
15108
+ ),
15109
+ /* @__PURE__ */ jsxRuntime.jsx(
15110
+ Button,
15111
+ {
15112
+ variant: period === "PM" ? "default" : "outline",
15113
+ size: "sm",
15114
+ className: "w-14 h-12",
15115
+ onClick: handlePeriodToggle,
15116
+ type: "button",
15117
+ children: "PM"
15118
+ }
15119
+ )
15120
+ ] })
14987
15121
  ] })
14988
- ] })
14989
- ] }) })
15122
+ ] }) })
15123
+ ] }),
15124
+ showClear && /* @__PURE__ */ jsxRuntime.jsx(
15125
+ ClearButton,
15126
+ {
15127
+ onClick: handleClear,
15128
+ ariaLabel: clearAriaLabel,
15129
+ className: "absolute right-2 top-1/2 -translate-y-1/2"
15130
+ }
15131
+ )
14990
15132
  ] });
14991
15133
  }
14992
15134
  );
@@ -16173,7 +16315,7 @@ var useFieldNames = ({
16173
16315
  };
16174
16316
  var useFieldNames_default = useFieldNames;
16175
16317
  var ROW_HEIGHT = 32;
16176
- var MIN_HEIGHT_EMPTY = 76;
16318
+ var MIN_HEIGHT_EMPTY = 68;
16177
16319
  var VirtualizedCommand = ({
16178
16320
  name,
16179
16321
  height = 292,
@@ -16305,7 +16447,7 @@ var VirtualizedCommand = ({
16305
16447
  onMouseMove: () => setIsKeyboardNavActive(false),
16306
16448
  children: [
16307
16449
  /* @__PURE__ */ jsxRuntime.jsx(CommandEmpty, { "data-testid": `command-item-empty-${name}`, children: internalOptions.length === 0 ? emptyContent : notFoundContent }),
16308
- /* @__PURE__ */ jsxRuntime.jsx(
16450
+ filteredOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
16309
16451
  CommandGroup,
16310
16452
  {
16311
16453
  style: {
@@ -16409,7 +16551,7 @@ var NonVirtualizedCommand = ({
16409
16551
  },
16410
16552
  children: [
16411
16553
  /* @__PURE__ */ jsxRuntime.jsx(CommandEmpty, { "data-testid": `command-item-empty-${name}`, children: internalOptions.length === 0 ? emptyContent : notFoundContent }),
16412
- /* @__PURE__ */ jsxRuntime.jsx(CommandGroup, { children: filteredOptions.map((option, index) => {
16554
+ filteredOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(CommandGroup, { children: filteredOptions.map((option, index) => {
16413
16555
  const optionLabel = getLabelField(option);
16414
16556
  const optionValue = getValueField(option);
16415
16557
  const labelRendered = labelRender ? labelRender(option) : optionLabel;
@@ -16470,6 +16612,10 @@ var ComboboxInner = ({
16470
16612
  onOpenChange,
16471
16613
  onClear,
16472
16614
  virtual = true,
16615
+ isLoading,
16616
+ loadingContent,
16617
+ popoverProps,
16618
+ popoverContentProps,
16473
16619
  ...props
16474
16620
  }, ref) => {
16475
16621
  const { getLabelField, getValueField } = useFieldNames_default({ fieldNames });
@@ -16488,6 +16634,9 @@ var ComboboxInner = ({
16488
16634
  });
16489
16635
  }, [getValueField, options, selectedValue]);
16490
16636
  const renderValue = React__namespace.useMemo(() => {
16637
+ if (isLoading) {
16638
+ return loadingContent ?? "Loading...";
16639
+ }
16491
16640
  if (!selectedValue) return placeholder2;
16492
16641
  if (currentSelectedOption) {
16493
16642
  return getLabelField(currentSelectedOption);
@@ -16496,7 +16645,15 @@ var ComboboxInner = ({
16496
16645
  return selectedValue;
16497
16646
  }
16498
16647
  return null;
16499
- }, [currentSelectedOption, getLabelField, placeholder2, selectedValue, showValueWhenNoMatch]);
16648
+ }, [
16649
+ currentSelectedOption,
16650
+ getLabelField,
16651
+ placeholder2,
16652
+ selectedValue,
16653
+ showValueWhenNoMatch,
16654
+ isLoading,
16655
+ loadingContent
16656
+ ]);
16500
16657
  const handleSelect = React__namespace.useCallback(
16501
16658
  (selected, option) => {
16502
16659
  setSelectedValue(selected);
@@ -16512,13 +16669,13 @@ var ComboboxInner = ({
16512
16669
  );
16513
16670
  const handleOpenPopover = React__namespace.useCallback(
16514
16671
  (isOpen) => {
16515
- if (disabled) return;
16672
+ if (disabled || isLoading) return;
16516
16673
  setOpenPopover(isOpen);
16517
16674
  if (typeof onOpenChange === "function") {
16518
16675
  onOpenChange(isOpen);
16519
16676
  }
16520
16677
  },
16521
- [disabled, onOpenChange, setOpenPopover]
16678
+ [disabled, isLoading, onOpenChange, setOpenPopover]
16522
16679
  );
16523
16680
  const handleClear = React__namespace.useCallback(
16524
16681
  (event) => {
@@ -16530,7 +16687,7 @@ var ComboboxInner = ({
16530
16687
  },
16531
16688
  [currentSelectedOption, onClear, selectedValue, setSelectedValue]
16532
16689
  );
16533
- return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: openPopover, onOpenChange: handleOpenPopover, children: [
16690
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: openPopover, onOpenChange: handleOpenPopover, ...popoverProps, children: [
16534
16691
  /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
16535
16692
  "button",
16536
16693
  {
@@ -16549,7 +16706,8 @@ var ComboboxInner = ({
16549
16706
  className
16550
16707
  ),
16551
16708
  "data-state": openPopover ? "open" : "closed",
16552
- disabled,
16709
+ "data-loading": isLoading,
16710
+ disabled: disabled || isLoading,
16553
16711
  onFocus,
16554
16712
  onBlur,
16555
16713
  "data-testid": `combobox-trigger-${name}`,
@@ -16558,14 +16716,14 @@ var ComboboxInner = ({
16558
16716
  /* @__PURE__ */ jsxRuntime.jsx(
16559
16717
  truncated_default,
16560
16718
  {
16561
- className: cn(!selectedValue && "text-sus-gray-1"),
16719
+ className: cn(!selectedValue && !isLoading && "text-sus-gray-1"),
16562
16720
  tooltipProps: { disabled: true },
16563
16721
  children: renderValue
16564
16722
  }
16565
16723
  ),
16566
16724
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
16567
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "size-4 opacity-50" }),
16568
- showClear && selectedValue && /* @__PURE__ */ jsxRuntime.jsx(
16725
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "sm", className: "size-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "size-4 opacity-50" }),
16726
+ showClear && selectedValue && !isLoading && /* @__PURE__ */ jsxRuntime.jsx(
16569
16727
  "span",
16570
16728
  {
16571
16729
  role: "button",
@@ -16584,9 +16742,17 @@ var ComboboxInner = ({
16584
16742
  /* @__PURE__ */ jsxRuntime.jsx(
16585
16743
  PopoverContent,
16586
16744
  {
16587
- className: "p-0 w-(--radix-popper-anchor-width) min-w-(--radix-popper-anchor-width) max-w-(--radix-popper-available-width) rounded-md",
16588
16745
  align: "start",
16589
16746
  sideOffset: 4,
16747
+ ...popoverContentProps,
16748
+ onWheel: (e) => {
16749
+ e.stopPropagation();
16750
+ popoverContentProps?.onWheel?.(e);
16751
+ },
16752
+ className: cn(
16753
+ "p-0 w-(--radix-popper-anchor-width) min-w-(--radix-popper-anchor-width) max-w-(--radix-popper-available-width) rounded-md",
16754
+ popoverContentProps?.className
16755
+ ),
16590
16756
  children: virtual ? /* @__PURE__ */ jsxRuntime.jsx(
16591
16757
  VirtualizedCommand_default,
16592
16758
  {
@@ -17152,6 +17318,65 @@ function MentionsPlugin2({
17152
17318
  });
17153
17319
  });
17154
17320
  }, [editor, mentions, mentionKeyMap, mentionConfig]);
17321
+ React.useEffect(() => {
17322
+ if (mentions.length === 0) return;
17323
+ return editor.registerCommand(
17324
+ lexical.PASTE_COMMAND,
17325
+ (event) => {
17326
+ const clipboardData = event.clipboardData;
17327
+ if (!clipboardData) return false;
17328
+ const pastedText = clipboardData.getData("text/plain");
17329
+ if (!pastedText) return false;
17330
+ const variableMatches = [...pastedText.matchAll(COMPLETE_VARIABLE_REGEX2)];
17331
+ const hasKnownVariable = variableMatches.some((match) => {
17332
+ const variableName = match[1];
17333
+ const fullMatch = match[0];
17334
+ return mentionKeyMap.has(variableName.toUpperCase()) || mentionKeyMap.has(fullMatch.toUpperCase());
17335
+ });
17336
+ if (!hasKnownVariable) return false;
17337
+ event.preventDefault();
17338
+ editor.update(
17339
+ () => {
17340
+ const selection = lexical.$getSelection();
17341
+ if (!lexical.$isRangeSelection(selection)) return;
17342
+ let lastIndex = 0;
17343
+ const nodesToInsert = [];
17344
+ for (const match of variableMatches) {
17345
+ const fullMatch = match[0];
17346
+ const variableName = match[1];
17347
+ const matchIndex = match.index;
17348
+ if (matchIndex > lastIndex) {
17349
+ const textBefore = pastedText.slice(lastIndex, matchIndex);
17350
+ nodesToInsert.push(lexical.$createTextNode(textBefore));
17351
+ }
17352
+ const mentionItem = mentionKeyMap.get(variableName.toUpperCase()) || mentionKeyMap.get(fullMatch.toUpperCase());
17353
+ if (mentionItem) {
17354
+ nodesToInsert.push(
17355
+ $createMentionNode2(
17356
+ mentionConfig.getName(mentionItem),
17357
+ mentionConfig.getLabel(mentionItem)
17358
+ )
17359
+ );
17360
+ nodesToInsert.push(lexical.$createTextNode(" "));
17361
+ } else {
17362
+ nodesToInsert.push(lexical.$createTextNode(fullMatch));
17363
+ }
17364
+ lastIndex = matchIndex + fullMatch.length;
17365
+ }
17366
+ if (lastIndex < pastedText.length) {
17367
+ nodesToInsert.push(lexical.$createTextNode(pastedText.slice(lastIndex)));
17368
+ }
17369
+ for (const node of nodesToInsert) {
17370
+ selection.insertNodes([node]);
17371
+ }
17372
+ },
17373
+ { tag: "paste-mention-conversion" }
17374
+ );
17375
+ return true;
17376
+ },
17377
+ lexical.COMMAND_PRIORITY_LOW
17378
+ );
17379
+ }, [editor, mentions, mentionKeyMap, mentionConfig]);
17155
17380
  const options = React.useMemo(() => {
17156
17381
  if (!queryString) {
17157
17382
  return mentions.map(
@@ -17295,6 +17520,41 @@ function OnChangePluginFiltered2({
17295
17520
  }, [editor, onChange, ignoreSelectionChange]);
17296
17521
  return null;
17297
17522
  }
17523
+ function MaxLengthPlugin2({ maxLength }) {
17524
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
17525
+ React.useEffect(() => {
17526
+ if (!maxLength) return;
17527
+ let lastRestoredTextLength = 0;
17528
+ return editor.registerNodeTransform(lexical.RootNode, (rootNode) => {
17529
+ const selection$1 = lexical.$getSelection();
17530
+ if (!lexical.$isRangeSelection(selection$1) || !selection$1.isCollapsed()) {
17531
+ return;
17532
+ }
17533
+ const prevTextLength = lastRestoredTextLength;
17534
+ const textLength = rootNode.getTextContentSize();
17535
+ if (prevTextLength !== textLength) {
17536
+ const delCount = textLength - maxLength;
17537
+ const anchor = selection$1.anchor;
17538
+ if (delCount > 0) {
17539
+ if (prevTextLength === maxLength && lastRestoredTextLength !== maxLength) {
17540
+ lastRestoredTextLength = maxLength;
17541
+ $restoreTextContent2(anchor);
17542
+ } else {
17543
+ selection.trimTextContentFromAnchor(editor, anchor, delCount);
17544
+ }
17545
+ }
17546
+ }
17547
+ });
17548
+ }, [editor, maxLength]);
17549
+ return null;
17550
+ }
17551
+ function $restoreTextContent2(anchor) {
17552
+ const selection = lexical.$getSelection();
17553
+ if (lexical.$isRangeSelection(selection)) {
17554
+ selection.anchor.set(anchor.key, anchor.offset, "text");
17555
+ selection.focus.set(anchor.key, anchor.offset, "text");
17556
+ }
17557
+ }
17298
17558
  var theme2 = {
17299
17559
  paragraph: "leading-6 text-sm text-foreground",
17300
17560
  placeholder: "pointer-events-none absolute left-0 top-0 px-3 py-2 text-sm text-muted-foreground select-none"
@@ -17308,6 +17568,7 @@ function InputMentionInner({
17308
17568
  disabled,
17309
17569
  editorClassName,
17310
17570
  autoFocus,
17571
+ maxLength,
17311
17572
  mentions,
17312
17573
  mentionConfig,
17313
17574
  mentionTrigger,
@@ -17404,7 +17665,8 @@ function InputMentionInner({
17404
17665
  mentionConfig: finalMentionConfig
17405
17666
  }
17406
17667
  ),
17407
- /* @__PURE__ */ jsxRuntime.jsx(OnChangePluginFiltered2, { onChange: handleChange, ignoreSelectionChange: true })
17668
+ /* @__PURE__ */ jsxRuntime.jsx(OnChangePluginFiltered2, { onChange: handleChange, ignoreSelectionChange: true }),
17669
+ maxLength && /* @__PURE__ */ jsxRuntime.jsx(MaxLengthPlugin2, { maxLength })
17408
17670
  ]
17409
17671
  }
17410
17672
  ) });