@metron-labs/quote-react 0.1.42 → 0.1.44

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.
@@ -3,6 +3,8 @@ export declare const MAX_LINES = 500;
3
3
  export declare const MAX_CHARS = 50000;
4
4
  /** Longer than this and it's usually a pasted paragraph, not a line item. */
5
5
  export declare const MAX_LINE_CHARS = 500;
6
+ /** Most of one product a basket line can ask for. Under the quote-request API's own 1,000,000 cap. */
7
+ export declare const MAX_ORDER_QTY = 100000;
6
8
  export declare const FILE_EXTENSIONS: {
7
9
  schedule: string[];
8
10
  photo: string[];
@@ -9,6 +9,8 @@ export const MAX_LINES = 500;
9
9
  export const MAX_CHARS = 50000;
10
10
  /** Longer than this and it's usually a pasted paragraph, not a line item. */
11
11
  export const MAX_LINE_CHARS = 500;
12
+ /** Most of one product a basket line can ask for. Under the quote-request API's own 1,000,000 cap. */
13
+ export const MAX_ORDER_QTY = 100000;
12
14
  /** Photos are scaled down to this before upload - plenty for reading a list. */
13
15
  const PHOTO_MAX_EDGE_PX = 2000;
14
16
  export const FILE_EXTENSIONS = {
@@ -8,7 +8,8 @@
8
8
  let moduleConfig = {};
9
9
  /** Called by QuoteProvider. Not part of the public API. */
10
10
  export function setModuleConfig(config) {
11
- moduleConfig = config;
11
+ // Codes are issued upper-case; a host that types or lower-cases one still gets its client.
12
+ moduleConfig = { ...config, client: config.client?.trim().toUpperCase() || undefined };
12
13
  }
13
14
  /** The active config, for code outside the React tree. */
14
15
  export function getQuoteConfig() {
@@ -0,0 +1,2 @@
1
+ /** Calls `onEscape` when Escape is pressed while `active` - for closing a dialog. */
2
+ export declare function useEscapeKey(active: boolean, onEscape: () => void): void;
@@ -0,0 +1,20 @@
1
+ import { useEffect, useRef } from "react";
2
+ /** Calls `onEscape` when Escape is pressed while `active` - for closing a dialog. */
3
+ export function useEscapeKey(active, onEscape) {
4
+ // Latest handler without re-subscribing on every render.
5
+ const handler = useRef(onEscape);
6
+ handler.current = onEscape;
7
+ useEffect(() => {
8
+ if (!active)
9
+ return;
10
+ const onKeyDown = (event) => {
11
+ // isComposing: Escape cancelling an IME composition, not meant for the dialog.
12
+ if (event.key !== "Escape" || event.defaultPrevented || event.isComposing)
13
+ return;
14
+ event.preventDefault();
15
+ handler.current();
16
+ };
17
+ document.addEventListener("keydown", onKeyDown);
18
+ return () => document.removeEventListener("keydown", onKeyDown);
19
+ }, [active]);
20
+ }
package/dist/styles.css CHANGED
@@ -948,8 +948,12 @@
948
948
 
949
949
  :where(.app-wrapper) .basket-card-stats {
950
950
  display: grid;
951
- grid-template-columns: minmax(0, 1fr) 150px 128px;
952
- align-items: end;
951
+ /* Equal qty columns so both wrap their unit (or neither) and stay aligned. */
952
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 128px;
953
+ /* Top-aligned so a wrapped unit under the qty box doesn't shift the row;
954
+ the total centres on the box via the shared height below. */
955
+ --basket-qty-h: 34px;
956
+ align-items: start;
953
957
  gap: 16px 28px;
954
958
  margin-top: 16px;
955
959
  padding-top: 5px;
@@ -979,10 +983,14 @@
979
983
  color: var(--text-dim);
980
984
  }
981
985
 
986
+ /* Wraps so the unit drops under the qty box when the host page makes the
987
+ card narrower than the viewport breakpoints expect, instead of running
988
+ into the next column. */
982
989
  :where(.app-wrapper) .basket-stat-value {
983
990
  display: flex;
991
+ flex-wrap: wrap;
984
992
  align-items: baseline;
985
- gap: 6px;
993
+ gap: 2px 6px;
986
994
  }
987
995
 
988
996
  :where(.app-wrapper) .basket-stat--right .basket-stat-value {
@@ -991,6 +999,12 @@
991
999
 
992
1000
  :where(.app-wrapper) .basket-stat--total .basket-stat-value {
993
1001
  justify-content: flex-end;
1002
+ min-height: var(--basket-qty-h);
1003
+ align-content: center;
1004
+ }
1005
+
1006
+ :where(.app-wrapper) .basket-qty-field .bom-qty-edit {
1007
+ height: var(--basket-qty-h);
994
1008
  }
995
1009
 
996
1010
  :where(.app-wrapper) .basket-stat-value--muted {
@@ -1111,11 +1125,18 @@
1111
1125
  :where(.app-wrapper) .basket-sidebar-row {
1112
1126
  display: flex;
1113
1127
  justify-content: space-between;
1128
+ align-items: baseline;
1129
+ /* A long label in the narrow sidebar wraps rather than running into the amount. */
1130
+ gap: 12px;
1114
1131
  padding: 7px 0;
1115
1132
  font-size: 14px;
1116
1133
  color: var(--text-mid);
1117
1134
  }
1118
1135
 
1136
+ :where(.app-wrapper) .basket-sidebar-row > :last-child {
1137
+ white-space: nowrap;
1138
+ }
1139
+
1119
1140
  :where(.app-wrapper) .basket-sidebar-row--total {
1120
1141
  border-top: 1px solid var(--border);
1121
1142
  margin-top: 6px;
@@ -6612,11 +6633,6 @@
6612
6633
  :where(.app-wrapper) .basket-stat--total {
6613
6634
  grid-column: 1 / -1;
6614
6635
  min-width: 0;
6615
- text-align: left;
6616
- }
6617
-
6618
- :where(.app-wrapper) .basket-stat--total .basket-stat-value {
6619
- justify-content: flex-start;
6620
6636
  }
6621
6637
 
6622
6638
  /* Basket cards on narrow phones: the 90px qty box + unit overflowed its
@@ -6628,6 +6644,7 @@
6628
6644
 
6629
6645
  :where(.app-wrapper) .basket-card-stats {
6630
6646
  gap: 14px 16px;
6647
+ --basket-qty-h: 44px;
6631
6648
  }
6632
6649
 
6633
6650
  :where(.app-wrapper) .basket-stat-label {
@@ -6649,7 +6666,6 @@
6649
6666
 
6650
6667
  :where(.app-wrapper) .basket-qty-field .bom-qty-edit {
6651
6668
  width: 100%;
6652
- height: 44px;
6653
6669
  padding-left: 8px;
6654
6670
  padding-right: 36px;
6655
6671
  }
@@ -6741,17 +6757,6 @@
6741
6757
  /* Category links: one swipeable row instead of wrapping. */
6742
6758
  }
6743
6759
 
6744
- /* Small phones: basket item total sits on the right, label still above. */
6745
- @media (max-width: 400px) {
6746
- :where(.app-wrapper) .basket-stat--total {
6747
- text-align: right;
6748
- }
6749
-
6750
- :where(.app-wrapper) .basket-stat--total .basket-stat-value {
6751
- justify-content: flex-end;
6752
- }
6753
- }
6754
-
6755
6760
  /* Very small phones: logo text pushed Account/Basket onto their own row.
6756
6761
  Keep just the logo mark (the svg has a fixed px size, so font-size: 0
6757
6762
  only hides the bare text node). Footer logo is untouched. */
@@ -123,7 +123,8 @@ export function getPricingBreakdown(item, options) {
123
123
  const salesUnitSize = item.saleUnitQuantity ?? item.salesUnitSize ?? item.uoiCount ?? 1;
124
124
  const wasteFactor = options?.wasteFactorEnabled === false ? 0 : (options?.wasteFactorOverride ?? item.wasteFactorOverride ?? item.wasteFactorDefault ?? 0);
125
125
  const hasOverride = item.sellUnitsOverride != null && Number.isFinite(item.sellUnitsOverride) && item.sellUnitsOverride > 0;
126
- const overriddenSellUnits = hasOverride ? (item.sellUnitsOverride ?? null) : null;
126
+ // A typed quantity follows the same rule as a calculated one: no part sheets or bags.
127
+ const overriddenSellUnits = hasOverride ? (discreteSellUnits.has(sellUnit) ? Math.ceil(item.sellUnitsOverride) : item.sellUnitsOverride) : null;
127
128
  const review = (reason) => ({
128
129
  requestedAmount,
129
130
  requestedUnit,
@@ -267,7 +268,7 @@ export function getPricingBreakdown(item, options) {
267
268
  return review(`Quantity review required, initial request needed ${requestedAmount} ${requestedUnit}.`);
268
269
  }
269
270
  const calculatedSellUnits = roundQuantity(rawSellUnits, roundingRule);
270
- const requiredSellUnits = hasOverride ? (item.sellUnitsOverride ?? null) : calculatedSellUnits;
271
+ const requiredSellUnits = hasOverride ? overriddenSellUnits : calculatedSellUnits;
271
272
  return {
272
273
  requestedAmount,
273
274
  requestedUnit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metron-labs/quote-react",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
4
4
  "description": "Metron Labs quote and estimating calculator components for React",
5
5
  "license": "UNLICENSED",
6
6
  "sideEffects": [