@almadar/ui 5.163.0 → 5.165.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/avl/index.js CHANGED
@@ -27152,6 +27152,7 @@ var init_DashboardLayout = __esm({
27152
27152
  onNotificationClick,
27153
27153
  showThemeToggle = true,
27154
27154
  sidebarFooter,
27155
+ sidebarContent,
27155
27156
  onSignOut: onSignOutProp,
27156
27157
  currentPath,
27157
27158
  layoutMode = "sidebar",
@@ -27277,7 +27278,11 @@ var init_DashboardLayout = __esm({
27277
27278
  {
27278
27279
  as: "nav",
27279
27280
  gap: "none",
27280
- className: cn("flex-1 py-4 space-y-1 overflow-y-auto", isRail ? "px-2" : "px-3"),
27281
+ className: cn(
27282
+ "py-4 space-y-1 overflow-y-auto",
27283
+ sidebarContent && !isRail ? "shrink-0" : "flex-1",
27284
+ isRail ? "px-2" : "px-3"
27285
+ ),
27281
27286
  children: navItems.map((item) => /* @__PURE__ */ jsx(
27282
27287
  NavLink,
27283
27288
  {
@@ -27289,6 +27294,7 @@ var init_DashboardLayout = __esm({
27289
27294
  ))
27290
27295
  }
27291
27296
  ),
27297
+ sidebarContent && !isRail && /* @__PURE__ */ jsx(Box, { className: "flex-1 overflow-y-auto border-t border-border dark:border-border px-2 py-3", children: sidebarContent }),
27292
27298
  sidebarFooter && /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
27293
27299
  ]
27294
27300
  }
@@ -29698,7 +29704,17 @@ function fileIcon(name) {
29698
29704
  return "file";
29699
29705
  }
29700
29706
  }
29701
- var TreeNodeItem, FlatTreeNodeItem, FileTree;
29707
+ function ancestorsOf(id, byId) {
29708
+ const out = /* @__PURE__ */ new Set();
29709
+ if (!id) return out;
29710
+ let cur = byId.get(id)?.parentId;
29711
+ while (cur && byId.has(cur) && !out.has(cur)) {
29712
+ out.add(cur);
29713
+ cur = byId.get(cur)?.parentId;
29714
+ }
29715
+ return out;
29716
+ }
29717
+ var TreeNodeItem, FlatTreeNodeItem, FlatFileTree, FileTree;
29702
29718
  var init_FileTree = __esm({
29703
29719
  "components/core/molecules/FileTree.tsx"() {
29704
29720
  "use client";
@@ -29789,42 +29805,77 @@ var init_FileTree = __esm({
29789
29805
  indent,
29790
29806
  childrenByParent,
29791
29807
  onNodeSelect,
29792
- defaultExpanded = false
29808
+ onNodeAction,
29809
+ nodeActionIcon,
29810
+ nodeActionLabel,
29811
+ selectedId,
29812
+ look,
29813
+ isExpanded,
29814
+ onToggle
29793
29815
  }) => {
29794
- const [expanded, setExpanded] = useState(defaultExpanded || depth < 1);
29795
29816
  const children = childrenByParent.get(item.id);
29796
29817
  const hasChildren = !!children && children.length > 0;
29818
+ const expanded = hasChildren && isExpanded(item.id, depth);
29819
+ const isSelected = selectedId !== void 0 && selectedId !== "" && item.id === selectedId;
29820
+ const nav = look === "nav";
29797
29821
  const handleClick = useCallback(() => {
29798
- if (hasChildren) setExpanded((prev) => !prev);
29822
+ if (hasChildren && !onNodeSelect) onToggle(item.id, expanded);
29799
29823
  onNodeSelect?.(item.id);
29800
- }, [hasChildren, item.id, onNodeSelect]);
29824
+ }, [hasChildren, expanded, item.id, onNodeSelect, onToggle]);
29825
+ const handleChevron = useCallback((e) => {
29826
+ e.stopPropagation();
29827
+ onToggle(item.id, expanded);
29828
+ }, [item.id, expanded, onToggle]);
29829
+ const handleAction = useCallback((e) => {
29830
+ e.stopPropagation();
29831
+ onNodeAction?.(item.id);
29832
+ }, [item.id, onNodeAction]);
29801
29833
  return /* @__PURE__ */ jsxs(Fragment, { children: [
29802
29834
  /* @__PURE__ */ jsxs(
29803
29835
  Box,
29804
29836
  {
29805
- className: "flex items-center gap-1.5 py-0.5 px-2 cursor-pointer rounded-sm transition-colors hover:bg-muted",
29837
+ 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"}`,
29806
29838
  style: { paddingLeft: depth * indent + 8 },
29807
29839
  onClick: handleClick,
29808
29840
  role: "treeitem",
29841
+ "aria-selected": isSelected,
29809
29842
  "aria-expanded": hasChildren ? expanded : void 0,
29810
29843
  children: [
29811
- hasChildren ? /* @__PURE__ */ jsx(
29844
+ hasChildren ? /* @__PURE__ */ jsx(Box, { onClick: handleChevron, className: "flex items-center flex-shrink-0", role: "button", "aria-label": expanded ? "Collapse" : "Expand", children: /* @__PURE__ */ jsx(
29812
29845
  Icon,
29813
29846
  {
29814
29847
  name: expanded ? "chevron-down" : "chevron-right",
29815
29848
  size: "xs",
29816
- className: "text-[var(--color-muted-foreground)] flex-shrink-0"
29849
+ className: isSelected ? "text-inherit" : "text-[var(--color-muted-foreground)]"
29817
29850
  }
29818
- ) : /* @__PURE__ */ jsx(Box, { style: { width: 12, flexShrink: 0 } }),
29851
+ ) }) : /* @__PURE__ */ jsx(Box, { style: { width: 12, flexShrink: 0 } }),
29819
29852
  /* @__PURE__ */ jsx(
29820
29853
  Icon,
29821
29854
  {
29822
- name: item.icon ?? (hasChildren ? expanded ? "folder-open" : "folder" : "file"),
29855
+ name: item.icon || (nav ? "file-text" : hasChildren ? expanded ? "folder-open" : "folder" : "file"),
29823
29856
  size: "xs",
29824
- className: hasChildren ? "text-[var(--color-warning)]" : "text-[var(--color-muted-foreground)]"
29857
+ className: isSelected ? "text-inherit" : nav || !hasChildren ? "text-[var(--color-muted-foreground)]" : "text-[var(--color-warning)]"
29858
+ }
29859
+ ),
29860
+ /* @__PURE__ */ jsx(
29861
+ Typography,
29862
+ {
29863
+ variant: "caption",
29864
+ className: `truncate ${nav ? "text-sm" : "font-mono text-xs"} !text-inherit ${isSelected ? "font-semibold" : ""}`,
29865
+ children: item.label
29825
29866
  }
29826
29867
  ),
29827
- /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "truncate font-mono text-xs", children: item.label })
29868
+ onNodeAction && /* @__PURE__ */ jsx(
29869
+ Box,
29870
+ {
29871
+ onClick: handleAction,
29872
+ role: "button",
29873
+ "aria-label": nodeActionLabel ?? "Node action",
29874
+ title: nodeActionLabel,
29875
+ 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"}`,
29876
+ children: /* @__PURE__ */ jsx(Icon, { name: nodeActionIcon ?? "plus", size: "xs", className: "text-inherit" })
29877
+ }
29878
+ )
29828
29879
  ]
29829
29880
  }
29830
29881
  ),
@@ -29835,47 +29886,116 @@ var init_FileTree = __esm({
29835
29886
  depth: depth + 1,
29836
29887
  indent,
29837
29888
  childrenByParent,
29838
- onNodeSelect
29889
+ onNodeSelect,
29890
+ onNodeAction,
29891
+ nodeActionIcon,
29892
+ nodeActionLabel,
29893
+ selectedId,
29894
+ look,
29895
+ isExpanded,
29896
+ onToggle
29839
29897
  },
29840
29898
  child.id
29841
29899
  )) })
29842
29900
  ] });
29843
29901
  };
29902
+ FlatFileTree = ({
29903
+ items,
29904
+ selectedId,
29905
+ look = "files",
29906
+ onNodeSelect,
29907
+ onNodeAction,
29908
+ nodeActionIcon,
29909
+ nodeActionLabel,
29910
+ className,
29911
+ indent = 16
29912
+ }) => {
29913
+ const [overrides, setOverrides] = useState(() => /* @__PURE__ */ new Map());
29914
+ const byId = React94__default.useMemo(() => new Map(items.map((node) => [node.id, node])), [items]);
29915
+ const selectedAncestors = React94__default.useMemo(() => ancestorsOf(selectedId, byId), [selectedId, byId]);
29916
+ const lastSelectedRef = React94__default.useRef(selectedId);
29917
+ React94__default.useEffect(() => {
29918
+ if (selectedId === lastSelectedRef.current) return;
29919
+ lastSelectedRef.current = selectedId;
29920
+ setOverrides((prev) => {
29921
+ if (![...selectedAncestors].some((a) => prev.get(a) === false)) return prev;
29922
+ const next = new Map(prev);
29923
+ for (const a of selectedAncestors) if (next.get(a) === false) next.delete(a);
29924
+ return next;
29925
+ });
29926
+ }, [selectedId, selectedAncestors]);
29927
+ const isExpanded = useCallback(
29928
+ (id, depth) => overrides.get(id) ?? (depth < 1 || selectedAncestors.has(id)),
29929
+ [overrides, selectedAncestors]
29930
+ );
29931
+ const onToggle = useCallback((id, effective) => {
29932
+ setOverrides((prev) => {
29933
+ const next = new Map(prev);
29934
+ next.set(id, !effective);
29935
+ return next;
29936
+ });
29937
+ }, []);
29938
+ if (items.length === 0) return null;
29939
+ const ids = new Set(items.map((node) => node.id));
29940
+ const childrenByParent = /* @__PURE__ */ new Map();
29941
+ const roots = [];
29942
+ for (const item of items) {
29943
+ if (item.parentId && ids.has(item.parentId)) {
29944
+ const siblings = childrenByParent.get(item.parentId);
29945
+ if (siblings) siblings.push(item);
29946
+ else childrenByParent.set(item.parentId, [item]);
29947
+ } else {
29948
+ roots.push(item);
29949
+ }
29950
+ }
29951
+ return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsx(
29952
+ FlatTreeNodeItem,
29953
+ {
29954
+ item,
29955
+ depth: 0,
29956
+ indent,
29957
+ childrenByParent,
29958
+ onNodeSelect,
29959
+ onNodeAction,
29960
+ nodeActionIcon,
29961
+ nodeActionLabel,
29962
+ selectedId,
29963
+ look,
29964
+ isExpanded,
29965
+ onToggle
29966
+ },
29967
+ item.id
29968
+ )) });
29969
+ };
29844
29970
  FileTree = ({
29845
29971
  tree,
29846
29972
  items,
29847
29973
  selectedPath,
29974
+ selectedId,
29975
+ look = "files",
29848
29976
  onFileSelect,
29849
29977
  onNodeSelect,
29978
+ onNodeAction,
29979
+ nodeActionIcon,
29980
+ nodeActionLabel,
29850
29981
  className,
29851
29982
  indent = 16
29852
29983
  }) => {
29853
29984
  if (items) {
29854
- if (items.length === 0) return null;
29855
- const ids = new Set(items.map((node) => node.id));
29856
- const childrenByParent = /* @__PURE__ */ new Map();
29857
- const roots = [];
29858
- for (const item of items) {
29859
- if (item.parentId && ids.has(item.parentId)) {
29860
- const siblings = childrenByParent.get(item.parentId);
29861
- if (siblings) siblings.push(item);
29862
- else childrenByParent.set(item.parentId, [item]);
29863
- } else {
29864
- roots.push(item);
29865
- }
29866
- }
29867
- return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsx(
29868
- FlatTreeNodeItem,
29985
+ return /* @__PURE__ */ jsx(
29986
+ FlatFileTree,
29869
29987
  {
29870
- item,
29871
- depth: 0,
29872
- indent,
29873
- childrenByParent,
29988
+ items,
29989
+ selectedId,
29990
+ look,
29874
29991
  onNodeSelect,
29875
- defaultExpanded: true
29876
- },
29877
- item.id
29878
- )) });
29992
+ onNodeAction,
29993
+ nodeActionIcon,
29994
+ nodeActionLabel,
29995
+ className,
29996
+ indent
29997
+ }
29998
+ );
29879
29999
  }
29880
30000
  if (!tree || tree.length === 0) return null;
29881
30001
  return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: tree.map((node) => /* @__PURE__ */ jsx(
@@ -39319,13 +39439,35 @@ var init_RichTextEditor = __esm({
39319
39439
  document.execCommand("createLink", false, safe);
39320
39440
  afterEdit();
39321
39441
  }, [afterEdit, t, toolbar.link]);
39442
+ const imageInputRef = useRef(null);
39443
+ const savedRangeRef = useRef(null);
39322
39444
  const execImage = useCallback(() => {
39323
- const url = window.prompt(t("richTextEditor.imagePrompt"));
39324
- if (!url) return;
39325
- const safe = safeUrl(url, ["http://", "https://", "data:image/"]) ?? `https://${url}`;
39326
- document.execCommand("insertImage", false, safe);
39327
- afterEdit();
39328
- }, [afterEdit, t]);
39445
+ const root = ref.current;
39446
+ const sel = window.getSelection();
39447
+ savedRangeRef.current = root && sel && sel.rangeCount > 0 && root.contains(sel.anchorNode) ? sel.getRangeAt(0).cloneRange() : null;
39448
+ imageInputRef.current?.click();
39449
+ }, []);
39450
+ const handleImageFile = useCallback((e) => {
39451
+ const file = e.target.files?.[0];
39452
+ e.target.value = "";
39453
+ if (!file) return;
39454
+ const reader = new FileReader();
39455
+ reader.onload = () => {
39456
+ const url = typeof reader.result === "string" ? reader.result : "";
39457
+ if (!url.startsWith("data:image/")) return;
39458
+ const root = ref.current;
39459
+ if (!root) return;
39460
+ root.focus();
39461
+ const sel = window.getSelection();
39462
+ if (sel && savedRangeRef.current && root.contains(savedRangeRef.current.startContainer)) {
39463
+ sel.removeAllRanges();
39464
+ sel.addRange(savedRangeRef.current);
39465
+ }
39466
+ document.execCommand("insertImage", false, url);
39467
+ afterEdit();
39468
+ };
39469
+ reader.readAsDataURL(file);
39470
+ }, [afterEdit]);
39329
39471
  const execRule = useCallback(() => {
39330
39472
  document.execCommand("insertHorizontalRule", false);
39331
39473
  afterEdit();
@@ -39338,6 +39480,18 @@ var init_RichTextEditor = __esm({
39338
39480
  }
39339
39481
  return /* @__PURE__ */ jsxs(Box, { className: cn("flex flex-col gap-2", className), children: [
39340
39482
  /* @__PURE__ */ jsx(RichTextStyles, {}),
39483
+ /* @__PURE__ */ jsx(
39484
+ "input",
39485
+ {
39486
+ ref: imageInputRef,
39487
+ type: "file",
39488
+ accept: "image/*",
39489
+ className: "hidden",
39490
+ "aria-hidden": "true",
39491
+ tabIndex: -1,
39492
+ onChange: handleImageFile
39493
+ }
39494
+ ),
39341
39495
  showToolbar && /* @__PURE__ */ jsxs(
39342
39496
  Box,
39343
39497
  {
@@ -45828,15 +45982,6 @@ var init_Form = __esm({
45828
45982
  "data-testid": `image-preview-${fieldName2}`
45829
45983
  }
45830
45984
  ) : null,
45831
- /* @__PURE__ */ jsx(
45832
- Input,
45833
- {
45834
- ...commonProps,
45835
- type: "url",
45836
- value: imageUrl,
45837
- onChange: (e) => handleChange(fieldName2, e.target.value)
45838
- }
45839
- ),
45840
45985
  /* @__PURE__ */ jsx(
45841
45986
  UploadDropZone,
45842
45987
  {
@@ -45855,6 +46000,16 @@ var init_Form = __esm({
45855
46000
  reader.readAsDataURL(f3);
45856
46001
  }
45857
46002
  }
46003
+ ),
46004
+ /* @__PURE__ */ jsx(
46005
+ Input,
46006
+ {
46007
+ ...commonProps,
46008
+ type: "url",
46009
+ placeholder: t("form.imageUrlFallback"),
46010
+ value: imageUrl,
46011
+ onChange: (e) => handleChange(fieldName2, e.target.value)
46012
+ }
45858
46013
  )
45859
46014
  ] });
45860
46015
  }