@clyrex-digital/clyrex-controls-dev 0.8.1-dev.20260924115534 → 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.
@@ -8,7 +8,7 @@ import {
8
8
  import "./chunk-56HSDML5.mjs";
9
9
 
10
10
  // src/components/pageRenderingEngine/nodes/DataBindingControls.tsx
11
- import { useCallback, useEffect, useMemo, useState } from "react";
11
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
12
12
  import { useRouter, useSearchParams } from "next/navigation";
13
13
 
14
14
  // src/components/controls/edit/InputControl.tsx
@@ -134,8 +134,9 @@ var CloseIcon = ({ className = "" }) => /* @__PURE__ */ jsx(
134
134
  );
135
135
  var normalizeSortOption = (option) => {
136
136
  const direction = option.direction.trim();
137
+ const title = option.title || humanize(option.name);
137
138
  return {
138
- label: option.title || humanize(option.name),
139
+ label: title,
139
140
  value: [option.name, direction].filter(Boolean).join(" ")
140
141
  };
141
142
  };
@@ -152,6 +153,17 @@ var DataBindingControls = ({
152
153
  const router = useRouter();
153
154
  const searchParams = useSearchParams();
154
155
  const [collapsedCategories, setCollapsedCategories] = useState({});
156
+ const [isSortOpen, setIsSortOpen] = useState(false);
157
+ const sortDropdownRef = useRef(null);
158
+ useEffect(() => {
159
+ const handleClickOutside = (event) => {
160
+ if (sortDropdownRef.current && !sortDropdownRef.current.contains(event.target)) {
161
+ setIsSortOpen(false);
162
+ }
163
+ };
164
+ document.addEventListener("mousedown", handleClickOutside);
165
+ return () => document.removeEventListener("mousedown", handleClickOutside);
166
+ }, []);
155
167
  const toggleCategory = (field) => {
156
168
  setCollapsedCategories((prev) => ({
157
169
  ...prev,
@@ -186,6 +198,8 @@ var DataBindingControls = ({
186
198
  }).filter((group) => group !== null);
187
199
  }, [facets, selectedFacetValues]);
188
200
  const normalizedSortOptions = (sortOptions ?? []).filter((option) => option?.name && option?.direction).map(normalizeSortOption).filter((option) => option.value);
201
+ const selectedSortOption = normalizedSortOptions.find((opt) => opt.value === currentSort) || normalizedSortOptions[0];
202
+ const selectedSortLabel = selectedSortOption?.label || "";
189
203
  const getQueryUrl = useCallback(
190
204
  (updates) => {
191
205
  const initialParams = searchParams ? searchParams.toString() : "";
@@ -318,27 +332,19 @@ var DataBindingControls = ({
318
332
  {
319
333
  className: mode === "toolbar" ? "my-6" : "flex flex-col rounded-md border bg-default p-3.5 shadow-sm",
320
334
  children: [
321
- shouldShowToolbar && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
322
- showSearchBar && // <form
323
- // role="search"
324
- // className="flex w-full flex-col gap-3 rounded-md border border-info bg-info-soft p-3 sm:flex-row sm:items-center md:min-w-0 md:flex-1"
325
- // onSubmit={(event) => {
326
- // event.preventDefault();
327
- // void handleSearch();
328
- // }}
329
- // >
330
- /* @__PURE__ */ jsxs(
335
+ shouldShowToolbar && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
336
+ showSearchBar && /* @__PURE__ */ jsxs(
331
337
  "form",
332
338
  {
333
339
  role: "search",
334
- className: "flex w-full flex-col gap-3 rounded-md sm:flex-row sm:items-center md:min-w-0 md:flex-1",
340
+ className: "flex w-full items-center gap-2 sm:flex-row md:min-w-0 md:flex-1",
335
341
  onSubmit: (event) => {
336
342
  event.preventDefault();
337
343
  void handleSearch();
338
344
  },
339
345
  children: [
340
- /* @__PURE__ */ jsxs("div", { className: "relative bg-default rounded-md min-w-0 flex-1", children: [
341
- /* @__PURE__ */ jsx(SearchIcon, { className: "pointer-events-none absolute left-4 top-1/2 z-10 h-5 w-5 -translate-y-1/2" }),
346
+ /* @__PURE__ */ jsxs("div", { className: "relative min-w-0 flex-1", children: [
347
+ /* @__PURE__ */ 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" }),
342
348
  /* @__PURE__ */ jsx(
343
349
  InputControl_default,
344
350
  {
@@ -349,7 +355,7 @@ var DataBindingControls = ({
349
355
  attributes: {
350
356
  placeholder: "Search discussions, topics, or keywords..."
351
357
  },
352
- inputClasses: "!mt-0 !rounded-md !py-3 !pl-11 !pr-4 !shadow-sm"
358
+ 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"
353
359
  }
354
360
  )
355
361
  ] }),
@@ -358,9 +364,9 @@ var DataBindingControls = ({
358
364
  {
359
365
  ButtonType: "Primary" /* Solid */,
360
366
  onClick: handleSearch,
361
- className: "!rounded-md !px-6 !py-3 sm:self-stretch",
367
+ className: "!h-10 !rounded-md !px-5 !py-0 flex items-center justify-center gap-1.5 shrink-0",
362
368
  children: [
363
- /* @__PURE__ */ jsx(SearchIcon, { className: "h-5 w-5" }),
369
+ /* @__PURE__ */ jsx(SearchIcon, { className: "h-4 w-4" }),
364
370
  /* @__PURE__ */ jsx("span", { children: "Search" })
365
371
  ]
366
372
  }
@@ -368,37 +374,52 @@ var DataBindingControls = ({
368
374
  ]
369
375
  }
370
376
  ),
371
- hasSortControls && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-3 rounded-md border border-info bg-info-soft p-3 text-sm md:ml-auto md:w-auto md:min-w-72 md:self-stretch", children: [
372
- /* @__PURE__ */ jsx(
373
- "label",
377
+ hasSortControls && /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: sortDropdownRef, children: [
378
+ /* @__PURE__ */ jsxs(
379
+ "button",
374
380
  {
375
- htmlFor: "sortOptions",
376
- className: "shrink-0 whitespace-nowrap font-medium",
377
- children: "Sort by"
381
+ type: "button",
382
+ onClick: () => setIsSortOpen((prev) => !prev),
383
+ "aria-expanded": isSortOpen,
384
+ 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",
385
+ children: [
386
+ /* @__PURE__ */ jsxs("span", { className: "whitespace-nowrap", children: [
387
+ /* @__PURE__ */ jsx("span", { className: "opacity-70", children: "Sort by: " }),
388
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", children: selectedSortLabel })
389
+ ] }),
390
+ /* @__PURE__ */ jsx(
391
+ ChevronDownIcon,
392
+ {
393
+ className: `h-4 w-4 shrink-0 transition-transform duration-200 ${isSortOpen ? "rotate-180" : ""}`
394
+ }
395
+ )
396
+ ]
378
397
  }
379
398
  ),
380
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 rounded-md bg-default md:min-w-48", children: /* @__PURE__ */ jsx(
381
- InputControl_default,
382
- {
383
- name: "sortOptions",
384
- controlType: InputControlType_default.select,
385
- value: currentSort,
386
- dataset: normalizedSortOptions,
387
- dataKeyFieldName: "value",
388
- dataTextFieldName: "label",
389
- callback: (updatedValues) => {
390
- const updatedSort = updatedValues.value || "";
391
- navigateWithoutReload(
392
- getQueryUrl({
393
- $orderby: updatedSort || void 0,
394
- orderBy: void 0
395
- })
396
- );
399
+ isSortOpen && /* @__PURE__ */ 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) => {
400
+ const isSelected = currentSort ? option.value === currentSort : option.value === normalizedSortOptions[0]?.value;
401
+ return /* @__PURE__ */ jsxs(
402
+ "button",
403
+ {
404
+ type: "button",
405
+ onClick: () => {
406
+ setIsSortOpen(false);
407
+ navigateWithoutReload(
408
+ getQueryUrl({
409
+ $orderby: option.value || void 0,
410
+ orderBy: void 0
411
+ })
412
+ );
413
+ },
414
+ 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"}`,
415
+ children: [
416
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: option.label }),
417
+ isSelected && /* @__PURE__ */ jsx(CheckIcon, { className: "h-4 w-4 shrink-0" })
418
+ ]
397
419
  },
398
- attributes: { placeholder: "Default" },
399
- inputClasses: "!mt-0 !rounded-md !border-info !bg-default !px-4 !py-3 !shadow-sm focus:!border-primary focus:!ring-primary-base"
400
- }
401
- ) })
420
+ option.value
421
+ );
422
+ }) })
402
423
  ] })
403
424
  ] }),
404
425
  shouldShowFacets && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
@@ -13,7 +13,7 @@ import {
13
13
  import "./chunk-IMNQO57B.mjs";
14
14
 
15
15
  // src/components/pageRenderingEngine/nodes/DataBindingControls.tsx
16
- import { useCallback, useEffect, useMemo, useState } from "react";
16
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
17
17
  import { useRouter, useSearchParams } from "next/navigation";
18
18
  import { jsx, jsxs } from "react/jsx-runtime";
19
19
  var humanize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
@@ -130,8 +130,9 @@ var CloseIcon = ({ className = "" }) => /* @__PURE__ */ jsx(
130
130
  );
131
131
  var normalizeSortOption = (option) => {
132
132
  const direction = option.direction.trim();
133
+ const title = option.title || humanize(option.name);
133
134
  return {
134
- label: option.title || humanize(option.name),
135
+ label: title,
135
136
  value: [option.name, direction].filter(Boolean).join(" ")
136
137
  };
137
138
  };
@@ -148,6 +149,17 @@ var DataBindingControls = ({
148
149
  const router = useRouter();
149
150
  const searchParams = useSearchParams();
150
151
  const [collapsedCategories, setCollapsedCategories] = useState({});
152
+ const [isSortOpen, setIsSortOpen] = useState(false);
153
+ const sortDropdownRef = useRef(null);
154
+ useEffect(() => {
155
+ const handleClickOutside = (event) => {
156
+ if (sortDropdownRef.current && !sortDropdownRef.current.contains(event.target)) {
157
+ setIsSortOpen(false);
158
+ }
159
+ };
160
+ document.addEventListener("mousedown", handleClickOutside);
161
+ return () => document.removeEventListener("mousedown", handleClickOutside);
162
+ }, []);
151
163
  const toggleCategory = (field) => {
152
164
  setCollapsedCategories((prev) => ({
153
165
  ...prev,
@@ -182,6 +194,8 @@ var DataBindingControls = ({
182
194
  }).filter((group) => group !== null);
183
195
  }, [facets, selectedFacetValues]);
184
196
  const normalizedSortOptions = (sortOptions ?? []).filter((option) => option?.name && option?.direction).map(normalizeSortOption).filter((option) => option.value);
197
+ const selectedSortOption = normalizedSortOptions.find((opt) => opt.value === currentSort) || normalizedSortOptions[0];
198
+ const selectedSortLabel = selectedSortOption?.label || "";
185
199
  const getQueryUrl = useCallback(
186
200
  (updates) => {
187
201
  const initialParams = searchParams ? searchParams.toString() : "";
@@ -314,27 +328,19 @@ var DataBindingControls = ({
314
328
  {
315
329
  className: mode === "toolbar" ? "my-6" : "flex flex-col rounded-md border bg-default p-3.5 shadow-sm",
316
330
  children: [
317
- shouldShowToolbar && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
318
- showSearchBar && // <form
319
- // role="search"
320
- // className="flex w-full flex-col gap-3 rounded-md border border-info bg-info-soft p-3 sm:flex-row sm:items-center md:min-w-0 md:flex-1"
321
- // onSubmit={(event) => {
322
- // event.preventDefault();
323
- // void handleSearch();
324
- // }}
325
- // >
326
- /* @__PURE__ */ jsxs(
331
+ shouldShowToolbar && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
332
+ showSearchBar && /* @__PURE__ */ jsxs(
327
333
  "form",
328
334
  {
329
335
  role: "search",
330
- className: "flex w-full flex-col gap-3 rounded-md sm:flex-row sm:items-center md:min-w-0 md:flex-1",
336
+ className: "flex w-full items-center gap-2 sm:flex-row md:min-w-0 md:flex-1",
331
337
  onSubmit: (event) => {
332
338
  event.preventDefault();
333
339
  void handleSearch();
334
340
  },
335
341
  children: [
336
- /* @__PURE__ */ jsxs("div", { className: "relative bg-default rounded-md min-w-0 flex-1", children: [
337
- /* @__PURE__ */ jsx(SearchIcon, { className: "pointer-events-none absolute left-4 top-1/2 z-10 h-5 w-5 -translate-y-1/2" }),
342
+ /* @__PURE__ */ jsxs("div", { className: "relative min-w-0 flex-1", children: [
343
+ /* @__PURE__ */ 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" }),
338
344
  /* @__PURE__ */ jsx(
339
345
  InputControl_default,
340
346
  {
@@ -345,7 +351,7 @@ var DataBindingControls = ({
345
351
  attributes: {
346
352
  placeholder: "Search discussions, topics, or keywords..."
347
353
  },
348
- inputClasses: "!mt-0 !rounded-md !py-3 !pl-11 !pr-4 !shadow-sm"
354
+ 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"
349
355
  }
350
356
  )
351
357
  ] }),
@@ -354,9 +360,9 @@ var DataBindingControls = ({
354
360
  {
355
361
  ButtonType: "Primary" /* Solid */,
356
362
  onClick: handleSearch,
357
- className: "!rounded-md !px-6 !py-3 sm:self-stretch",
363
+ className: "!h-10 !rounded-md !px-5 !py-0 flex items-center justify-center gap-1.5 shrink-0",
358
364
  children: [
359
- /* @__PURE__ */ jsx(SearchIcon, { className: "h-5 w-5" }),
365
+ /* @__PURE__ */ jsx(SearchIcon, { className: "h-4 w-4" }),
360
366
  /* @__PURE__ */ jsx("span", { children: "Search" })
361
367
  ]
362
368
  }
@@ -364,37 +370,52 @@ var DataBindingControls = ({
364
370
  ]
365
371
  }
366
372
  ),
367
- hasSortControls && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-3 rounded-md border border-info bg-info-soft p-3 text-sm md:ml-auto md:w-auto md:min-w-72 md:self-stretch", children: [
368
- /* @__PURE__ */ jsx(
369
- "label",
373
+ hasSortControls && /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: sortDropdownRef, children: [
374
+ /* @__PURE__ */ jsxs(
375
+ "button",
370
376
  {
371
- htmlFor: "sortOptions",
372
- className: "shrink-0 whitespace-nowrap font-medium",
373
- children: "Sort by"
377
+ type: "button",
378
+ onClick: () => setIsSortOpen((prev) => !prev),
379
+ "aria-expanded": isSortOpen,
380
+ 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",
381
+ children: [
382
+ /* @__PURE__ */ jsxs("span", { className: "whitespace-nowrap", children: [
383
+ /* @__PURE__ */ jsx("span", { className: "opacity-70", children: "Sort by: " }),
384
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", children: selectedSortLabel })
385
+ ] }),
386
+ /* @__PURE__ */ jsx(
387
+ ChevronDownIcon,
388
+ {
389
+ className: `h-4 w-4 shrink-0 transition-transform duration-200 ${isSortOpen ? "rotate-180" : ""}`
390
+ }
391
+ )
392
+ ]
374
393
  }
375
394
  ),
376
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 rounded-md bg-default md:min-w-48", children: /* @__PURE__ */ jsx(
377
- InputControl_default,
378
- {
379
- name: "sortOptions",
380
- controlType: InputControlType_default.select,
381
- value: currentSort,
382
- dataset: normalizedSortOptions,
383
- dataKeyFieldName: "value",
384
- dataTextFieldName: "label",
385
- callback: (updatedValues) => {
386
- const updatedSort = updatedValues.value || "";
387
- navigateWithoutReload(
388
- getQueryUrl({
389
- $orderby: updatedSort || void 0,
390
- orderBy: void 0
391
- })
392
- );
395
+ isSortOpen && /* @__PURE__ */ 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) => {
396
+ const isSelected = currentSort ? option.value === currentSort : option.value === normalizedSortOptions[0]?.value;
397
+ return /* @__PURE__ */ jsxs(
398
+ "button",
399
+ {
400
+ type: "button",
401
+ onClick: () => {
402
+ setIsSortOpen(false);
403
+ navigateWithoutReload(
404
+ getQueryUrl({
405
+ $orderby: option.value || void 0,
406
+ orderBy: void 0
407
+ })
408
+ );
409
+ },
410
+ 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"}`,
411
+ children: [
412
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: option.label }),
413
+ isSelected && /* @__PURE__ */ jsx(CheckIcon, { className: "h-4 w-4 shrink-0" })
414
+ ]
393
415
  },
394
- attributes: { placeholder: "Default" },
395
- inputClasses: "!mt-0 !rounded-md !border-info !bg-default !px-4 !py-3 !shadow-sm focus:!border-primary focus:!ring-primary-base"
396
- }
397
- ) })
416
+ option.value
417
+ );
418
+ }) })
398
419
  ] })
399
420
  ] }),
400
421
  shouldShowFacets && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
package/dist/index.js CHANGED
@@ -5812,8 +5812,9 @@ var init_DataBindingControls = __esm({
5812
5812
  );
5813
5813
  normalizeSortOption = (option) => {
5814
5814
  const direction = option.direction.trim();
5815
+ const title = option.title || humanize(option.name);
5815
5816
  return {
5816
- label: option.title || humanize(option.name),
5817
+ label: title,
5817
5818
  value: [option.name, direction].filter(Boolean).join(" ")
5818
5819
  };
5819
5820
  };
@@ -5830,6 +5831,17 @@ var init_DataBindingControls = __esm({
5830
5831
  const router = (0, import_navigation.useRouter)();
5831
5832
  const searchParams = (0, import_navigation.useSearchParams)();
5832
5833
  const [collapsedCategories, setCollapsedCategories] = (0, import_react59.useState)({});
5834
+ const [isSortOpen, setIsSortOpen] = (0, import_react59.useState)(false);
5835
+ const sortDropdownRef = (0, import_react59.useRef)(null);
5836
+ (0, import_react59.useEffect)(() => {
5837
+ const handleClickOutside = (event) => {
5838
+ if (sortDropdownRef.current && !sortDropdownRef.current.contains(event.target)) {
5839
+ setIsSortOpen(false);
5840
+ }
5841
+ };
5842
+ document.addEventListener("mousedown", handleClickOutside);
5843
+ return () => document.removeEventListener("mousedown", handleClickOutside);
5844
+ }, []);
5833
5845
  const toggleCategory = (field) => {
5834
5846
  setCollapsedCategories((prev) => ({
5835
5847
  ...prev,
@@ -5864,6 +5876,8 @@ var init_DataBindingControls = __esm({
5864
5876
  }).filter((group) => group !== null);
5865
5877
  }, [facets, selectedFacetValues]);
5866
5878
  const normalizedSortOptions = (sortOptions ?? []).filter((option) => option?.name && option?.direction).map(normalizeSortOption).filter((option) => option.value);
5879
+ const selectedSortOption = normalizedSortOptions.find((opt) => opt.value === currentSort) || normalizedSortOptions[0];
5880
+ const selectedSortLabel = selectedSortOption?.label || "";
5867
5881
  const getQueryUrl = (0, import_react59.useCallback)(
5868
5882
  (updates) => {
5869
5883
  const initialParams = searchParams ? searchParams.toString() : "";
@@ -5996,27 +6010,19 @@ var init_DataBindingControls = __esm({
5996
6010
  {
5997
6011
  className: mode === "toolbar" ? "my-6" : "flex flex-col rounded-md border bg-default p-3.5 shadow-sm",
5998
6012
  children: [
5999
- shouldShowToolbar && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
6000
- showSearchBar && // <form
6001
- // role="search"
6002
- // className="flex w-full flex-col gap-3 rounded-md border border-info bg-info-soft p-3 sm:flex-row sm:items-center md:min-w-0 md:flex-1"
6003
- // onSubmit={(event) => {
6004
- // event.preventDefault();
6005
- // void handleSearch();
6006
- // }}
6007
- // >
6008
- /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6013
+ shouldShowToolbar && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
6014
+ showSearchBar && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6009
6015
  "form",
6010
6016
  {
6011
6017
  role: "search",
6012
- className: "flex w-full flex-col gap-3 rounded-md sm:flex-row sm:items-center md:min-w-0 md:flex-1",
6018
+ className: "flex w-full items-center gap-2 sm:flex-row md:min-w-0 md:flex-1",
6013
6019
  onSubmit: (event) => {
6014
6020
  event.preventDefault();
6015
6021
  void handleSearch();
6016
6022
  },
6017
6023
  children: [
6018
- /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "relative bg-default rounded-md min-w-0 flex-1", children: [
6019
- /* @__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" }),
6024
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "relative min-w-0 flex-1", children: [
6025
+ /* @__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" }),
6020
6026
  /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6021
6027
  InputControl_default,
6022
6028
  {
@@ -6027,7 +6033,7 @@ var init_DataBindingControls = __esm({
6027
6033
  attributes: {
6028
6034
  placeholder: "Search discussions, topics, or keywords..."
6029
6035
  },
6030
- inputClasses: "!mt-0 !rounded-md !py-3 !pl-11 !pr-4 !shadow-sm"
6036
+ 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"
6031
6037
  }
6032
6038
  )
6033
6039
  ] }),
@@ -6036,9 +6042,9 @@ var init_DataBindingControls = __esm({
6036
6042
  {
6037
6043
  ButtonType: "Primary" /* Solid */,
6038
6044
  onClick: handleSearch,
6039
- className: "!rounded-md !px-6 !py-3 sm:self-stretch",
6045
+ className: "!h-10 !rounded-md !px-5 !py-0 flex items-center justify-center gap-1.5 shrink-0",
6040
6046
  children: [
6041
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SearchIcon, { className: "h-5 w-5" }),
6047
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SearchIcon, { className: "h-4 w-4" }),
6042
6048
  /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { children: "Search" })
6043
6049
  ]
6044
6050
  }
@@ -6046,37 +6052,52 @@ var init_DataBindingControls = __esm({
6046
6052
  ]
6047
6053
  }
6048
6054
  ),
6049
- hasSortControls && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex w-full items-center gap-3 rounded-md border border-info bg-info-soft p-3 text-sm md:ml-auto md:w-auto md:min-w-72 md:self-stretch", children: [
6050
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6051
- "label",
6055
+ hasSortControls && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "relative shrink-0", ref: sortDropdownRef, children: [
6056
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6057
+ "button",
6052
6058
  {
6053
- htmlFor: "sortOptions",
6054
- className: "shrink-0 whitespace-nowrap font-medium",
6055
- children: "Sort by"
6059
+ type: "button",
6060
+ onClick: () => setIsSortOpen((prev) => !prev),
6061
+ "aria-expanded": isSortOpen,
6062
+ 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",
6063
+ children: [
6064
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("span", { className: "whitespace-nowrap", children: [
6065
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "opacity-70", children: "Sort by: " }),
6066
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "font-semibold", children: selectedSortLabel })
6067
+ ] }),
6068
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6069
+ ChevronDownIcon,
6070
+ {
6071
+ className: `h-4 w-4 shrink-0 transition-transform duration-200 ${isSortOpen ? "rotate-180" : ""}`
6072
+ }
6073
+ )
6074
+ ]
6056
6075
  }
6057
6076
  ),
6058
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "min-w-0 flex-1 rounded-md bg-default md:min-w-48", children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6059
- InputControl_default,
6060
- {
6061
- name: "sortOptions",
6062
- controlType: InputControlType_default.select,
6063
- value: currentSort,
6064
- dataset: normalizedSortOptions,
6065
- dataKeyFieldName: "value",
6066
- dataTextFieldName: "label",
6067
- callback: (updatedValues) => {
6068
- const updatedSort = updatedValues.value || "";
6069
- navigateWithoutReload(
6070
- getQueryUrl({
6071
- $orderby: updatedSort || void 0,
6072
- orderBy: void 0
6073
- })
6074
- );
6077
+ 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) => {
6078
+ const isSelected = currentSort ? option.value === currentSort : option.value === normalizedSortOptions[0]?.value;
6079
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6080
+ "button",
6081
+ {
6082
+ type: "button",
6083
+ onClick: () => {
6084
+ setIsSortOpen(false);
6085
+ navigateWithoutReload(
6086
+ getQueryUrl({
6087
+ $orderby: option.value || void 0,
6088
+ orderBy: void 0
6089
+ })
6090
+ );
6091
+ },
6092
+ 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"}`,
6093
+ children: [
6094
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "whitespace-nowrap", children: option.label }),
6095
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(CheckIcon, { className: "h-4 w-4 shrink-0" })
6096
+ ]
6075
6097
  },
6076
- attributes: { placeholder: "Default" },
6077
- inputClasses: "!mt-0 !rounded-md !border-info !bg-default !px-4 !py-3 !shadow-sm focus:!border-primary focus:!ring-primary-base"
6078
- }
6079
- ) })
6098
+ option.value
6099
+ );
6100
+ }) })
6080
6101
  ] })
6081
6102
  ] }),
6082
6103
  shouldShowFacets && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex flex-col gap-3", children: [
package/dist/index.mjs CHANGED
@@ -2420,7 +2420,7 @@ var GoogleMapNode_default = GoogleMapNode;
2420
2420
  // src/components/pageRenderingEngine/nodes/DivContainer.tsx
2421
2421
  import { jsx as jsx37, jsxs as jsxs15 } from "react/jsx-runtime";
2422
2422
  var Pagination = dynamic13(() => import("./Pagination-STOHS3LO.mjs"), { ssr: true });
2423
- var DataBindingControls = dynamic13(() => import("./DataBindingControls-5H5K5GVR.mjs"), {
2423
+ var DataBindingControls = dynamic13(() => import("./DataBindingControls-6IS5F3QX.mjs"), {
2424
2424
  ssr: true
2425
2425
  });
2426
2426
  var Slider = dynamic13(() => import("./Slider-42T6THEQ.mjs"), {
package/dist/server.js CHANGED
@@ -5810,8 +5810,9 @@ var init_DataBindingControls = __esm({
5810
5810
  );
5811
5811
  normalizeSortOption = (option) => {
5812
5812
  const direction = option.direction.trim();
5813
+ const title = option.title || humanize(option.name);
5813
5814
  return {
5814
- label: option.title || humanize(option.name),
5815
+ label: title,
5815
5816
  value: [option.name, direction].filter(Boolean).join(" ")
5816
5817
  };
5817
5818
  };
@@ -5828,6 +5829,17 @@ var init_DataBindingControls = __esm({
5828
5829
  const router = (0, import_navigation.useRouter)();
5829
5830
  const searchParams = (0, import_navigation.useSearchParams)();
5830
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
+ }, []);
5831
5843
  const toggleCategory = (field) => {
5832
5844
  setCollapsedCategories((prev) => ({
5833
5845
  ...prev,
@@ -5862,6 +5874,8 @@ var init_DataBindingControls = __esm({
5862
5874
  }).filter((group) => group !== null);
5863
5875
  }, [facets, selectedFacetValues]);
5864
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 || "";
5865
5879
  const getQueryUrl = (0, import_react59.useCallback)(
5866
5880
  (updates) => {
5867
5881
  const initialParams = searchParams ? searchParams.toString() : "";
@@ -5994,27 +6008,19 @@ var init_DataBindingControls = __esm({
5994
6008
  {
5995
6009
  className: mode === "toolbar" ? "my-6" : "flex flex-col rounded-md border bg-default p-3.5 shadow-sm",
5996
6010
  children: [
5997
- shouldShowToolbar && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
5998
- showSearchBar && // <form
5999
- // role="search"
6000
- // className="flex w-full flex-col gap-3 rounded-md border border-info bg-info-soft p-3 sm:flex-row sm:items-center md:min-w-0 md:flex-1"
6001
- // onSubmit={(event) => {
6002
- // event.preventDefault();
6003
- // void handleSearch();
6004
- // }}
6005
- // >
6006
- /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
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: [
6012
+ showSearchBar && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
6007
6013
  "form",
6008
6014
  {
6009
6015
  role: "search",
6010
- className: "flex w-full flex-col gap-3 rounded-md 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",
6011
6017
  onSubmit: (event) => {
6012
6018
  event.preventDefault();
6013
6019
  void handleSearch();
6014
6020
  },
6015
6021
  children: [
6016
- /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "relative bg-default rounded-md min-w-0 flex-1", children: [
6017
- /* @__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" }),
6018
6024
  /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6019
6025
  InputControl_default,
6020
6026
  {
@@ -6025,7 +6031,7 @@ var init_DataBindingControls = __esm({
6025
6031
  attributes: {
6026
6032
  placeholder: "Search discussions, topics, or keywords..."
6027
6033
  },
6028
- inputClasses: "!mt-0 !rounded-md !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"
6029
6035
  }
6030
6036
  )
6031
6037
  ] }),
@@ -6034,9 +6040,9 @@ var init_DataBindingControls = __esm({
6034
6040
  {
6035
6041
  ButtonType: "Primary" /* Solid */,
6036
6042
  onClick: handleSearch,
6037
- className: "!rounded-md !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",
6038
6044
  children: [
6039
- /* @__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" }),
6040
6046
  /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { children: "Search" })
6041
6047
  ]
6042
6048
  }
@@ -6044,37 +6050,52 @@ var init_DataBindingControls = __esm({
6044
6050
  ]
6045
6051
  }
6046
6052
  ),
6047
- hasSortControls && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex w-full items-center gap-3 rounded-md border border-info bg-info-soft p-3 text-sm md:ml-auto md:w-auto md:min-w-72 md:self-stretch", children: [
6048
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6049
- "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",
6050
6056
  {
6051
- htmlFor: "sortOptions",
6052
- className: "shrink-0 whitespace-nowrap font-medium",
6053
- 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
+ ]
6054
6073
  }
6055
6074
  ),
6056
- /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "min-w-0 flex-1 rounded-md bg-default md:min-w-48", children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
6057
- InputControl_default,
6058
- {
6059
- name: "sortOptions",
6060
- controlType: InputControlType_default.select,
6061
- value: currentSort,
6062
- dataset: normalizedSortOptions,
6063
- dataKeyFieldName: "value",
6064
- dataTextFieldName: "label",
6065
- callback: (updatedValues) => {
6066
- const updatedSort = updatedValues.value || "";
6067
- navigateWithoutReload(
6068
- getQueryUrl({
6069
- $orderby: updatedSort || void 0,
6070
- orderBy: void 0
6071
- })
6072
- );
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
+ ]
6073
6095
  },
6074
- attributes: { placeholder: "Default" },
6075
- inputClasses: "!mt-0 !rounded-md !border-info !bg-default !px-4 !py-3 !shadow-sm focus:!border-primary focus:!ring-primary-base"
6076
- }
6077
- ) })
6096
+ option.value
6097
+ );
6098
+ }) })
6078
6099
  ] })
6079
6100
  ] }),
6080
6101
  shouldShowFacets && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "flex flex-col gap-3", children: [
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-G5TRSVEU.mjs"), {
2391
+ var DataBindingControls = dynamic13(() => import("./DataBindingControls-25UC5W3R.mjs"), {
2392
2392
  ssr: true
2393
2393
  });
2394
2394
  var Slider = dynamic13(() => import("./Slider-UNNVADVP.mjs"), {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clyrex-digital/clyrex-controls-dev",
3
- "version": "0.8.1-dev.20260924115534",
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",