@underverse-ui/underverse 1.0.98 → 1.0.100

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/README.md CHANGED
@@ -109,27 +109,26 @@ All components follow [Vercel Web Interface Guidelines](https://github.com/verce
109
109
 
110
110
  ---
111
111
 
112
- ## Entry Points
112
+ ## Package Exports
113
113
 
114
- Package được chia thành 2 entry points để tối ưu cho Server Components:
115
-
116
- ### Main Entry (Server-safe)
114
+ Hiện tại package publish **một public entry point duy nhất**:
117
115
 
118
116
  ```tsx
119
- // dist/index.js - Các components không phụ thuộc react-hook-form
120
- // Có thể sử dụng trong cả Server Components và Client Components
121
- import { Button, Skeleton, DatePicker, DataTable } from "@underverse-ui/underverse";
117
+ import {
118
+ Button,
119
+ DataTable,
120
+ Form,
121
+ FormField,
122
+ UEditor,
123
+ } from "@underverse-ui/underverse";
122
124
  ```
123
125
 
124
- ### Form Entry (Client-only)
125
-
126
- ```tsx
127
- // dist/form.js - Form components (phụ thuộc react-hook-form)
128
- // Chỉ sử dụng trong Client Components ("use client")
129
- import { Form, FormField, FormItem, FormLabel, FormMessage } from "@underverse-ui/underverse/form";
130
- ```
126
+ Không subpath export như `@underverse-ui/underverse/form` ở version hiện tại.
131
127
 
132
- **Lưu ý:** Form components yêu cầu `react-hook-form` và `@hookform/resolvers` nên chỉ hoạt động ở client-side.
128
+ Lưu ý:
129
+ - Nhiều component trong package là client component và nên dùng trong môi trường React client.
130
+ - Form primitives yêu cầu `react-hook-form` và `@hookform/resolvers`.
131
+ - `UEditor` và các component dựa trên Tiptap yêu cầu peer dependencies tương ứng.
133
132
 
134
133
  ---
135
134
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "1.0.98",
3
+ "version": "1.0.100",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 225,
6
6
  "exports": [
@@ -361,7 +361,7 @@
361
361
  {
362
362
  "name": "cn",
363
363
  "kind": "value",
364
- "source": "../../../lib/utils/cn",
364
+ "source": "./utils/cn",
365
365
  "reexport": true,
366
366
  "local": false
367
367
  },
@@ -1487,14 +1487,14 @@
1487
1487
  {
1488
1488
  "name": "UnderverseProvider",
1489
1489
  "kind": "value",
1490
- "source": "../../../lib/i18n/translation-adapter",
1490
+ "source": "./contexts/translation-adapter",
1491
1491
  "reexport": true,
1492
1492
  "local": false
1493
1493
  },
1494
1494
  {
1495
1495
  "name": "UnderverseProviderProps",
1496
1496
  "kind": "type",
1497
- "source": "../../../lib/i18n/translation-adapter",
1497
+ "source": "./contexts/translation-adapter",
1498
1498
  "reexport": true,
1499
1499
  "local": false
1500
1500
  },
@@ -1557,7 +1557,7 @@
1557
1557
  {
1558
1558
  "name": "useUnderverseI18n",
1559
1559
  "kind": "value",
1560
- "source": "../../../lib/i18n/translation-adapter",
1560
+ "source": "./contexts/translation-adapter",
1561
1561
  "reexport": true,
1562
1562
  "local": false,
1563
1563
  "aliasOf": "useTranslations"
@@ -1565,7 +1565,7 @@
1565
1565
  {
1566
1566
  "name": "useUnderverseI18nLocale",
1567
1567
  "kind": "value",
1568
- "source": "../../../lib/i18n/translation-adapter",
1568
+ "source": "./contexts/translation-adapter",
1569
1569
  "reexport": true,
1570
1570
  "local": false,
1571
1571
  "aliasOf": "useLocale"
@@ -1587,7 +1587,7 @@
1587
1587
  {
1588
1588
  "name": "VARIANT_STYLES_ALERT",
1589
1589
  "kind": "value",
1590
- "source": "../../../lib/constants/constants-ui/alert",
1590
+ "source": "./constants/alert",
1591
1591
  "reexport": true,
1592
1592
  "local": false
1593
1593
  },
package/dist/index.cjs CHANGED
@@ -171,7 +171,7 @@ __export(index_exports, {
171
171
  VIETNAM_HOLIDAYS: () => VIETNAM_HOLIDAYS,
172
172
  VerticalTabs: () => VerticalTabs,
173
173
  Watermark: () => Watermark_default,
174
- cn: () => cn2,
174
+ cn: () => cn,
175
175
  cnLocal: () => cn,
176
176
  extractImageSrcsFromHtml: () => extractImageSrcsFromHtml,
177
177
  getAnimationStyles: () => getAnimationStyles,
@@ -2000,8 +2000,9 @@ function useSmartTranslations(namespace) {
2000
2000
  return internalT;
2001
2001
  }
2002
2002
  return (key) => {
2003
- const primaryLocale = nextIntlBridge?.locale ?? internalLocale;
2004
- const fallbackLocale = environmentLocale && environmentLocale !== primaryLocale ? environmentLocale : null;
2003
+ const resolvedEnvironmentLocale = environmentLocale && environmentLocale !== internalLocale ? environmentLocale : null;
2004
+ const primaryLocale = nextIntlBridge?.locale ?? resolvedEnvironmentLocale ?? internalLocale;
2005
+ const fallbackLocale = resolvedEnvironmentLocale && resolvedEnvironmentLocale !== primaryLocale ? resolvedEnvironmentLocale : null;
2005
2006
  let translated = null;
2006
2007
  if (nextIntlBridge) {
2007
2008
  const nextIntlResult = nextIntlBridge.translate(namespace, key);
@@ -2033,10 +2034,20 @@ function useSmartLocale() {
2033
2034
  const forceInternal = React6.useContext(ForceInternalContext);
2034
2035
  const nextIntlBridge = useNextIntlBridge();
2035
2036
  const internalLocale = useUnderverseLocale();
2037
+ const [environmentLocale, setEnvironmentLocale] = React6.useState(null);
2038
+ React6.useEffect(() => {
2039
+ if (forceInternal) return;
2040
+ if (nextIntlBridge) return;
2041
+ if (internalLocale !== "en") return;
2042
+ const detected = getEnvironmentLocale(internalLocale);
2043
+ if (detected !== internalLocale) {
2044
+ setEnvironmentLocale(detected);
2045
+ }
2046
+ }, [forceInternal, internalLocale, nextIntlBridge]);
2036
2047
  if (forceInternal) {
2037
2048
  return internalLocale;
2038
2049
  }
2039
- return nextIntlBridge?.locale ?? internalLocale;
2050
+ return nextIntlBridge?.locale ?? environmentLocale ?? internalLocale;
2040
2051
  }
2041
2052
 
2042
2053
  // src/components/Input.tsx
@@ -7381,9 +7392,10 @@ var Combobox = ({
7381
7392
  {
7382
7393
  id: `combobox-item-${index}`,
7383
7394
  type: "button",
7395
+ role: "option",
7384
7396
  tabIndex: -1,
7385
7397
  disabled: itemDisabled,
7386
- "aria-pressed": isSelected,
7398
+ "aria-selected": isSelected,
7387
7399
  onClick: () => handleSelect(item),
7388
7400
  style: {
7389
7401
  animationDelay: open ? `${Math.min(index * 15, 150)}ms` : "0ms"
@@ -7488,40 +7500,50 @@ var Combobox = ({
7488
7500
  }
7489
7501
  )
7490
7502
  ] }) }),
7491
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { ref: optionsViewportRef, className: "overflow-y-auto overscroll-contain", style: { maxHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: cn(size === "sm" ? "p-1" : size === "lg" ? "p-2" : "p-1.5"), children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
7492
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "relative", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-10 h-10 rounded-full border-2 border-primary/20 border-t-primary animate-spin" }) }),
7493
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: loadingText })
7494
- ] }) }) : filteredOptions.length > 0 ? groupedOptions ? (
7495
- // Render grouped options with global index tracking
7496
- (() => {
7497
- let globalIndex = 0;
7498
- return Object.entries(groupedOptions).map(([group, items]) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: cn(globalIndex > 0 && "mt-2 pt-2 border-t border-border/30"), children: [
7499
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "px-3 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: group }),
7500
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "space-y-0.5", children: items.map((item) => {
7501
- const index = globalIndex++;
7502
- return renderOptionItem(item, index);
7503
- }) })
7504
- ] }, group));
7505
- })()
7506
- ) : (
7507
- // Render flat options
7508
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "space-y-0.5", children: filteredOptions.map((item, index) => renderOptionItem(item, index)) })
7509
- ) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
7510
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-12 h-12 rounded-full bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react13.SearchX, { className: "h-6 w-6 text-muted-foreground/60" }) }),
7511
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "space-y-1", children: [
7512
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "block text-sm font-medium text-foreground", children: emptyText }),
7513
- query && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "block text-xs text-muted-foreground", children: "Try a different search term" })
7514
- ] }),
7515
- query && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
7516
- "button",
7517
- {
7518
- type: "button",
7519
- onClick: () => setQuery(""),
7520
- className: "px-3 py-1.5 text-xs font-medium text-primary bg-primary/10 rounded-full hover:bg-primary/20 transition-colors",
7521
- children: "Clear search"
7522
- }
7523
- )
7524
- ] }) }) }) })
7503
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
7504
+ "div",
7505
+ {
7506
+ ref: optionsViewportRef,
7507
+ role: "listbox",
7508
+ "aria-labelledby": labelId,
7509
+ className: "overflow-y-auto overscroll-contain",
7510
+ style: { maxHeight },
7511
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: cn(size === "sm" ? "p-1" : size === "lg" ? "p-2" : "p-1.5"), children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
7512
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "relative", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-10 h-10 rounded-full border-2 border-primary/20 border-t-primary animate-spin" }) }),
7513
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: loadingText })
7514
+ ] }) }) : filteredOptions.length > 0 ? groupedOptions ? (
7515
+ // Render grouped options with global index tracking
7516
+ (() => {
7517
+ let globalIndex = 0;
7518
+ return Object.entries(groupedOptions).map(([group, items]) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: cn(globalIndex > 0 && "mt-2 pt-2 border-t border-border/30"), children: [
7519
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "px-3 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: group }),
7520
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "space-y-0.5", children: items.map((item) => {
7521
+ const index = globalIndex++;
7522
+ return renderOptionItem(item, index);
7523
+ }) })
7524
+ ] }, group));
7525
+ })()
7526
+ ) : (
7527
+ // Render flat options
7528
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "space-y-0.5", children: filteredOptions.map((item, index) => renderOptionItem(item, index)) })
7529
+ ) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
7530
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-12 h-12 rounded-full bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react13.SearchX, { className: "h-6 w-6 text-muted-foreground/60" }) }),
7531
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "space-y-1", children: [
7532
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "block text-sm font-medium text-foreground", children: emptyText }),
7533
+ query && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "block text-xs text-muted-foreground", children: "Try a different search term" })
7534
+ ] }),
7535
+ query && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
7536
+ "button",
7537
+ {
7538
+ type: "button",
7539
+ onClick: () => setQuery(""),
7540
+ className: "px-3 py-1.5 text-xs font-medium text-primary bg-primary/10 rounded-full hover:bg-primary/20 transition-colors",
7541
+ children: "Clear search"
7542
+ }
7543
+ )
7544
+ ] }) }) })
7545
+ }
7546
+ )
7525
7547
  ]
7526
7548
  }
7527
7549
  );
@@ -17161,6 +17183,42 @@ function getInitialExpandedNodes(categories, {
17161
17183
  function getExpandedNodesState(expandedIds, uncontrolledExpandedNodes) {
17162
17184
  return expandedIds !== void 0 ? new Set(expandedIds) : uncontrolledExpandedNodes;
17163
17185
  }
17186
+ function collectAncestorIds(categories, categoryId) {
17187
+ const ancestorIds = getAncestorPathIds(categories, categoryId);
17188
+ ancestorIds.delete(categoryId);
17189
+ return ancestorIds;
17190
+ }
17191
+ function collectDescendantIds(childrenMap, categoryId) {
17192
+ const descendants = /* @__PURE__ */ new Set();
17193
+ const stack = [categoryId];
17194
+ while (stack.length > 0) {
17195
+ const currentId = stack.pop();
17196
+ for (const child of childrenMap.get(currentId) ?? []) {
17197
+ if (descendants.has(child.id)) continue;
17198
+ descendants.add(child.id);
17199
+ stack.push(child.id);
17200
+ }
17201
+ }
17202
+ return descendants;
17203
+ }
17204
+ function pruneAncestorSelection(categories, childrenMap, selected, fromCategoryId) {
17205
+ const byId = new Map(categories.map((category) => [category.id, category]));
17206
+ let current = byId.get(fromCategoryId);
17207
+ let guard = 0;
17208
+ while (current && typeof current.parent_id === "number" && guard++ < categories.length) {
17209
+ const parent = byId.get(current.parent_id);
17210
+ if (!parent) break;
17211
+ const descendantIds = collectDescendantIds(childrenMap, parent.id);
17212
+ const hasSelectedDescendant = Array.from(descendantIds).some((id) => selected.has(id));
17213
+ if (!hasSelectedDescendant) {
17214
+ selected.delete(parent.id);
17215
+ }
17216
+ current = parent;
17217
+ }
17218
+ }
17219
+ function toCategoryOrderSelection(categories, selected) {
17220
+ return categories.map((category) => category.id).filter((categoryId) => selected.has(categoryId));
17221
+ }
17164
17222
  function CategoryTreeSelect(props) {
17165
17223
  const tv = useSmartTranslations("ValidationInput");
17166
17224
  const {
@@ -17321,15 +17379,17 @@ function CategoryTreeSelect(props) {
17321
17379
  const newSelected = new Set(valueArray);
17322
17380
  if (newSelected.has(categoryId)) {
17323
17381
  newSelected.delete(categoryId);
17324
- const children = childrenMap.get(categoryId) || [];
17325
- children.forEach((child) => newSelected.delete(child.id));
17382
+ for (const descendantId of collectDescendantIds(childrenMap, categoryId)) {
17383
+ newSelected.delete(descendantId);
17384
+ }
17385
+ pruneAncestorSelection(categories, childrenMap, newSelected, categoryId);
17326
17386
  } else {
17327
17387
  newSelected.add(categoryId);
17328
- if (category.parent_id) {
17329
- newSelected.add(category.parent_id);
17388
+ for (const ancestorId of collectAncestorIds(categories, categoryId)) {
17389
+ newSelected.add(ancestorId);
17330
17390
  }
17331
17391
  }
17332
- onChange(Array.from(newSelected));
17392
+ onChange(toCategoryOrderSelection(categories, newSelected));
17333
17393
  }
17334
17394
  };
17335
17395
  const renderCategory = (category, level = 0) => {
@@ -17683,11 +17743,10 @@ function CategoryTreeSelect(props) {
17683
17743
  "shadow-2xl backdrop-blur-xl"
17684
17744
  ),
17685
17745
  trigger: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
17686
- "button",
17746
+ "div",
17687
17747
  {
17688
17748
  id: resolvedId,
17689
- type: "button",
17690
- disabled,
17749
+ tabIndex: disabled ? -1 : 0,
17691
17750
  role: "combobox",
17692
17751
  "aria-haspopup": "tree",
17693
17752
  "aria-expanded": isOpen,
@@ -17696,6 +17755,13 @@ function CategoryTreeSelect(props) {
17696
17755
  "aria-describedby": describedBy,
17697
17756
  "aria-required": required,
17698
17757
  "aria-invalid": !!effectiveError,
17758
+ onKeyDown: (event) => {
17759
+ if (disabled) return;
17760
+ if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
17761
+ event.preventDefault();
17762
+ handleOpenChange(!isOpen);
17763
+ }
17764
+ },
17699
17765
  className: cn(
17700
17766
  "group flex w-full items-center justify-between rounded-full transition-all duration-200",
17701
17767
  "backdrop-blur-sm",
@@ -17734,13 +17800,11 @@ function CategoryTreeSelect(props) {
17734
17800
  ] }),
17735
17801
  /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "ml-2 flex shrink-0 items-center gap-1.5", children: [
17736
17802
  allowClear && selectedCount > 0 && !disabled && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
17737
- "div",
17803
+ "button",
17738
17804
  {
17739
- role: "button",
17740
- tabIndex: 0,
17805
+ type: "button",
17741
17806
  "aria-label": "Clear selection",
17742
17807
  onClick: handleClear,
17743
- onKeyDown: (event) => (event.key === "Enter" || event.key === " ") && handleClear(event),
17744
17808
  className: cn(
17745
17809
  "opacity-0 group-hover:opacity-100 transition-all duration-200",
17746
17810
  "p-1 rounded-lg hover:bg-destructive/10 text-muted-foreground hover:text-destructive",
@@ -21447,6 +21511,18 @@ function DataTableBodyRows({
21447
21511
  var import_react30 = __toESM(require("react"), 1);
21448
21512
  var import_lucide_react35 = require("lucide-react");
21449
21513
  var import_jsx_runtime64 = require("react/jsx-runtime");
21514
+ function getColumnLabel(title) {
21515
+ if (typeof title === "string" || typeof title === "number") {
21516
+ return String(title).replace(/\s+/g, " ").trim();
21517
+ }
21518
+ if (Array.isArray(title)) {
21519
+ return title.map((item) => getColumnLabel(item)).filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
21520
+ }
21521
+ if (import_react30.default.isValidElement(title)) {
21522
+ return getColumnLabel(title.props.children);
21523
+ }
21524
+ return "";
21525
+ }
21450
21526
  function DataTableHeader({
21451
21527
  headerRows,
21452
21528
  headerAlign,
@@ -21471,12 +21547,13 @@ function DataTableHeader({
21471
21547
  if (!col.filter) return null;
21472
21548
  const key = col.key;
21473
21549
  const commonProps = { className: "w-full", size };
21550
+ const columnLabel = getColumnLabel(col.title) || key;
21474
21551
  if (col.filter.type === "text") {
21475
21552
  return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
21476
21553
  Input_default,
21477
21554
  {
21478
21555
  ...commonProps,
21479
- placeholder: col.filter.placeholder || `Search ${String(col.title)}`,
21556
+ placeholder: col.filter.placeholder || `Search ${columnLabel}`,
21480
21557
  value: filters[key] || "",
21481
21558
  onChange: (e) => {
21482
21559
  setCurPage(1);
@@ -21497,7 +21574,7 @@ function DataTableHeader({
21497
21574
  setCurPage(1);
21498
21575
  setFilters((prev) => ({ ...prev, [key]: value || void 0 }));
21499
21576
  },
21500
- placeholder: col.filter.placeholder || `Select ${String(col.title)}`
21577
+ placeholder: col.filter.placeholder || `Select ${columnLabel}`
21501
21578
  }
21502
21579
  );
21503
21580
  }
@@ -21506,7 +21583,7 @@ function DataTableHeader({
21506
21583
  DatePicker,
21507
21584
  {
21508
21585
  size,
21509
- placeholder: col.filter.placeholder || `Select ${String(col.title)}`,
21586
+ placeholder: col.filter.placeholder || `Select ${columnLabel}`,
21510
21587
  value: filters[key] || null,
21511
21588
  onChange: (date) => {
21512
21589
  setCurPage(1);
@@ -21538,16 +21615,19 @@ function DataTableHeader({
21538
21615
  }
21539
21616
  const isRightAlign = col.align === "right" || !col.align && headerAlign === "right";
21540
21617
  const isCenterAlign = col.align === "center" || !col.align && headerAlign === "center";
21618
+ const columnLabel = getColumnLabel(col.title) || col.key;
21541
21619
  const titleContent = /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: "flex items-center gap-1", children: [
21542
21620
  /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: cn("font-medium whitespace-nowrap select-text", headerTitleClass), children: col.title }),
21543
21621
  col.sortable && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
21544
21622
  Tooltip,
21545
21623
  {
21546
21624
  placement: "top",
21547
- content: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "text-xs font-medium", children: `Sort by ${String(col.title)}` }),
21625
+ content: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "text-xs font-medium", children: `Sort by ${columnLabel}` }),
21548
21626
  children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
21549
21627
  "button",
21550
21628
  {
21629
+ type: "button",
21630
+ title: `Sort by ${columnLabel}`,
21551
21631
  className: cn(
21552
21632
  "p-1 rounded-lg transition-all duration-200 hover:bg-accent",
21553
21633
  sort?.key === col.key ? "opacity-100 bg-accent" : "opacity-60 hover:opacity-100"
@@ -21560,7 +21640,7 @@ function DataTableHeader({
21560
21640
  return null;
21561
21641
  });
21562
21642
  },
21563
- "aria-label": "Sort",
21643
+ "aria-label": `Sort by ${columnLabel}`,
21564
21644
  children: /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("svg", { viewBox: "0 0 20 20", fill: "none", className: cn("inline-block", sortIconClass), children: [
21565
21645
  /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
21566
21646
  "path",
@@ -21598,15 +21678,17 @@ function DataTableHeader({
21598
21678
  Tooltip,
21599
21679
  {
21600
21680
  placement: "top",
21601
- content: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "text-xs font-medium", children: `Filter by ${String(col.title)}` }),
21681
+ content: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "text-xs font-medium", children: `Filter by ${columnLabel}` }),
21602
21682
  children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
21603
21683
  "button",
21604
21684
  {
21685
+ type: "button",
21686
+ title: `Filter by ${columnLabel}`,
21605
21687
  className: cn(
21606
21688
  "p-1.5 rounded-lg transition-all duration-200 hover:bg-accent",
21607
21689
  filters[col.key] ? "bg-accent text-primary" : "text-muted-foreground"
21608
21690
  ),
21609
- "aria-label": "Filter",
21691
+ "aria-label": `Filter by ${columnLabel}`,
21610
21692
  children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(import_lucide_react35.Filter, { className: "w-4 h-4" })
21611
21693
  }
21612
21694
  )
@@ -22762,7 +22844,7 @@ var useFormField = () => {
22762
22844
  var FormItemContext = React63.createContext({});
22763
22845
  var FormItem = React63.forwardRef(({ className, ...props }, ref) => {
22764
22846
  const id = React63.useId();
22765
- return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { ref, className: cn("space-y-2", className), ...props }) });
22847
+ return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { ref, className: cn("group space-y-2", className), ...props }) });
22766
22848
  });
22767
22849
  FormItem.displayName = "FormItem";
22768
22850
  var FormLabel = React63.forwardRef(
@@ -22770,10 +22852,24 @@ var FormLabel = React63.forwardRef(
22770
22852
  const { error, formItemId } = useFormField();
22771
22853
  const config = React63.useContext(FormConfigContext);
22772
22854
  const sizeClass = config.size === "sm" ? "text-xs" : config.size === "lg" ? "text-base" : "text-sm";
22773
- return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(Label, { ref, className: cn(sizeClass, error && "text-destructive", className), htmlFor: formItemId, ...props, children: [
22774
- children,
22775
- required && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "text-destructive ml-1", children: "*" })
22776
- ] });
22855
+ return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(
22856
+ Label,
22857
+ {
22858
+ ref,
22859
+ className: cn(
22860
+ sizeClass,
22861
+ "transition-colors duration-200",
22862
+ error ? "text-destructive" : "group-focus-within:text-primary",
22863
+ className
22864
+ ),
22865
+ htmlFor: formItemId,
22866
+ ...props,
22867
+ children: [
22868
+ children,
22869
+ required && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "text-destructive ml-1", children: "*" })
22870
+ ]
22871
+ }
22872
+ );
22777
22873
  }
22778
22874
  );
22779
22875
  FormLabel.displayName = "FormLabel";
@@ -23122,16 +23218,8 @@ function LanguageSwitcherHeadless({
23122
23218
  ] });
23123
23219
  }
23124
23220
 
23125
- // ../../lib/utils/cn.ts
23126
- var import_clsx2 = require("clsx");
23127
- var import_tailwind_merge2 = require("tailwind-merge");
23128
- function cn2(...inputs) {
23129
- return (0, import_tailwind_merge2.twMerge)((0, import_clsx2.clsx)(...inputs));
23130
- }
23131
-
23132
- // ../../lib/constants/constants-ui/alert.ts
23221
+ // src/constants/alert.ts
23133
23222
  var VARIANT_STYLES_ALERT = {
23134
- // Use system colors for background + border; leave text colors to content
23135
23223
  default: "border border-border bg-card/60 backdrop-blur-sm",
23136
23224
  info: "border border-info/30 bg-info/10 backdrop-blur-sm",
23137
23225
  success: "border border-success/30 bg-success/10 backdrop-blur-sm",
@@ -23139,7 +23227,7 @@ var VARIANT_STYLES_ALERT = {
23139
23227
  error: "border border-destructive/30 bg-destructive/10 backdrop-blur-sm"
23140
23228
  };
23141
23229
 
23142
- // ../../lib/i18n/translation-adapter.tsx
23230
+ // src/contexts/translation-adapter.tsx
23143
23231
  var React65 = __toESM(require("react"), 1);
23144
23232
  var import_jsx_runtime73 = require("react/jsx-runtime");
23145
23233
  function isUnresolvedTranslation2(value, namespace, key) {
@@ -31787,6 +31875,7 @@ var UEditor = import_react52.default.forwardRef(({
31787
31875
  const t = useSmartTranslations("UEditor");
31788
31876
  const effectivePlaceholder = placeholder ?? t("placeholder");
31789
31877
  const inFlightPrepareRef = (0, import_react52.useRef)(null);
31878
+ const lastAppliedContentRef = (0, import_react52.useRef)(content ?? "");
31790
31879
  const editorContentRef = (0, import_react52.useRef)(null);
31791
31880
  const tableColumnGuideRef = (0, import_react52.useRef)(null);
31792
31881
  const tableRowGuideRef = (0, import_react52.useRef)(null);
@@ -32089,10 +32178,12 @@ var UEditor = import_react52.default.forwardRef(({
32089
32178
  [content, editor, uploadImageForSave]
32090
32179
  );
32091
32180
  (0, import_react52.useEffect)(() => {
32092
- if (editor && content !== editor.getHTML()) {
32093
- if (editor.isEmpty && content) {
32094
- editor.commands.setContent(content);
32095
- }
32181
+ if (!editor) return;
32182
+ const nextContent = content ?? "";
32183
+ if (lastAppliedContentRef.current === nextContent) return;
32184
+ lastAppliedContentRef.current = nextContent;
32185
+ if (editor.getHTML() !== nextContent) {
32186
+ editor.commands.setContent(nextContent, { emitUpdate: false });
32096
32187
  }
32097
32188
  }, [content, editor]);
32098
32189
  (0, import_react52.useEffect)(() => {