@juspay/svelte-ui-components 2.81.0 → 2.81.2

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.
@@ -244,6 +244,7 @@
244
244
  placeholder={selectData.placeholder ?? ''}
245
245
  disabled={selectData.disabled ?? false}
246
246
  testId={selectData.testId}
247
+ itemTestId={selectData.itemTestId}
247
248
  onchange={(selectedIds) => {
248
249
  if (selectedIds.length > 0) {
249
250
  column.onSelect?.(rowIndex, selectedIds[0]);
@@ -134,6 +134,9 @@ export const asSelectCellData = (value) => {
134
134
  if (typeof record.testId === 'string') {
135
135
  selectData.testId = record.testId;
136
136
  }
137
+ if (typeof record.itemTestId === 'string') {
138
+ selectData.itemTestId = record.itemTestId;
139
+ }
137
140
  return selectData;
138
141
  };
139
142
  export const asInputCellData = (value) => {
@@ -97,6 +97,8 @@ export type TableSelectCellData = {
97
97
  placeholder?: string;
98
98
  disabled?: boolean;
99
99
  testId?: string;
100
+ /** Per-option test id prefix — each option emits `data-pw="{itemTestId}-{id}"`. */
101
+ itemTestId?: string;
100
102
  };
101
103
  /** Cell shape for `type: 'input'` — the handler lives on the column. */
102
104
  export type TableInputCellData = {
@@ -94,25 +94,37 @@ export function computeSankeyLayout(nodes, links, width, height, nodeWidth = 16,
94
94
  columnGroups.get(col).push(n.id);
95
95
  }
96
96
  const colWidth = maxCol === 0 ? 0 : (width - nodeWidth) / maxCol;
97
+ const columnPadding = new Map();
97
98
  // Initialize y positions
98
99
  const nodeY = new Map();
99
100
  const nodeH = new Map();
100
- for (const [, ids] of columnGroups) {
101
+ for (const [col, ids] of columnGroups) {
101
102
  const totalValue = ids.reduce((s, id) => s + (nodeValues.get(id) ?? 0), 0);
102
- const availableHeight = height - (ids.length - 1) * nodePadding;
103
- let y = 0;
104
- for (const id of ids) {
103
+ const gapCount = ids.length - 1;
104
+ const availableHeight = height - gapCount * nodePadding;
105
+ const renderedHeights = ids.map((id) => {
105
106
  const val = nodeValues.get(id) ?? 0;
106
107
  const h = totalValue > 0 ? (val / totalValue) * availableHeight : availableHeight / ids.length;
107
- const renderedH = Math.max(minLinkWidth, h);
108
+ return Math.max(minLinkWidth, h);
109
+ });
110
+ const sumRendered = renderedHeights.reduce((s, h) => s + h, 0);
111
+ const paddingBudget = gapCount > 0 ? (height - sumRendered) / gapCount : 0;
112
+ const effectivePadding = Math.max(0, Math.min(nodePadding, paddingBudget));
113
+ const scale = sumRendered > height && sumRendered > 0 ? height / sumRendered : 1;
114
+ columnPadding.set(col, effectivePadding);
115
+ let y = 0;
116
+ for (let index = 0; index < ids.length; index++) {
117
+ const id = ids[index];
118
+ const renderedH = renderedHeights[index] * scale;
108
119
  nodeY.set(id, y);
109
120
  nodeH.set(id, renderedH);
110
- y += renderedH + nodePadding;
121
+ y += renderedH + effectivePadding;
111
122
  }
112
123
  }
113
124
  // Iterative relaxation (upstream pass)
114
125
  for (let iter = 0; iter < iterations; iter++) {
115
- for (const [, ids] of columnGroups) {
126
+ for (const [col, ids] of columnGroups) {
127
+ const padding = columnPadding.get(col) ?? nodePadding;
116
128
  for (const id of ids) {
117
129
  const deps = incoming.get(id) ?? [];
118
130
  const totalDepValue = deps.reduce((s, d) => s + d.value, 0);
@@ -128,15 +140,13 @@ export function computeSankeyLayout(nodes, links, width, height, nodeWidth = 16,
128
140
  nodeY.set(id, Math.max(0, weightedY - (nodeH.get(id) ?? 0) / 2));
129
141
  }
130
142
  }
131
- // Resolve overlaps: push down from the top.
132
- ids.sort((a, b) => (nodeY.get(a) ?? 0) - (nodeY.get(b) ?? 0));
133
143
  let y = 0;
134
144
  for (const id of ids) {
135
145
  const cy = nodeY.get(id) ?? 0;
136
146
  if (cy < y) {
137
147
  nodeY.set(id, y);
138
148
  }
139
- y = (nodeY.get(id) ?? 0) + (nodeH.get(id) ?? 0) + nodePadding;
149
+ y = (nodeY.get(id) ?? 0) + (nodeH.get(id) ?? 0) + padding;
140
150
  }
141
151
  // Resolve overlaps: pull back up from the bottom. The push-down pass
142
152
  // above only ever grows a column's block downward, so a fan-out whose
@@ -151,7 +161,7 @@ export function computeSankeyLayout(nodes, links, width, height, nodeWidth = 16,
151
161
  if (nodeBottom > bottomBoundary) {
152
162
  nodeY.set(id, bottomBoundary - (nodeH.get(id) ?? 0));
153
163
  }
154
- bottomBoundary = (nodeY.get(id) ?? 0) - nodePadding;
164
+ bottomBoundary = (nodeY.get(id) ?? 0) - padding;
155
165
  }
156
166
  // Re-centre the column's node group within [0, height]: once overlaps
157
167
  // are resolved, anchor the block at the midpoint of its remaining
@@ -184,25 +194,53 @@ export function computeSankeyLayout(nodes, links, width, height, nodeWidth = 16,
184
194
  column: columns.get(n.id) ?? 0
185
195
  }));
186
196
  const nodeById = new Map(computedNodes.map((n) => [n.id, n]));
187
- // Build computed links with Bezier paths
188
- const sourceOffsets = new Map();
189
- const targetOffsets = new Map();
190
- for (const n of nodes) {
191
- sourceOffsets.set(n.id, nodeY.get(n.id) ?? 0);
192
- targetOffsets.set(n.id, nodeY.get(n.id) ?? 0);
197
+ const linkKey = (l) => `${l.source}${l.target}`;
198
+ const linkWidths = new Map();
199
+ for (const l of links) {
200
+ const sVal = nodeValues.get(l.source) ?? 1;
201
+ const sourceH = nodeH.get(l.source) ?? 0;
202
+ linkWidths.set(linkKey(l), Math.max(minLinkWidth, (l.value / Math.max(sVal, 1)) * sourceH));
203
+ }
204
+ const linkSy = new Map();
205
+ const linkTy = new Map();
206
+ const bySource = new Map();
207
+ const byTarget = new Map();
208
+ for (const l of links) {
209
+ if (!bySource.has(l.source)) {
210
+ bySource.set(l.source, []);
211
+ }
212
+ bySource.get(l.source).push(l);
213
+ if (!byTarget.has(l.target)) {
214
+ byTarget.set(l.target, []);
215
+ }
216
+ byTarget.get(l.target).push(l);
217
+ }
218
+ for (const [source, group] of bySource) {
219
+ group.sort((a, b) => (nodeY.get(a.target) ?? 0) - (nodeY.get(b.target) ?? 0));
220
+ let offset = nodeY.get(source) ?? 0;
221
+ for (const l of group) {
222
+ const w = linkWidths.get(linkKey(l)) ?? 0;
223
+ linkSy.set(linkKey(l), offset + w / 2);
224
+ offset += w;
225
+ }
226
+ }
227
+ for (const [target, group] of byTarget) {
228
+ group.sort((a, b) => (nodeY.get(a.source) ?? 0) - (nodeY.get(b.source) ?? 0));
229
+ let offset = nodeY.get(target) ?? 0;
230
+ for (const l of group) {
231
+ const w = linkWidths.get(linkKey(l)) ?? 0;
232
+ linkTy.set(linkKey(l), offset + w / 2);
233
+ offset += w;
234
+ }
193
235
  }
194
236
  const computedLinks = links.map((l) => {
195
237
  const sourceNode = nodeById.get(l.source);
196
238
  const targetNode = nodeById.get(l.target);
197
- const sVal = nodeValues.get(l.source) ?? 1;
198
- const sourceH = nodeH.get(l.source) ?? 0;
199
- const linkWidth = Math.max(minLinkWidth, (l.value / Math.max(sVal, 1)) * sourceH);
239
+ const linkWidth = linkWidths.get(linkKey(l)) ?? minLinkWidth;
200
240
  const sx = (sourceNode?.x ?? 0) + nodeWidth;
201
- const sy = (sourceOffsets.get(l.source) ?? 0) + linkWidth / 2;
241
+ const sy = linkSy.get(linkKey(l)) ?? 0;
202
242
  const tx = targetNode?.x ?? 0;
203
- const ty = (targetOffsets.get(l.target) ?? 0) + linkWidth / 2;
204
- sourceOffsets.set(l.source, (sourceOffsets.get(l.source) ?? 0) + linkWidth);
205
- targetOffsets.set(l.target, (targetOffsets.get(l.target) ?? 0) + linkWidth);
243
+ const ty = linkTy.get(linkKey(l)) ?? 0;
206
244
  const midX = (sx + tx) / 2;
207
245
  const path = `M ${sx} ${sy} C ${midX} ${sy}, ${midX} ${ty}, ${tx} ${ty}`;
208
246
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.81.0",
3
+ "version": "2.81.2",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",