@almadar/ui 5.163.0 → 5.164.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.
@@ -24880,6 +24880,7 @@ var init_DashboardLayout = __esm({
24880
24880
  onNotificationClick,
24881
24881
  showThemeToggle = true,
24882
24882
  sidebarFooter,
24883
+ sidebarContent,
24883
24884
  onSignOut: onSignOutProp,
24884
24885
  currentPath,
24885
24886
  layoutMode = "sidebar",
@@ -25005,7 +25006,11 @@ var init_DashboardLayout = __esm({
25005
25006
  {
25006
25007
  as: "nav",
25007
25008
  gap: "none",
25008
- className: cn("flex-1 py-4 space-y-1 overflow-y-auto", isRail ? "px-2" : "px-3"),
25009
+ className: cn(
25010
+ "py-4 space-y-1 overflow-y-auto",
25011
+ sidebarContent && !isRail ? "shrink-0" : "flex-1",
25012
+ isRail ? "px-2" : "px-3"
25013
+ ),
25009
25014
  children: navItems.map((item) => /* @__PURE__ */ jsx(
25010
25015
  NavLink,
25011
25016
  {
@@ -25017,6 +25022,7 @@ var init_DashboardLayout = __esm({
25017
25022
  ))
25018
25023
  }
25019
25024
  ),
25025
+ sidebarContent && !isRail && /* @__PURE__ */ jsx(Box, { className: "flex-1 overflow-y-auto border-t border-border dark:border-border px-2 py-3", children: sidebarContent }),
25020
25026
  sidebarFooter && /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
25021
25027
  ]
25022
25028
  }
@@ -27426,7 +27432,17 @@ function fileIcon(name) {
27426
27432
  return "file";
27427
27433
  }
27428
27434
  }
27429
- var TreeNodeItem, FlatTreeNodeItem, FileTree;
27435
+ function ancestorsOf(id, byId) {
27436
+ const out = /* @__PURE__ */ new Set();
27437
+ if (!id) return out;
27438
+ let cur = byId.get(id)?.parentId;
27439
+ while (cur && byId.has(cur) && !out.has(cur)) {
27440
+ out.add(cur);
27441
+ cur = byId.get(cur)?.parentId;
27442
+ }
27443
+ return out;
27444
+ }
27445
+ var TreeNodeItem, FlatTreeNodeItem, FlatFileTree, FileTree;
27430
27446
  var init_FileTree = __esm({
27431
27447
  "components/core/molecules/FileTree.tsx"() {
27432
27448
  "use client";
@@ -27517,42 +27533,77 @@ var init_FileTree = __esm({
27517
27533
  indent,
27518
27534
  childrenByParent,
27519
27535
  onNodeSelect,
27520
- defaultExpanded = false
27536
+ onNodeAction,
27537
+ nodeActionIcon,
27538
+ nodeActionLabel,
27539
+ selectedId,
27540
+ look,
27541
+ isExpanded,
27542
+ onToggle
27521
27543
  }) => {
27522
- const [expanded, setExpanded] = useState(defaultExpanded || depth < 1);
27523
27544
  const children = childrenByParent.get(item.id);
27524
27545
  const hasChildren = !!children && children.length > 0;
27546
+ const expanded = hasChildren && isExpanded(item.id, depth);
27547
+ const isSelected = selectedId !== void 0 && selectedId !== "" && item.id === selectedId;
27548
+ const nav = look === "nav";
27525
27549
  const handleClick = useCallback(() => {
27526
- if (hasChildren) setExpanded((prev) => !prev);
27550
+ if (hasChildren && !onNodeSelect) onToggle(item.id, expanded);
27527
27551
  onNodeSelect?.(item.id);
27528
- }, [hasChildren, item.id, onNodeSelect]);
27552
+ }, [hasChildren, expanded, item.id, onNodeSelect, onToggle]);
27553
+ const handleChevron = useCallback((e) => {
27554
+ e.stopPropagation();
27555
+ onToggle(item.id, expanded);
27556
+ }, [item.id, expanded, onToggle]);
27557
+ const handleAction = useCallback((e) => {
27558
+ e.stopPropagation();
27559
+ onNodeAction?.(item.id);
27560
+ }, [item.id, onNodeAction]);
27529
27561
  return /* @__PURE__ */ jsxs(Fragment, { children: [
27530
27562
  /* @__PURE__ */ jsxs(
27531
27563
  Box,
27532
27564
  {
27533
- className: "flex items-center gap-1.5 py-0.5 px-2 cursor-pointer rounded-sm transition-colors hover:bg-muted",
27565
+ className: `group/treerow flex items-center gap-1.5 px-2 cursor-pointer rounded-sm transition-colors ${nav ? "py-1" : "py-0.5"} ${isSelected ? "bg-primary text-primary-foreground" : nav ? "text-foreground hover:bg-muted" : "hover:bg-muted"}`,
27534
27566
  style: { paddingLeft: depth * indent + 8 },
27535
27567
  onClick: handleClick,
27536
27568
  role: "treeitem",
27569
+ "aria-selected": isSelected,
27537
27570
  "aria-expanded": hasChildren ? expanded : void 0,
27538
27571
  children: [
27539
- hasChildren ? /* @__PURE__ */ jsx(
27572
+ hasChildren ? /* @__PURE__ */ jsx(Box, { onClick: handleChevron, className: "flex items-center flex-shrink-0", role: "button", "aria-label": expanded ? "Collapse" : "Expand", children: /* @__PURE__ */ jsx(
27540
27573
  Icon,
27541
27574
  {
27542
27575
  name: expanded ? "chevron-down" : "chevron-right",
27543
27576
  size: "xs",
27544
- className: "text-[var(--color-muted-foreground)] flex-shrink-0"
27577
+ className: isSelected ? "text-inherit" : "text-[var(--color-muted-foreground)]"
27545
27578
  }
27546
- ) : /* @__PURE__ */ jsx(Box, { style: { width: 12, flexShrink: 0 } }),
27579
+ ) }) : /* @__PURE__ */ jsx(Box, { style: { width: 12, flexShrink: 0 } }),
27547
27580
  /* @__PURE__ */ jsx(
27548
27581
  Icon,
27549
27582
  {
27550
- name: item.icon ?? (hasChildren ? expanded ? "folder-open" : "folder" : "file"),
27583
+ name: item.icon || (nav ? "file-text" : hasChildren ? expanded ? "folder-open" : "folder" : "file"),
27551
27584
  size: "xs",
27552
- className: hasChildren ? "text-[var(--color-warning)]" : "text-[var(--color-muted-foreground)]"
27585
+ className: isSelected ? "text-inherit" : nav || !hasChildren ? "text-[var(--color-muted-foreground)]" : "text-[var(--color-warning)]"
27586
+ }
27587
+ ),
27588
+ /* @__PURE__ */ jsx(
27589
+ Typography,
27590
+ {
27591
+ variant: "caption",
27592
+ className: `truncate ${nav ? "text-sm" : "font-mono text-xs"} !text-inherit ${isSelected ? "font-semibold" : ""}`,
27593
+ children: item.label
27553
27594
  }
27554
27595
  ),
27555
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "truncate font-mono text-xs", children: item.label })
27596
+ onNodeAction && /* @__PURE__ */ jsx(
27597
+ Box,
27598
+ {
27599
+ onClick: handleAction,
27600
+ role: "button",
27601
+ "aria-label": nodeActionLabel ?? "Node action",
27602
+ title: nodeActionLabel,
27603
+ className: `ml-auto flex-shrink-0 rounded-sm p-0.5 opacity-0 group-hover/treerow:opacity-100 transition-opacity ${isSelected ? "hover:bg-primary-foreground/20" : "hover:bg-border"}`,
27604
+ children: /* @__PURE__ */ jsx(Icon, { name: nodeActionIcon ?? "plus", size: "xs", className: "text-inherit" })
27605
+ }
27606
+ )
27556
27607
  ]
27557
27608
  }
27558
27609
  ),
@@ -27563,47 +27614,116 @@ var init_FileTree = __esm({
27563
27614
  depth: depth + 1,
27564
27615
  indent,
27565
27616
  childrenByParent,
27566
- onNodeSelect
27617
+ onNodeSelect,
27618
+ onNodeAction,
27619
+ nodeActionIcon,
27620
+ nodeActionLabel,
27621
+ selectedId,
27622
+ look,
27623
+ isExpanded,
27624
+ onToggle
27567
27625
  },
27568
27626
  child.id
27569
27627
  )) })
27570
27628
  ] });
27571
27629
  };
27630
+ FlatFileTree = ({
27631
+ items,
27632
+ selectedId,
27633
+ look = "files",
27634
+ onNodeSelect,
27635
+ onNodeAction,
27636
+ nodeActionIcon,
27637
+ nodeActionLabel,
27638
+ className,
27639
+ indent = 16
27640
+ }) => {
27641
+ const [overrides, setOverrides] = useState(() => /* @__PURE__ */ new Map());
27642
+ const byId = React87__default.useMemo(() => new Map(items.map((node) => [node.id, node])), [items]);
27643
+ const selectedAncestors = React87__default.useMemo(() => ancestorsOf(selectedId, byId), [selectedId, byId]);
27644
+ const lastSelectedRef = React87__default.useRef(selectedId);
27645
+ React87__default.useEffect(() => {
27646
+ if (selectedId === lastSelectedRef.current) return;
27647
+ lastSelectedRef.current = selectedId;
27648
+ setOverrides((prev) => {
27649
+ if (![...selectedAncestors].some((a) => prev.get(a) === false)) return prev;
27650
+ const next = new Map(prev);
27651
+ for (const a of selectedAncestors) if (next.get(a) === false) next.delete(a);
27652
+ return next;
27653
+ });
27654
+ }, [selectedId, selectedAncestors]);
27655
+ const isExpanded = useCallback(
27656
+ (id, depth) => overrides.get(id) ?? (depth < 1 || selectedAncestors.has(id)),
27657
+ [overrides, selectedAncestors]
27658
+ );
27659
+ const onToggle = useCallback((id, effective) => {
27660
+ setOverrides((prev) => {
27661
+ const next = new Map(prev);
27662
+ next.set(id, !effective);
27663
+ return next;
27664
+ });
27665
+ }, []);
27666
+ if (items.length === 0) return null;
27667
+ const ids = new Set(items.map((node) => node.id));
27668
+ const childrenByParent = /* @__PURE__ */ new Map();
27669
+ const roots = [];
27670
+ for (const item of items) {
27671
+ if (item.parentId && ids.has(item.parentId)) {
27672
+ const siblings = childrenByParent.get(item.parentId);
27673
+ if (siblings) siblings.push(item);
27674
+ else childrenByParent.set(item.parentId, [item]);
27675
+ } else {
27676
+ roots.push(item);
27677
+ }
27678
+ }
27679
+ return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsx(
27680
+ FlatTreeNodeItem,
27681
+ {
27682
+ item,
27683
+ depth: 0,
27684
+ indent,
27685
+ childrenByParent,
27686
+ onNodeSelect,
27687
+ onNodeAction,
27688
+ nodeActionIcon,
27689
+ nodeActionLabel,
27690
+ selectedId,
27691
+ look,
27692
+ isExpanded,
27693
+ onToggle
27694
+ },
27695
+ item.id
27696
+ )) });
27697
+ };
27572
27698
  FileTree = ({
27573
27699
  tree,
27574
27700
  items,
27575
27701
  selectedPath,
27702
+ selectedId,
27703
+ look = "files",
27576
27704
  onFileSelect,
27577
27705
  onNodeSelect,
27706
+ onNodeAction,
27707
+ nodeActionIcon,
27708
+ nodeActionLabel,
27578
27709
  className,
27579
27710
  indent = 16
27580
27711
  }) => {
27581
27712
  if (items) {
27582
- if (items.length === 0) return null;
27583
- const ids = new Set(items.map((node) => node.id));
27584
- const childrenByParent = /* @__PURE__ */ new Map();
27585
- const roots = [];
27586
- for (const item of items) {
27587
- if (item.parentId && ids.has(item.parentId)) {
27588
- const siblings = childrenByParent.get(item.parentId);
27589
- if (siblings) siblings.push(item);
27590
- else childrenByParent.set(item.parentId, [item]);
27591
- } else {
27592
- roots.push(item);
27593
- }
27594
- }
27595
- return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsx(
27596
- FlatTreeNodeItem,
27713
+ return /* @__PURE__ */ jsx(
27714
+ FlatFileTree,
27597
27715
  {
27598
- item,
27599
- depth: 0,
27600
- indent,
27601
- childrenByParent,
27716
+ items,
27717
+ selectedId,
27718
+ look,
27602
27719
  onNodeSelect,
27603
- defaultExpanded: true
27604
- },
27605
- item.id
27606
- )) });
27720
+ onNodeAction,
27721
+ nodeActionIcon,
27722
+ nodeActionLabel,
27723
+ className,
27724
+ indent
27725
+ }
27726
+ );
27607
27727
  }
27608
27728
  if (!tree || tree.length === 0) return null;
27609
27729
  return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: tree.map((node) => /* @__PURE__ */ jsx(
@@ -37047,13 +37167,35 @@ var init_RichTextEditor = __esm({
37047
37167
  document.execCommand("createLink", false, safe);
37048
37168
  afterEdit();
37049
37169
  }, [afterEdit, t, toolbar.link]);
37170
+ const imageInputRef = useRef(null);
37171
+ const savedRangeRef = useRef(null);
37050
37172
  const execImage = useCallback(() => {
37051
- const url = window.prompt(t("richTextEditor.imagePrompt"));
37052
- if (!url) return;
37053
- const safe = safeUrl(url, ["http://", "https://", "data:image/"]) ?? `https://${url}`;
37054
- document.execCommand("insertImage", false, safe);
37055
- afterEdit();
37056
- }, [afterEdit, t]);
37173
+ const root = ref.current;
37174
+ const sel = window.getSelection();
37175
+ savedRangeRef.current = root && sel && sel.rangeCount > 0 && root.contains(sel.anchorNode) ? sel.getRangeAt(0).cloneRange() : null;
37176
+ imageInputRef.current?.click();
37177
+ }, []);
37178
+ const handleImageFile = useCallback((e) => {
37179
+ const file = e.target.files?.[0];
37180
+ e.target.value = "";
37181
+ if (!file) return;
37182
+ const reader = new FileReader();
37183
+ reader.onload = () => {
37184
+ const url = typeof reader.result === "string" ? reader.result : "";
37185
+ if (!url.startsWith("data:image/")) return;
37186
+ const root = ref.current;
37187
+ if (!root) return;
37188
+ root.focus();
37189
+ const sel = window.getSelection();
37190
+ if (sel && savedRangeRef.current && root.contains(savedRangeRef.current.startContainer)) {
37191
+ sel.removeAllRanges();
37192
+ sel.addRange(savedRangeRef.current);
37193
+ }
37194
+ document.execCommand("insertImage", false, url);
37195
+ afterEdit();
37196
+ };
37197
+ reader.readAsDataURL(file);
37198
+ }, [afterEdit]);
37057
37199
  const execRule = useCallback(() => {
37058
37200
  document.execCommand("insertHorizontalRule", false);
37059
37201
  afterEdit();
@@ -37066,6 +37208,18 @@ var init_RichTextEditor = __esm({
37066
37208
  }
37067
37209
  return /* @__PURE__ */ jsxs(Box, { className: cn("flex flex-col gap-2", className), children: [
37068
37210
  /* @__PURE__ */ jsx(RichTextStyles, {}),
37211
+ /* @__PURE__ */ jsx(
37212
+ "input",
37213
+ {
37214
+ ref: imageInputRef,
37215
+ type: "file",
37216
+ accept: "image/*",
37217
+ className: "hidden",
37218
+ "aria-hidden": "true",
37219
+ tabIndex: -1,
37220
+ onChange: handleImageFile
37221
+ }
37222
+ ),
37069
37223
  showToolbar && /* @__PURE__ */ jsxs(
37070
37224
  Box,
37071
37225
  {
@@ -43965,15 +44119,6 @@ var init_Form = __esm({
43965
44119
  "data-testid": `image-preview-${fieldName2}`
43966
44120
  }
43967
44121
  ) : null,
43968
- /* @__PURE__ */ jsx(
43969
- Input,
43970
- {
43971
- ...commonProps,
43972
- type: "url",
43973
- value: imageUrl,
43974
- onChange: (e) => handleChange(fieldName2, e.target.value)
43975
- }
43976
- ),
43977
44122
  /* @__PURE__ */ jsx(
43978
44123
  UploadDropZone,
43979
44124
  {
@@ -43992,6 +44137,16 @@ var init_Form = __esm({
43992
44137
  reader.readAsDataURL(f3);
43993
44138
  }
43994
44139
  }
44140
+ ),
44141
+ /* @__PURE__ */ jsx(
44142
+ Input,
44143
+ {
44144
+ ...commonProps,
44145
+ type: "url",
44146
+ placeholder: t("form.imageUrlFallback"),
44147
+ value: imageUrl,
44148
+ onChange: (e) => handleChange(fieldName2, e.target.value)
44149
+ }
43995
44150
  )
43996
44151
  ] });
43997
44152
  }