@spotlab/hand-matrix 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -95,6 +95,13 @@ goes in as props and changes come out through `on*Change` callbacks.
95
95
  produces).
96
96
  - `cellsToComboWeights(cells, actionIndex): Map<string, number>` — collapse
97
97
  one action back to absolute per-combo weights, ready to save as a range.
98
+ - `cellsToRanges(original, seed, cells): HandFrequency[][]` — turn the
99
+ edited grid back into one hand-keyed range per action. `original` holds
100
+ each action's range (or `null`) in editor action order, `seed` the cells
101
+ the editor opened with, and `cells` the cells after editing. Combos whose
102
+ cell still matches its seed keep each action's original hands verbatim;
103
+ changed combos, and every combo of a `null` action, are rebuilt from the
104
+ cells.
98
105
  - `comboWeightsToCells(cells, actionWeights, passive): EditorCell[]` — the
99
106
  inverse: re-derive the cells' conditional weights from absolute
100
107
  per-action ranges, keeping each cell's `avail`.
@@ -109,10 +116,10 @@ goes in as props and changes come out through `on*Change` callbacks.
109
116
  `WeightInputProps`.
110
117
 
111
118
  **The editor works at combo level.** A cell is one of the 169 combos (`AKs`,
112
- `AKo`, `QQ`), so suit-specific hands such as `AsKd` don't survive a round
113
- trip: `seedCells` takes combo weights, and `cellsToComboWeights` returns
114
- them. To keep suit-specific hands, set them aside before editing and add them
115
- back to the result yourself.
119
+ `AKo`, `QQ`) at a whole-percent weight, so suit-specific hands such as
120
+ `AsKd:0.7` and fractional weights such as `QQ:0.333` can't be shown exactly:
121
+ `seedCells` takes combo weights, and `cellsToComboWeights` returns them. Save
122
+ with `cellsToRanges` to keep them for every combo the user didn't repaint.
116
123
 
117
124
  ## Theming
118
125
 
@@ -1,3 +1,4 @@
1
+ import { HandFrequency } from '@spotlab/range';
1
2
  export declare const COMBO_INDEX: Map<string, number>;
2
3
  /**
3
4
  * One matrix cell's painted state: `avail` is the combo's cap (its weight in the
@@ -8,6 +9,8 @@ export type EditorCell = {
8
9
  avail: number;
9
10
  weights: number[];
10
11
  };
12
+ /** A copy of `cells` with `combo`'s cell replaced by `cell`. */
13
+ export declare const withCell: (cells: EditorCell[], combo: string, cell: EditorCell) => EditorCell[];
11
14
  /**
12
15
  * Seed the 169-cell grid from a parent range's per-combo availability and each
13
16
  * action's own per-combo range, both expressed as absolute weights (a share of
@@ -24,3 +27,21 @@ export declare function cellsToComboWeights(cells: EditorCell[], actionIndex: nu
24
27
  * `actionWeights` naming the remainder action.
25
28
  */
26
29
  export declare function comboWeightsToCells(cells: EditorCell[], actionWeights: (Map<string, number> | null)[], passive: number): EditorCell[];
30
+ /**
31
+ * Turn an edited grid back into one hand-keyed range per action, keeping each
32
+ * action's original hands wherever the editor didn't touch them. `original`
33
+ * holds each action's range in editor action order, `seed` the cells the
34
+ * editor opened with, and `cells` the cells after editing.
35
+ *
36
+ * A combo whose cell still equals its seed cell (same `avail`, same weights)
37
+ * keeps every action's original hands verbatim, so input the whole-percent grid
38
+ * can't represent — a single suit (`AhKh:0.7`) or a fractional weight
39
+ * (`QQ:0.333`) — survives a save. A changed combo is rebuilt from the cells as
40
+ * `cellsToComboWeights` does. An action whose original is `null` (e.g. an
41
+ * implicit fold) is always rebuilt from the cells in full.
42
+ *
43
+ * Each rebuilt combo takes its first original hand's place, and combos the
44
+ * original didn't have follow in matrix order, so an unedited range formats
45
+ * back to the string it came from.
46
+ */
47
+ export declare function cellsToRanges(original: (HandFrequency[] | null)[], seed: EditorCell[], cells: EditorCell[]): HandFrequency[][];
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export { RangeGrid } from './range-grid';
3
3
  export { RangeMatrixEditor, type RangeMatrixEditorProps, } from './editor/matrix-editor';
4
4
  export { ActionWeightSliders, type ActionWeightSlidersProps, type WeightInputProps, } from './editor/action-sliders';
5
5
  export { useRangePainting, type PaintTool } from './editor/use-range-painting';
6
- export { seedCells, cellsToComboWeights, comboWeightsToCells, type EditorCell, } from './editor/cells';
6
+ export { seedCells, cellsToComboWeights, cellsToRanges, comboWeightsToCells, type EditorCell, } from './editor/cells';
7
7
  export { rebalance, type EditorActionSpec } from './editor/weights';
package/dist/index.js CHANGED
@@ -1,69 +1,69 @@
1
- import { useCallback as g, useEffect as y, useLayoutEffect as I, useMemo as x, useRef as v } from "react";
2
- import { COMBOS as b, parseRange as S, toComboWeights as A } from "@spotlab/range";
3
- import { jsx as h, jsxs as w } from "react/jsx-runtime";
4
- var P = (t, e) => Array.from({ length: Math.ceil(t.length / e) }, (n, r) => t.slice(r * e, r * e + e)), D = "to right", N = (t) => t.length === 0 ? "transparent" : `linear-gradient(${D}, ${t.join(", ")})`, W = (t) => {
1
+ import { useCallback as g, useEffect as N, useLayoutEffect as S, useMemo as I, useRef as b } from "react";
2
+ import { COMBOS as v, comboOf as A, expandComboWeights as M, parseRange as P, toComboWeights as W } from "@spotlab/range";
3
+ import { jsx as p, jsxs as w } from "react/jsx-runtime";
4
+ var D = (t, e) => Array.from({ length: Math.ceil(t.length / e) }, (n, r) => t.slice(r * e, r * e + e)), T = "to right", R = (t) => t.length === 0 ? "transparent" : `linear-gradient(${T}, ${t.join(", ")})`, O = (t) => {
5
5
  const e = t.target;
6
6
  e.hasPointerCapture(t.pointerId) && e.releasePointerCapture(t.pointerId);
7
- }, M = (t, e, n) => {
7
+ }, $ = (t, e, n) => {
8
8
  const r = t.ownerDocument.elementFromPoint(e, n)?.closest("[data-combo]");
9
9
  return !r || !t.contains(r) ? null : r.getAttribute("data-combo");
10
- }, T = ({ combo: t, onPointerDown: e, comboStyle: n }) => /* @__PURE__ */ h("div", {
10
+ }, B = ({ combo: t, onPointerDown: e, comboStyle: n }) => /* @__PURE__ */ p("div", {
11
11
  className: "spotlab-hm-cell",
12
12
  "data-combo": t,
13
13
  style: n?.(t),
14
14
  onPointerDown: e && ((r) => {
15
- W(r), e(t, r);
15
+ O(r), e(t, r);
16
16
  }),
17
- children: /* @__PURE__ */ h("div", {
17
+ children: /* @__PURE__ */ p("div", {
18
18
  className: "spotlab-hm-cell-inner",
19
- children: /* @__PURE__ */ h("div", {
19
+ children: /* @__PURE__ */ p("div", {
20
20
  "data-combo": t,
21
21
  children: t
22
22
  })
23
23
  })
24
- }), B = ({ row: t, ...e }) => /* @__PURE__ */ h("div", {
24
+ }), F = ({ row: t, ...e }) => /* @__PURE__ */ p("div", {
25
25
  className: "spotlab-hm-row",
26
- children: t.map((n, r) => /* @__PURE__ */ h(T, {
26
+ children: t.map((n, r) => /* @__PURE__ */ p(B, {
27
27
  combo: n,
28
28
  ...e
29
29
  }, r))
30
- }), F = {
30
+ }), X = {
31
31
  touchAction: "none",
32
32
  userSelect: "none",
33
33
  WebkitUserSelect: "none",
34
34
  WebkitTouchCallout: "none"
35
- }, R = ({ className: t, onPointerDown: e, onPointerEnter: n, ...r }) => {
36
- const a = v(null), o = (i) => {
37
- const l = M(i.currentTarget, i.clientX, i.clientY);
38
- l !== a.current && (a.current = l, l && n?.(l, i));
35
+ }, x = ({ className: t, onPointerDown: e, onPointerEnter: n, ...r }) => {
36
+ const a = b(null), o = (s) => {
37
+ const l = $(s.currentTarget, s.clientX, s.clientY);
38
+ l !== a.current && (a.current = l, l && n?.(l, s));
39
39
  };
40
- return /* @__PURE__ */ h("div", {
40
+ return /* @__PURE__ */ p("div", {
41
41
  className: ["spotlab-hm-root", t].filter(Boolean).join(" "),
42
- style: e ? F : void 0,
43
- onPointerDown: n && ((i) => {
44
- a.current = M(i.currentTarget, i.clientX, i.clientY);
42
+ style: e ? X : void 0,
43
+ onPointerDown: n && ((s) => {
44
+ a.current = $(s.currentTarget, s.clientX, s.clientY);
45
45
  }),
46
46
  onPointerMove: n && o,
47
47
  onPointerLeave: n && (() => a.current = null),
48
- children: P(b, 13).map((i, l) => /* @__PURE__ */ h(B, {
49
- row: i,
48
+ children: D(v, 13).map((s, l) => /* @__PURE__ */ p(F, {
49
+ row: s,
50
50
  onPointerDown: e,
51
51
  ...r
52
52
  }, l))
53
53
  });
54
- }, O = (t, e) => ({ background: N([`${t} 0 ${e * 100}%`, `transparent ${e * 100}% 100%`]) }), V = ({ range: t, fill: e = "var(--spotlab-hm-fill)", className: n }) => {
55
- const r = x(() => {
56
- const a = typeof t == "string" ? S(t) : t;
57
- return A(a);
54
+ }, _ = (t, e) => ({ background: R([`${t} 0 ${e * 100}%`, `transparent ${e * 100}% 100%`]) }), rt = ({ range: t, fill: e = "var(--spotlab-hm-fill)", className: n }) => {
55
+ const r = I(() => {
56
+ const a = typeof t == "string" ? P(t) : t;
57
+ return W(a);
58
58
  }, [t]);
59
- return /* @__PURE__ */ h(R, {
59
+ return /* @__PURE__ */ p(x, {
60
60
  className: n,
61
61
  comboStyle: (a) => {
62
62
  const o = r.get(a);
63
- return o ? O(e, o) : {};
63
+ return o ? _(e, o) : {};
64
64
  }
65
65
  });
66
- }, X = (t) => Math.max(0, Math.min(100, Math.round(t))), j = (t, e) => {
66
+ }, j = (t) => Math.max(0, Math.min(100, Math.round(t))), q = (t, e) => {
67
67
  const n = t.map(Math.round);
68
68
  let r = 100 - n.reduce((a, o) => a + o, 0);
69
69
  if (r !== 0) {
@@ -80,55 +80,58 @@ var P = (t, e) => Array.from({ length: Math.ceil(t.length / e) }, (n, r) => t.sl
80
80
  }
81
81
  }
82
82
  return n;
83
- }, _ = (t, e, n, r) => {
84
- const a = X(n), o = t.slice();
83
+ }, G = (t, e, n, r) => {
84
+ const a = j(n), o = t.slice();
85
85
  o[e] = a;
86
- const i = 100 - a, l = [];
87
- for (let s = 0; s < t.length; s++) s !== e && l.push(s);
88
- const u = l.reduce((s, p) => s + t[p], 0);
89
- if (u <= 0) {
90
- l.forEach((p) => o[p] = 0);
91
- const s = e === r ? l[0] : r;
92
- s != null && (o[s] = i);
93
- } else l.forEach((s) => o[s] = Math.round(t[s] / u * i));
94
- return j(o, e);
95
- }, $ = (t, e) => t.reduce((n, r, a) => a === e ? n : n + r, 0), k = new Map(b.map((t, e) => [t, e])), L = (t, e) => {
86
+ const s = 100 - a, l = [];
87
+ for (let i = 0; i < t.length; i++) i !== e && l.push(i);
88
+ const c = l.reduce((i, d) => i + t[d], 0);
89
+ if (c <= 0) {
90
+ l.forEach((d) => o[d] = 0);
91
+ const i = e === r ? l[0] : r;
92
+ i != null && (o[i] = s);
93
+ } else l.forEach((i) => o[i] = Math.round(t[i] / c * s));
94
+ return q(o, e);
95
+ }, E = (t, e) => t.reduce((n, r, a) => a === e ? n : n + r, 0), C = new Map(v.map((t, e) => [t, e])), U = (t, e, n) => {
96
+ const r = t.slice();
97
+ return r[C.get(e)] = n, r;
98
+ }, L = (t, e) => {
96
99
  const n = t.slice();
97
- let r = $(n, e);
100
+ let r = E(n, e);
98
101
  if (r > 100) {
99
102
  const a = 100 / r;
100
103
  for (let o = 0; o < n.length; o++) o !== e && (n[o] = Math.round(n[o] * a));
101
- r = $(n, e);
104
+ r = E(n, e);
102
105
  }
103
106
  return n[e] = Math.max(0, 100 - r), n;
104
107
  };
105
- function Z(t, e, n) {
106
- return b.map((r) => {
108
+ function nt(t, e, n) {
109
+ return v.map((r) => {
107
110
  const a = t ? t.get(r) ?? 0 : 1, o = e.map(() => 0);
108
111
  return a <= 0 ? (o[n] = 100, {
109
112
  avail: 0,
110
113
  weights: o
111
- }) : (e.forEach((i, l) => {
112
- const u = i?.get(r) ?? 0;
113
- o[l] = Math.round(u / a * 100);
114
+ }) : (e.forEach((s, l) => {
115
+ const c = s?.get(r) ?? 0;
116
+ o[l] = Math.round(c / a * 100);
114
117
  }), {
115
118
  avail: a,
116
119
  weights: L(o, n)
117
120
  });
118
121
  });
119
122
  }
120
- var q = 8;
121
- function z(t, e) {
123
+ var Y = 8;
124
+ function H(t, e) {
122
125
  const n = /* @__PURE__ */ new Map();
123
126
  return t.forEach((r, a) => {
124
127
  if (r.avail <= 0) return;
125
- const o = r.weights.reduce((l, u) => l + u, 0);
128
+ const o = r.weights.reduce((l, c) => l + c, 0);
126
129
  if (!o) return;
127
- const i = Number((r.weights[e] / o * r.avail).toFixed(q));
128
- i && n.set(b[a], i);
130
+ const s = Number((r.weights[e] / o * r.avail).toFixed(Y));
131
+ s && n.set(v[a], s);
129
132
  }), n;
130
133
  }
131
- function tt(t, e, n) {
134
+ function at(t, e, n) {
132
135
  return t.map((r, a) => {
133
136
  const o = e.map(() => 0);
134
137
  if (r.avail <= 0)
@@ -136,127 +139,145 @@ function tt(t, e, n) {
136
139
  avail: r.avail,
137
140
  weights: o
138
141
  };
139
- const i = b[a];
140
- return e.forEach((l, u) => {
141
- const s = l?.get(i) ?? 0;
142
- o[u] = Math.round(s / r.avail * 100);
142
+ const s = v[a];
143
+ return e.forEach((l, c) => {
144
+ const i = l?.get(s) ?? 0;
145
+ o[c] = Math.round(i / r.avail * 100);
143
146
  }), {
144
147
  avail: r.avail,
145
148
  weights: L(o, n)
146
149
  };
147
150
  });
148
151
  }
149
- function G(t) {
150
- const { cells: e, brush: n, tool: r, onCellsChange: a, onBrushChange: o, onToolChange: i } = t, l = v(null), u = g(() => {
152
+ var Q = (t, e) => t.avail !== e.avail || t.weights.length !== e.weights.length || t.weights.some((n, r) => n !== e.weights[r]);
153
+ function ot(t, e, n) {
154
+ const r = new Set(v.filter((a, o) => Q(e[o], n[o])));
155
+ return t.map((a, o) => {
156
+ const s = H(n, o);
157
+ if (a === null) return M(s);
158
+ const l = (d) => {
159
+ const h = s.get(d);
160
+ return h ? M(/* @__PURE__ */ new Map([[d, h]])) : [];
161
+ }, c = [], i = /* @__PURE__ */ new Set();
162
+ for (const d of a) {
163
+ const h = A(d.hand);
164
+ r.has(h) ? i.has(h) || (i.add(h), c.push(...l(h))) : c.push(d);
165
+ }
166
+ for (const d of r) i.has(d) || c.push(...l(d));
167
+ return c;
168
+ });
169
+ }
170
+ function J(t) {
171
+ const { cells: e, brush: n, tool: r, onCellsChange: a, onBrushChange: o, onToolChange: s } = t, l = b(null), c = g(() => {
151
172
  l.current?.abort(), l.current = null;
152
173
  }, []);
153
- y(() => u, [u]);
154
- const s = v(e);
155
- I(() => {
156
- s.current = e;
174
+ N(() => c, [c]);
175
+ const i = b(e);
176
+ S(() => {
177
+ i.current = e;
157
178
  }, [e]);
158
- const p = g((c) => {
159
- const d = s.current, f = k.get(c);
160
- if (d[f].avail <= 0) return;
161
- const C = d.slice();
162
- C[f] = {
163
- avail: d[f].avail,
179
+ const d = g((u) => {
180
+ const f = i.current, { avail: m } = f[C.get(u)];
181
+ if (m <= 0) return;
182
+ const k = U(f, u, {
183
+ avail: m,
164
184
  weights: [...n]
165
- }, s.current = C, a(C);
166
- }, [n, a]), m = g((c) => {
167
- const d = k.get(c);
168
- e[d].avail <= 0 || (o([...e[d].weights]), i("brush"));
185
+ });
186
+ i.current = k, a(k);
187
+ }, [n, a]), h = g((u) => {
188
+ const f = C.get(u);
189
+ e[f].avail <= 0 || (o([...e[f].weights]), s("brush"));
169
190
  }, [
170
191
  e,
171
192
  o,
172
- i
193
+ s
173
194
  ]);
174
195
  return {
175
- onCellDown: g((c) => {
196
+ onCellDown: g((u) => {
176
197
  if (r === "eyedropper") {
177
- m(c);
198
+ h(u);
178
199
  return;
179
200
  }
180
- u();
181
- const d = new AbortController();
182
- l.current = d, window.addEventListener("pointerup", u, { signal: d.signal }), window.addEventListener("pointercancel", u, { signal: d.signal }), p(c);
201
+ c();
202
+ const f = new AbortController();
203
+ l.current = f, window.addEventListener("pointerup", c, { signal: f.signal }), window.addEventListener("pointercancel", c, { signal: f.signal }), d(u);
183
204
  }, [
184
205
  r,
185
- m,
186
- p,
187
- u
206
+ h,
207
+ d,
208
+ c
188
209
  ]),
189
- onCellEnter: g((c) => {
190
- l.current && r === "brush" && p(c);
191
- }, [r, p])
210
+ onCellEnter: g((u) => {
211
+ l.current && r === "brush" && d(u);
212
+ }, [r, d])
192
213
  };
193
214
  }
194
- var E = (t, e, n) => {
215
+ var y = (t, e, n) => {
195
216
  const r = [];
196
217
  let a = 0;
197
218
  return n.forEach((o) => {
198
- const i = (t.weights[o] || 0) / 100;
199
- if (i <= 0) return;
219
+ const s = (t.weights[o] || 0) / 100;
220
+ if (s <= 0) return;
200
221
  const l = e[o].color;
201
- r.push(`${l} ${a * 100}%`), r.push(`${l} ${(a + i) * 100}%`), a += i;
202
- }), r.length > 0 && a < 1 - 1e-9 && r.push(`transparent ${a * 100}% 100%`), N(r);
203
- }, et = ({ cells: t, actions: e, order: n, brush: r, tool: a, onCellsChange: o, onBrushChange: i, onToolChange: l, className: u }) => {
204
- const { onCellDown: s, onCellEnter: p } = G({
222
+ r.push(`${l} ${a * 100}%`), r.push(`${l} ${(a + s) * 100}%`), a += s;
223
+ }), r.length > 0 && a < 1 - 1e-9 && r.push(`transparent ${a * 100}% 100%`), R(r);
224
+ }, lt = ({ cells: t, actions: e, order: n, brush: r, tool: a, onCellsChange: o, onBrushChange: s, onToolChange: l, className: c }) => {
225
+ const { onCellDown: i, onCellEnter: d } = J({
205
226
  cells: t,
206
227
  brush: r,
207
228
  tool: a,
208
229
  onCellsChange: o,
209
- onBrushChange: i,
230
+ onBrushChange: s,
210
231
  onToolChange: l
211
232
  });
212
- return /* @__PURE__ */ h(R, {
213
- className: u,
214
- comboStyle: (m) => {
215
- const c = t[k.get(m)];
216
- if (c.avail <= 0) return {
233
+ return /* @__PURE__ */ p(x, {
234
+ className: c,
235
+ comboStyle: (h) => {
236
+ const u = t[C.get(h)];
237
+ if (u.avail <= 0) return {
217
238
  background: "var(--spotlab-hm-dead-hatch)",
218
239
  backgroundColor: "var(--spotlab-hm-muted-bg)",
219
240
  cursor: "not-allowed",
220
241
  color: "var(--spotlab-hm-muted-fg)"
221
242
  };
222
- const d = a === "eyedropper" ? "copy" : "pointer";
223
- if (c.avail < 1) {
224
- const f = `${c.avail * 100}%`;
243
+ const f = a === "eyedropper" ? "copy" : "pointer";
244
+ if (u.avail < 1) {
245
+ const m = `${u.avail * 100}%`;
225
246
  return {
226
- background: `repeating-linear-gradient(180deg, var(--spotlab-hm-muted-fg) 0 4px, transparent 4px 8px), var(--spotlab-hm-dead-hatch), ${E(c, e, n)}`,
227
- backgroundSize: `1px 100%, ${(1 - c.avail) * 100}% 100%, ${c.avail * 100}% 100%`,
228
- backgroundPosition: `${f} center, right center, left center`,
247
+ background: `repeating-linear-gradient(180deg, var(--spotlab-hm-muted-fg) 0 4px, transparent 4px 8px), var(--spotlab-hm-dead-hatch), ${y(u, e, n)}`,
248
+ backgroundSize: `1px 100%, ${(1 - u.avail) * 100}% 100%, ${u.avail * 100}% 100%`,
249
+ backgroundPosition: `${m} center, right center, left center`,
229
250
  backgroundRepeat: "no-repeat, no-repeat, no-repeat",
230
- cursor: d
251
+ cursor: f
231
252
  };
232
253
  }
233
254
  return {
234
- background: E(c, e, n),
235
- cursor: d
255
+ background: y(u, e, n),
256
+ cursor: f
236
257
  };
237
258
  },
238
- onPointerDown: s,
239
- onPointerEnter: p
259
+ onPointerDown: i,
260
+ onPointerEnter: d
240
261
  });
241
- }, U = (t) => /* @__PURE__ */ h("input", {
262
+ }, K = (t) => /* @__PURE__ */ p("input", {
242
263
  type: "number",
243
264
  className: "spotlab-hm-weight-input",
244
265
  ...t
245
- }), Y = ({ value: t, color: e, passive: n, onChange: r }) => {
246
- const a = v(null), o = v(!1), i = g((l) => {
247
- const u = a.current;
248
- if (!u) return;
249
- const s = u.getBoundingClientRect();
250
- r((l - s.left) / s.width * 100);
266
+ }), V = ({ value: t, color: e, passive: n, onChange: r }) => {
267
+ const a = b(null), o = b(!1), s = g((l) => {
268
+ const c = a.current;
269
+ if (!c) return;
270
+ const i = c.getBoundingClientRect();
271
+ r((l - i.left) / i.width * 100);
251
272
  }, [r]);
252
- return y(() => {
253
- const l = (s) => {
254
- o.current && i(s.clientX);
255
- }, u = () => o.current = !1;
256
- return window.addEventListener("pointermove", l), window.addEventListener("pointerup", u), () => {
257
- window.removeEventListener("pointermove", l), window.removeEventListener("pointerup", u);
273
+ return N(() => {
274
+ const l = (i) => {
275
+ o.current && s(i.clientX);
276
+ }, c = () => o.current = !1;
277
+ return window.addEventListener("pointermove", l), window.addEventListener("pointerup", c), () => {
278
+ window.removeEventListener("pointermove", l), window.removeEventListener("pointerup", c);
258
279
  };
259
- }, [i]), /* @__PURE__ */ h("div", {
280
+ }, [s]), /* @__PURE__ */ p("div", {
260
281
  ref: a,
261
282
  role: "slider",
262
283
  "aria-valuenow": t,
@@ -265,9 +286,9 @@ var E = (t, e, n) => {
265
286
  className: "spotlab-hm-slider-track",
266
287
  style: { background: `linear-gradient(to right, ${e} 0 ${t}%, var(--spotlab-hm-track-bg) ${t}% 100%)` },
267
288
  onPointerDown: (l) => {
268
- o.current = !0, i(l.clientX);
289
+ o.current = !0, s(l.clientX);
269
290
  },
270
- children: /* @__PURE__ */ h("div", {
291
+ children: /* @__PURE__ */ p("div", {
271
292
  className: "spotlab-hm-slider-thumb",
272
293
  style: {
273
294
  left: `${t}%`,
@@ -275,60 +296,61 @@ var E = (t, e, n) => {
275
296
  }
276
297
  })
277
298
  });
278
- }, H = (t, e) => /* @__PURE__ */ w("span", {
299
+ }, Z = (t, e) => /* @__PURE__ */ w("span", {
279
300
  className: "spotlab-hm-slider-label",
280
- children: [t.label, e && /* @__PURE__ */ h("span", {
301
+ children: [t.label, e && /* @__PURE__ */ p("span", {
281
302
  className: "spotlab-hm-slider-remainder",
282
303
  children: " · remainder"
283
304
  })]
284
- }), rt = ({ actions: t, order: e, weights: n, passive: r, onWeightsChange: a, renderActionLabel: o, renderActionExtra: i, components: l, className: u }) => {
285
- const s = l?.WeightInput ?? U, p = (m, c) => {
286
- a(_(n, m, c, r));
305
+ }), st = ({ actions: t, order: e, weights: n, passive: r, onWeightsChange: a, renderActionLabel: o, renderActionExtra: s, components: l, className: c }) => {
306
+ const i = l?.WeightInput ?? K, d = (h, u) => {
307
+ a(G(n, h, u, r));
287
308
  };
288
- return /* @__PURE__ */ h("div", {
289
- className: ["spotlab-hm-root spotlab-hm-sliders", u].filter(Boolean).join(" "),
290
- children: e.map((m) => {
291
- const c = t[m], d = m === r;
309
+ return /* @__PURE__ */ p("div", {
310
+ className: ["spotlab-hm-root spotlab-hm-sliders", c].filter(Boolean).join(" "),
311
+ children: e.map((h) => {
312
+ const u = t[h], f = h === r;
292
313
  return /* @__PURE__ */ w("div", {
293
314
  className: "spotlab-hm-slider-row",
294
- "data-passive": d,
315
+ "data-passive": f,
295
316
  children: [/* @__PURE__ */ w("div", {
296
317
  className: "spotlab-hm-slider-header",
297
318
  children: [
298
- /* @__PURE__ */ h("span", {
319
+ /* @__PURE__ */ p("span", {
299
320
  className: "spotlab-hm-slider-swatch",
300
- style: { background: c.color }
321
+ style: { background: u.color }
301
322
  }),
302
- o?.(c, m, d) ?? H(c, d),
303
- i?.(c, m)
323
+ o?.(u, h, f) ?? Z(u, f),
324
+ s?.(u, h)
304
325
  ]
305
326
  }), /* @__PURE__ */ w("div", {
306
327
  className: "spotlab-hm-slider-track-row",
307
- children: [/* @__PURE__ */ h(Y, {
308
- value: n[m],
309
- color: c.color,
310
- passive: d,
311
- onChange: (f) => p(m, f)
312
- }), /* @__PURE__ */ h(s, {
313
- value: n[m],
314
- "aria-label": `${c.label} weight`,
315
- onChange: (f) => p(m, parseInt(f.target.value || "0", 10))
328
+ children: [/* @__PURE__ */ p(V, {
329
+ value: n[h],
330
+ color: u.color,
331
+ passive: f,
332
+ onChange: (m) => d(h, m)
333
+ }), /* @__PURE__ */ p(i, {
334
+ value: n[h],
335
+ "aria-label": `${u.label} weight`,
336
+ onChange: (m) => d(h, parseInt(m.target.value || "0", 10))
316
337
  })]
317
338
  })]
318
- }, c.id);
339
+ }, u.id);
319
340
  })
320
341
  });
321
342
  };
322
343
  export {
323
- rt as ActionWeightSliders,
324
- D as CELL_FILL_DIRECTION,
325
- R as HandMatrix,
326
- V as RangeGrid,
327
- et as RangeMatrixEditor,
328
- N as cellFillGradient,
329
- z as cellsToComboWeights,
330
- tt as comboWeightsToCells,
331
- _ as rebalance,
332
- Z as seedCells,
333
- G as useRangePainting
344
+ st as ActionWeightSliders,
345
+ T as CELL_FILL_DIRECTION,
346
+ x as HandMatrix,
347
+ rt as RangeGrid,
348
+ lt as RangeMatrixEditor,
349
+ R as cellFillGradient,
350
+ H as cellsToComboWeights,
351
+ ot as cellsToRanges,
352
+ at as comboWeightsToCells,
353
+ G as rebalance,
354
+ nt as seedCells,
355
+ J as useRangePainting
334
356
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spotlab/hand-matrix",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A 13x13 poker hand-matrix React component: the read-only grid primitive and a range-string-aware display component, themed through CSS custom properties.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,19 +40,19 @@
40
40
  "LICENSE"
41
41
  ],
42
42
  "peerDependencies": {
43
- "@spotlab/range": "^0.2.0",
43
+ "@spotlab/range": "^0.3.0",
44
44
  "react": "^19.0.0",
45
45
  "react-dom": "^19.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@spotlab/range": "0.2.0",
48
+ "@spotlab/range": "0.3.0",
49
49
  "@storybook/addon-vitest": "^10.5.5",
50
50
  "@storybook/react-vite": "^10.5.5",
51
51
  "@types/react": "^19.2.16",
52
52
  "@types/react-dom": "^19.2.3",
53
53
  "@vitejs/plugin-react": "^6.0.0",
54
- "@vitest/browser": "^4.1.4",
55
- "@vitest/browser-playwright": "4.1.11",
54
+ "@vitest/browser": "^5.0.0",
55
+ "@vitest/browser-playwright": "^5.0.0",
56
56
  "playwright": "^1.49.1",
57
57
  "react": "^19.2.7",
58
58
  "react-dom": "^19.2.7",
@@ -60,7 +60,7 @@
60
60
  "typescript": "^6.0.0",
61
61
  "vite": "^8.3.1",
62
62
  "vite-plugin-dts": "^5.1.2",
63
- "vitest": "^4.1.4"
63
+ "vitest": "^5.0.0"
64
64
  },
65
65
  "publishConfig": {
66
66
  "access": "public"