@underverse-ui/underverse 0.2.43 → 0.2.44

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.d.cts CHANGED
@@ -994,14 +994,26 @@ interface Category {
994
994
  name: string;
995
995
  parent_id?: number | null;
996
996
  }
997
+ interface CategoryTreeSelectLabels {
998
+ /** Text shown when no categories available */
999
+ emptyText?: string;
1000
+ /** Text shown when categories are selected, receives count as parameter */
1001
+ selectedText?: (count: number) => string;
1002
+ }
997
1003
  interface CategoryTreeSelectProps {
998
1004
  categories: Category[];
999
- value: number[];
1000
- onChange: (selectedIds: number[]) => void;
1005
+ value?: number[];
1006
+ onChange?: (selectedIds: number[]) => void;
1001
1007
  placeholder?: string;
1002
1008
  disabled?: boolean;
1003
- }
1004
- declare function CategoryTreeSelect({ categories, value, onChange, placeholder, disabled }: CategoryTreeSelectProps): react_jsx_runtime.JSX.Element;
1009
+ /** When true, renders as a read-only tree view without select functionality */
1010
+ viewOnly?: boolean;
1011
+ /** Default expanded state for all nodes in viewOnly mode */
1012
+ defaultExpanded?: boolean;
1013
+ /** i18n labels for localization */
1014
+ labels?: CategoryTreeSelectLabels;
1015
+ }
1016
+ declare function CategoryTreeSelect({ categories, value, onChange, placeholder, disabled, viewOnly, defaultExpanded, labels, }: CategoryTreeSelectProps): react_jsx_runtime.JSX.Element;
1005
1017
 
1006
1018
  type Fit = "cover" | "contain";
1007
1019
  interface SmartImageProps {
package/dist/index.d.ts CHANGED
@@ -994,14 +994,26 @@ interface Category {
994
994
  name: string;
995
995
  parent_id?: number | null;
996
996
  }
997
+ interface CategoryTreeSelectLabels {
998
+ /** Text shown when no categories available */
999
+ emptyText?: string;
1000
+ /** Text shown when categories are selected, receives count as parameter */
1001
+ selectedText?: (count: number) => string;
1002
+ }
997
1003
  interface CategoryTreeSelectProps {
998
1004
  categories: Category[];
999
- value: number[];
1000
- onChange: (selectedIds: number[]) => void;
1005
+ value?: number[];
1006
+ onChange?: (selectedIds: number[]) => void;
1001
1007
  placeholder?: string;
1002
1008
  disabled?: boolean;
1003
- }
1004
- declare function CategoryTreeSelect({ categories, value, onChange, placeholder, disabled }: CategoryTreeSelectProps): react_jsx_runtime.JSX.Element;
1009
+ /** When true, renders as a read-only tree view without select functionality */
1010
+ viewOnly?: boolean;
1011
+ /** Default expanded state for all nodes in viewOnly mode */
1012
+ defaultExpanded?: boolean;
1013
+ /** i18n labels for localization */
1014
+ labels?: CategoryTreeSelectLabels;
1015
+ }
1016
+ declare function CategoryTreeSelect({ categories, value, onChange, placeholder, disabled, viewOnly, defaultExpanded, labels, }: CategoryTreeSelectProps): react_jsx_runtime.JSX.Element;
1005
1017
 
1006
1018
  type Fit = "cover" | "contain";
1007
1019
  interface SmartImageProps {
package/dist/index.js CHANGED
@@ -7944,12 +7944,26 @@ function OverlayControls({
7944
7944
  }
7945
7945
 
7946
7946
  // ../../components/ui/CategoryTreeSelect.tsx
7947
- import { useState as useState25 } from "react";
7947
+ import { useState as useState25, useEffect as useEffect15 } from "react";
7948
7948
  import { ChevronRight as ChevronRight5, ChevronDown as ChevronDown3, Check as Check6 } from "lucide-react";
7949
7949
  import { Fragment as Fragment11, jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
7950
- function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1ECDn danh m\u1EE5c", disabled }) {
7950
+ var defaultLabels = {
7951
+ emptyText: "No categories",
7952
+ selectedText: (count) => `${count} selected`
7953
+ };
7954
+ function CategoryTreeSelect({
7955
+ categories,
7956
+ value = [],
7957
+ onChange,
7958
+ placeholder = "Select category",
7959
+ disabled,
7960
+ viewOnly = false,
7961
+ defaultExpanded = false,
7962
+ labels
7963
+ }) {
7951
7964
  const [isOpen, setIsOpen] = useState25(false);
7952
7965
  const [expandedNodes, setExpandedNodes] = useState25(/* @__PURE__ */ new Set());
7966
+ const mergedLabels = { ...defaultLabels, ...labels };
7953
7967
  const parentCategories = categories.filter((c) => !c.parent_id);
7954
7968
  const childrenMap = /* @__PURE__ */ new Map();
7955
7969
  categories.forEach((cat) => {
@@ -7960,6 +7974,12 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
7960
7974
  childrenMap.get(cat.parent_id).push(cat);
7961
7975
  }
7962
7976
  });
7977
+ useEffect15(() => {
7978
+ if (viewOnly && defaultExpanded) {
7979
+ const allParentIds = categories.filter((c) => childrenMap.has(c.id)).map((c) => c.id);
7980
+ setExpandedNodes(new Set(allParentIds));
7981
+ }
7982
+ }, [viewOnly, defaultExpanded, categories]);
7963
7983
  const toggleExpand = (id) => {
7964
7984
  const newExpanded = new Set(expandedNodes);
7965
7985
  if (newExpanded.has(id)) {
@@ -7970,6 +7990,7 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
7970
7990
  setExpandedNodes(newExpanded);
7971
7991
  };
7972
7992
  const handleSelect = (categoryId, category) => {
7993
+ if (viewOnly || !onChange) return;
7973
7994
  const newSelected = new Set(value);
7974
7995
  if (newSelected.has(categoryId)) {
7975
7996
  newSelected.delete(categoryId);
@@ -7993,20 +8014,14 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
7993
8014
  "div",
7994
8015
  {
7995
8016
  className: cn(
7996
- "relative flex items-center gap-2 px-3 py-2 cursor-pointer rounded-md transition-colors",
7997
- "hover:bg-accent",
7998
- // Selected state: subtle bg + square left indicator, avoid left rounding
7999
- isSelected && "bg-primary/10 rounded-r-md"
8017
+ "relative flex items-center gap-2 px-3 py-2 rounded-md transition-colors",
8018
+ !viewOnly && "cursor-pointer hover:bg-accent",
8019
+ // Selected state: subtle bg + square left indicator (only in select mode)
8020
+ !viewOnly && isSelected && "bg-primary/10 rounded-r-md"
8000
8021
  ),
8001
8022
  style: { paddingLeft: `${level * 1.5 + 0.75}rem` },
8002
8023
  children: [
8003
- isSelected && /* @__PURE__ */ jsx36(
8004
- "span",
8005
- {
8006
- "aria-hidden": true,
8007
- className: "absolute left-0 top-0 bottom-0 w-1 bg-primary"
8008
- }
8009
- ),
8024
+ !viewOnly && isSelected && /* @__PURE__ */ jsx36("span", { "aria-hidden": true, className: "absolute left-0 top-0 bottom-0 w-1 bg-primary" }),
8010
8025
  hasChildren ? /* @__PURE__ */ jsx36(
8011
8026
  "button",
8012
8027
  {
@@ -8019,25 +8034,24 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8019
8034
  children: isExpanded ? /* @__PURE__ */ jsx36(ChevronDown3, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx36(ChevronRight5, { className: "w-4 h-4" })
8020
8035
  }
8021
8036
  ) : /* @__PURE__ */ jsx36("span", { className: "w-5" }),
8022
- /* @__PURE__ */ jsxs31(
8023
- "div",
8024
- {
8025
- onClick: () => handleSelect(category.id, category),
8026
- className: "flex items-center gap-2 flex-1",
8027
- children: [
8028
- /* @__PURE__ */ jsx36(
8029
- "div",
8030
- {
8031
- className: cn(
8032
- "w-4 h-4 border-2 rounded flex items-center justify-center transition-colors",
8033
- isSelected ? "bg-primary border-primary" : "border-muted-foreground/30"
8034
- ),
8035
- children: isSelected && /* @__PURE__ */ jsx36(Check6, { className: "w-3 h-3 text-primary-foreground" })
8036
- }
8037
- ),
8038
- /* @__PURE__ */ jsx36("span", { className: cn("text-sm", isSelected && "font-medium text-primary"), children: category.name })
8039
- ]
8040
- }
8037
+ viewOnly ? (
8038
+ // View-only mode: just display the name
8039
+ /* @__PURE__ */ jsx36("span", { className: "text-sm", children: category.name })
8040
+ ) : (
8041
+ // Select mode: clickable with checkbox
8042
+ /* @__PURE__ */ jsxs31("div", { onClick: () => handleSelect(category.id, category), className: "flex items-center gap-2 flex-1", children: [
8043
+ /* @__PURE__ */ jsx36(
8044
+ "div",
8045
+ {
8046
+ className: cn(
8047
+ "w-4 h-4 border-2 rounded flex items-center justify-center transition-colors",
8048
+ isSelected ? "bg-primary border-primary" : "border-muted-foreground/30"
8049
+ ),
8050
+ children: isSelected && /* @__PURE__ */ jsx36(Check6, { className: "w-3 h-3 text-primary-foreground" })
8051
+ }
8052
+ ),
8053
+ /* @__PURE__ */ jsx36("span", { className: cn("text-sm", isSelected && "font-medium text-primary"), children: category.name })
8054
+ ] })
8041
8055
  )
8042
8056
  ]
8043
8057
  }
@@ -8045,8 +8059,11 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8045
8059
  hasChildren && isExpanded && /* @__PURE__ */ jsx36("div", { children: children.map((child) => renderCategory(child, level + 1)) })
8046
8060
  ] }, category.id);
8047
8061
  };
8062
+ if (viewOnly) {
8063
+ return /* @__PURE__ */ jsx36("div", { className: cn("rounded-md border bg-background p-2", disabled && "opacity-50"), children: parentCategories.length === 0 ? /* @__PURE__ */ jsx36("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: mergedLabels.emptyText }) : parentCategories.map((cat) => renderCategory(cat)) });
8064
+ }
8048
8065
  const selectedCount = value.length;
8049
- const displayText = selectedCount > 0 ? `\u0110\xE3 ch\u1ECDn ${selectedCount} danh m\u1EE5c` : placeholder;
8066
+ const displayText = selectedCount > 0 ? mergedLabels.selectedText(selectedCount) : placeholder;
8050
8067
  return /* @__PURE__ */ jsxs31("div", { className: "relative", children: [
8051
8068
  /* @__PURE__ */ jsxs31(
8052
8069
  "button",
@@ -8079,7 +8096,7 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8079
8096
  "rounded-md border bg-popover text-popover-foreground shadow-md",
8080
8097
  "backdrop-blur-sm bg-popover/95 border-border/60"
8081
8098
  ),
8082
- children: /* @__PURE__ */ jsx36("div", { className: "p-1", children: parentCategories.length === 0 ? /* @__PURE__ */ jsx36("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "Kh\xF4ng c\xF3 danh m\u1EE5c n\xE0o" }) : parentCategories.map((cat) => renderCategory(cat)) })
8099
+ children: /* @__PURE__ */ jsx36("div", { className: "p-1", children: parentCategories.length === 0 ? /* @__PURE__ */ jsx36("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: mergedLabels.emptyText }) : parentCategories.map((cat) => renderCategory(cat)) })
8083
8100
  }
8084
8101
  )
8085
8102
  ] })
@@ -10304,16 +10321,16 @@ var Grid_default = Grid;
10304
10321
  import { useMemo as useMemo6, useState as useState33, useRef as useRef14 } from "react";
10305
10322
 
10306
10323
  // ../../components/ui/ChartTooltip.tsx
10307
- import { useEffect as useEffect18, useState as useState32 } from "react";
10324
+ import { useEffect as useEffect19, useState as useState32 } from "react";
10308
10325
  import { createPortal as createPortal10 } from "react-dom";
10309
10326
  import { Fragment as Fragment16, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
10310
10327
  function ChartTooltip({ x, y, visible, label, value, color, secondaryLabel, secondaryValue, items, containerRef }) {
10311
10328
  const [isMounted, setIsMounted] = useState32(false);
10312
10329
  const [position, setPosition] = useState32(null);
10313
- useEffect18(() => {
10330
+ useEffect19(() => {
10314
10331
  setIsMounted(true);
10315
10332
  }, []);
10316
- useEffect18(() => {
10333
+ useEffect19(() => {
10317
10334
  if (visible && containerRef?.current) {
10318
10335
  const rect = containerRef.current.getBoundingClientRect();
10319
10336
  setPosition({
@@ -11665,11 +11682,11 @@ function GaugeChart({
11665
11682
  }
11666
11683
 
11667
11684
  // ../../components/ui/ClientOnly.tsx
11668
- import { useEffect as useEffect19, useState as useState38 } from "react";
11685
+ import { useEffect as useEffect20, useState as useState38 } from "react";
11669
11686
  import { Fragment as Fragment19, jsx as jsx53 } from "react/jsx-runtime";
11670
11687
  function ClientOnly({ children, fallback = null }) {
11671
11688
  const [hasMounted, setHasMounted] = useState38(false);
11672
- useEffect19(() => {
11689
+ useEffect20(() => {
11673
11690
  setHasMounted(true);
11674
11691
  }, []);
11675
11692
  if (!hasMounted) {
@@ -12622,7 +12639,7 @@ function AccessDenied({
12622
12639
 
12623
12640
  // ../../components/ui/ThemeToggleHeadless.tsx
12624
12641
  import { Moon, Sun, Monitor } from "lucide-react";
12625
- import { useEffect as useEffect21, useRef as useRef19, useState as useState39 } from "react";
12642
+ import { useEffect as useEffect22, useRef as useRef19, useState as useState39 } from "react";
12626
12643
  import { createPortal as createPortal11 } from "react-dom";
12627
12644
  import { Fragment as Fragment21, jsx as jsx61, jsxs as jsxs55 } from "react/jsx-runtime";
12628
12645
  function ThemeToggleHeadless({
@@ -12635,7 +12652,7 @@ function ThemeToggleHeadless({
12635
12652
  const [mounted, setMounted] = useState39(false);
12636
12653
  const triggerRef = useRef19(null);
12637
12654
  const [dropdownPosition, setDropdownPosition] = useState39(null);
12638
- useEffect21(() => setMounted(true), []);
12655
+ useEffect22(() => setMounted(true), []);
12639
12656
  const themes = [
12640
12657
  { value: "light", label: labels?.light ?? "Light", icon: Sun },
12641
12658
  { value: "dark", label: labels?.dark ?? "Dark", icon: Moon },