@octaviaflow/core 3.0.18-beta.20 → 3.0.18-beta.22

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/workflow.js CHANGED
@@ -617,8 +617,238 @@ function ConfigPanel({
617
617
  );
618
618
  }
619
619
 
620
+ // src/workflow/components/FxPanel/FxPanel.tsx
621
+ import {
622
+ useMemo as useMemo2,
623
+ useState as useState4
624
+ } from "react";
625
+ import {
626
+ ChevronRightIcon,
627
+ CloseIcon,
628
+ SearchIcon
629
+ } from "@octaviaflow/icons";
630
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
631
+ var KIND_GLYPH = {
632
+ function: "\u0192",
633
+ variable: "\u2B21"
634
+ };
635
+ function FxPanel({
636
+ categories,
637
+ title = "FX / IO",
638
+ hint = /* @__PURE__ */ jsxs2(Fragment2, { children: [
639
+ "Drag items into any input field with the ",
640
+ /* @__PURE__ */ jsx2("strong", { children: "FX" }),
641
+ " indicator"
642
+ ] }),
643
+ search: controlledSearch,
644
+ defaultSearch = "",
645
+ onSearchChange,
646
+ expandedCategory: controlledExpanded,
647
+ defaultExpandedCategory,
648
+ onExpandedCategoryChange,
649
+ onClose,
650
+ onItemDragStart,
651
+ onItemSelect,
652
+ width = 292,
653
+ emptyLabel = "No matches",
654
+ searchPlaceholder = "Search functions & variables\u2026",
655
+ className,
656
+ style
657
+ }) {
658
+ const [internalSearch, setInternalSearch] = useState4(defaultSearch);
659
+ const search = controlledSearch ?? internalSearch;
660
+ const setSearch = (next) => {
661
+ if (controlledSearch === void 0) setInternalSearch(next);
662
+ onSearchChange?.(next);
663
+ };
664
+ const [internalExpanded, setInternalExpanded] = useState4(
665
+ defaultExpandedCategory !== void 0 ? defaultExpandedCategory : categories.find((c) => c.items.length > 0)?.id ?? null
666
+ );
667
+ const expanded = controlledExpanded ?? internalExpanded;
668
+ const setExpanded = (id) => {
669
+ if (controlledExpanded === void 0) setInternalExpanded(id);
670
+ onExpandedCategoryChange?.(id);
671
+ };
672
+ const [draggingId, setDraggingId] = useState4(null);
673
+ const filtered = useMemo2(() => {
674
+ const q = search.trim().toLowerCase();
675
+ const matched = q ? categories.map((cat) => {
676
+ if (cat.label.toLowerCase().includes(q)) return cat;
677
+ const items = cat.items.filter(
678
+ (it) => it.label.toLowerCase().includes(q) || (it.description?.toLowerCase().includes(q) ?? false)
679
+ );
680
+ return { ...cat, items };
681
+ }) : categories;
682
+ return matched.filter((cat) => cat.items.length > 0);
683
+ }, [categories, search]);
684
+ const hasResults = filtered.length > 0;
685
+ const handleDragStart = (item, category) => (e) => {
686
+ setDraggingId(item.id);
687
+ if (onItemDragStart) {
688
+ onItemDragStart(item, category, e);
689
+ } else {
690
+ e.dataTransfer.setData("text/plain", item.insertValue);
691
+ e.dataTransfer.setData("application/x-fx-insert", item.insertValue);
692
+ e.dataTransfer.setData(
693
+ "application/x-fx-type",
694
+ category.kind ?? "function"
695
+ );
696
+ e.dataTransfer.effectAllowed = "copy";
697
+ }
698
+ };
699
+ return /* @__PURE__ */ jsxs2(
700
+ "aside",
701
+ {
702
+ className: cn("ods-flow-fx-panel", className),
703
+ style: { width, ...style },
704
+ "aria-label": typeof title === "string" ? title : "FX / IO",
705
+ children: [
706
+ /* @__PURE__ */ jsxs2("header", { className: "ods-flow-fx-panel__header", children: [
707
+ /* @__PURE__ */ jsx2("h3", { className: "ods-flow-fx-panel__title", children: title }),
708
+ onClose && /* @__PURE__ */ jsx2(
709
+ "button",
710
+ {
711
+ type: "button",
712
+ className: "ods-flow-fx-panel__close",
713
+ onClick: onClose,
714
+ "aria-label": "Close FX panel",
715
+ children: /* @__PURE__ */ jsx2(CloseIcon, { size: "sm" })
716
+ }
717
+ )
718
+ ] }),
719
+ /* @__PURE__ */ jsxs2("div", { className: "ods-flow-fx-panel__search", children: [
720
+ /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__search-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SearchIcon, { size: "sm" }) }),
721
+ /* @__PURE__ */ jsx2(
722
+ "input",
723
+ {
724
+ type: "text",
725
+ className: "ods-flow-fx-panel__search-input",
726
+ placeholder: searchPlaceholder,
727
+ "aria-label": "Search functions and variables",
728
+ value: search,
729
+ onChange: (e) => setSearch(e.target.value)
730
+ }
731
+ ),
732
+ search && /* @__PURE__ */ jsx2(
733
+ "button",
734
+ {
735
+ type: "button",
736
+ className: "ods-flow-fx-panel__search-clear",
737
+ onClick: () => setSearch(""),
738
+ "aria-label": "Clear search",
739
+ children: /* @__PURE__ */ jsx2(CloseIcon, { size: "sm" })
740
+ }
741
+ )
742
+ ] }),
743
+ hint && /* @__PURE__ */ jsx2("p", { className: "ods-flow-fx-panel__hint", children: hint }),
744
+ /* @__PURE__ */ jsxs2("div", { className: "ods-flow-fx-panel__list", children: [
745
+ filtered.map((cat) => {
746
+ const isOpen = expanded === cat.id;
747
+ const kind = cat.kind ?? "function";
748
+ return /* @__PURE__ */ jsxs2("div", { className: "ods-flow-fx-panel__category", children: [
749
+ /* @__PURE__ */ jsxs2(
750
+ "button",
751
+ {
752
+ type: "button",
753
+ className: "ods-flow-fx-panel__category-header",
754
+ "aria-expanded": isOpen,
755
+ onClick: () => setExpanded(isOpen ? null : cat.id),
756
+ children: [
757
+ /* @__PURE__ */ jsx2(
758
+ ChevronRightIcon,
759
+ {
760
+ size: "sm",
761
+ className: cn(
762
+ "ods-flow-fx-panel__chevron",
763
+ isOpen && "ods-flow-fx-panel__chevron--open"
764
+ )
765
+ }
766
+ ),
767
+ /* @__PURE__ */ jsxs2(
768
+ "span",
769
+ {
770
+ className: cn(
771
+ "ods-flow-fx-panel__badge",
772
+ `ods-flow-fx-panel__badge--${kind}`
773
+ ),
774
+ children: [
775
+ /* @__PURE__ */ jsx2(
776
+ "span",
777
+ {
778
+ className: "ods-flow-fx-panel__badge-glyph",
779
+ "aria-hidden": "true",
780
+ children: KIND_GLYPH[kind]
781
+ }
782
+ ),
783
+ cat.label
784
+ ]
785
+ }
786
+ ),
787
+ /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__count", children: cat.items.length })
788
+ ]
789
+ }
790
+ ),
791
+ isOpen && /* @__PURE__ */ jsx2("div", { className: "ods-flow-fx-panel__items", children: cat.items.map((item) => /* @__PURE__ */ jsxs2(
792
+ "div",
793
+ {
794
+ className: cn(
795
+ "ods-flow-fx-panel__item",
796
+ draggingId === item.id && "ods-flow-fx-panel__item--dragging"
797
+ ),
798
+ draggable: true,
799
+ title: item.insertValue,
800
+ onDragStart: handleDragStart(item, cat),
801
+ onDragEnd: () => setDraggingId(null),
802
+ onClick: onItemSelect ? () => onItemSelect(item, cat) : void 0,
803
+ children: [
804
+ /* @__PURE__ */ jsx2("code", { className: "ods-flow-fx-panel__item-label", children: item.label }),
805
+ item.description && /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__item-desc", children: item.description })
806
+ ]
807
+ },
808
+ item.id
809
+ )) })
810
+ ] }, cat.id);
811
+ }),
812
+ !hasResults && /* @__PURE__ */ jsx2("div", { className: "ods-flow-fx-panel__empty", children: emptyLabel })
813
+ ] })
814
+ ]
815
+ }
816
+ );
817
+ }
818
+
819
+ // src/workflow/components/FxPanel/FxToggleButton.tsx
820
+ import { FunctionIcon } from "@octaviaflow/icons";
821
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
822
+ function FxToggleButton({
823
+ active = false,
824
+ label = "FX",
825
+ className,
826
+ type = "button",
827
+ ...rest
828
+ }) {
829
+ return /* @__PURE__ */ jsxs3(
830
+ "button",
831
+ {
832
+ ...rest,
833
+ type,
834
+ className: cn(
835
+ "ods-flow-fx-toggle",
836
+ active && "ods-flow-fx-toggle--active",
837
+ label == null && "ods-flow-fx-toggle--icon-only",
838
+ className
839
+ ),
840
+ "aria-pressed": active,
841
+ title: active ? "Hide FX / IO" : "Show FX / IO",
842
+ children: [
843
+ /* @__PURE__ */ jsx3(FunctionIcon, { size: "sm", className: "ods-flow-fx-toggle__icon" }),
844
+ label != null && /* @__PURE__ */ jsx3("span", { className: "ods-flow-fx-toggle__label", children: label })
845
+ ]
846
+ }
847
+ );
848
+ }
849
+
620
850
  // src/workflow/components/NodeToolbar/NodeToolbar.tsx
621
- import { jsx as jsx2 } from "react/jsx-runtime";
851
+ import { jsx as jsx4 } from "react/jsx-runtime";
622
852
  function NodeToolbar({
623
853
  isVisible,
624
854
  position = "top",
@@ -633,7 +863,7 @@ function NodeToolbar({
633
863
  const show = isVisible ?? node.selected;
634
864
  if (!show) return null;
635
865
  const inverseScale = 1 / viewport.zoom;
636
- return /* @__PURE__ */ jsx2(
866
+ return /* @__PURE__ */ jsx4(
637
867
  "div",
638
868
  {
639
869
  className: cn("ods-node-toolbar", `ods-node-toolbar--${position}`, className),
@@ -645,7 +875,7 @@ function NodeToolbar({
645
875
  onPointerDown: (e) => e.stopPropagation(),
646
876
  onMouseDown: (e) => e.stopPropagation(),
647
877
  onClick: (e) => e.stopPropagation(),
648
- children: /* @__PURE__ */ jsx2(
878
+ children: /* @__PURE__ */ jsx4(
649
879
  "div",
650
880
  {
651
881
  className: "ods-node-toolbar__inner",
@@ -798,6 +1028,8 @@ export {
798
1028
  FlowEdge,
799
1029
  FlowNode,
800
1030
  ForEachNode,
1031
+ FxPanel,
1032
+ FxToggleButton,
801
1033
  GroupNode,
802
1034
  Handle,
803
1035
  HttpRequestNode,