@propeller-commerce/propeller-v2-react-ui 0.4.25 → 0.4.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1103,6 +1103,12 @@ interface UseOrdersOptions {
1103
1103
  orderStatuses?: string[];
1104
1104
  /** Fields the free-text `term` is matched against. */
1105
1105
  termFields?: OrderSearchFields[];
1106
+ /**
1107
+ * Seed the search/filter form on mount — e.g. rehydrated from the URL query
1108
+ * so a bookmarked/shared filtered view restores. The consumer is responsible
1109
+ * for the fetch: pass this and the first fetch uses the seeded criteria.
1110
+ */
1111
+ initialSearchForm?: OrderSearchForm;
1106
1112
  /** Portal configuration: image search/variant filters. */
1107
1113
  configuration?: {
1108
1114
  imageSearchFiltersGrid?: MediaImageProductSearchInput;
@@ -4727,6 +4733,19 @@ interface OrderListProps {
4727
4733
  enableSearch?: boolean;
4728
4734
  /** Fields enabled for searching (UI inputs) */
4729
4735
  searchFields?: string[];
4736
+ /**
4737
+ * Seed the filter form on mount — typically rehydrated from the URL query so
4738
+ * a bookmarked/shared filtered view restores on reload. Pair with
4739
+ * `onSearchApply` so the page can keep the URL in sync when filters change.
4740
+ */
4741
+ initialSearchForm?: OrderSearchForm;
4742
+ /**
4743
+ * Fires when the user applies or clears filters (the "Search"/"Clear"
4744
+ * buttons). Receives the active filter form. Use it to persist the filters
4745
+ * to the URL — the component owns no router, so the page decides how (and
4746
+ * whether) to reflect them in `location`.
4747
+ */
4748
+ onSearchApply?: (form: OrderSearchForm) => void;
4730
4749
  /** Term fields configuration (backend) */
4731
4750
  termFields?: any[];
4732
4751
  /** Override company ID for order filtering (respects company switcher) */
@@ -6591,6 +6610,20 @@ interface SearchBarProps {
6591
6610
  onResultClick?: (result: SearchBarResult) => void;
6592
6611
  /** Fires when "View all results" is clicked. Receives the search term. */
6593
6612
  onViewAllClick?: (term: string) => void;
6613
+ /**
6614
+ * Build the destination URL for a result item. When provided, each result
6615
+ * renders as a real `<a href>` (middle-clickable, new-tab, crawlable) while
6616
+ * `onResultClick` still fires for SPA navigation. Omit to keep the
6617
+ * div-based fallback.
6618
+ */
6619
+ getResultHref?: (result: SearchBarResult) => string;
6620
+ /**
6621
+ * Build the destination URL for the "View all results" CTA. When provided,
6622
+ * the CTA renders as a real `<a href>` (middle-clickable, new-tab, crawlable,
6623
+ * keyboard-focusable) while `onViewAllClick` still fires for SPA navigation.
6624
+ * Omit to keep the div-based fallback.
6625
+ */
6626
+ getViewAllHref?: (term: string) => string;
6594
6627
  /** Custom price formatting function */
6595
6628
  formatPrice?: (price: number) => string;
6596
6629
  /** Currency symbol for prices. Resolved from PropellerProvider when omitted; defaults to '€'. */
package/dist/index.d.ts CHANGED
@@ -1103,6 +1103,12 @@ interface UseOrdersOptions {
1103
1103
  orderStatuses?: string[];
1104
1104
  /** Fields the free-text `term` is matched against. */
1105
1105
  termFields?: OrderSearchFields[];
1106
+ /**
1107
+ * Seed the search/filter form on mount — e.g. rehydrated from the URL query
1108
+ * so a bookmarked/shared filtered view restores. The consumer is responsible
1109
+ * for the fetch: pass this and the first fetch uses the seeded criteria.
1110
+ */
1111
+ initialSearchForm?: OrderSearchForm;
1106
1112
  /** Portal configuration: image search/variant filters. */
1107
1113
  configuration?: {
1108
1114
  imageSearchFiltersGrid?: MediaImageProductSearchInput;
@@ -4727,6 +4733,19 @@ interface OrderListProps {
4727
4733
  enableSearch?: boolean;
4728
4734
  /** Fields enabled for searching (UI inputs) */
4729
4735
  searchFields?: string[];
4736
+ /**
4737
+ * Seed the filter form on mount — typically rehydrated from the URL query so
4738
+ * a bookmarked/shared filtered view restores on reload. Pair with
4739
+ * `onSearchApply` so the page can keep the URL in sync when filters change.
4740
+ */
4741
+ initialSearchForm?: OrderSearchForm;
4742
+ /**
4743
+ * Fires when the user applies or clears filters (the "Search"/"Clear"
4744
+ * buttons). Receives the active filter form. Use it to persist the filters
4745
+ * to the URL — the component owns no router, so the page decides how (and
4746
+ * whether) to reflect them in `location`.
4747
+ */
4748
+ onSearchApply?: (form: OrderSearchForm) => void;
4730
4749
  /** Term fields configuration (backend) */
4731
4750
  termFields?: any[];
4732
4751
  /** Override company ID for order filtering (respects company switcher) */
@@ -6591,6 +6610,20 @@ interface SearchBarProps {
6591
6610
  onResultClick?: (result: SearchBarResult) => void;
6592
6611
  /** Fires when "View all results" is clicked. Receives the search term. */
6593
6612
  onViewAllClick?: (term: string) => void;
6613
+ /**
6614
+ * Build the destination URL for a result item. When provided, each result
6615
+ * renders as a real `<a href>` (middle-clickable, new-tab, crawlable) while
6616
+ * `onResultClick` still fires for SPA navigation. Omit to keep the
6617
+ * div-based fallback.
6618
+ */
6619
+ getResultHref?: (result: SearchBarResult) => string;
6620
+ /**
6621
+ * Build the destination URL for the "View all results" CTA. When provided,
6622
+ * the CTA renders as a real `<a href>` (middle-clickable, new-tab, crawlable,
6623
+ * keyboard-focusable) while `onViewAllClick` still fires for SPA navigation.
6624
+ * Omit to keep the div-based fallback.
6625
+ */
6626
+ getViewAllHref?: (term: string) => string;
6594
6627
  /** Custom price formatting function */
6595
6628
  formatPrice?: (price: number) => string;
6596
6629
  /** Currency symbol for prices. Resolved from PropellerProvider when omitted; defaults to '€'. */
package/dist/index.js CHANGED
@@ -1698,7 +1698,9 @@ function useOrders(options) {
1698
1698
  const [orders, setOrders] = useState([]);
1699
1699
  const [loading, setLoading] = useState(false);
1700
1700
  const [error, setError] = useState(null);
1701
- const [searchForm, setSearchForm] = useState({});
1701
+ const [searchForm, setSearchForm] = useState(
1702
+ options.initialSearchForm ?? {}
1703
+ );
1702
1704
  const pagination = usePagination(options.itemsPerPage ?? 10);
1703
1705
  const fetchOrders = useCallback(
1704
1706
  async (page = 1) => {
@@ -3555,6 +3557,13 @@ function AccountIconAndMenu(rawProps) {
3555
3557
  setMenuOpen(false);
3556
3558
  if (props.onMenuItemClick) props.onMenuItemClick(href);
3557
3559
  }
3560
+ function handleMenuLinkClick(event, href) {
3561
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
3562
+ return;
3563
+ }
3564
+ event.preventDefault();
3565
+ handleMenuItemClick(href);
3566
+ }
3558
3567
  function handleLogoutClick() {
3559
3568
  setMenuOpen(false);
3560
3569
  if (props.onLogoutClick) props.onLogoutClick();
@@ -3603,10 +3612,10 @@ function AccountIconAndMenu(rawProps) {
3603
3612
  /* @__PURE__ */ jsx("p", { className: "propeller-account-menu__user-name font-medium text-foreground truncate", children: getUserName() })
3604
3613
  ] }),
3605
3614
  /* @__PURE__ */ jsx("nav", { className: "propeller-account-menu__nav py-2", children: /* @__PURE__ */ jsx("ul", { className: "propeller-account-menu__list space-y-0.5", children: getMenuLinks()?.map((link) => /* @__PURE__ */ jsx("li", { className: "propeller-account-menu__item", children: /* @__PURE__ */ jsx(
3606
- "button",
3615
+ "a",
3607
3616
  {
3608
- type: "button",
3609
- onClick: (event) => handleMenuItemClick(link.href),
3617
+ href: link.href,
3618
+ onClick: (event) => handleMenuLinkClick(event, link.href),
3610
3619
  "data-active": isActiveLink(link.href) ? "true" : "false",
3611
3620
  className: `propeller-account-menu__link flex w-full items-center gap-3 px-4 py-2.5 text-sm font-medium transition-colors ${isActiveLink(link.href) ? "bg-secondary/5 text-secondary border-l-2 border-secondary" : "text-muted-foreground hover:bg-surface-hover hover:text-foreground"}`,
3612
3621
  children: link.label
@@ -3669,11 +3678,11 @@ function AccountIconAndMenu(rawProps) {
3669
3678
  /* @__PURE__ */ jsx("p", { className: "propeller-account-menu__user-name font-medium text-foreground truncate", children: getUserName() })
3670
3679
  ] }),
3671
3680
  /* @__PURE__ */ jsx("nav", { className: "propeller-account-menu__nav", children: /* @__PURE__ */ jsx("ul", { className: "propeller-account-menu__list space-y-0.5", children: getMenuLinks()?.map((link) => /* @__PURE__ */ jsx("li", { className: "propeller-account-menu__item", children: /* @__PURE__ */ jsx(
3672
- "button",
3681
+ "a",
3673
3682
  {
3674
- type: "button",
3683
+ href: link.href,
3675
3684
  className: "propeller-account-menu__link flex w-full items-center gap-3 px-3 py-2 text-sm font-medium rounded-control text-muted-foreground hover:bg-surface-hover hover:text-foreground transition-colors",
3676
- onClick: (event) => handleMenuItemClick(link.href),
3685
+ onClick: (event) => handleMenuLinkClick(event, link.href),
3677
3686
  children: link.label
3678
3687
  }
3679
3688
  ) }, link.href)) }) }),
@@ -11830,7 +11839,8 @@ function OrderList(rawProps) {
11830
11839
  orderStatuses: props.orderStatus,
11831
11840
  termFields: props.termFields,
11832
11841
  itemsPerPage: props.initialItemsPerPage || 10,
11833
- channelIds: props.channelIds
11842
+ channelIds: props.channelIds,
11843
+ initialSearchForm: props.initialSearchForm
11834
11844
  });
11835
11845
  function handlePageChange(newPage) {
11836
11846
  if (newPage >= 1 && newPage <= totalPages) {
@@ -12143,6 +12153,7 @@ function OrderList(rawProps) {
12143
12153
  onClick: (event) => {
12144
12154
  setSearchForm({});
12145
12155
  fetchOrders(1);
12156
+ props.onSearchApply?.({});
12146
12157
  },
12147
12158
  children: getLabel(props.labels, "clearButton", "Clear")
12148
12159
  }
@@ -12151,7 +12162,10 @@ function OrderList(rawProps) {
12151
12162
  "button",
12152
12163
  {
12153
12164
  className: "propeller-order-list__search-btn inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-control shadow-sm text-primary-foreground bg-primary hover:bg-primary/80 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary",
12154
- onClick: (event) => fetchOrders(1),
12165
+ onClick: (event) => {
12166
+ fetchOrders(1);
12167
+ props.onSearchApply?.(searchForm);
12168
+ },
12155
12169
  children: getLabel(props.labels, "searchButton", "Search")
12156
12170
  }
12157
12171
  )
@@ -12171,7 +12185,7 @@ function OrderList(rawProps) {
12171
12185
  /* @__PURE__ */ jsx("tbody", { className: "propeller-order-list__tbody bg-card divide-y divide-border", children: orders?.map((order) => /* @__PURE__ */ jsx(
12172
12186
  "tr",
12173
12187
  {
12174
- className: "propeller-order-list__row hover:bg-surface-hover",
12188
+ className: `propeller-order-list__row hover:bg-surface-hover ${rowsClickable ? "cursor-pointer" : ""}`,
12175
12189
  "data-clickable": rowsClickable ? "true" : "false",
12176
12190
  onClick: (event) => rowsClickable && props.onOrderClick(order.id),
12177
12191
  children: columns?.map((col) => /* @__PURE__ */ jsxs(
@@ -17261,6 +17275,9 @@ function SearchBar(rawProps) {
17261
17275
  }
17262
17276
  setShowDropdown(false);
17263
17277
  }
17278
+ function isModifiedClick(event) {
17279
+ return event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
17280
+ }
17264
17281
  useEffect(() => {
17265
17282
  const listener = (e) => {
17266
17283
  if (containerRef.current && !containerRef.current.contains(e.target)) {
@@ -17314,39 +17331,68 @@ function SearchBar(rawProps) {
17314
17331
  ] }) }),
17315
17332
  showDropdown ? /* @__PURE__ */ jsxs("div", { className: "propeller-search-bar__dropdown absolute top-full left-0 right-0 mt-2 bg-card rounded-container shadow-xl border border-border z-50 flex flex-col max-h-96", children: [
17316
17333
  results.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
17317
- /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__results flex-1 overflow-y-auto", children: results?.map((result, index) => /* @__PURE__ */ jsxs(
17318
- "div",
17334
+ /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__results flex-1 overflow-y-auto", children: results?.map((result, index) => {
17335
+ const ResultTag = props.getResultHref ? "a" : "div";
17336
+ const resultProps = props.getResultHref ? {
17337
+ href: props.getResultHref(result),
17338
+ onClick: (event) => {
17339
+ if (isModifiedClick(event)) return;
17340
+ event.preventDefault();
17341
+ handleResultClick(result);
17342
+ }
17343
+ } : { onClick: (_event) => handleResultClick(result) };
17344
+ return /* @__PURE__ */ jsxs(
17345
+ ResultTag,
17346
+ {
17347
+ className: "propeller-search-bar__result flex items-center gap-4 p-3 hover:bg-surface-hover cursor-pointer border-b border-border-subtle last:border-b-0",
17348
+ ...resultProps,
17349
+ children: [
17350
+ result.imageUrl || noImageUrl() ? /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__result-media relative w-16 h-16 flex-shrink-0", children: /* @__PURE__ */ jsx(
17351
+ "img",
17352
+ {
17353
+ className: "propeller-search-bar__result-image w-full h-full object-contain",
17354
+ src: result.imageUrl || noImageUrl(),
17355
+ alt: result.name
17356
+ }
17357
+ ) }) : null,
17358
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
17359
+ /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__result-name font-semibold truncate", children: result.name }),
17360
+ result.sku ? /* @__PURE__ */ jsxs("div", { className: "propeller-search-bar__result-sku text-sm text-muted-foreground", children: [
17361
+ "SKU: ",
17362
+ result.sku
17363
+ ] }) : null
17364
+ ] }),
17365
+ result.price !== void 0 && result.price !== null ? /* @__PURE__ */ jsxs("div", { className: "propeller-search-bar__result-price text-sm font-semibold text-foreground flex-shrink-0 text-right", children: [
17366
+ /* @__PURE__ */ jsx("span", { className: "propeller-search-bar__result-price-value", children: formatItemPrice(leadingPrice(result)) }),
17367
+ /* @__PURE__ */ jsx("span", { className: "propeller-search-bar__result-price-label block text-xs font-normal text-muted-foreground", children: priceTaxLabel() })
17368
+ ] }) : null
17369
+ ]
17370
+ },
17371
+ result.id + "-" + index
17372
+ );
17373
+ }) }),
17374
+ itemsFound > results.length ? props.getViewAllHref ? /* @__PURE__ */ jsxs(
17375
+ "a",
17319
17376
  {
17320
- className: "propeller-search-bar__result flex items-center gap-4 p-3 hover:bg-surface-hover cursor-pointer border-b border-border-subtle last:border-b-0",
17321
- onClick: (event) => handleResultClick(result),
17377
+ href: props.getViewAllHref(localTerm),
17378
+ className: "propeller-search-bar__view-all block flex-shrink-0 p-3 text-center text-primary hover:bg-primary/5 cursor-pointer font-semibold border-t border-border bg-card rounded-b-container",
17379
+ onClick: (event) => {
17380
+ if (isModifiedClick(event)) return;
17381
+ event.preventDefault();
17382
+ handleViewAllClick();
17383
+ },
17322
17384
  children: [
17323
- result.imageUrl || noImageUrl() ? /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__result-media relative w-16 h-16 flex-shrink-0", children: /* @__PURE__ */ jsx(
17324
- "img",
17325
- {
17326
- className: "propeller-search-bar__result-image w-full h-full object-contain",
17327
- src: result.imageUrl || noImageUrl(),
17328
- alt: result.name
17329
- }
17330
- ) }) : null,
17331
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
17332
- /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__result-name font-semibold truncate", children: result.name }),
17333
- result.sku ? /* @__PURE__ */ jsxs("div", { className: "propeller-search-bar__result-sku text-sm text-muted-foreground", children: [
17334
- "SKU: ",
17335
- result.sku
17336
- ] }) : null
17337
- ] }),
17338
- result.price !== void 0 && result.price !== null ? /* @__PURE__ */ jsxs("div", { className: "propeller-search-bar__result-price text-sm font-semibold text-foreground flex-shrink-0 text-right", children: [
17339
- /* @__PURE__ */ jsx("span", { className: "propeller-search-bar__result-price-value", children: formatItemPrice(leadingPrice(result)) }),
17340
- /* @__PURE__ */ jsx("span", { className: "propeller-search-bar__result-price-label block text-xs font-normal text-muted-foreground", children: priceTaxLabel() })
17341
- ] }) : null
17385
+ getLabel(props.labels, "viewAll", "View all results"),
17386
+ " (",
17387
+ itemsFound,
17388
+ ")"
17342
17389
  ]
17343
- },
17344
- result.id + "-" + index
17345
- )) }),
17346
- itemsFound > results.length ? /* @__PURE__ */ jsxs(
17347
- "div",
17390
+ }
17391
+ ) : /* @__PURE__ */ jsxs(
17392
+ "button",
17348
17393
  {
17349
- className: "propeller-search-bar__view-all flex-shrink-0 p-3 text-center text-primary hover:bg-primary/5 cursor-pointer font-semibold border-t border-border bg-card rounded-b-container",
17394
+ type: "button",
17395
+ className: "propeller-search-bar__view-all block w-full flex-shrink-0 p-3 text-center text-primary hover:bg-primary/5 cursor-pointer font-semibold border-t border-border bg-card rounded-b-container",
17350
17396
  onClick: (event) => handleViewAllClick(),
17351
17397
  children: [
17352
17398
  getLabel(props.labels, "viewAll", "View all results"),