@particle-academy/react-fancy 2.5.0 → 2.7.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
@@ -1,4 +1,4 @@
1
- import { forwardRef, useId, useRef, useEffect, useState, useCallback, useMemo, createContext, Children, isValidElement, cloneElement, useLayoutEffect, useContext, Fragment as Fragment$1 } from 'react';
1
+ import { forwardRef, createContext, useId, useRef, useEffect, useState, useCallback, useMemo, Children, isValidElement, cloneElement, useLayoutEffect, useContext, Fragment as Fragment$1 } from 'react';
2
2
  import { clsx } from 'clsx';
3
3
  import { twMerge } from 'tailwind-merge';
4
4
  import * as LucideIcons from 'lucide-react';
@@ -2556,6 +2556,288 @@ var Action = forwardRef(
2556
2556
  }
2557
2557
  );
2558
2558
  Action.displayName = "Action";
2559
+ function useControllableState(controlledValue, defaultValue, onChange) {
2560
+ const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
2561
+ const isControlled = controlledValue !== void 0;
2562
+ const value = isControlled ? controlledValue : uncontrolledValue;
2563
+ const onChangeRef = useRef(onChange);
2564
+ onChangeRef.current = onChange;
2565
+ const setValue = useCallback(
2566
+ (next) => {
2567
+ const nextValue = typeof next === "function" ? next(value) : next;
2568
+ if (!isControlled) {
2569
+ setUncontrolledValue(nextValue);
2570
+ }
2571
+ onChangeRef.current?.(nextValue);
2572
+ },
2573
+ [isControlled, value]
2574
+ );
2575
+ return [value, setValue];
2576
+ }
2577
+ var AccordionPanelContext = createContext(null);
2578
+ function useAccordionPanel() {
2579
+ const ctx = useContext(AccordionPanelContext);
2580
+ if (!ctx) {
2581
+ throw new Error(
2582
+ "AccordionPanel components must be used inside <AccordionPanel>"
2583
+ );
2584
+ }
2585
+ return ctx;
2586
+ }
2587
+ var AccordionSectionContext = createContext(null);
2588
+ function useAccordionSection() {
2589
+ const ctx = useContext(AccordionSectionContext);
2590
+ if (!ctx) {
2591
+ throw new Error(
2592
+ "<AccordionPanel.Trigger> must be rendered inside <AccordionPanel.Section>"
2593
+ );
2594
+ }
2595
+ return ctx;
2596
+ }
2597
+ function AccordionPanelSection({
2598
+ id,
2599
+ pinned = false,
2600
+ className,
2601
+ openClassName,
2602
+ closedClassName,
2603
+ unstyled,
2604
+ children
2605
+ }) {
2606
+ const panel = useAccordionPanel();
2607
+ const { orientation, isOpen, toggle, registerSection } = panel;
2608
+ useEffect(() => registerSection(id), [id, registerSection]);
2609
+ const open = pinned || isOpen(id);
2610
+ const sectionCtx = useMemo(
2611
+ () => ({
2612
+ id,
2613
+ open,
2614
+ pinned,
2615
+ orientation,
2616
+ toggle: () => toggle(id)
2617
+ }),
2618
+ [id, open, pinned, orientation, toggle]
2619
+ );
2620
+ return /* @__PURE__ */ jsx(AccordionSectionContext.Provider, { value: sectionCtx, children: /* @__PURE__ */ jsx(
2621
+ "div",
2622
+ {
2623
+ "data-react-fancy-accordion-section": "",
2624
+ "data-state": open ? "open" : "closed",
2625
+ "data-pinned": pinned ? "" : void 0,
2626
+ "data-orientation": orientation,
2627
+ className: cn(
2628
+ "flex shrink-0",
2629
+ !unstyled && "items-center gap-1",
2630
+ orientation === "horizontal" ? "flex-row" : "flex-col",
2631
+ className,
2632
+ open ? openClassName : closedClassName
2633
+ ),
2634
+ children
2635
+ }
2636
+ ) });
2637
+ }
2638
+ AccordionPanelSection.displayName = "AccordionPanelSection";
2639
+ function renderSlot(slot, state) {
2640
+ return typeof slot === "function" ? slot(state) : slot;
2641
+ }
2642
+ function AccordionPanelTrigger({
2643
+ children,
2644
+ className,
2645
+ "aria-label": ariaLabel
2646
+ }) {
2647
+ const { id, open, orientation, toggle } = useAccordionSection();
2648
+ const state = { id, open, orientation, toggle };
2649
+ if (children !== void 0) {
2650
+ return /* @__PURE__ */ jsx(
2651
+ "div",
2652
+ {
2653
+ "data-react-fancy-accordion-trigger": "",
2654
+ "data-state": open ? "open" : "closed",
2655
+ "data-orientation": orientation,
2656
+ className,
2657
+ children: renderSlot(children, state)
2658
+ }
2659
+ );
2660
+ }
2661
+ if (open) {
2662
+ return /* @__PURE__ */ jsx(
2663
+ "button",
2664
+ {
2665
+ type: "button",
2666
+ onClick: toggle,
2667
+ "aria-label": ariaLabel ?? "Collapse section",
2668
+ "data-react-fancy-accordion-trigger": "",
2669
+ "data-state": "open",
2670
+ "data-orientation": orientation,
2671
+ className: cn(
2672
+ "group relative flex shrink-0 items-center justify-center cursor-pointer",
2673
+ "text-zinc-500 dark:text-zinc-500",
2674
+ "hover:text-zinc-900 dark:hover:text-zinc-100",
2675
+ // Wide hitbox + thin visible line (drawn via ::before).
2676
+ orientation === "horizontal" ? "w-3 self-stretch" : "h-3 self-stretch",
2677
+ "before:absolute before:bg-zinc-200 dark:before:bg-zinc-700",
2678
+ "before:transition-colors group-hover:before:bg-zinc-400 dark:group-hover:before:bg-zinc-500",
2679
+ orientation === "horizontal" ? "before:top-0 before:bottom-0 before:w-px before:left-1/2 before:-translate-x-1/2" : "before:left-0 before:right-0 before:h-px before:top-1/2 before:-translate-y-1/2",
2680
+ "transition-colors",
2681
+ className
2682
+ ),
2683
+ children: /* @__PURE__ */ jsx(
2684
+ ChevronIcon,
2685
+ {
2686
+ orientation,
2687
+ purpose: "collapse",
2688
+ className: "relative opacity-0 group-hover:opacity-100 transition-opacity"
2689
+ }
2690
+ )
2691
+ }
2692
+ );
2693
+ }
2694
+ return /* @__PURE__ */ jsx(
2695
+ "button",
2696
+ {
2697
+ type: "button",
2698
+ onClick: toggle,
2699
+ "aria-label": ariaLabel ?? "Expand section",
2700
+ "data-react-fancy-accordion-trigger": "",
2701
+ "data-state": "closed",
2702
+ "data-orientation": orientation,
2703
+ className: cn(
2704
+ "flex shrink-0 items-center justify-center rounded-md",
2705
+ "text-zinc-400 dark:text-zinc-500",
2706
+ "hover:text-zinc-900 dark:hover:text-zinc-100",
2707
+ "hover:bg-zinc-100 dark:hover:bg-zinc-800",
2708
+ "transition-colors cursor-pointer",
2709
+ orientation === "horizontal" ? "h-8 w-6 mx-0.5" : "w-8 h-6 my-0.5",
2710
+ className
2711
+ ),
2712
+ children: /* @__PURE__ */ jsx(ChevronIcon, { orientation, purpose: "expand" })
2713
+ }
2714
+ );
2715
+ }
2716
+ AccordionPanelTrigger.displayName = "AccordionPanelTrigger";
2717
+ function ChevronIcon({
2718
+ orientation,
2719
+ purpose,
2720
+ className
2721
+ }) {
2722
+ const transform = orientation === "horizontal" ? "rotate(180deg)" : purpose === "expand" ? "rotate(90deg)" : "rotate(270deg)";
2723
+ return /* @__PURE__ */ jsx(
2724
+ "svg",
2725
+ {
2726
+ viewBox: "0 0 16 16",
2727
+ width: "12",
2728
+ height: "12",
2729
+ fill: "none",
2730
+ stroke: "currentColor",
2731
+ strokeWidth: "2",
2732
+ strokeLinecap: "round",
2733
+ strokeLinejoin: "round",
2734
+ style: { transform },
2735
+ className,
2736
+ "aria-hidden": "true",
2737
+ children: /* @__PURE__ */ jsx("polyline", { points: "6 4 10 8 6 12" })
2738
+ }
2739
+ );
2740
+ }
2741
+ function AccordionPanelContent({
2742
+ children,
2743
+ className,
2744
+ unstyled
2745
+ }) {
2746
+ const { open, orientation } = useAccordionSection();
2747
+ if (!open) return null;
2748
+ return /* @__PURE__ */ jsx(
2749
+ "div",
2750
+ {
2751
+ "data-react-fancy-accordion-content": "",
2752
+ "data-orientation": orientation,
2753
+ className: cn(
2754
+ !unstyled && "flex items-center gap-1",
2755
+ !unstyled && (orientation === "horizontal" ? "flex-row" : "flex-col"),
2756
+ className
2757
+ ),
2758
+ children
2759
+ }
2760
+ );
2761
+ }
2762
+ AccordionPanelContent.displayName = "AccordionPanelContent";
2763
+ function AccordionPanelRoot({
2764
+ orientation = "horizontal",
2765
+ value: controlledValue,
2766
+ defaultValue,
2767
+ onValueChange,
2768
+ className,
2769
+ children
2770
+ }) {
2771
+ const [openIds, setOpenIds] = useControllableState(
2772
+ controlledValue,
2773
+ defaultValue ?? [],
2774
+ onValueChange
2775
+ );
2776
+ const openSet = useMemo(() => new Set(openIds), [openIds]);
2777
+ const isOpen = useCallback((id) => openSet.has(id), [openSet]);
2778
+ const open = useCallback(
2779
+ (id) => {
2780
+ setOpenIds(openSet.has(id) ? openIds ?? [] : [...openIds ?? [], id]);
2781
+ },
2782
+ [openSet, openIds, setOpenIds]
2783
+ );
2784
+ const close = useCallback(
2785
+ (id) => {
2786
+ setOpenIds((openIds ?? []).filter((x) => x !== id));
2787
+ },
2788
+ [openIds, setOpenIds]
2789
+ );
2790
+ const toggle = useCallback(
2791
+ (id) => {
2792
+ setOpenIds(
2793
+ openSet.has(id) ? (openIds ?? []).filter((x) => x !== id) : [...openIds ?? [], id]
2794
+ );
2795
+ },
2796
+ [openSet, openIds, setOpenIds]
2797
+ );
2798
+ const [sectionIds, setSectionIds] = useState([]);
2799
+ const orderRef = useRef([]);
2800
+ const registerSection = useCallback((id) => {
2801
+ if (!orderRef.current.includes(id)) {
2802
+ orderRef.current = [...orderRef.current, id];
2803
+ setSectionIds(orderRef.current);
2804
+ }
2805
+ return () => {
2806
+ orderRef.current = orderRef.current.filter((x) => x !== id);
2807
+ setSectionIds(orderRef.current);
2808
+ };
2809
+ }, []);
2810
+ const ctx = useMemo(
2811
+ () => ({
2812
+ orientation,
2813
+ isOpen,
2814
+ toggle,
2815
+ open,
2816
+ close,
2817
+ sectionIds,
2818
+ registerSection
2819
+ }),
2820
+ [orientation, isOpen, toggle, open, close, sectionIds, registerSection]
2821
+ );
2822
+ return /* @__PURE__ */ jsx(AccordionPanelContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
2823
+ "div",
2824
+ {
2825
+ "data-react-fancy-accordion-panel": "",
2826
+ "data-orientation": orientation,
2827
+ className: cn(
2828
+ "inline-flex items-stretch",
2829
+ orientation === "horizontal" ? "flex-row" : "flex-col",
2830
+ className
2831
+ ),
2832
+ children
2833
+ }
2834
+ ) });
2835
+ }
2836
+ AccordionPanelRoot.displayName = "AccordionPanel";
2837
+ var AccordionPanel = AccordionPanelRoot;
2838
+ AccordionPanel.Section = AccordionPanelSection;
2839
+ AccordionPanel.Trigger = AccordionPanelTrigger;
2840
+ AccordionPanel.Content = AccordionPanelContent;
2559
2841
 
2560
2842
  // src/components/inputs/inputs.utils.ts
2561
2843
  var inputSizeClasses = {
@@ -3442,24 +3724,6 @@ var Select = forwardRef(
3442
3724
  }
3443
3725
  );
3444
3726
  Select.displayName = "Select";
3445
- function useControllableState(controlledValue, defaultValue, onChange) {
3446
- const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
3447
- const isControlled = controlledValue !== void 0;
3448
- const value = isControlled ? controlledValue : uncontrolledValue;
3449
- const onChangeRef = useRef(onChange);
3450
- onChangeRef.current = onChange;
3451
- const setValue = useCallback(
3452
- (next) => {
3453
- const nextValue = typeof next === "function" ? next(value) : next;
3454
- if (!isControlled) {
3455
- setUncontrolledValue(nextValue);
3456
- }
3457
- onChangeRef.current?.(nextValue);
3458
- },
3459
- [isControlled, value]
3460
- );
3461
- return [value, setValue];
3462
- }
3463
3727
  var Checkbox = forwardRef(
3464
3728
  ({
3465
3729
  size = "md",
@@ -10849,11 +11113,13 @@ var KanbanColumnContext = createContext("");
10849
11113
  function useKanbanColumn() {
10850
11114
  return useContext(KanbanColumnContext);
10851
11115
  }
11116
+ var DEFAULT_COLUMN_CLASSES = "min-h-[200px] w-72 rounded-xl bg-zinc-50 p-3 dark:bg-zinc-800/50";
10852
11117
  function KanbanColumn({
10853
11118
  children,
10854
11119
  id,
10855
11120
  title,
10856
- className
11121
+ className,
11122
+ unstyled
10857
11123
  }) {
10858
11124
  const { onCardMove, draggedCard, dragSource } = useKanban();
10859
11125
  const [dragOver, setDragOver] = useState(false);
@@ -10880,7 +11146,8 @@ function KanbanColumn({
10880
11146
  onDragOver: handleDragOver,
10881
11147
  onDragLeave: handleDragLeave,
10882
11148
  className: cn(
10883
- "flex min-h-[200px] w-72 flex-col rounded-xl bg-zinc-50 p-3 dark:bg-zinc-800/50",
11149
+ "flex flex-col",
11150
+ !unstyled && DEFAULT_COLUMN_CLASSES,
10884
11151
  dragOver && "ring-2 ring-blue-400 ring-inset",
10885
11152
  className
10886
11153
  ),
@@ -10892,7 +11159,8 @@ function KanbanColumn({
10892
11159
  ) });
10893
11160
  }
10894
11161
  KanbanColumn.displayName = "KanbanColumn";
10895
- function KanbanCard({ children, id, className }) {
11162
+ var DEFAULT_CARD_CLASSES = "rounded-lg border border-zinc-200 bg-white p-3 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-700 dark:bg-zinc-900";
11163
+ function KanbanCard({ children, id, className, unstyled }) {
10896
11164
  const { setDraggedCard, setDragSource } = useKanban();
10897
11165
  const columnId = useKanbanColumn();
10898
11166
  const handleDragStart = useCallback(() => {
@@ -10911,7 +11179,9 @@ function KanbanCard({ children, id, className }) {
10911
11179
  onDragStart: handleDragStart,
10912
11180
  onDragEnd: handleDragEnd,
10913
11181
  className: cn(
10914
- "cursor-grab rounded-lg border border-zinc-200 bg-white p-3 shadow-sm transition-shadow hover:shadow-md active:cursor-grabbing dark:border-zinc-700 dark:bg-zinc-900",
11182
+ // Drag affordance kept even when unstyled so users still see grab cursors.
11183
+ "cursor-grab active:cursor-grabbing",
11184
+ !unstyled && DEFAULT_CARD_CLASSES,
10915
11185
  className
10916
11186
  ),
10917
11187
  children
@@ -11593,7 +11863,7 @@ function DiagramToolbar({ className }) {
11593
11863
  const canImport = importableRef.current;
11594
11864
  const handleDownload = useCallback(
11595
11865
  async (format) => {
11596
- const { serializeToERD, serializeToUML, serializeToDFD } = await import('./diagram.serializers-OK4HP7AB.js');
11866
+ const { serializeToERD, serializeToUML, serializeToDFD } = await import('./diagram.serializers-6RPUO46U.js');
11597
11867
  let content;
11598
11868
  switch (format) {
11599
11869
  case "erd":
@@ -11624,7 +11894,7 @@ function DiagramToolbar({ className }) {
11624
11894
  if (!file || !onImport) return;
11625
11895
  const text = await file.text();
11626
11896
  const ext = file.name.split(".").pop()?.toLowerCase();
11627
- const { deserializeSchema } = await import('./diagram.serializers-OK4HP7AB.js');
11897
+ const { deserializeSchema } = await import('./diagram.serializers-6RPUO46U.js');
11628
11898
  let format = "erd";
11629
11899
  if (ext === "puml" || ext === "uml") format = "uml";
11630
11900
  else if (ext === "dfd") format = "dfd";
@@ -11692,9 +11962,12 @@ var VERTICAL_GAP = 60;
11692
11962
  function getEntityHeight(fieldCount) {
11693
11963
  return HEADER_HEIGHT2 + Math.max(fieldCount, 1) * FIELD_HEIGHT2;
11694
11964
  }
11965
+ function resolveEntityId(entity) {
11966
+ return entity.id ?? entity.name;
11967
+ }
11695
11968
  function computeDiagramLayout(schema) {
11696
11969
  const positions = /* @__PURE__ */ new Map();
11697
- const entityIds = new Set(schema.entities.map((e) => e.id));
11970
+ const entityIds = new Set(schema.entities.map(resolveEntityId));
11698
11971
  const incoming = /* @__PURE__ */ new Map();
11699
11972
  for (const id of entityIds) {
11700
11973
  incoming.set(id, /* @__PURE__ */ new Set());
@@ -11715,7 +11988,7 @@ function computeDiagramLayout(schema) {
11715
11988
  }
11716
11989
  }
11717
11990
  if (queue.length === 0 && entityIds.size > 0) {
11718
- const firstId = schema.entities[0].id;
11991
+ const firstId = resolveEntityId(schema.entities[0]);
11719
11992
  rowAssignment.set(firstId, 0);
11720
11993
  assigned.add(firstId);
11721
11994
  queue.push(firstId);
@@ -11755,7 +12028,7 @@ function computeDiagramLayout(schema) {
11755
12028
  }
11756
12029
  const fieldCounts = /* @__PURE__ */ new Map();
11757
12030
  for (const entity of schema.entities) {
11758
- fieldCounts.set(entity.id, entity.fields?.length ?? 0);
12031
+ fieldCounts.set(resolveEntityId(entity), entity.fields?.length ?? 0);
11759
12032
  }
11760
12033
  const sortedRows = Array.from(rows.keys()).sort((a, b) => a - b);
11761
12034
  let currentY = 0;
@@ -11952,7 +12225,7 @@ function FolderIcon({ open }) {
11952
12225
  }
11953
12226
  return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "shrink-0", children: /* @__PURE__ */ jsx("path", { d: "M1.5 3a1 1 0 011-1h3l1.5 1.5H13a1 1 0 011 1v8a1 1 0 01-1 1H2.5a1 1 0 01-1-1V3z", fill: "#fbbf24" }) });
11954
12227
  }
11955
- function ChevronIcon({ open }) {
12228
+ function ChevronIcon2({ open }) {
11956
12229
  return /* @__PURE__ */ jsx(
11957
12230
  "svg",
11958
12231
  {
@@ -12134,7 +12407,7 @@ function TreeNode({ node, depth }) {
12134
12407
  ),
12135
12408
  style: { paddingLeft },
12136
12409
  children: [
12137
- isFolder && /* @__PURE__ */ jsx(ChevronIcon, { open: isExpanded }),
12410
+ isFolder && /* @__PURE__ */ jsx(ChevronIcon2, { open: isExpanded }),
12138
12411
  !isFolder && /* @__PURE__ */ jsx("span", { className: "w-3.5 shrink-0" }),
12139
12412
  showIcons && (node.icon ?? (isFolder ? /* @__PURE__ */ jsx(FolderIcon, { open: isExpanded }) : /* @__PURE__ */ jsx(FileIcon, { ext: node.ext ?? node.label.split(".").pop() }))),
12140
12413
  /* @__PURE__ */ jsx("span", { className: "truncate", children: node.label })
@@ -12263,6 +12536,6 @@ var TreeNav = Object.assign(TreeNavRoot, {
12263
12536
  Node: TreeNode
12264
12537
  });
12265
12538
 
12266
- export { Accordion, Action, Autocomplete, Avatar, Badge, Brand, Breadcrumbs, Calendar, Callout, Canvas, Card, Carousel, Chart, Checkbox, CheckboxGroup, ColorPicker, Command, Composer, ContentRenderer, ContextMenu, DatePicker, Diagram, Dropdown, EMOJI_CATEGORY_ORDER, EMOJI_DATA, EMOJI_ENTRIES, Editor, Emoji, EmojiSelect, Field, FileUpload, Heading, Icon, Input, Kanban, Menu2 as Menu, MobileMenu, Modal, MultiSwitch, Navbar, OtpInput, Pagination, Pillbox, Popover, Portal, Profile, Progress, RadioGroup, SKIN_TONES, Select, Separator, Sidebar, Skeleton, Slider, Switch, Table, Tabs, Text, Textarea, TimePicker, Timeline, Toast, Tooltip, TreeNav, applyTone, cn, configureIcons, find, hasSkinTones, registerExtension, registerExtensions, registerIconSet, registerIcons, resolve, sanitizeHref, sanitizeHtml, search, skinTones, useAccordion, useAnimation, useCanvas, useCarousel, useCommand, useContextMenu, useControllableState, useDiagram, useDropdown, useEditor, useEscapeKey, useFileUpload, useFloatingPosition, useFocusTrap, useId12 as useId, useKanban, useMenu, useMobileMenu, useModal, useNavbar, useNodeRegistry, useOutsideClick, usePanZoom, usePopover, useSidebar, useTabs, useToast, useTreeNav };
12539
+ export { Accordion, AccordionPanel, AccordionPanelContent, AccordionPanelSection, AccordionPanelTrigger, Action, Autocomplete, Avatar, Badge, Brand, Breadcrumbs, Calendar, Callout, Canvas, Card, Carousel, Chart, Checkbox, CheckboxGroup, ColorPicker, Command, Composer, ContentRenderer, ContextMenu, DatePicker, Diagram, Dropdown, EMOJI_CATEGORY_ORDER, EMOJI_DATA, EMOJI_ENTRIES, Editor, Emoji, EmojiSelect, Field, FileUpload, Heading, Icon, Input, Kanban, Menu2 as Menu, MobileMenu, Modal, MultiSwitch, Navbar, OtpInput, Pagination, Pillbox, Popover, Portal, Profile, Progress, RadioGroup, SKIN_TONES, Select, Separator, Sidebar, Skeleton, Slider, Switch, Table, Tabs, Text, Textarea, TimePicker, Timeline, Toast, Tooltip, TreeNav, applyTone, cn, configureIcons, find, hasSkinTones, registerExtension, registerExtensions, registerIconSet, registerIcons, resolve, sanitizeHref, sanitizeHtml, search, skinTones, useAccordion, useAccordionPanel, useAccordionSection, useAnimation, useCanvas, useCarousel, useCommand, useContextMenu, useControllableState, useDiagram, useDropdown, useEditor, useEscapeKey, useFileUpload, useFloatingPosition, useFocusTrap, useId12 as useId, useKanban, useMenu, useMobileMenu, useModal, useNavbar, useNodeRegistry, useOutsideClick, usePanZoom, usePopover, useSidebar, useTabs, useToast, useTreeNav };
12267
12540
  //# sourceMappingURL=index.js.map
12268
12541
  //# sourceMappingURL=index.js.map