@pihanga2/shadcn 0.2.23 → 0.2.25

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.
@@ -1,3 +1,4 @@
1
+ import { PiCardRef } from '@pihanga2/core';
1
2
  import { ChartConfig } from '../../components/ui/chart';
2
3
  export declare const PI_CHART_GRAPH_CARD = "shad/chart-graph";
3
4
  export declare const Chart: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<PiChartGraphProps, S, {}>) => import('@pihanga2/core').PiCardDef;
@@ -143,9 +144,9 @@ export type PiChartReferenceLine = {
143
144
  };
144
145
  export type PiChartGraphProps = {
145
146
  /**
146
- * Whether to render an **area** chart (filled) or a **line** chart (stroke only).
147
+ * Whether to render an area, line, or bar chart.
147
148
  */
148
- chartType: "area" | "line";
149
+ chartType: "area" | "line" | "bar";
149
150
  /**
150
151
  * Array of data points. Each entry must contain:
151
152
  * - a value for `xDataKey` (used for the x-axis labels), and
@@ -161,7 +162,7 @@ export type PiChartGraphProps = {
161
162
  */
162
163
  data: Array<Record<string, unknown>>;
163
164
  /**
164
- * Series definitions. Each entry describes one plotted line or area.
165
+ * Series definitions. Each entry describes one plotted line, area, or bar.
165
166
  * At least one series is required.
166
167
  */
167
168
  series: PiChartSeries[];
@@ -248,6 +249,16 @@ export type PiChartGraphProps = {
248
249
  * Range 0–1. Defaults to `0.4`.
249
250
  */
250
251
  fillOpacity?: number;
252
+ /**
253
+ * Gap between bar categories as a percentage string or pixel value.
254
+ * Only applies to bar charts. Defaults to `"10%"` (the Recharts default).
255
+ */
256
+ barGap?: number | string;
257
+ /**
258
+ * Radius in pixels applied to the top corners of each bar.
259
+ * Only applies to bar charts. Defaults to `0`.
260
+ */
261
+ barRadius?: number;
251
262
  /**
252
263
  * Suppress the recharts entry animation so the chart renders immediately
253
264
  * without the "draw-in" / "unfold" effect.
@@ -281,6 +292,13 @@ export type PiChartGraphProps = {
281
292
  * ```
282
293
  */
283
294
  referenceAreas?: PiChartReferenceArea[];
295
+ /**
296
+ * Optional Pihanga card rendered directly below the chart. Useful for
297
+ * controls that operate on the plotted data, such as a range slider.
298
+ */
299
+ footerCard?: PiCardRef;
300
+ /** Extra Tailwind / CSS classes applied to the footer card wrapper. */
301
+ footerClassName?: string;
284
302
  /** Extra Tailwind / CSS classes forwarded to the outermost element. */
285
303
  className?: string;
286
304
  };
@@ -5,66 +5,76 @@ import { useEffect as r, useRef as i, useState as a } from "react";
5
5
  import "@pihanga2/core";
6
6
  import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
7
  //#region src/cards/slider/slider.component.tsx
8
- var c = (c) => {
9
- let { name: l, value: u, defaultValue: d, selfManaged: f = !1, min: p = 0, max: m = 100, step: h = 1, disabled: g, debounceMs: _ = 0, suppressChangedEvents: v = !1, label: y, showTicks: b = !1, tickStep: x, majorTickStep: S, suppressTickLabels: C = !1, tickLabels: w, className: T, cardName: E, onChanged: D, onCommitted: O } = c, k = t(), A = k.isInForm && !!l, [j, M] = a(d ?? u ?? p), N = A ? k.formData[l] ?? p : f ? j : u ?? p, P = i(N);
10
- P.current = N;
11
- let F = i(!1), [I, L] = a(N), R = F.current ? I : N, z = i(null), B = i(N);
8
+ function c(e) {
9
+ return Array.isArray(e) ? e : [e];
10
+ }
11
+ function l(e, t) {
12
+ return e.length > 1 ? [e[0] ?? t, e[1] ?? t] : e[0] ?? t;
13
+ }
14
+ function ee(e) {
15
+ return Array.isArray(e) ? `${e[0]} – ${e[1]}` : String(e);
16
+ }
17
+ var u = (u) => {
18
+ let { name: d, value: f, defaultValue: te, selfManaged: p = !1, min: m = 0, max: h = 100, step: g = 1, minStepsBetweenThumbs: _, disabled: v, debounceMs: y = 0, suppressChangedEvents: b = !1, label: x, showTicks: S = !1, tickStep: C, majorTickStep: w, suppressTickLabels: ne = !1, tickLabels: T, className: E, cardName: D, onChanged: O, onCommitted: k } = u, A = t(), j = A.isInForm && !!d, [M, N] = a(te ?? f ?? m), P = j ? A.formData[d] ?? m : p ? M : f ?? m, F = i(P);
19
+ F.current = P;
20
+ let I = i(!1), [L, R] = a(P), z = I.current ? L : P, B = i(null), V = i(P);
12
21
  r(() => () => {
13
- z.current != null && clearTimeout(z.current);
22
+ B.current != null && clearTimeout(B.current);
14
23
  }, []);
15
- function V(e) {
16
- v || (A ? k.handleChange(l, e) : D({
17
- name: l,
24
+ function H(e) {
25
+ b || (j ? A.handleChange(d, e) : O({
26
+ name: d,
18
27
  value: e
19
28
  }));
20
29
  }
21
- function H() {
22
- F.current = !0, L(P.current);
23
- }
24
- function U(e) {
25
- let t = e[0] ?? p;
26
- F.current && (L(t), f && !A && M(t), _ > 0 ? (B.current = t, z.current != null && clearTimeout(z.current), z.current = setTimeout(() => {
27
- z.current = null, V(B.current);
28
- }, _)) : V(t));
30
+ function U() {
31
+ I.current = !0, R(F.current);
29
32
  }
30
33
  function W(e) {
31
- let t = e[0] ?? p;
32
- F.current = !1, L(P.current), z.current != null && (clearTimeout(z.current), z.current = null), _ > 0 && !v && V(t), O({
33
- name: l,
34
+ let t = l(e, m);
35
+ I.current && (R(t), p && !j && N(t), y > 0 ? (V.current = t, B.current != null && clearTimeout(B.current), B.current = setTimeout(() => {
36
+ B.current = null, H(V.current);
37
+ }, y)) : H(t));
38
+ }
39
+ function re(e) {
40
+ let t = l(e, m);
41
+ I.current = !1, R(F.current), B.current != null && (clearTimeout(B.current), B.current = null), y > 0 && !b && H(t), k({
42
+ name: d,
34
43
  value: t
35
44
  });
36
45
  }
37
- let G = x === void 0 ? h : x, K = G > 0, q = K ? G : h, J = q > 0 ? Math.max(q, Math.round((m - p) / 5 / q) * q) : 0, Y = S === void 0 ? J : S, X = Y > 0, Z = b && (K || X), Q = K ? G : Y, $ = Z ? Array.from({ length: Math.round((m - p) / Q) + 1 }, (e, t) => p + t * Q) : [];
46
+ let G = C === void 0 ? g : C, K = G > 0, q = K ? G : g, ie = q > 0 ? Math.max(q, Math.round((h - m) / 5 / q) * q) : 0, J = w === void 0 ? ie : w, Y = J > 0, X = S && (K || Y), Z = K ? G : J, ae = X ? Array.from({ length: Math.round((h - m) / Z) + 1 }, (e, t) => m + t * Z) : [], Q = c(z), $ = Q[0] ?? m, oe = Q[1] ?? $;
38
47
  return /* @__PURE__ */ s("div", {
39
- "data-pihanga": E,
40
- className: e("flex flex-col gap-2", T),
48
+ "data-pihanga": D,
49
+ className: e("flex flex-col gap-2", E),
41
50
  children: [
42
- y && /* @__PURE__ */ s("div", {
51
+ x && /* @__PURE__ */ s("div", {
43
52
  className: "flex items-center justify-between",
44
53
  children: [/* @__PURE__ */ o("span", {
45
54
  className: "text-sm font-medium leading-none",
46
- children: y
55
+ children: x
47
56
  }), /* @__PURE__ */ o("span", {
48
57
  className: "text-sm text-muted-foreground tabular-nums",
49
- children: R
58
+ children: ee(z)
50
59
  })]
51
60
  }),
52
61
  /* @__PURE__ */ o(n, {
53
- value: [R],
54
- min: p,
55
- max: m,
56
- step: h,
57
- disabled: g,
58
- onPointerDown: H,
59
- onValueChange: U,
60
- onValueCommit: W,
62
+ value: Q,
63
+ min: m,
64
+ max: h,
65
+ step: g,
66
+ minStepsBetweenThumbs: _,
67
+ disabled: v,
68
+ onPointerDown: U,
69
+ onValueChange: W,
70
+ onValueCommit: re,
61
71
  className: "w-full"
62
72
  }),
63
- Z && /* @__PURE__ */ o("div", {
73
+ X && /* @__PURE__ */ o("div", {
64
74
  "aria-hidden": "true",
65
75
  className: "flex w-full items-baseline justify-between px-0.5",
66
- children: $.map((t) => {
67
- let n = X && (t - p) % Y === 0, r = t <= R, i = !C && (n || !X), a = i ? w && t in w ? w[t] : String(t) : "";
76
+ children: ae.map((t) => {
77
+ let n = Y && (t - m) % J === 0, r = Q.length > 1 ? t >= $ && t <= oe : t <= $, i = !ne && (n || !Y), a = i ? T && t in T ? T[t] : String(t) : "";
68
78
  return /* @__PURE__ */ s("div", {
69
79
  className: "flex flex-col items-center gap-1",
70
80
  children: [/* @__PURE__ */ o("div", { className: e("w-0.5 rounded-full transition-all duration-300", r ? "bg-primary" : "bg-primary/20", n ? "h-3" : "h-1.5") }), /* @__PURE__ */ o("span", {
@@ -78,6 +88,6 @@ var c = (c) => {
78
88
  });
79
89
  };
80
90
  //#endregion
81
- export { c as SliderComponent };
91
+ export { u as SliderComponent };
82
92
 
83
93
  //# sourceMappingURL=slider.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"slider.component.js","names":[],"sources":["../../../src/cards/slider/slider.component.tsx"],"sourcesContent":["import React, {useEffect, useRef, useState} from \"react\";\nimport {type PiCardProps} from \"@pihanga2/core\";\nimport {Slider as SliderUI} from \"@/components/ui/slider\";\nimport {useFormContext} from \"@/cards/form/form.context\";\nimport {cn} from \"@/lib/utils\";\nimport type {PiSliderEvents, PiSliderProps} from \"./slider.types\";\n\nexport const SliderComponent = (\n props: PiCardProps<PiSliderProps, PiSliderEvents>,\n): React.ReactNode => {\n const {\n name,\n value: propValue,\n defaultValue,\n selfManaged = false,\n min = 0,\n max = 100,\n step = 1,\n disabled,\n debounceMs = 0,\n suppressChangedEvents = false,\n label,\n showTicks = false,\n tickStep,\n majorTickStep,\n suppressTickLabels = false,\n tickLabels,\n className,\n cardName,\n onChanged,\n onCommitted,\n } = props;\n\n // Detect if we are inside a pi/form card via React context.\n const form = useFormContext();\n const useFormData = form.isInForm && Boolean(name);\n\n // Internal state used only when selfManaged=true and not bound to a form.\n const [managedValue, setManagedValue] = useState<number>(\n defaultValue ?? propValue ?? min,\n );\n\n // The \"canonical\" value from props / form / self-managed state.\n const canonicalValue = useFormData\n ? ((form.formData[name!] as number | undefined) ?? min)\n : selfManaged\n ? managedValue\n : (propValue ?? min);\n\n // Keep a ref to the latest canonical value so event handlers always see\n // the most up-to-date external value even if it changed during a drag.\n const canonicalValueRef = useRef(canonicalValue);\n canonicalValueRef.current = canonicalValue;\n\n // \"Interactive\" drag state — tracks knob position independently while the\n // user is actively dragging so we don't need an external value round-trip.\n const isDragging = useRef(false);\n const [interactiveValue, setInteractiveValue] =\n useState<number>(canonicalValue);\n\n // While dragging the knob follows pointer; otherwise it reflects the canonical value.\n const displayValue = isDragging.current ? interactiveValue : canonicalValue;\n\n // Refs for debounce: timer handle + latest pending value.\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingValue = useRef<number>(canonicalValue);\n\n // Clean up any pending debounce timer on unmount.\n useEffect(() => {\n return () => {\n if (debounceTimer.current != null) clearTimeout(debounceTimer.current);\n };\n }, []);\n\n function dispatchChanged(newValue: number) {\n // Silently skip if the caller has opted out of continuous change events.\n if (suppressChangedEvents) return;\n\n if (useFormData) {\n form.handleChange(name!, newValue);\n } else {\n onChanged({name, value: newValue});\n }\n }\n\n // Called on pointer/touch down — enter interactive mode so the knob can\n // move freely without waiting for an external state update.\n function handlePointerDown() {\n isDragging.current = true;\n setInteractiveValue(canonicalValueRef.current);\n }\n\n function handleValueChange(newValues: number[]) {\n const newValue = newValues[0] ?? min;\n\n // Guard: only update interactive display when actively dragging.\n if (!isDragging.current) return;\n\n setInteractiveValue(newValue);\n\n // Update internal state immediately so the thumb moves without lag.\n if (selfManaged && !useFormData) {\n setManagedValue(newValue);\n }\n\n if (debounceMs > 0) {\n // Record the latest value and (re)start the quiet-window timer.\n pendingValue.current = newValue;\n if (debounceTimer.current != null) clearTimeout(debounceTimer.current);\n debounceTimer.current = setTimeout(() => {\n debounceTimer.current = null;\n dispatchChanged(pendingValue.current);\n }, debounceMs);\n } else {\n dispatchChanged(newValue);\n }\n }\n\n function handleValueCommit(newValues: number[]) {\n const newValue = newValues[0] ?? min;\n\n // Leave interactive mode — on next render displayValue reverts to canonicalValue,\n // snapping the knob back to whatever the external state currently says.\n isDragging.current = false;\n setInteractiveValue(canonicalValueRef.current);\n\n // Cancel any pending debounced onChanged — the committed event supersedes it.\n if (debounceTimer.current != null) {\n clearTimeout(debounceTimer.current);\n debounceTimer.current = null;\n }\n\n // When debounce is active (and not suppressed), flush the final value via\n // onChanged before the committed event so listeners always see both.\n if (debounceMs > 0 && !suppressChangedEvents) {\n dispatchChanged(newValue);\n }\n\n onCommitted({name, value: newValue});\n }\n\n // --- Tick calculations ---\n // tickStep=0 → hide minor ticks only (major still shown if majorTickStep > 0)\n // majorTickStep=0 → hide major ticks only (minor still shown if tickStep > 0)\n // Both 0 → nothing shown\n const effectiveMinorStep = tickStep !== undefined ? tickStep : step;\n const showMinorTicks = effectiveMinorStep > 0;\n\n // Auto major step: use minor step as base when minor ticks are on;\n // fall back to `step` when minor ticks are off (tickStep=0) so that\n // major ticks can still be auto-calculated from the slider's step.\n const autoMajorBase = showMinorTicks ? effectiveMinorStep : step;\n const autoMajorStep =\n autoMajorBase > 0\n ? Math.max(\n autoMajorBase,\n Math.round((max - min) / 5 / autoMajorBase) * autoMajorBase,\n )\n : 0;\n // majorTickStep undefined → auto; 0 → no major ticks; >0 → explicit value\n const resolvedMajorStep =\n majorTickStep !== undefined ? majorTickStep : autoMajorStep;\n const showMajorTicks = resolvedMajorStep > 0;\n\n const renderTicks = showTicks && (showMinorTicks || showMajorTicks);\n\n // Use minor spacing as base when available; fall back to major spacing.\n const baseStep = showMinorTicks ? effectiveMinorStep : resolvedMajorStep;\n const ticks = renderTicks\n ? Array.from(\n {length: Math.round((max - min) / baseStep) + 1},\n (_, i) => min + i * baseStep,\n )\n : [];\n\n return (\n <div\n data-pihanga={cardName}\n className={cn(\"flex flex-col gap-2\", className)}\n >\n {label && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium leading-none\">{label}</span>\n <span className=\"text-sm text-muted-foreground tabular-nums\">\n {displayValue}\n </span>\n </div>\n )}\n <SliderUI\n value={[displayValue]}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n onPointerDown={handlePointerDown}\n onValueChange={handleValueChange}\n onValueCommit={handleValueCommit}\n className=\"w-full\"\n />\n {renderTicks && (\n <div\n aria-hidden=\"true\"\n className=\"flex w-full items-baseline justify-between px-0.5\"\n >\n {ticks.map((tick) => {\n const isMajor =\n showMajorTicks && (tick - min) % resolvedMajorStep === 0;\n const isActive = tick <= displayValue;\n // Show label when:\n // • suppressTickLabels is false, AND\n // • tick is major OR there are no major ticks (label every minor tick)\n const showLabel =\n !suppressTickLabels && (isMajor || !showMajorTicks);\n const labelText = showLabel\n ? tickLabels && tick in tickLabels\n ? tickLabels[tick]\n : String(tick)\n : \"\";\n\n return (\n <div key={tick} className=\"flex flex-col items-center gap-1\">\n <div\n className={cn(\n \"w-0.5 rounded-full transition-all duration-300\",\n isActive ? \"bg-primary\" : \"bg-primary/20\",\n isMajor ? \"h-3\" : \"h-1.5\",\n )}\n />\n <span\n className={cn(\n \"text-xs font-medium transition-colors\",\n showLabel\n ? \"text-muted-foreground opacity-100\"\n : \"opacity-0\",\n )}\n >\n {labelText}\n </span>\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"slider.component.js","names":[],"sources":["../../../src/cards/slider/slider.component.tsx"],"sourcesContent":["import React, {useEffect, useRef, useState} from \"react\";\nimport {type PiCardProps} from \"@pihanga2/core\";\nimport {Slider as SliderUI} from \"@/components/ui/slider\";\nimport {useFormContext} from \"@/cards/form/form.context\";\nimport {cn} from \"@/lib/utils\";\nimport type {\n PiSliderEvents,\n PiSliderProps,\n PiSliderValue,\n} from \"./slider.types\";\n\nfunction toValues(value: PiSliderValue): number[] {\n return Array.isArray(value) ? value : [value];\n}\n\nfunction fromValues(values: number[], fallback: number): PiSliderValue {\n return values.length > 1\n ? [values[0] ?? fallback, values[1] ?? fallback]\n : (values[0] ?? fallback);\n}\n\nfunction formatValue(value: PiSliderValue): string {\n return Array.isArray(value) ? `${value[0]} – ${value[1]}` : String(value);\n}\n\nexport const SliderComponent = (\n props: PiCardProps<PiSliderProps, PiSliderEvents>,\n): React.ReactNode => {\n const {\n name,\n value: propValue,\n defaultValue,\n selfManaged = false,\n min = 0,\n max = 100,\n step = 1,\n minStepsBetweenThumbs,\n disabled,\n debounceMs = 0,\n suppressChangedEvents = false,\n label,\n showTicks = false,\n tickStep,\n majorTickStep,\n suppressTickLabels = false,\n tickLabels,\n className,\n cardName,\n onChanged,\n onCommitted,\n } = props;\n\n // Detect if we are inside a pi/form card via React context.\n const form = useFormContext();\n const useFormData = form.isInForm && Boolean(name);\n\n // Internal state used only when selfManaged=true and not bound to a form.\n const [managedValue, setManagedValue] = useState<PiSliderValue>(\n defaultValue ?? propValue ?? min,\n );\n\n // The \"canonical\" value from props / form / self-managed state.\n const canonicalValue = useFormData\n ? ((form.formData[name!] as PiSliderValue | undefined) ?? min)\n : selfManaged\n ? managedValue\n : (propValue ?? min);\n\n // Keep a ref to the latest canonical value so event handlers always see\n // the most up-to-date external value even if it changed during a drag.\n const canonicalValueRef = useRef(canonicalValue);\n canonicalValueRef.current = canonicalValue;\n\n // \"Interactive\" drag state — tracks knob position independently while the\n // user is actively dragging so we don't need an external value round-trip.\n const isDragging = useRef(false);\n const [interactiveValue, setInteractiveValue] =\n useState<PiSliderValue>(canonicalValue);\n\n // While dragging the knob follows pointer; otherwise it reflects the canonical value.\n const displayValue = isDragging.current ? interactiveValue : canonicalValue;\n\n // Refs for debounce: timer handle + latest pending value.\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingValue = useRef<PiSliderValue>(canonicalValue);\n\n // Clean up any pending debounce timer on unmount.\n useEffect(() => {\n return () => {\n if (debounceTimer.current != null) clearTimeout(debounceTimer.current);\n };\n }, []);\n\n function dispatchChanged(newValue: PiSliderValue) {\n // Silently skip if the caller has opted out of continuous change events.\n if (suppressChangedEvents) return;\n\n if (useFormData) {\n form.handleChange(name!, newValue);\n } else {\n onChanged({name, value: newValue});\n }\n }\n\n // Called on pointer/touch down — enter interactive mode so the knob can\n // move freely without waiting for an external state update.\n function handlePointerDown() {\n isDragging.current = true;\n setInteractiveValue(canonicalValueRef.current);\n }\n\n function handleValueChange(newValues: number[]) {\n const newValue = fromValues(newValues, min);\n\n // Guard: only update interactive display when actively dragging.\n if (!isDragging.current) return;\n\n setInteractiveValue(newValue);\n\n // Update internal state immediately so the thumb moves without lag.\n if (selfManaged && !useFormData) {\n setManagedValue(newValue);\n }\n\n if (debounceMs > 0) {\n // Record the latest value and (re)start the quiet-window timer.\n pendingValue.current = newValue;\n if (debounceTimer.current != null) clearTimeout(debounceTimer.current);\n debounceTimer.current = setTimeout(() => {\n debounceTimer.current = null;\n dispatchChanged(pendingValue.current);\n }, debounceMs);\n } else {\n dispatchChanged(newValue);\n }\n }\n\n function handleValueCommit(newValues: number[]) {\n const newValue = fromValues(newValues, min);\n\n // Leave interactive mode — on next render displayValue reverts to canonicalValue,\n // snapping the knob back to whatever the external state currently says.\n isDragging.current = false;\n setInteractiveValue(canonicalValueRef.current);\n\n // Cancel any pending debounced onChanged — the committed event supersedes it.\n if (debounceTimer.current != null) {\n clearTimeout(debounceTimer.current);\n debounceTimer.current = null;\n }\n\n // When debounce is active (and not suppressed), flush the final value via\n // onChanged before the committed event so listeners always see both.\n if (debounceMs > 0 && !suppressChangedEvents) {\n dispatchChanged(newValue);\n }\n\n onCommitted({name, value: newValue});\n }\n\n // --- Tick calculations ---\n // tickStep=0 → hide minor ticks only (major still shown if majorTickStep > 0)\n // majorTickStep=0 → hide major ticks only (minor still shown if tickStep > 0)\n // Both 0 → nothing shown\n const effectiveMinorStep = tickStep !== undefined ? tickStep : step;\n const showMinorTicks = effectiveMinorStep > 0;\n\n // Auto major step: use minor step as base when minor ticks are on;\n // fall back to `step` when minor ticks are off (tickStep=0) so that\n // major ticks can still be auto-calculated from the slider's step.\n const autoMajorBase = showMinorTicks ? effectiveMinorStep : step;\n const autoMajorStep =\n autoMajorBase > 0\n ? Math.max(\n autoMajorBase,\n Math.round((max - min) / 5 / autoMajorBase) * autoMajorBase,\n )\n : 0;\n // majorTickStep undefined → auto; 0 → no major ticks; >0 → explicit value\n const resolvedMajorStep =\n majorTickStep !== undefined ? majorTickStep : autoMajorStep;\n const showMajorTicks = resolvedMajorStep > 0;\n\n const renderTicks = showTicks && (showMinorTicks || showMajorTicks);\n\n // Use minor spacing as base when available; fall back to major spacing.\n const baseStep = showMinorTicks ? effectiveMinorStep : resolvedMajorStep;\n const ticks = renderTicks\n ? Array.from(\n {length: Math.round((max - min) / baseStep) + 1},\n (_, i) => min + i * baseStep,\n )\n : [];\n\n const displayValues = toValues(displayValue);\n const selectedMin = displayValues[0] ?? min;\n const selectedMax = displayValues[1] ?? selectedMin;\n\n return (\n <div\n data-pihanga={cardName}\n className={cn(\"flex flex-col gap-2\", className)}\n >\n {label && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium leading-none\">{label}</span>\n <span className=\"text-sm text-muted-foreground tabular-nums\">\n {formatValue(displayValue)}\n </span>\n </div>\n )}\n <SliderUI\n value={displayValues}\n min={min}\n max={max}\n step={step}\n minStepsBetweenThumbs={minStepsBetweenThumbs}\n disabled={disabled}\n onPointerDown={handlePointerDown}\n onValueChange={handleValueChange}\n onValueCommit={handleValueCommit}\n className=\"w-full\"\n />\n {renderTicks && (\n <div\n aria-hidden=\"true\"\n className=\"flex w-full items-baseline justify-between px-0.5\"\n >\n {ticks.map((tick) => {\n const isMajor =\n showMajorTicks && (tick - min) % resolvedMajorStep === 0;\n const isActive =\n displayValues.length > 1\n ? tick >= selectedMin && tick <= selectedMax\n : tick <= selectedMin;\n // Show label when:\n // • suppressTickLabels is false, AND\n // • tick is major OR there are no major ticks (label every minor tick)\n const showLabel =\n !suppressTickLabels && (isMajor || !showMajorTicks);\n const labelText = showLabel\n ? tickLabels && tick in tickLabels\n ? tickLabels[tick]\n : String(tick)\n : \"\";\n\n return (\n <div key={tick} className=\"flex flex-col items-center gap-1\">\n <div\n className={cn(\n \"w-0.5 rounded-full transition-all duration-300\",\n isActive ? \"bg-primary\" : \"bg-primary/20\",\n isMajor ? \"h-3\" : \"h-1.5\",\n )}\n />\n <span\n className={cn(\n \"text-xs font-medium transition-colors\",\n showLabel\n ? \"text-muted-foreground opacity-100\"\n : \"opacity-0\",\n )}\n >\n {labelText}\n </span>\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -24,6 +24,8 @@ export declare const onSliderCommitted: <S extends import('@pihanga2/core').Redu
24
24
  export declare const onPiSliderCommitted: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core/types').PiRegisterMinimal, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
25
25
  cardID: string;
26
26
  } & PiSliderCommittedEvent>) => void;
27
+ /** A single selected value or an inclusive two-thumb range. */
28
+ export type PiSliderValue = number | [number, number];
27
29
  export type PiSliderProps = {
28
30
  /**
29
31
  * Field name used to bind to FormContext when inside a pi/form card.
@@ -32,16 +34,17 @@ export type PiSliderProps = {
32
34
  */
33
35
  name?: string;
34
36
  /**
35
- * Controlled numeric value (single thumb).
37
+ * Controlled value. A number renders one thumb; a tuple renders two thumbs
38
+ * with the selected range highlighted between them.
36
39
  * Ignored when `name` is set and the component is inside a pi/form,
37
40
  * and also ignored when `selfManaged` is true (use `defaultValue` instead).
38
41
  */
39
- value?: number;
42
+ value?: PiSliderValue;
40
43
  /**
41
44
  * Initial value used when `selfManaged` is true.
42
45
  * Falls back to `value` → `min` → 0 if not provided.
43
46
  */
44
- defaultValue?: number;
47
+ defaultValue?: PiSliderValue;
45
48
  /**
46
49
  * When true the slider keeps its own internal value state.
47
50
  * Dragging updates the displayed value immediately without needing an
@@ -58,6 +61,11 @@ export type PiSliderProps = {
58
61
  max?: number;
59
62
  /** Stepping interval between selectable values. Defaults to 1. */
60
63
  step?: number;
64
+ /**
65
+ * Minimum permitted distance between two thumbs, measured in `step`
66
+ * increments. Only applies when `value` is a range.
67
+ */
68
+ minStepsBetweenThumbs?: number;
61
69
  /** When true, the slider is disabled and non-interactive. */
62
70
  disabled?: boolean;
63
71
  /**
@@ -126,14 +134,14 @@ export type PiSliderProps = {
126
134
  export type PiSliderChangedEvent = {
127
135
  /** Field name, mirrors the `name` prop if provided. */
128
136
  name?: string;
129
- /** New numeric value after the change. */
130
- value: number;
137
+ /** New scalar or range value after the change. */
138
+ value: PiSliderValue;
131
139
  };
132
140
  export type PiSliderCommittedEvent = {
133
141
  /** Field name, mirrors the `name` prop if provided. */
134
142
  name?: string;
135
- /** Final settled value when the user released the thumb. */
136
- value: number;
143
+ /** Final settled scalar or range value when the user released a thumb. */
144
+ value: PiSliderValue;
137
145
  };
138
146
  export type PiSliderEvents = {
139
147
  onChanged: PiSliderChangedEvent;
@@ -1 +1 @@
1
- {"version":3,"file":"slider.types.js","names":[],"sources":["../../../src/cards/slider/slider.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_SLIDER_CARD = \"shad/slider\";\n\nexport const Slider = createCardDeclaration<PiSliderProps, PiSliderEvents>(\n PI_SLIDER_CARD,\n);\n\nexport const PI_SLIDER_ACTION = registerActions(PI_SLIDER_CARD, [\n \"changed\",\n \"committed\",\n]);\n\nexport const onSliderChanged = createOnAction<PiSliderChangedEvent>(\n PI_SLIDER_ACTION.CHANGED,\n);\n\n/** @deprecated Use `onSliderChanged` instead. */\nexport const onPiSliderChanged = onSliderChanged;\n\n/**\n * Fired once when the user **finishes** dragging (mouse-up / touch-end).\n *\n * Use this instead of `onSliderChanged` when you only care about the final\n * settled value and do not want to process every intermediate drag position.\n */\nexport const onSliderCommitted = createOnAction<PiSliderCommittedEvent>(\n PI_SLIDER_ACTION.COMMITTED,\n);\n\n/** @deprecated Use `onSliderCommitted` instead. */\nexport const onPiSliderCommitted = onSliderCommitted;\n\n// ---------------------------------------------------------------------------\n// Props & Events\n// ---------------------------------------------------------------------------\n\nexport type PiSliderProps = {\n /**\n * Field name used to bind to FormContext when inside a pi/form card.\n * When provided the component reads its value from form data and writes\n * back via form.handleChange.\n */\n name?: string;\n\n /**\n * Controlled numeric value (single thumb).\n * Ignored when `name` is set and the component is inside a pi/form,\n * and also ignored when `selfManaged` is true (use `defaultValue` instead).\n */\n value?: number;\n\n /**\n * Initial value used when `selfManaged` is true.\n * Falls back to `value` → `min` → 0 if not provided.\n */\n defaultValue?: number;\n\n /**\n * When true the slider keeps its own internal value state.\n * Dragging updates the displayed value immediately without needing an\n * external state update. `onChanged` is still fired on every change so\n * the app can react (or log) without being responsible for feeding the\n * value back as a prop.\n *\n * Ignored when inside a pi/form (form state takes precedence).\n */\n selfManaged?: boolean;\n\n /** Minimum value of the slider range. Defaults to 0. */\n min?: number;\n\n /** Maximum value of the slider range. Defaults to 100. */\n max?: number;\n\n /** Stepping interval between selectable values. Defaults to 1. */\n step?: number;\n\n /** When true, the slider is disabled and non-interactive. */\n disabled?: boolean;\n\n /**\n * Debounce delay in milliseconds applied to `onChanged` during continuous\n * drag. Only the last value within each quiet window is dispatched.\n *\n * Useful when the `onChanged` handler is expensive (e.g. triggers an API\n * call) — set e.g. `debounceMs: 200` to cap the event rate.\n *\n * Has no effect on `onCommitted`, which always fires exactly once at\n * drag-end regardless of this setting.\n *\n * Omit (or set to 0) to dispatch on every drag position (default behaviour).\n */\n debounceMs?: number;\n\n /**\n * When true, `onSliderChanged` is **never** dispatched during drag.\n *\n * Use this together with `onSliderCommitted` when you only care about\n * the final settled value and want to eliminate all intermediate Redux\n * actions entirely (stricter than `debounceMs`).\n *\n * `onSliderCommitted` is unaffected and always fires on release.\n * `selfManaged` visual updates are also unaffected.\n */\n suppressChangedEvents?: boolean;\n\n /**\n * Optional label text rendered above the slider.\n * When provided, the current numeric value is shown to the right of the label.\n */\n label?: string;\n\n /**\n * When true, tick marks are rendered below the slider track.\n */\n showTicks?: boolean;\n\n /**\n * Spacing between consecutive **minor** tick marks in value units.\n * Defaults to `step`.\n * Set to `0` to hide minor ticks only — major ticks are still shown\n * if `majorTickStep` is non-zero.\n */\n tickStep?: number;\n\n /**\n * Interval (in value units) between **major** (tall, labelled) ticks.\n * Defaults to `(max - min) / 5` rounded to the nearest `tickStep`.\n * Set to `0` to hide major ticks only — minor ticks are still shown\n * if `tickStep` is non-zero.\n */\n majorTickStep?: number;\n\n /**\n * When true, no tick labels are rendered at all (only the tick marks).\n * Overrides `tickLabels` and the default numeric labels.\n */\n suppressTickLabels?: boolean;\n\n /**\n * Custom labels keyed by value. When provided for a value, the custom\n * string is shown instead of the numeric value. Pass `\"\"` to suppress the\n * label for a specific tick.\n *\n * @example { 0: \"Min\", 50: \"Mid\", 100: \"Max\" }\n */\n tickLabels?: Record<number, string>;\n\n /** Extra Tailwind / CSS classes forwarded to the root wrapper element. */\n className?: string;\n};\n\nexport type PiSliderChangedEvent = {\n /** Field name, mirrors the `name` prop if provided. */\n name?: string;\n /** New numeric value after the change. */\n value: number;\n};\n\nexport type PiSliderCommittedEvent = {\n /** Field name, mirrors the `name` prop if provided. */\n name?: string;\n /** Final settled value when the user released the thumb. */\n value: number;\n};\n\nexport type PiSliderEvents = {\n onChanged: PiSliderChangedEvent;\n onCommitted: PiSliderCommittedEvent;\n};\n"],"mappings":";;AAMA,IAAa,IAAiB,eAEjB,IAAS,EACpB,CACF,GAEa,IAAmB,EAAgB,GAAgB,CAC9D,WACA,WACF,CAAC,GAEY,IAAkB,EAC7B,EAAiB,OACnB,GAGa,IAAoB,GAQpB,IAAoB,EAC/B,EAAiB,SACnB,GAGa,IAAsB"}
1
+ {"version":3,"file":"slider.types.js","names":[],"sources":["../../../src/cards/slider/slider.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_SLIDER_CARD = \"shad/slider\";\n\nexport const Slider = createCardDeclaration<PiSliderProps, PiSliderEvents>(\n PI_SLIDER_CARD,\n);\n\nexport const PI_SLIDER_ACTION = registerActions(PI_SLIDER_CARD, [\n \"changed\",\n \"committed\",\n]);\n\nexport const onSliderChanged = createOnAction<PiSliderChangedEvent>(\n PI_SLIDER_ACTION.CHANGED,\n);\n\n/** @deprecated Use `onSliderChanged` instead. */\nexport const onPiSliderChanged = onSliderChanged;\n\n/**\n * Fired once when the user **finishes** dragging (mouse-up / touch-end).\n *\n * Use this instead of `onSliderChanged` when you only care about the final\n * settled value and do not want to process every intermediate drag position.\n */\nexport const onSliderCommitted = createOnAction<PiSliderCommittedEvent>(\n PI_SLIDER_ACTION.COMMITTED,\n);\n\n/** @deprecated Use `onSliderCommitted` instead. */\nexport const onPiSliderCommitted = onSliderCommitted;\n\n// ---------------------------------------------------------------------------\n// Props & Events\n// ---------------------------------------------------------------------------\n\n/** A single selected value or an inclusive two-thumb range. */\nexport type PiSliderValue = number | [number, number];\n\nexport type PiSliderProps = {\n /**\n * Field name used to bind to FormContext when inside a pi/form card.\n * When provided the component reads its value from form data and writes\n * back via form.handleChange.\n */\n name?: string;\n\n /**\n * Controlled value. A number renders one thumb; a tuple renders two thumbs\n * with the selected range highlighted between them.\n * Ignored when `name` is set and the component is inside a pi/form,\n * and also ignored when `selfManaged` is true (use `defaultValue` instead).\n */\n value?: PiSliderValue;\n\n /**\n * Initial value used when `selfManaged` is true.\n * Falls back to `value` → `min` → 0 if not provided.\n */\n defaultValue?: PiSliderValue;\n\n /**\n * When true the slider keeps its own internal value state.\n * Dragging updates the displayed value immediately without needing an\n * external state update. `onChanged` is still fired on every change so\n * the app can react (or log) without being responsible for feeding the\n * value back as a prop.\n *\n * Ignored when inside a pi/form (form state takes precedence).\n */\n selfManaged?: boolean;\n\n /** Minimum value of the slider range. Defaults to 0. */\n min?: number;\n\n /** Maximum value of the slider range. Defaults to 100. */\n max?: number;\n\n /** Stepping interval between selectable values. Defaults to 1. */\n step?: number;\n\n /**\n * Minimum permitted distance between two thumbs, measured in `step`\n * increments. Only applies when `value` is a range.\n */\n minStepsBetweenThumbs?: number;\n\n /** When true, the slider is disabled and non-interactive. */\n disabled?: boolean;\n\n /**\n * Debounce delay in milliseconds applied to `onChanged` during continuous\n * drag. Only the last value within each quiet window is dispatched.\n *\n * Useful when the `onChanged` handler is expensive (e.g. triggers an API\n * call) — set e.g. `debounceMs: 200` to cap the event rate.\n *\n * Has no effect on `onCommitted`, which always fires exactly once at\n * drag-end regardless of this setting.\n *\n * Omit (or set to 0) to dispatch on every drag position (default behaviour).\n */\n debounceMs?: number;\n\n /**\n * When true, `onSliderChanged` is **never** dispatched during drag.\n *\n * Use this together with `onSliderCommitted` when you only care about\n * the final settled value and want to eliminate all intermediate Redux\n * actions entirely (stricter than `debounceMs`).\n *\n * `onSliderCommitted` is unaffected and always fires on release.\n * `selfManaged` visual updates are also unaffected.\n */\n suppressChangedEvents?: boolean;\n\n /**\n * Optional label text rendered above the slider.\n * When provided, the current numeric value is shown to the right of the label.\n */\n label?: string;\n\n /**\n * When true, tick marks are rendered below the slider track.\n */\n showTicks?: boolean;\n\n /**\n * Spacing between consecutive **minor** tick marks in value units.\n * Defaults to `step`.\n * Set to `0` to hide minor ticks only — major ticks are still shown\n * if `majorTickStep` is non-zero.\n */\n tickStep?: number;\n\n /**\n * Interval (in value units) between **major** (tall, labelled) ticks.\n * Defaults to `(max - min) / 5` rounded to the nearest `tickStep`.\n * Set to `0` to hide major ticks only — minor ticks are still shown\n * if `tickStep` is non-zero.\n */\n majorTickStep?: number;\n\n /**\n * When true, no tick labels are rendered at all (only the tick marks).\n * Overrides `tickLabels` and the default numeric labels.\n */\n suppressTickLabels?: boolean;\n\n /**\n * Custom labels keyed by value. When provided for a value, the custom\n * string is shown instead of the numeric value. Pass `\"\"` to suppress the\n * label for a specific tick.\n *\n * @example { 0: \"Min\", 50: \"Mid\", 100: \"Max\" }\n */\n tickLabels?: Record<number, string>;\n\n /** Extra Tailwind / CSS classes forwarded to the root wrapper element. */\n className?: string;\n};\n\nexport type PiSliderChangedEvent = {\n /** Field name, mirrors the `name` prop if provided. */\n name?: string;\n /** New scalar or range value after the change. */\n value: PiSliderValue;\n};\n\nexport type PiSliderCommittedEvent = {\n /** Field name, mirrors the `name` prop if provided. */\n name?: string;\n /** Final settled scalar or range value when the user released a thumb. */\n value: PiSliderValue;\n};\n\nexport type PiSliderEvents = {\n onChanged: PiSliderChangedEvent;\n onCommitted: PiSliderCommittedEvent;\n};\n"],"mappings":";;AAMA,IAAa,IAAiB,eAEjB,IAAS,EACpB,CACF,GAEa,IAAmB,EAAgB,GAAgB,CAC9D,WACA,WACF,CAAC,GAEY,IAAkB,EAC7B,EAAiB,OACnB,GAGa,IAAoB,GAQpB,IAAoB,EAC/B,EAAiB,SACnB,GAGa,IAAsB"}
@@ -1,14 +1,14 @@
1
1
  import { DEF_GUTTER_WIDTH as e, DEF_HEADER_HEIGHT as t, DEF_LANE_WIDTH as n, DEF_MAX_ROWS as r, DEF_ROW_HEIGHT as i, SWIMLANE_ACTION as a, SWIMLANE_CARD as o, SWIMLANE_DATA_ACTION as s, Swimlane as c, dispatchSwimlaneEvents as l, dispatchSwimlaneRowsCleared as u, onSwimlaneEventsReceived as d, onSwimlaneHoverChanged as f, onSwimlaneItemClicked as p, onSwimlaneRowsCleared as m } from "./swimlane.types.js";
2
- import { clearSwimlaneRows as h, createSwimlaneModel as g, describeSwimlaneTarget as _, formatTs as v, ingestSwimlaneEvents as y, projectPending as b, registerSwimlaneModel as x, relatedCallIds as S, rowTouches as C } from "./swimlane.model.js";
3
- import { SwimlaneComponent as w } from "./swimlane.component.js";
4
- import { actionTypesToEvents as T, registerCardComponent as E } from "@pihanga2/core";
2
+ import { clearSwimlaneRows as h, createSwimlaneModel as g, describeSwimlaneTarget as _, formatTs as v, ingestSwimlaneEvents as y, projectPending as b, registerSwimlaneModel as x, relatedCallIds as S, rowTouches as C, stepKeyOf as w, stepRows as T } from "./swimlane.model.js";
3
+ import { SwimlaneComponent as E } from "./swimlane.component.js";
4
+ import { actionTypesToEvents as D, registerCardComponent as O } from "@pihanga2/core";
5
5
  //#region src/cards/swimlane/index.ts
6
- E({
6
+ O({
7
7
  name: o,
8
- component: w,
9
- events: T(a)
8
+ component: E,
9
+ events: D(a)
10
10
  });
11
11
  //#endregion
12
- export { e as DEF_GUTTER_WIDTH, t as DEF_HEADER_HEIGHT, n as DEF_LANE_WIDTH, r as DEF_MAX_ROWS, i as DEF_ROW_HEIGHT, a as SWIMLANE_ACTION, o as SWIMLANE_CARD, s as SWIMLANE_DATA_ACTION, c as Swimlane, h as clearSwimlaneRows, g as createSwimlaneModel, _ as describeSwimlaneTarget, l as dispatchSwimlaneEvents, u as dispatchSwimlaneRowsCleared, v as formatTs, y as ingestSwimlaneEvents, d as onSwimlaneEventsReceived, f as onSwimlaneHoverChanged, p as onSwimlaneItemClicked, m as onSwimlaneRowsCleared, b as projectPending, x as registerSwimlaneModel, S as relatedCallIds, C as rowTouches };
12
+ export { e as DEF_GUTTER_WIDTH, t as DEF_HEADER_HEIGHT, n as DEF_LANE_WIDTH, r as DEF_MAX_ROWS, i as DEF_ROW_HEIGHT, a as SWIMLANE_ACTION, o as SWIMLANE_CARD, s as SWIMLANE_DATA_ACTION, c as Swimlane, h as clearSwimlaneRows, g as createSwimlaneModel, _ as describeSwimlaneTarget, l as dispatchSwimlaneEvents, u as dispatchSwimlaneRowsCleared, v as formatTs, y as ingestSwimlaneEvents, d as onSwimlaneEventsReceived, f as onSwimlaneHoverChanged, p as onSwimlaneItemClicked, m as onSwimlaneRowsCleared, b as projectPending, x as registerSwimlaneModel, S as relatedCallIds, C as rowTouches, w as stepKeyOf, T as stepRows };
13
13
 
14
14
  //# sourceMappingURL=index.js.map