@omnibase/shadcn 0.7.2 → 0.8.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.
Files changed (3) hide show
  1. package/dist/index.cjs +190 -23
  2. package/dist/index.js +192 -25
  3. package/package.json +3 -2
package/dist/index.cjs CHANGED
@@ -1886,19 +1886,96 @@ function CommandItem({
1886
1886
  // src/permissions-selector/index.tsx
1887
1887
  var import_lucide_react5 = require("lucide-react");
1888
1888
  var import_jsx_runtime29 = require("react/jsx-runtime");
1889
+ function groupOptions(options) {
1890
+ const result = {
1891
+ ungrouped: [],
1892
+ groups: {}
1893
+ };
1894
+ for (const option of options) {
1895
+ if (!option.group) {
1896
+ result.ungrouped.push(option);
1897
+ continue;
1898
+ }
1899
+ if (!result.groups[option.group]) {
1900
+ result.groups[option.group] = { options: [], subGroups: {} };
1901
+ }
1902
+ if (option.subGroup) {
1903
+ if (!result.groups[option.group].subGroups[option.subGroup]) {
1904
+ result.groups[option.group].subGroups[option.subGroup] = [];
1905
+ }
1906
+ result.groups[option.group].subGroups[option.subGroup].push(option);
1907
+ } else {
1908
+ result.groups[option.group].options.push(option);
1909
+ }
1910
+ }
1911
+ return result;
1912
+ }
1913
+ function renderCommandItem(option, value, onChange, setOpen, indent = 0) {
1914
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1915
+ CommandItem,
1916
+ {
1917
+ value: option.label,
1918
+ onSelect: () => {
1919
+ onChange(option.value);
1920
+ setOpen(false);
1921
+ },
1922
+ className: indent > 0 ? "pl-8" : void 0,
1923
+ children: [
1924
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1925
+ import_lucide_react5.Check,
1926
+ {
1927
+ className: cn(
1928
+ "mr-2 h-4 w-4",
1929
+ value === option.value ? "opacity-100" : "opacity-0"
1930
+ )
1931
+ }
1932
+ ),
1933
+ option.label
1934
+ ]
1935
+ },
1936
+ option.value
1937
+ );
1938
+ }
1889
1939
  function Combobox({
1890
1940
  options,
1891
1941
  value,
1892
1942
  onChange,
1943
+ onSelectMultiple,
1893
1944
  placeholder,
1894
1945
  searchPlaceholder = "Search...",
1895
1946
  emptyText = "No results found.",
1896
1947
  disabled = false,
1897
- className = ""
1948
+ className = "",
1949
+ grouped = false
1898
1950
  }) {
1899
1951
  const [open, setOpen] = (0, import_react3.useState)(false);
1900
1952
  const selectedOption = options.find((opt) => opt.value === value);
1901
1953
  const isEmpty = options.length === 0;
1954
+ const groupedOpts = (0, import_react3.useMemo)(
1955
+ () => grouped ? groupOptions(options) : null,
1956
+ [options, grouped]
1957
+ );
1958
+ const hasGroups = groupedOpts && Object.keys(groupedOpts.groups).length > 0;
1959
+ const handleSelectGroup = (groupName) => {
1960
+ if (!groupedOpts || !onSelectMultiple) return;
1961
+ const group = groupedOpts.groups[groupName];
1962
+ if (!group) return;
1963
+ const allValues = [
1964
+ ...group.options.map((o) => o.value),
1965
+ ...Object.values(group.subGroups).flatMap(
1966
+ (opts) => opts.map((o) => o.value)
1967
+ )
1968
+ ];
1969
+ onSelectMultiple(allValues);
1970
+ setOpen(false);
1971
+ };
1972
+ const handleSelectSubGroup = (groupName, subGroupName) => {
1973
+ if (!groupedOpts || !onSelectMultiple) return;
1974
+ const subOpts = groupedOpts.groups[groupName]?.subGroups[subGroupName];
1975
+ if (!subOpts) return;
1976
+ onSelectMultiple(subOpts.map((o) => o.value));
1977
+ setOpen(false);
1978
+ };
1902
1979
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(Popover, { open, onOpenChange: setOpen, children: [
1903
1980
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1904
1981
  Button,
@@ -1927,29 +2004,69 @@ function Combobox({
1927
2004
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CommandInput, { placeholder: searchPlaceholder }),
1928
2005
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(CommandList, { children: [
1929
2006
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CommandEmpty, { children: emptyText }),
1930
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CommandGroup, { children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1931
- CommandItem,
1932
- {
1933
- value: option.label,
1934
- onSelect: () => {
1935
- onChange(option.value);
1936
- setOpen(false);
1937
- },
1938
- children: [
1939
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1940
- import_lucide_react5.Check,
1941
- {
1942
- className: cn(
1943
- "mr-2 h-4 w-4",
1944
- value === option.value ? "opacity-100" : "opacity-0"
2007
+ grouped && hasGroups ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
2008
+ groupedOpts.ungrouped.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CommandGroup, { children: groupedOpts.ungrouped.map(
2009
+ (opt) => renderCommandItem(opt, value, onChange, setOpen)
2010
+ ) }),
2011
+ Object.entries(groupedOpts.groups).map(
2012
+ ([groupName, group]) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
2013
+ CommandGroup,
2014
+ {
2015
+ heading: onSelectMultiple ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
2016
+ "button",
2017
+ {
2018
+ type: "button",
2019
+ onClick: (e) => {
2020
+ e.preventDefault();
2021
+ e.stopPropagation();
2022
+ handleSelectGroup(groupName);
2023
+ },
2024
+ className: "flex items-center gap-1.5 hover:text-foreground transition-colors group w-full text-left",
2025
+ children: [
2026
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react5.Plus, { className: "h-3 w-3 opacity-0 group-hover:opacity-70 transition-opacity" }),
2027
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { children: groupName })
2028
+ ]
2029
+ }
2030
+ ) : groupName,
2031
+ children: [
2032
+ group.options.map(
2033
+ (opt) => renderCommandItem(opt, value, onChange, setOpen)
2034
+ ),
2035
+ Object.entries(group.subGroups).map(
2036
+ ([subGroupName, subOpts]) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "mt-1", children: [
2037
+ onSelectMultiple ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
2038
+ "button",
2039
+ {
2040
+ type: "button",
2041
+ onClick: (e) => {
2042
+ e.preventDefault();
2043
+ e.stopPropagation();
2044
+ handleSelectSubGroup(groupName, subGroupName);
2045
+ },
2046
+ className: "flex items-center gap-1.5 px-2 py-1 text-[11px] text-muted-foreground/70 uppercase tracking-wider hover:text-muted-foreground transition-colors group w-full text-left",
2047
+ children: [
2048
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-muted-foreground/40 group-hover:text-muted-foreground/70", children: "\u2014" }),
2049
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react5.Plus, { className: "h-2.5 w-2.5 opacity-0 group-hover:opacity-70 transition-opacity" }),
2050
+ subGroupName
2051
+ ]
2052
+ }
2053
+ ) : /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-1.5 px-2 py-1 text-[11px] text-muted-foreground/70 uppercase tracking-wider", children: [
2054
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-muted-foreground/40", children: "\u2014" }),
2055
+ subGroupName
2056
+ ] }),
2057
+ subOpts.map(
2058
+ (opt) => renderCommandItem(opt, value, onChange, setOpen, 1)
2059
+ )
2060
+ ] }, subGroupName)
1945
2061
  )
1946
- }
1947
- ),
1948
- option.label
1949
- ]
1950
- },
1951
- option.value
1952
- )) })
2062
+ ]
2063
+ },
2064
+ groupName
2065
+ )
2066
+ )
2067
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CommandGroup, { children: options.map(
2068
+ (option) => renderCommandItem(option, value, onChange, setOpen)
2069
+ ) })
1953
2070
  ] })
1954
2071
  ] })
1955
2072
  }
@@ -1992,8 +2109,20 @@ function PermissionsSelector({
1992
2109
  const getRelationsForNamespace = (namespace) => {
1993
2110
  const def = definitions.find((d) => d.namespace === namespace);
1994
2111
  if (!def) return [];
2112
+ if (def.relationsMetadata && def.relationsMetadata.length > 0) {
2113
+ return def.relationsMetadata.map((rm) => ({
2114
+ value: rm.name,
2115
+ label: rm.displayName,
2116
+ group: rm.group ?? null,
2117
+ subGroup: rm.subGroup ?? null
2118
+ }));
2119
+ }
1995
2120
  return def.relations.filter((rel) => rel.startsWith("can_") || rel.startsWith("is_")).map((rel) => ({ value: rel, label: formatRelation(rel) }));
1996
2121
  };
2122
+ const hasRelationsMetadata = (namespace) => {
2123
+ const def = definitions.find((d) => d.namespace === namespace);
2124
+ return !!(def?.relationsMetadata && def.relationsMetadata.length > 0);
2125
+ };
1997
2126
  const getObjectsForNamespace = (namespace) => {
1998
2127
  if (namespace.toLowerCase() === "tenant") {
1999
2128
  return null;
@@ -2034,6 +2163,42 @@ function PermissionsSelector({
2034
2163
  return finalRows;
2035
2164
  });
2036
2165
  };
2166
+ const addMultiplePermissions = (rowId, namespace, relations) => {
2167
+ if (relations.length === 0) return;
2168
+ setPermissionRows((currentRows) => {
2169
+ const rowIndex = currentRows.findIndex((r) => r.id === rowId);
2170
+ if (rowIndex === -1) return currentRows;
2171
+ const existingRelations = new Set(
2172
+ currentRows.filter((r) => r.namespace === namespace && r.relation).map((r) => r.relation)
2173
+ );
2174
+ const newRelations = relations.filter((r) => !existingRelations.has(r));
2175
+ if (newRelations.length === 0) return currentRows;
2176
+ const updatedRows = [...currentRows];
2177
+ updatedRows[rowIndex] = {
2178
+ ...updatedRows[rowIndex],
2179
+ namespace,
2180
+ relation: newRelations[0]
2181
+ };
2182
+ const additionalRows = newRelations.slice(1).map((relation) => ({
2183
+ id: generateId(),
2184
+ namespace,
2185
+ relation,
2186
+ objectId: ""
2187
+ }));
2188
+ updatedRows.splice(rowIndex + 1, 0, ...additionalRows);
2189
+ const lastRow = updatedRows[updatedRows.length - 1];
2190
+ if (hasAnyValue(lastRow)) {
2191
+ updatedRows.push({
2192
+ id: generateId(),
2193
+ namespace: "",
2194
+ relation: "",
2195
+ objectId: ""
2196
+ });
2197
+ }
2198
+ onPermissionsChange?.(updatedRows);
2199
+ return updatedRows;
2200
+ });
2201
+ };
2037
2202
  const buildPermissions = () => {
2038
2203
  return permissionRows.filter((row) => row.namespace && row.relation).map((row) => buildPermissionString(row)).filter((perm) => perm !== "");
2039
2204
  };
@@ -2079,9 +2244,11 @@ function PermissionsSelector({
2079
2244
  options: relationOptions,
2080
2245
  value: row.relation,
2081
2246
  onChange: (value) => updatePermissionRow(row.id, "relation", value),
2247
+ onSelectMultiple: (relations) => addMultiplePermissions(row.id, row.namespace, relations),
2082
2248
  placeholder: "Permission",
2083
2249
  searchPlaceholder: "Search permissions...",
2084
2250
  disabled: disabled || !row.namespace,
2251
+ grouped: hasRelationsMetadata(row.namespace),
2085
2252
  className: cn(
2086
2253
  "min-w-40 ease-out duration-450",
2087
2254
  showObjectSelect ? "w-1/3 rounded-none border-r-0" : "w-2/3 rounded-l-none"
package/dist/index.js CHANGED
@@ -1827,21 +1827,98 @@ function CommandItem({
1827
1827
  }
1828
1828
 
1829
1829
  // src/permissions-selector/index.tsx
1830
- import { Trash2, Check as Check3, ChevronsUpDown } from "lucide-react";
1831
- import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
1830
+ import { Trash2, Check as Check3, ChevronsUpDown, Plus } from "lucide-react";
1831
+ import { Fragment as Fragment3, jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
1832
+ function groupOptions(options) {
1833
+ const result = {
1834
+ ungrouped: [],
1835
+ groups: {}
1836
+ };
1837
+ for (const option of options) {
1838
+ if (!option.group) {
1839
+ result.ungrouped.push(option);
1840
+ continue;
1841
+ }
1842
+ if (!result.groups[option.group]) {
1843
+ result.groups[option.group] = { options: [], subGroups: {} };
1844
+ }
1845
+ if (option.subGroup) {
1846
+ if (!result.groups[option.group].subGroups[option.subGroup]) {
1847
+ result.groups[option.group].subGroups[option.subGroup] = [];
1848
+ }
1849
+ result.groups[option.group].subGroups[option.subGroup].push(option);
1850
+ } else {
1851
+ result.groups[option.group].options.push(option);
1852
+ }
1853
+ }
1854
+ return result;
1855
+ }
1856
+ function renderCommandItem(option, value, onChange, setOpen, indent = 0) {
1857
+ return /* @__PURE__ */ jsxs18(
1858
+ CommandItem,
1859
+ {
1860
+ value: option.label,
1861
+ onSelect: () => {
1862
+ onChange(option.value);
1863
+ setOpen(false);
1864
+ },
1865
+ className: indent > 0 ? "pl-8" : void 0,
1866
+ children: [
1867
+ /* @__PURE__ */ jsx29(
1868
+ Check3,
1869
+ {
1870
+ className: cn(
1871
+ "mr-2 h-4 w-4",
1872
+ value === option.value ? "opacity-100" : "opacity-0"
1873
+ )
1874
+ }
1875
+ ),
1876
+ option.label
1877
+ ]
1878
+ },
1879
+ option.value
1880
+ );
1881
+ }
1832
1882
  function Combobox({
1833
1883
  options,
1834
1884
  value,
1835
1885
  onChange,
1886
+ onSelectMultiple,
1836
1887
  placeholder,
1837
1888
  searchPlaceholder = "Search...",
1838
1889
  emptyText = "No results found.",
1839
1890
  disabled = false,
1840
- className = ""
1891
+ className = "",
1892
+ grouped = false
1841
1893
  }) {
1842
1894
  const [open, setOpen] = useState6(false);
1843
1895
  const selectedOption = options.find((opt) => opt.value === value);
1844
1896
  const isEmpty = options.length === 0;
1897
+ const groupedOpts = useMemo2(
1898
+ () => grouped ? groupOptions(options) : null,
1899
+ [options, grouped]
1900
+ );
1901
+ const hasGroups = groupedOpts && Object.keys(groupedOpts.groups).length > 0;
1902
+ const handleSelectGroup = (groupName) => {
1903
+ if (!groupedOpts || !onSelectMultiple) return;
1904
+ const group = groupedOpts.groups[groupName];
1905
+ if (!group) return;
1906
+ const allValues = [
1907
+ ...group.options.map((o) => o.value),
1908
+ ...Object.values(group.subGroups).flatMap(
1909
+ (opts) => opts.map((o) => o.value)
1910
+ )
1911
+ ];
1912
+ onSelectMultiple(allValues);
1913
+ setOpen(false);
1914
+ };
1915
+ const handleSelectSubGroup = (groupName, subGroupName) => {
1916
+ if (!groupedOpts || !onSelectMultiple) return;
1917
+ const subOpts = groupedOpts.groups[groupName]?.subGroups[subGroupName];
1918
+ if (!subOpts) return;
1919
+ onSelectMultiple(subOpts.map((o) => o.value));
1920
+ setOpen(false);
1921
+ };
1845
1922
  return /* @__PURE__ */ jsxs18(Popover, { open, onOpenChange: setOpen, children: [
1846
1923
  /* @__PURE__ */ jsx29(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs18(
1847
1924
  Button,
@@ -1870,29 +1947,69 @@ function Combobox({
1870
1947
  /* @__PURE__ */ jsx29(CommandInput, { placeholder: searchPlaceholder }),
1871
1948
  /* @__PURE__ */ jsxs18(CommandList, { children: [
1872
1949
  /* @__PURE__ */ jsx29(CommandEmpty, { children: emptyText }),
1873
- /* @__PURE__ */ jsx29(CommandGroup, { children: options.map((option) => /* @__PURE__ */ jsxs18(
1874
- CommandItem,
1875
- {
1876
- value: option.label,
1877
- onSelect: () => {
1878
- onChange(option.value);
1879
- setOpen(false);
1880
- },
1881
- children: [
1882
- /* @__PURE__ */ jsx29(
1883
- Check3,
1884
- {
1885
- className: cn(
1886
- "mr-2 h-4 w-4",
1887
- value === option.value ? "opacity-100" : "opacity-0"
1950
+ grouped && hasGroups ? /* @__PURE__ */ jsxs18(Fragment3, { children: [
1951
+ groupedOpts.ungrouped.length > 0 && /* @__PURE__ */ jsx29(CommandGroup, { children: groupedOpts.ungrouped.map(
1952
+ (opt) => renderCommandItem(opt, value, onChange, setOpen)
1953
+ ) }),
1954
+ Object.entries(groupedOpts.groups).map(
1955
+ ([groupName, group]) => /* @__PURE__ */ jsxs18(
1956
+ CommandGroup,
1957
+ {
1958
+ heading: onSelectMultiple ? /* @__PURE__ */ jsxs18(
1959
+ "button",
1960
+ {
1961
+ type: "button",
1962
+ onClick: (e) => {
1963
+ e.preventDefault();
1964
+ e.stopPropagation();
1965
+ handleSelectGroup(groupName);
1966
+ },
1967
+ className: "flex items-center gap-1.5 hover:text-foreground transition-colors group w-full text-left",
1968
+ children: [
1969
+ /* @__PURE__ */ jsx29(Plus, { className: "h-3 w-3 opacity-0 group-hover:opacity-70 transition-opacity" }),
1970
+ /* @__PURE__ */ jsx29("span", { children: groupName })
1971
+ ]
1972
+ }
1973
+ ) : groupName,
1974
+ children: [
1975
+ group.options.map(
1976
+ (opt) => renderCommandItem(opt, value, onChange, setOpen)
1977
+ ),
1978
+ Object.entries(group.subGroups).map(
1979
+ ([subGroupName, subOpts]) => /* @__PURE__ */ jsxs18("div", { className: "mt-1", children: [
1980
+ onSelectMultiple ? /* @__PURE__ */ jsxs18(
1981
+ "button",
1982
+ {
1983
+ type: "button",
1984
+ onClick: (e) => {
1985
+ e.preventDefault();
1986
+ e.stopPropagation();
1987
+ handleSelectSubGroup(groupName, subGroupName);
1988
+ },
1989
+ className: "flex items-center gap-1.5 px-2 py-1 text-[11px] text-muted-foreground/70 uppercase tracking-wider hover:text-muted-foreground transition-colors group w-full text-left",
1990
+ children: [
1991
+ /* @__PURE__ */ jsx29("span", { className: "text-muted-foreground/40 group-hover:text-muted-foreground/70", children: "\u2014" }),
1992
+ /* @__PURE__ */ jsx29(Plus, { className: "h-2.5 w-2.5 opacity-0 group-hover:opacity-70 transition-opacity" }),
1993
+ subGroupName
1994
+ ]
1995
+ }
1996
+ ) : /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1.5 px-2 py-1 text-[11px] text-muted-foreground/70 uppercase tracking-wider", children: [
1997
+ /* @__PURE__ */ jsx29("span", { className: "text-muted-foreground/40", children: "\u2014" }),
1998
+ subGroupName
1999
+ ] }),
2000
+ subOpts.map(
2001
+ (opt) => renderCommandItem(opt, value, onChange, setOpen, 1)
2002
+ )
2003
+ ] }, subGroupName)
1888
2004
  )
1889
- }
1890
- ),
1891
- option.label
1892
- ]
1893
- },
1894
- option.value
1895
- )) })
2005
+ ]
2006
+ },
2007
+ groupName
2008
+ )
2009
+ )
2010
+ ] }) : /* @__PURE__ */ jsx29(CommandGroup, { children: options.map(
2011
+ (option) => renderCommandItem(option, value, onChange, setOpen)
2012
+ ) })
1896
2013
  ] })
1897
2014
  ] })
1898
2015
  }
@@ -1935,8 +2052,20 @@ function PermissionsSelector({
1935
2052
  const getRelationsForNamespace = (namespace) => {
1936
2053
  const def = definitions.find((d) => d.namespace === namespace);
1937
2054
  if (!def) return [];
2055
+ if (def.relationsMetadata && def.relationsMetadata.length > 0) {
2056
+ return def.relationsMetadata.map((rm) => ({
2057
+ value: rm.name,
2058
+ label: rm.displayName,
2059
+ group: rm.group ?? null,
2060
+ subGroup: rm.subGroup ?? null
2061
+ }));
2062
+ }
1938
2063
  return def.relations.filter((rel) => rel.startsWith("can_") || rel.startsWith("is_")).map((rel) => ({ value: rel, label: formatRelation(rel) }));
1939
2064
  };
2065
+ const hasRelationsMetadata = (namespace) => {
2066
+ const def = definitions.find((d) => d.namespace === namespace);
2067
+ return !!(def?.relationsMetadata && def.relationsMetadata.length > 0);
2068
+ };
1940
2069
  const getObjectsForNamespace = (namespace) => {
1941
2070
  if (namespace.toLowerCase() === "tenant") {
1942
2071
  return null;
@@ -1977,6 +2106,42 @@ function PermissionsSelector({
1977
2106
  return finalRows;
1978
2107
  });
1979
2108
  };
2109
+ const addMultiplePermissions = (rowId, namespace, relations) => {
2110
+ if (relations.length === 0) return;
2111
+ setPermissionRows((currentRows) => {
2112
+ const rowIndex = currentRows.findIndex((r) => r.id === rowId);
2113
+ if (rowIndex === -1) return currentRows;
2114
+ const existingRelations = new Set(
2115
+ currentRows.filter((r) => r.namespace === namespace && r.relation).map((r) => r.relation)
2116
+ );
2117
+ const newRelations = relations.filter((r) => !existingRelations.has(r));
2118
+ if (newRelations.length === 0) return currentRows;
2119
+ const updatedRows = [...currentRows];
2120
+ updatedRows[rowIndex] = {
2121
+ ...updatedRows[rowIndex],
2122
+ namespace,
2123
+ relation: newRelations[0]
2124
+ };
2125
+ const additionalRows = newRelations.slice(1).map((relation) => ({
2126
+ id: generateId(),
2127
+ namespace,
2128
+ relation,
2129
+ objectId: ""
2130
+ }));
2131
+ updatedRows.splice(rowIndex + 1, 0, ...additionalRows);
2132
+ const lastRow = updatedRows[updatedRows.length - 1];
2133
+ if (hasAnyValue(lastRow)) {
2134
+ updatedRows.push({
2135
+ id: generateId(),
2136
+ namespace: "",
2137
+ relation: "",
2138
+ objectId: ""
2139
+ });
2140
+ }
2141
+ onPermissionsChange?.(updatedRows);
2142
+ return updatedRows;
2143
+ });
2144
+ };
1980
2145
  const buildPermissions = () => {
1981
2146
  return permissionRows.filter((row) => row.namespace && row.relation).map((row) => buildPermissionString(row)).filter((perm) => perm !== "");
1982
2147
  };
@@ -2022,9 +2187,11 @@ function PermissionsSelector({
2022
2187
  options: relationOptions,
2023
2188
  value: row.relation,
2024
2189
  onChange: (value) => updatePermissionRow(row.id, "relation", value),
2190
+ onSelectMultiple: (relations) => addMultiplePermissions(row.id, row.namespace, relations),
2025
2191
  placeholder: "Permission",
2026
2192
  searchPlaceholder: "Search permissions...",
2027
2193
  disabled: disabled || !row.namespace,
2194
+ grouped: hasRelationsMetadata(row.namespace),
2028
2195
  className: cn(
2029
2196
  "min-w-40 ease-out duration-450",
2030
2197
  showObjectSelect ? "w-1/3 rounded-none border-r-0" : "w-2/3 rounded-l-none"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnibase/shadcn",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "OmniBase ShadCN UI Package",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,9 +40,10 @@
40
40
  "react-dom": "^19.1.0"
41
41
  },
42
42
  "dependencies": {
43
- "@omnibase/core-js": "0.14.0",
43
+ "@omnibase/core-js": "0.17.0",
44
44
  "@radix-ui/react-avatar": "^1.1.11",
45
45
  "@radix-ui/react-checkbox": "^1.3.3",
46
+ "@radix-ui/react-collapsible": "^1.1.12",
46
47
  "@radix-ui/react-dialog": "^1.1.15",
47
48
  "@radix-ui/react-label": "^2.1.8",
48
49
  "@radix-ui/react-popover": "^1.1.15",