react-raffle-picker 0.2.0 → 0.2.1

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
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
- <img src="https://i.ibb.co/k2MRQCqT/logo-nbg.png" alt="react-raffle-picker logo placeholder" width="160" />
2
+ <img src="https://i.ibb.co/yB50hJTv/logo-nbg.png" alt="react-raffle-picker logo placeholder" width="160" />
3
3
 
4
4
  <h1>react-raffle-picker</h1>
5
5
 
@@ -69,7 +69,7 @@ This means:
69
69
  ```tsx
70
70
  import { RafflePick } from 'react-raffle-picker'
71
71
 
72
- <RafflePick min={1} max={100} interval={100} inertia onSelect={(v) => console.log(v)}>
72
+ ;<RafflePick min={1} max={100} interval={100} inertia onSelect={(v) => console.log(v)}>
73
73
  <RafflePick.Value animation="roll" className="my-value" />
74
74
  <RafflePick.Button startLabel="Pick" stopLabel="Stop" />
75
75
  </RafflePick>
@@ -79,7 +79,7 @@ import { RafflePick } from 'react-raffle-picker'
79
79
 
80
80
  The library is headless — no global stylesheet is required for `Value`, `Button`, or `Countdown`. You bring your own CSS.
81
81
 
82
- For the **slot reel** (`<RafflePick.Slots>`), a minimal stylesheet *is* required to make the column animate. Two ways to load it:
82
+ For the **slot reel** (`<RafflePick.Slots>`), a minimal stylesheet _is_ required to make the column animate. Two ways to load it:
83
83
 
84
84
  1. **Auto-injected at runtime** — `<RafflePick.Slots>` injects a `<style data-rrp-slot-base>` tag into `document.head` on first mount. No action needed in CSR apps.
85
85
  2. **Static import** (recommended for SSR / strict CSP / full control):
@@ -96,21 +96,21 @@ For the **slot reel** (`<RafflePick.Slots>`), a minimal stylesheet *is* required
96
96
 
97
97
  Provides context. Renders an optional wrapper element (`as` prop, default `'div'`).
98
98
 
99
- | Prop | Type | Default | Notes |
100
- | ------------ | --------------------------- | ---------- | --------------------------------------------- |
101
- | `min`, `max` | `number` | `1`, `100` | Numeric range. Ignored if `items` provided. |
102
- | `items` | `string[]` | — | Switches to item mode (cycles through names). |
103
- | `interval` | `number` (ms, clamped ≥ 50) | `100` | Tick speed. |
104
- | `random` | `boolean` | `true` | Random pick vs sequential. |
105
- | `inertia` | `boolean` | `false` | Soft start / soft stop ramp. |
106
- | `autoStart` | `boolean` | `true` | Begin cycling on mount. |
107
- | `initialValue` | `number \| string` | — | Starting display before first run. Number for `min`/`max` mode, string for `items` mode. |
108
- | `finalValue` | `number \| string` | — | Forces settle to land on this value. Cycle still appears random; only final freeze is rigged. |
109
- | `onSelect` | `(value) => void` | — | Fires once per round on freeze. |
110
- | `as` | `ElementType` | `'div'` | Wrapper tag. |
111
- | `className` | `string` | — | Wrapper class. |
112
- | `style` | `CSSProperties` | — | Wrapper style. |
113
- | `children` | `ReactNode` | — | Sub-components. |
99
+ | Prop | Type | Default | Notes |
100
+ | -------------- | --------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
101
+ | `min`, `max` | `number` | `1`, `100` | Numeric range. Ignored if `items` provided. |
102
+ | `items` | `string[]` | — | Switches to item mode (cycles through names). |
103
+ | `interval` | `number` (ms, clamped ≥ 50) | `100` | Tick speed. |
104
+ | `random` | `boolean` | `true` | Random pick vs sequential. |
105
+ | `inertia` | `boolean` | `false` | Soft start / soft stop ramp. |
106
+ | `autoStart` | `boolean` | `true` | Begin cycling on mount. |
107
+ | `initialValue` | `number \| string` | — | Starting display before first run. Number for `min`/`max` mode, string for `items` mode. For `<Slots>`, each character seeds the corresponding reel. |
108
+ | `finalValue` | `number \| string` | — | Forces settle to land on this value. Cycle still appears random; only final freeze is rigged. For `<Slots>`, each character is the final char of the corresponding reel. |
109
+ | `onSelect` | `(value) => void` | — | Fires once per round on freeze. |
110
+ | `as` | `ElementType` | `'div'` | Wrapper tag. |
111
+ | `className` | `string` | — | Wrapper class. |
112
+ | `style` | `CSSProperties` | — | Wrapper style. |
113
+ | `children` | `ReactNode` | — | Sub-components. |
114
114
 
115
115
  ### `<RafflePick.Value>`
116
116
 
@@ -153,6 +153,8 @@ Schedules auto-freeze after `seconds`. Renders an SVG ring + numeric label by de
153
153
 
154
154
  Independent multi-reel slot machine. Each reel ticks on its own and stops with a stagger.
155
155
 
156
+ `initialValue` (root prop) seeds reels char-by-char before the first run. `finalValue` rigs the freeze so each reel lands on the corresponding character — useful for predetermined winners or scripted demos.
157
+
156
158
  | Prop | Type | Default | Notes |
157
159
  | -------------------------------------------------- | -------------------------- | -------------- | -------------------------------------------- |
158
160
  | `length` | `number` | `3` | Number of reels. |
package/dist/index.cjs CHANGED
@@ -258,6 +258,8 @@ function RafflePickRoot({ items, min = 1, max = 100, interval = 100, random = tr
258
258
  inertia,
259
259
  hasItems,
260
260
  initialIndex,
261
+ initialValue,
262
+ finalValue,
261
263
  valueRef,
262
264
  displayValue,
263
265
  subscribe,
@@ -272,6 +274,8 @@ function RafflePickRoot({ items, min = 1, max = 100, interval = 100, random = tr
272
274
  inertia,
273
275
  hasItems,
274
276
  initialIndex,
277
+ initialValue,
278
+ finalValue,
275
279
  displayValue,
276
280
  subscribe,
277
281
  start,
@@ -456,7 +460,19 @@ const makeRefs = () => ({
456
460
  });
457
461
  function RafflePickSlots({ length = 3, chars = "0123456789", spinInterval = 80, staggerMs = 220, className, slotClassName, style, slotStyle, onResult }) {
458
462
  injectSlotStyles();
459
- const { phase } = useRaffleContext("RafflePick.Slots");
463
+ const { phase, initialValue, finalValue } = useRaffleContext("RafflePick.Slots");
464
+ const initialChars = (0, react.useMemo)(() => {
465
+ const seed = typeof initialValue === "string" ? initialValue : "";
466
+ return Array.from({ length }, (_, i) => seed[i] ?? "");
467
+ }, [initialValue, length]);
468
+ const finalChars = (0, react.useMemo)(() => {
469
+ if (typeof finalValue !== "string") return null;
470
+ return Array.from({ length }, (_, i) => finalValue[i] ?? "");
471
+ }, [finalValue, length]);
472
+ const finalCharsRef = (0, react.useRef)(finalChars);
473
+ (0, react.useEffect)(() => {
474
+ finalCharsRef.current = finalChars;
475
+ }, [finalChars]);
460
476
  const slotsRef = (0, react.useRef)([]);
461
477
  const stopTimersRef = (0, react.useRef)([]);
462
478
  const finalRef = (0, react.useRef)([]);
@@ -514,6 +530,8 @@ function RafflePickSlots({ length = 3, chars = "0123456789", spinInterval = 80,
514
530
  for (let i = 0; i < length; i++) {
515
531
  const id = setTimeout(() => {
516
532
  const s = slotsRef.current[i];
533
+ const forced = finalCharsRef.current?.[i];
534
+ if (forced) writeSlot(s, s.currChar, forced, forced);
517
535
  s.stopped = true;
518
536
  if (s.root) s.root.setAttribute("data-stopped", "");
519
537
  finalRef.current[i] = s.currChar;
@@ -529,17 +547,19 @@ function RafflePickSlots({ length = 3, chars = "0123456789", spinInterval = 80,
529
547
  ]);
530
548
  (0, react.useEffect)(() => {
531
549
  if (phase !== "idle") return;
532
- const c = pool[0] ?? "";
550
+ const fallback = pool[0] ?? "";
533
551
  for (let i = 0; i < length; i++) {
534
552
  const s = slotsRef.current[i];
553
+ const c = initialChars[i] || fallback;
535
554
  s.stopped = true;
536
555
  writeSlot(s, c, c, c);
537
- if (s.root) s.root.removeAttribute("data-stopped");
556
+ if (s.root) s.root.setAttribute("data-stopped", "");
538
557
  }
539
558
  }, [
540
559
  phase,
541
560
  length,
542
- pool
561
+ pool,
562
+ initialChars
543
563
  ]);
544
564
  const cls = (0, react.useMemo)(() => joinClassNames("rrp-slots", className), [className]);
545
565
  const slotCls = (0, react.useMemo)(() => joinClassNames("rrp-slot", slotClassName), [slotClassName]);
package/dist/index.d.cts CHANGED
@@ -135,6 +135,8 @@ interface RaffleContextValue {
135
135
  inertia: boolean;
136
136
  hasItems: boolean;
137
137
  initialIndex: number;
138
+ initialValue?: RafflePickValue$1;
139
+ finalValue?: RafflePickValue$1;
138
140
  valueRef: RefObject<number>;
139
141
  displayValue: (index: number) => RafflePickValue$1;
140
142
  subscribe: (fn: (value: number) => void) => () => void;
package/dist/index.d.mts CHANGED
@@ -135,6 +135,8 @@ interface RaffleContextValue {
135
135
  inertia: boolean;
136
136
  hasItems: boolean;
137
137
  initialIndex: number;
138
+ initialValue?: RafflePickValue$1;
139
+ finalValue?: RafflePickValue$1;
138
140
  valueRef: RefObject<number>;
139
141
  displayValue: (index: number) => RafflePickValue$1;
140
142
  subscribe: (fn: (value: number) => void) => () => void;
package/dist/index.mjs CHANGED
@@ -257,6 +257,8 @@ function RafflePickRoot({ items, min = 1, max = 100, interval = 100, random = tr
257
257
  inertia,
258
258
  hasItems,
259
259
  initialIndex,
260
+ initialValue,
261
+ finalValue,
260
262
  valueRef,
261
263
  displayValue,
262
264
  subscribe,
@@ -271,6 +273,8 @@ function RafflePickRoot({ items, min = 1, max = 100, interval = 100, random = tr
271
273
  inertia,
272
274
  hasItems,
273
275
  initialIndex,
276
+ initialValue,
277
+ finalValue,
274
278
  displayValue,
275
279
  subscribe,
276
280
  start,
@@ -455,7 +459,19 @@ const makeRefs = () => ({
455
459
  });
456
460
  function RafflePickSlots({ length = 3, chars = "0123456789", spinInterval = 80, staggerMs = 220, className, slotClassName, style, slotStyle, onResult }) {
457
461
  injectSlotStyles();
458
- const { phase } = useRaffleContext("RafflePick.Slots");
462
+ const { phase, initialValue, finalValue } = useRaffleContext("RafflePick.Slots");
463
+ const initialChars = useMemo(() => {
464
+ const seed = typeof initialValue === "string" ? initialValue : "";
465
+ return Array.from({ length }, (_, i) => seed[i] ?? "");
466
+ }, [initialValue, length]);
467
+ const finalChars = useMemo(() => {
468
+ if (typeof finalValue !== "string") return null;
469
+ return Array.from({ length }, (_, i) => finalValue[i] ?? "");
470
+ }, [finalValue, length]);
471
+ const finalCharsRef = useRef(finalChars);
472
+ useEffect(() => {
473
+ finalCharsRef.current = finalChars;
474
+ }, [finalChars]);
459
475
  const slotsRef = useRef([]);
460
476
  const stopTimersRef = useRef([]);
461
477
  const finalRef = useRef([]);
@@ -513,6 +529,8 @@ function RafflePickSlots({ length = 3, chars = "0123456789", spinInterval = 80,
513
529
  for (let i = 0; i < length; i++) {
514
530
  const id = setTimeout(() => {
515
531
  const s = slotsRef.current[i];
532
+ const forced = finalCharsRef.current?.[i];
533
+ if (forced) writeSlot(s, s.currChar, forced, forced);
516
534
  s.stopped = true;
517
535
  if (s.root) s.root.setAttribute("data-stopped", "");
518
536
  finalRef.current[i] = s.currChar;
@@ -528,17 +546,19 @@ function RafflePickSlots({ length = 3, chars = "0123456789", spinInterval = 80,
528
546
  ]);
529
547
  useEffect(() => {
530
548
  if (phase !== "idle") return;
531
- const c = pool[0] ?? "";
549
+ const fallback = pool[0] ?? "";
532
550
  for (let i = 0; i < length; i++) {
533
551
  const s = slotsRef.current[i];
552
+ const c = initialChars[i] || fallback;
534
553
  s.stopped = true;
535
554
  writeSlot(s, c, c, c);
536
- if (s.root) s.root.removeAttribute("data-stopped");
555
+ if (s.root) s.root.setAttribute("data-stopped", "");
537
556
  }
538
557
  }, [
539
558
  phase,
540
559
  length,
541
- pool
560
+ pool,
561
+ initialChars
542
562
  ]);
543
563
  const cls = useMemo(() => joinClassNames("rrp-slots", className), [className]);
544
564
  const slotCls = useMemo(() => joinClassNames("rrp-slot", slotClassName), [slotClassName]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-raffle-picker",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Headless React raffle picker for giveaways, winner draws, countdowns, and slot-machine UIs.",
5
5
  "type": "module",
6
6
  "publishConfig": {