@clyrex-digital/clyrex-controls-dev 0.8.1-dev.20260924103055 → 0.8.1-dev.20260925110206

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/server.js CHANGED
@@ -5684,7 +5684,7 @@ var DataBindingControls_exports = {};
5684
5684
  __export(DataBindingControls_exports, {
5685
5685
  default: () => DataBindingControls_default
5686
5686
  });
5687
- var import_react59, import_navigation, import_jsx_runtime86, ACTIVE_QUERY_CONTROLS_EVENT, humanize, escapeODataString, parseFacetSelections, buildFacetFilter, parseODataSearch, SearchIcon, ResetIcon, normalizeSortOption, DataBindingControls, DataBindingControls_default;
5687
+ var import_react59, import_navigation, import_jsx_runtime86, humanize, escapeODataString, parseFacetSelections, buildFacetFilter, parseODataSearch, SearchIcon, ResetIcon, FilterSlidersIcon, ChevronDownIcon, CheckIcon, CloseIcon, normalizeSortOption, DataBindingControls, DataBindingControls_default;
5688
5688
  var init_DataBindingControls = __esm({
5689
5689
  "src/components/pageRenderingEngine/nodes/DataBindingControls.tsx"() {
5690
5690
  "use strict";
@@ -5696,22 +5696,15 @@ var init_DataBindingControls = __esm({
5696
5696
  init_InputControlType();
5697
5697
  init_StyleTypes();
5698
5698
  import_jsx_runtime86 = require("react/jsx-runtime");
5699
- ACTIVE_QUERY_CONTROLS_EVENT = "data-binding-active-query-controls";
5700
5699
  humanize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
5701
5700
  escapeODataString = (value) => value.replace(/'/g, "''");
5702
5701
  parseFacetSelections = (filter) => {
5703
5702
  const selections = /* @__PURE__ */ new Map();
5704
- const groupedExpression = /\(\s*([^()]+?)\s+eq\s+'((?:''|[^'])*)'\s*\)/gi;
5705
- for (const match of filter.matchAll(groupedExpression)) {
5706
- selections.set(match[1].trim(), match[2].replace(/''/g, "'"));
5707
- }
5708
- if (selections.size === 0) {
5709
- const ungroupedExpression = /(?:^|\s+and\s+)\s*([^()]+?)\s+eq\s+'((?:''|[^'])*)'\s*(?=\s+and\s+|$)/gi;
5710
- for (const match of filter.matchAll(ungroupedExpression)) {
5711
- selections.set(
5712
- match[1].trim(),
5713
- match[2].replace(/''/g, "'")
5714
- );
5703
+ if (!filter || !filter.trim()) return selections;
5704
+ const expression = /(?:^|[\s()]+(?:and[\s()]+)?)([\w./-]+)\s+eq\s+'((?:''|[^'])*)'/gi;
5705
+ for (const match of filter.matchAll(expression)) {
5706
+ if (match[1] && match[2] !== void 0) {
5707
+ selections.set(match[1].trim(), match[2].replace(/''/g, "'"));
5715
5708
  }
5716
5709
  }
5717
5710
  return selections;
@@ -5762,10 +5755,64 @@ var init_DataBindingControls = __esm({
5762
5755
  ]
5763
5756
  }
5764
5757
  );
5758
+ FilterSlidersIcon = ({ className = "" }) => /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
5759
+ "svg",
5760
+ {
5761
+ "aria-hidden": "true",
5762
+ className,
5763
+ fill: "none",
5764
+ viewBox: "0 0 24 24",
5765
+ stroke: "currentColor",
5766
+ strokeWidth: "2",
5767
+ children: [
5768
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 4h18M3 12h18M3 20h18" }),
5769
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("circle", { cx: "8", cy: "4", r: "2", fill: "currentColor" }),
5770
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("circle", { cx: "16", cy: "12", r: "2", fill: "currentColor" }),
5771
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("circle", { cx: "10", cy: "20", r: "2", fill: "currentColor" })
5772
+ ]
5773
+ }
5774
+ );
5775
+ ChevronDownIcon = ({ className = "" }) => /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
5776
+ "svg",
5777
+ {
5778
+ "aria-hidden": "true",
5779
+ className,
5780
+ fill: "none",
5781
+ viewBox: "0 0 24 24",
5782
+ stroke: "currentColor",
5783
+ strokeWidth: "2",
5784
+ children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m19 9-7 7-7-7" })
5785
+ }
5786
+ );
5787
+ CheckIcon = ({ className = "" }) => /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
5788
+ "svg",
5789
+ {
5790
+ "aria-hidden": "true",
5791
+ className,
5792
+ fill: "none",
5793
+ viewBox: "0 0 24 24",
5794
+ stroke: "currentColor",
5795
+ strokeWidth: "2.5",
5796
+ children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" })
5797
+ }
5798
+ );
5799
+ CloseIcon = ({ className = "" }) => /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
5800
+ "svg",
5801
+ {
5802
+ "aria-hidden": "true",
5803
+ className,
5804
+ fill: "none",
5805
+ viewBox: "0 0 24 24",
5806
+ stroke: "currentColor",
5807
+ strokeWidth: "2",
5808
+ children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" })
5809
+ }
5810
+ );
5765
5811
  normalizeSortOption = (option) => {
5766
5812
  const direction = option.direction.trim();
5813
+ const title = option.title || humanize(option.name);
5767
5814
  return {
5768
- label: option.title || humanize(option.name),
5815
+ label: title,
5769
5816
  value: [option.name, direction].filter(Boolean).join(" ")
5770
5817
  };
5771
5818
  };
@@ -5780,59 +5827,59 @@ var init_DataBindingControls = __esm({
5780
5827
  mode = "toolbar"
5781
5828
  }) => {
5782
5829
  const router = (0, import_navigation.useRouter)();
5783
- const facetGroups = Object.entries(facets ?? {}).filter(
5784
- ([, values]) => Array.isArray(values) && values.length > 0
5830
+ const searchParams = (0, import_navigation.useSearchParams)();
5831
+ const [collapsedCategories, setCollapsedCategories] = (0, import_react59.useState)({});
5832
+ const [isSortOpen, setIsSortOpen] = (0, import_react59.useState)(false);
5833
+ const sortDropdownRef = (0, import_react59.useRef)(null);
5834
+ (0, import_react59.useEffect)(() => {
5835
+ const handleClickOutside = (event) => {
5836
+ if (sortDropdownRef.current && !sortDropdownRef.current.contains(event.target)) {
5837
+ setIsSortOpen(false);
5838
+ }
5839
+ };
5840
+ document.addEventListener("mousedown", handleClickOutside);
5841
+ return () => document.removeEventListener("mousedown", handleClickOutside);
5842
+ }, []);
5843
+ const toggleCategory = (field) => {
5844
+ setCollapsedCategories((prev) => ({
5845
+ ...prev,
5846
+ [field]: !prev[field]
5847
+ }));
5848
+ };
5849
+ const searchParamsFilter = searchParams?.get("$filter") ?? searchParams?.get("filter");
5850
+ const searchParamsSort = searchParams?.get("$orderby") ?? searchParams?.get("orderBy");
5851
+ const searchParamsSearch = searchParams?.get("$search");
5852
+ const currentFilter = searchParamsFilter !== void 0 && searchParamsFilter !== null ? searchParamsFilter : query?.["$filter"] ?? query?.filter ?? "";
5853
+ const currentSort = searchParamsSort !== void 0 && searchParamsSort !== null ? searchParamsSort : query?.["$orderby"] ?? query?.orderBy ?? "";
5854
+ const currentSearch = parseODataSearch(
5855
+ searchParamsSearch !== void 0 && searchParamsSearch !== null ? searchParamsSearch : query?.["$search"]
5785
5856
  );
5786
- const normalizedSortOptions = (sortOptions ?? []).filter((option) => option?.name && option?.direction).map(normalizeSortOption).filter((option) => option.value);
5787
- const queryFilter = query?.["$filter"] ?? query?.filter ?? "";
5788
- const querySort = query?.["$orderby"] ?? query?.orderBy ?? "";
5789
- const [currentFilter, setCurrentFilter] = (0, import_react59.useState)(queryFilter);
5790
- const [currentSort, setCurrentSort] = (0, import_react59.useState)(querySort);
5791
- const selectedFacetValues = parseFacetSelections(currentFilter);
5792
- const currentSearch = parseODataSearch(query?.["$search"]);
5793
5857
  const [searchText, setSearchText] = (0, import_react59.useState)(currentSearch);
5794
- const lastNavigationUrl = (0, import_react59.useRef)(null);
5795
5858
  (0, import_react59.useEffect)(() => {
5796
5859
  setSearchText(currentSearch);
5797
5860
  }, [currentSearch]);
5798
- (0, import_react59.useEffect)(() => {
5799
- setCurrentFilter(queryFilter);
5800
- }, [queryFilter]);
5801
- (0, import_react59.useEffect)(() => {
5802
- setCurrentSort(querySort);
5803
- }, [querySort]);
5804
- (0, import_react59.useEffect)(() => {
5805
- const syncActiveQueryControls2 = (event) => {
5806
- const { filter, sort } = event.detail;
5807
- if (filter !== void 0) {
5808
- setCurrentFilter(filter);
5809
- }
5810
- if (sort !== void 0) {
5811
- setCurrentSort(sort);
5812
- }
5813
- };
5814
- window.addEventListener(
5815
- ACTIVE_QUERY_CONTROLS_EVENT,
5816
- syncActiveQueryControls2
5817
- );
5818
- return () => window.removeEventListener(
5819
- ACTIVE_QUERY_CONTROLS_EVENT,
5820
- syncActiveQueryControls2
5821
- );
5822
- }, []);
5823
- const syncActiveQueryControls = (0, import_react59.useCallback)(
5824
- (updates) => {
5825
- window.dispatchEvent(
5826
- new CustomEvent(ACTIVE_QUERY_CONTROLS_EVENT, {
5827
- detail: updates
5828
- })
5829
- );
5830
- },
5831
- []
5861
+ const selectedFacetValues = (0, import_react59.useMemo)(
5862
+ () => parseFacetSelections(currentFilter),
5863
+ [currentFilter]
5832
5864
  );
5865
+ const activeFacets = (0, import_react59.useMemo)(() => {
5866
+ if (!facets) return [];
5867
+ return Object.entries(facets).map(([field, values]) => {
5868
+ if (!Array.isArray(values)) return null;
5869
+ const filteredValues = values.filter(
5870
+ (item) => item.count > 0 || selectedFacetValues.get(field) === item.value
5871
+ );
5872
+ if (filteredValues.length === 0) return null;
5873
+ return [field, filteredValues];
5874
+ }).filter((group) => group !== null);
5875
+ }, [facets, selectedFacetValues]);
5876
+ const normalizedSortOptions = (sortOptions ?? []).filter((option) => option?.name && option?.direction).map(normalizeSortOption).filter((option) => option.value);
5877
+ const selectedSortOption = normalizedSortOptions.find((opt) => opt.value === currentSort) || normalizedSortOptions[0];
5878
+ const selectedSortLabel = selectedSortOption?.label || "";
5833
5879
  const getQueryUrl = (0, import_react59.useCallback)(
5834
5880
  (updates) => {
5835
- const params = new URLSearchParams(query ?? {});
5881
+ const initialParams = searchParams ? searchParams.toString() : "";
5882
+ const params = new URLSearchParams(initialParams || query || {});
5836
5883
  Object.entries(updates).forEach(([key, value]) => {
5837
5884
  if (value) {
5838
5885
  params.set(key, value);
@@ -5847,38 +5894,48 @@ var init_DataBindingControls = __esm({
5847
5894
  ).join("&");
5848
5895
  return queryString ? `${path}?${queryString}` : path;
5849
5896
  },
5850
- [path, query]
5897
+ [path, query, searchParams]
5851
5898
  );
5852
- const getFacetUrl = (field, value) => {
5853
- const updatedSelections = new Map(selectedFacetValues);
5854
- if (value) {
5855
- updatedSelections.set(field, value);
5856
- } else {
5857
- updatedSelections.delete(field);
5858
- }
5859
- const updatedFilter = buildFacetFilter(updatedSelections);
5860
- syncActiveQueryControls({ filter: updatedFilter });
5861
- return getQueryUrl({
5862
- $filter: updatedFilter || void 0,
5863
- filter: void 0
5864
- });
5865
- };
5866
5899
  const navigateWithoutReload = (0, import_react59.useCallback)(
5867
5900
  (url) => {
5868
- if (lastNavigationUrl.current === url) {
5869
- return;
5870
- }
5871
5901
  if (typeof window !== "undefined") {
5872
5902
  const targetUrl = new URL(url, window.location.origin);
5873
5903
  if (targetUrl.pathname === window.location.pathname && targetUrl.search === window.location.search) {
5874
5904
  return;
5875
5905
  }
5876
5906
  }
5877
- lastNavigationUrl.current = url;
5878
5907
  router.push(url, { scroll: false });
5879
5908
  },
5880
5909
  [router]
5881
5910
  );
5911
+ const getFacetUrl = (field, value) => {
5912
+ const updatedSelections = new Map(selectedFacetValues);
5913
+ if (value) {
5914
+ if (updatedSelections.get(field) === value) {
5915
+ updatedSelections.delete(field);
5916
+ } else {
5917
+ updatedSelections.set(field, value);
5918
+ }
5919
+ } else {
5920
+ updatedSelections.delete(field);
5921
+ }
5922
+ const updatedFilter = buildFacetFilter(updatedSelections);
5923
+ return getQueryUrl({
5924
+ $filter: updatedFilter || void 0,
5925
+ filter: void 0
5926
+ });
5927
+ };
5928
+ const clearFacetField = (field) => {
5929
+ const updatedSelections = new Map(selectedFacetValues);
5930
+ updatedSelections.delete(field);
5931
+ const updatedFilter = buildFacetFilter(updatedSelections);
5932
+ navigateWithoutReload(
5933
+ getQueryUrl({
5934
+ $filter: updatedFilter || void 0,
5935
+ filter: void 0
5936
+ })
5937
+ );
5938
+ };
5882
5939
  const handleSearch = (0, import_react59.useCallback)(async () => {
5883
5940
  const trimmedSearchText = searchText.trim();
5884
5941
  if (trimmedSearchText === currentSearch.trim()) {
@@ -5892,7 +5949,6 @@ var init_DataBindingControls = __esm({
5892
5949
  return { isSuccessful: true };
5893
5950
  }, [currentSearch, getQueryUrl, navigateWithoutReload, searchText]);
5894
5951
  const handleClearAll = (0, import_react59.useCallback)(async () => {
5895
- syncActiveQueryControls({ filter: "", sort: "" });
5896
5952
  navigateWithoutReload(
5897
5953
  getQueryUrl({
5898
5954
  $filter: void 0,
@@ -5902,67 +5958,69 @@ var init_DataBindingControls = __esm({
5902
5958
  })
5903
5959
  );
5904
5960
  return { isSuccessful: true };
5905
- }, [getQueryUrl, navigateWithoutReload, syncActiveQueryControls]);
5906
- const hasFacetControls = showFacets && facetGroups.length > 0;
5961
+ }, [getQueryUrl, navigateWithoutReload]);
5962
+ const hasFacetControls = showFacets && activeFacets.length > 0;
5907
5963
  const hasSortControls = showSortOptions && normalizedSortOptions.length > 0;
5908
5964
  const hasActiveFiltersOrSort = Boolean(
5909
5965
  currentFilter.trim() || currentSort.trim()
5910
5966
  );
5967
+ const selectedFacetCount = selectedFacetValues.size;
5911
5968
  const shouldShowToolbarClearAll = hasActiveFiltersOrSort && !hasFacetControls;
5912
5969
  const shouldShowToolbar = mode === "toolbar" && (showSearchBar || hasSortControls || shouldShowToolbarClearAll);
5913
5970
  const shouldShowFacets = mode === "facets" && hasFacetControls;
5914
5971
  if (!shouldShowToolbar && !shouldShowFacets) {
5915
5972
  return null;
5916
5973
  }
5917
- const renderFacetLinks = (field, values, stacked = false) => /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: stacked ? "flex flex-col gap-0.5" : "flex flex-wrap gap-1.5", children: [
5918
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
5974
+ const renderFacetItems = (field, values) => /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "flex flex-col gap-1 pt-1 pb-1", children: values.map((facet) => {
5975
+ const isSelected = selectedFacetValues.get(field) === facet.value;
5976
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
5919
5977
  "button",
5920
5978
  {
5921
5979
  type: "button",
5922
- onClick: () => navigateWithoutReload(getFacetUrl(field)),
5923
- "aria-pressed": !selectedFacetValues.has(field),
5924
- className: `cursor-pointer px-3 py-1.5 text-left text-sm transition-all duration-150 ease-out hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${stacked ? "w-full" : "border"} ${selectedFacetValues.has(field) ? "hover:bg-info-soft" : "bg-primary-base font-semibold hover:brightness-95"}`,
5925
- children: "All"
5926
- }
5927
- ),
5928
- values.map((facet) => {
5929
- const isSelected = selectedFacetValues.get(field) === facet.value;
5930
- return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
5931
- "button",
5932
- {
5933
- type: "button",
5934
- onClick: () => navigateWithoutReload(getFacetUrl(field, facet.value)),
5935
- "aria-pressed": isSelected,
5936
- className: `cursor-pointer px-3 py-1.5 text-left text-sm transition-all duration-150 ease-out hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${stacked ? "w-full" : "border"} ${isSelected ? "bg-primary-base font-semibold hover:brightness-95" : "hover:bg-info-soft"}`,
5937
- children: [
5938
- facet.value,
5939
- " (",
5940
- facet.count,
5941
- ")"
5942
- ]
5943
- },
5944
- `${field}-${facet.value}`
5945
- );
5946
- })
5947
- ] });
5980
+ onClick: () => navigateWithoutReload(getFacetUrl(field, facet.value)),
5981
+ "aria-pressed": isSelected,
5982
+ className: `group flex w-full cursor-pointer items-center justify-between gap-2.5 rounded-md px-2 py-1.5 text-left text-sm transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary-base ${isSelected ? "bg-neutral-soft font-medium border " : "hover:bg-neutral-weak border border-transparent"}`,
5983
+ children: [
5984
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex min-w-0 items-center gap-2.5", children: [
5985
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
5986
+ "span",
5987
+ {
5988
+ className: `flex h-4 w-4 shrink-0 items-center justify-center rounded-[3px] border transition-colors duration-150 ${isSelected ? "bg-primary-base border-primary-base" : " group-hover:border-neutral-strong bg-default"}`,
5989
+ children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(CheckIcon, { className: "h-3 w-3" })
5990
+ }
5991
+ ),
5992
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "leading-snug break-words", children: facet.value })
5993
+ ] }),
5994
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
5995
+ "span",
5996
+ {
5997
+ className: `shrink-0 rounded px-1.5 py-0.5 text-xs transition-colors ml-2 ${isSelected ? "bg-neutral-weak font-semibold" : "bg-neutral-weak font-normal"}`,
5998
+ children: facet.count
5999
+ }
6000
+ )
6001
+ ]
6002
+ },
6003
+ `${field}-${facet.value}`
6004
+ );
6005
+ }) });
5948
6006
  return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
5949
6007
  "div",
5950
6008
  {
5951
- className: mode === "toolbar" ? "my-6" : "flex flex-col gap-2 border bg-default rounded-[5px] p-3",
6009
+ className: mode === "toolbar" ? "my-6" : "flex flex-col rounded-md border bg-default p-3.5 shadow-sm",
5952
6010
  children: [
5953
- shouldShowToolbar && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
6011
+ shouldShowToolbar && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
5954
6012
  showSearchBar && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
5955
6013
  "form",
5956
6014
  {
5957
6015
  role: "search",
5958
- className: "flex w-full flex-col gap-3 rounded-xl border border-info bg-info-soft p-3 sm:flex-row sm:items-center md:min-w-0 md:flex-1",
6016
+ className: "flex w-full items-center gap-2 sm:flex-row md:min-w-0 md:flex-1",
5959
6017
  onSubmit: (event) => {
5960
6018
  event.preventDefault();
5961
6019
  void handleSearch();
5962
6020
  },
5963
6021
  children: [
5964
- /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "relative bg-default rounded-xl min-w-0 flex-1", children: [
5965
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SearchIcon, { className: "pointer-events-none absolute left-4 top-1/2 z-10 h-5 w-5 -translate-y-1/2" }),
6022
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "relative min-w-0 flex-1", children: [
6023
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SearchIcon, { className: "pointer-events-none absolute left-3.5 top-1/2 z-10 h-4 w-4 -translate-y-1/2 opacity-60" }),
5966
6024
  /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
5967
6025
  InputControl_default,
5968
6026
  {
@@ -5973,7 +6031,7 @@ var init_DataBindingControls = __esm({
5973
6031
  attributes: {
5974
6032
  placeholder: "Search discussions, topics, or keywords..."
5975
6033
  },
5976
- inputClasses: "!mt-0 !rounded-xl !py-3 !pl-11 !pr-4 !shadow-sm"
6034
+ inputClasses: "!mt-0 !h-10 !rounded-md !border-neutral-soft !bg-default !py-2 !pl-10 !pr-4 !shadow-sm !text-sm focus:!border-primary-base focus:!ring-primary-base"
5977
6035
  }
5978
6036
  )
5979
6037
  ] }),
@@ -5982,9 +6040,9 @@ var init_DataBindingControls = __esm({
5982
6040
  {
5983
6041
  ButtonType: "Primary" /* Solid */,
5984
6042
  onClick: handleSearch,
5985
- className: "!rounded-xl !px-6 !py-3 sm:self-stretch",
6043
+ className: "!h-10 !rounded-md !px-5 !py-0 flex items-center justify-center gap-1.5 shrink-0",
5986
6044
  children: [
5987
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SearchIcon, { className: "h-5 w-5" }),
6045
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SearchIcon, { className: "h-4 w-4" }),
5988
6046
  /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { children: "Search" })
5989
6047
  ]
5990
6048
  }
@@ -5992,61 +6050,124 @@ var init_DataBindingControls = __esm({
5992
6050
  ]
5993
6051
  }
5994
6052
  ),
5995
- hasSortControls && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex w-full items-center gap-3 rounded-2xl border border-info bg-info-soft p-3 text-sm md:ml-auto md:w-auto md:min-w-72 md:self-stretch", children: [
5996
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
5997
- "label",
6053
+ hasSortControls && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "relative shrink-0", ref: sortDropdownRef, children: [
6054
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6055
+ "button",
5998
6056
  {
5999
- htmlFor: "sortOptions",
6000
- className: "shrink-0 whitespace-nowrap font-medium",
6001
- children: "Sort by"
6057
+ type: "button",
6058
+ onClick: () => setIsSortOpen((prev) => !prev),
6059
+ "aria-expanded": isSortOpen,
6060
+ className: "inline-flex h-10 items-center justify-between gap-2.5 rounded-md border bg-default px-3.5 text-sm font-normal shadow-sm transition-all hover:bg-neutral-weak focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-base cursor-pointer whitespace-nowrap",
6061
+ children: [
6062
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("span", { className: "whitespace-nowrap", children: [
6063
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "opacity-70", children: "Sort by: " }),
6064
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "font-semibold", children: selectedSortLabel })
6065
+ ] }),
6066
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6067
+ ChevronDownIcon,
6068
+ {
6069
+ className: `h-4 w-4 shrink-0 transition-transform duration-200 ${isSortOpen ? "rotate-180" : ""}`
6070
+ }
6071
+ )
6072
+ ]
6002
6073
  }
6003
6074
  ),
6004
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "min-w-0 flex-1 rounded-xl bg-default md:min-w-48", children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6005
- InputControl_default,
6006
- {
6007
- name: "sortOptions",
6008
- controlType: InputControlType_default.select,
6009
- value: currentSort,
6010
- dataset: normalizedSortOptions,
6011
- dataKeyFieldName: "value",
6012
- dataTextFieldName: "label",
6013
- callback: (updatedValues) => {
6014
- const updatedSort = updatedValues.value || "";
6015
- syncActiveQueryControls({ sort: updatedSort });
6016
- navigateWithoutReload(
6017
- getQueryUrl({
6018
- $orderby: updatedSort || void 0,
6019
- orderBy: void 0
6020
- })
6021
- );
6075
+ isSortOpen && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "absolute right-0 z-50 mt-1.5 w-max min-w-full max-h-80 overflow-y-auto rounded-lg border border-neutral-soft bg-default p-1 shadow-lg", children: normalizedSortOptions.map((option) => {
6076
+ const isSelected = currentSort ? option.value === currentSort : option.value === normalizedSortOptions[0]?.value;
6077
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6078
+ "button",
6079
+ {
6080
+ type: "button",
6081
+ onClick: () => {
6082
+ setIsSortOpen(false);
6083
+ navigateWithoutReload(
6084
+ getQueryUrl({
6085
+ $orderby: option.value || void 0,
6086
+ orderBy: void 0
6087
+ })
6088
+ );
6089
+ },
6090
+ className: `flex w-full items-center justify-between gap-4 rounded-md px-3 py-1.5 text-left text-sm transition-colors cursor-pointer whitespace-nowrap ${isSelected ? "bg-primary-base font-semibold" : "hover:bg-neutral-soft font-normal"}`,
6091
+ children: [
6092
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "whitespace-nowrap", children: option.label }),
6093
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(CheckIcon, { className: "h-4 w-4 shrink-0" })
6094
+ ]
6022
6095
  },
6023
- attributes: { placeholder: "Default" },
6024
- inputClasses: "!mt-0 !rounded-xl !border-info !bg-default !px-4 !py-3 !text-body !shadow-sm focus:!border-primary focus:!ring-primary-base"
6025
- }
6026
- ) })
6096
+ option.value
6097
+ );
6098
+ }) })
6027
6099
  ] })
6028
6100
  ] }),
6029
- shouldShowFacets && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex items-center justify-between gap-2 border-b pb-2", children: [
6030
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("h2", { className: "text-lg font-semibold", children: "Filters" }),
6031
- hasActiveFiltersOrSort && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6101
+ shouldShowFacets && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex flex-col gap-3", children: [
6102
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex items-center justify-between border-b pb-3", children: [
6103
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex items-center gap-2", children: [
6104
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(FilterSlidersIcon, { className: "h-4 w-4" }),
6105
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("h2", { className: "text-base font-semibold tracking-tight", children: "Filters" }),
6106
+ selectedFacetCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "flex h-5 items-center justify-center rounded-full bg-primary-base px-2 text-xs font-semibold", children: selectedFacetCount })
6107
+ ] }),
6108
+ hasActiveFiltersOrSort && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6109
+ "button",
6110
+ {
6111
+ type: "button",
6112
+ onClick: () => void handleClearAll(),
6113
+ className: "group inline-flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium transition-colors duration-150 hover:bg-neutral-soft focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary-base",
6114
+ "aria-label": "Clear all filters and sorting",
6115
+ children: [
6116
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(ResetIcon, { className: "h-3.5 w-3.5 transition-transform duration-200 group-hover:-rotate-45" }),
6117
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { children: "Clear all" })
6118
+ ]
6119
+ }
6120
+ )
6121
+ ] }),
6122
+ selectedFacetCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "flex flex-wrap gap-1.5 pb-2.5 border-b ", children: Array.from(selectedFacetValues.entries()).map(([field, value]) => /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6032
6123
  "button",
6033
6124
  {
6034
6125
  type: "button",
6035
- onClick: () => void handleClearAll(),
6036
- className: "group inline-flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-primary transition-colors duration-150 hover:bg-info-soft hover:text-primary-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
6037
- "aria-label": "Clear all filters and sorting",
6126
+ onClick: () => clearFacetField(field),
6127
+ className: "group inline-flex max-w-full cursor-pointer items-center gap-1.5 rounded-md border bg-neutral-weak px-2.5 py-1 text-xs transition-colors hover:bg-neutral-soft",
6128
+ title: `Remove filter: ${value}`,
6038
6129
  children: [
6039
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(ResetIcon, { className: "h-3.5 w-3.5 transition-transform duration-200 group-hover:-rotate-45" }),
6040
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { children: "Clear all" })
6130
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "font-medium truncate max-w-[150px]", children: value }),
6131
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(CloseIcon, { className: "h-3.5 w-3.5 shrink-0 opacity-60 group-hover:opacity-100" })
6041
6132
  ]
6042
- }
6043
- )
6044
- ] }),
6045
- shouldShowFacets && facetGroups.length === 1 && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("nav", { "aria-label": `${humanize(facetGroups[0][0])} filters`, children: renderFacetLinks(facetGroups[0][0], facetGroups[0][1]) }),
6046
- shouldShowFacets && facetGroups.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "flex flex-col", children: facetGroups.map(([field, values]) => /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("details", { className: "border-b py-1 last:border-b-0", open: true, children: [
6047
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("summary", { className: "cursor-pointer rounded-md px-2 py-1.5 font-semibold transition-colors duration-150 hover:bg-info-soft focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary", children: humanize(field) }),
6048
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "px-1 pb-1 pt-0.5", children: renderFacetLinks(field, values, true) })
6049
- ] }, field)) })
6133
+ },
6134
+ `chip-${field}`
6135
+ )) }),
6136
+ activeFacets.length === 1 && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("nav", { "aria-label": `${humanize(activeFacets[0][0])} filters`, children: /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "py-1", children: [
6137
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "px-1 pb-2 font-medium text-sm", children: humanize(activeFacets[0][0]) }),
6138
+ renderFacetItems(activeFacets[0][0], activeFacets[0][1])
6139
+ ] }) }),
6140
+ activeFacets.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "flex flex-col divide-y divide-neutral-soft", children: activeFacets.map(([field, values]) => {
6141
+ const isOpen = !collapsedCategories[field];
6142
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6143
+ "div",
6144
+ {
6145
+ className: "py-2 first:pt-0 last:pb-0",
6146
+ children: [
6147
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6148
+ "button",
6149
+ {
6150
+ type: "button",
6151
+ onClick: () => toggleCategory(field),
6152
+ className: "flex w-full cursor-pointer items-center justify-between rounded-md px-1.5 py-1.5 text-sm font-semibold transition-colors duration-150 hover:bg-neutral-weak focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary-base",
6153
+ children: [
6154
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { children: humanize(field) }),
6155
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6156
+ ChevronDownIcon,
6157
+ {
6158
+ className: `h-4 w-4 transition-transform duration-200 ease-out ${isOpen ? "rotate-180" : ""}`
6159
+ }
6160
+ )
6161
+ ]
6162
+ }
6163
+ ),
6164
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "pt-1", children: renderFacetItems(field, values) })
6165
+ ]
6166
+ },
6167
+ field
6168
+ );
6169
+ }) })
6170
+ ] })
6050
6171
  ]
6051
6172
  }
6052
6173
  );
@@ -9389,7 +9510,7 @@ var DivContainer = async (props) => {
9389
9510
  renderedContainer,
9390
9511
  paginationControl
9391
9512
  ] }),
9392
- dataBindingProperties?.showFacets && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("aside", { className: " lg:sticky lg:top-[90px] lg:self-start", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9513
+ dataBindingProperties?.showFacets && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("aside", { children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9393
9514
  DataBindingControls2,
9394
9515
  {
9395
9516
  mode: "facets",
package/dist/server.mjs CHANGED
@@ -2388,7 +2388,7 @@ var GoogleMapNode_default = GoogleMapNode;
2388
2388
  // src/components/pageRenderingEngine/nodes/DivContainer.tsx
2389
2389
  import { jsx as jsx37, jsxs as jsxs15 } from "react/jsx-runtime";
2390
2390
  var Pagination = dynamic13(() => import("./Pagination-V53JCR6P.mjs"), { ssr: true });
2391
- var DataBindingControls = dynamic13(() => import("./DataBindingControls-UDRT7JFF.mjs"), {
2391
+ var DataBindingControls = dynamic13(() => import("./DataBindingControls-25UC5W3R.mjs"), {
2392
2392
  ssr: true
2393
2393
  });
2394
2394
  var Slider = dynamic13(() => import("./Slider-UNNVADVP.mjs"), {
@@ -2929,7 +2929,7 @@ var DivContainer = async (props) => {
2929
2929
  renderedContainer,
2930
2930
  paginationControl
2931
2931
  ] }),
2932
- dataBindingProperties?.showFacets && /* @__PURE__ */ jsx37("aside", { className: " lg:sticky lg:top-[90px] lg:self-start", children: /* @__PURE__ */ jsx37(
2932
+ dataBindingProperties?.showFacets && /* @__PURE__ */ jsx37("aside", { children: /* @__PURE__ */ jsx37(
2933
2933
  DataBindingControls,
2934
2934
  {
2935
2935
  mode: "facets",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clyrex-digital/clyrex-controls-dev",
3
- "version": "0.8.1-dev.20260924103055",
3
+ "version": "0.8.1-dev.20260925110206",
4
4
  "description": "Reusable React components",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",