@usableapp/cardds 0.7.108 → 0.7.109

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,6 +1,6 @@
1
1
  import type { ComponentProps, ReactNode } from 'react';
2
- /** what asked for the move: a `tap` on another step, a `swipe` across the steps, the step's `skip` link */
3
- export type SheetStepsReason = 'tap' | 'swipe' | 'skip';
2
+ /** what asked for the move: a `tap` on another step, the step's `skip` link */
3
+ export type SheetStepsReason = 'tap' | 'skip';
4
4
  export interface SheetStepsProps extends ComponentProps<'div'> {
5
5
  /** optional: a `Btn` that finishes, under the steps — always in sight, `disabled` until EVERY step is done (its work, or
6
6
  * skipped: `at` has reached `count`). None = the task ends when `at` reaches `count` — the last step's own action, or its skip. */
@@ -17,10 +17,10 @@ export interface SheetStepsProps extends ComponentProps<'div'> {
17
17
  reached?: number;
18
18
  /** UNCONTROLLED: the step it starts on (default 0) */
19
19
  defaultAt?: number;
20
- /** the steps ASK to move — a tap on another step, a swipe, a skip. Controlled: set `at` to it (or don't). */
20
+ /** the steps ASK to move — a tap on another step, a skip. Controlled: set `at` to it (or don't). */
21
21
  onAtChange?: (at: number, reason: SheetStepsReason) => void;
22
- /** true: the member may go back and forth — a tap on a done or next step opens it, an up/down swipe moves one step.
23
- * false (default): only forward — the done steps stay in the pile, never opened again (a swipe up passes only a `skippable` step). */
22
+ /** true: the member may go back and forth — a tap on a done or next step opens it.
23
+ * false (default): only forward — the done steps stay in the pile, never opened again. */
24
24
  reversible?: boolean;
25
25
  /** true: each step's number in a disc before its title — a white disc on the step's bar (Lh 2026-09-25). Default: titles only. */
26
26
  numbered?: boolean;
@@ -32,7 +32,7 @@ export interface SheetStepsProps extends ComponentProps<'div'> {
32
32
  * steps on the last one. Goes in a Sheet's `Body`.
33
33
  * The sheet may be dragged down to peek and back — the body keeps where it was scrolled.
34
34
  */
35
- export declare function SheetSteps({ at, reached, defaultAt, onAtChange, reversible, numbered, finish, className, style, children, onFocus, onBlur, onPointerDown, onPointerUp, onPointerCancel, ...rest }: SheetStepsProps): import("react").JSX.Element;
35
+ export declare function SheetSteps({ at, reached, defaultAt, onAtChange, reversible, numbered, finish, className, style, children, onFocus, onBlur, ...rest }: SheetStepsProps): import("react").JSX.Element;
36
36
  export interface SheetStepProps extends Omit<ComponentProps<'li'>, 'title'> {
37
37
  /** the step's name — always shown: over the open step, on the folded line of a done one */
38
38
  title: ReactNode;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Children, cloneElement, createContext, isValidElement, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
- import { focusWhenActive, swipeOf, useControlled } from '../state.js';
3
+ import { focusWhenActive, useControlled } from '../state.js';
4
4
  import { cx } from '../cx.js';
5
5
  import { Icon } from '../type/Icon.js';
6
6
  import { Link } from '../actions/Link.js';
@@ -10,7 +10,6 @@ import { word } from '../words.js';
10
10
  import { usePile } from './sheetStepsPile.js';
11
11
  import { StepCursor } from './stepCursor.js';
12
12
  const MOVE_MS = 350; // = --motion-sheet: the accordion's beat
13
- const SWIPE_REM = 3; // an up/down move longer than this (and mostly up/down) is a swipe
14
13
  /* the open step's first field — the one that takes the cursor, and the one a typed character is handed to (an input or a textarea) */
15
14
  const NOW_FIELD = '.sheet-step--now .sheet-step__inner :is(input:not([type=checkbox], [type=radio], [type=hidden]), textarea)';
16
15
  const coarse = () => typeof matchMedia === 'function' && matchMedia('(pointer: coarse)').matches; // a phone: its keyboard follows a finger, not a script
@@ -22,7 +21,7 @@ const StepCtx = createContext(null);
22
21
  * steps on the last one. Goes in a Sheet's `Body`.
23
22
  * The sheet may be dragged down to peek and back — the body keeps where it was scrolled.
24
23
  */
25
- export function SheetSteps({ at, reached, defaultAt = 0, onAtChange, reversible = false, numbered = false, finish, className, style, children, onFocus, onBlur, onPointerDown, onPointerUp, onPointerCancel, ...rest }) {
24
+ export function SheetSteps({ at, reached, defaultAt = 0, onAtChange, reversible = false, numbered = false, finish, className, style, children, onFocus, onBlur, ...rest }) {
26
25
  const [current, request] = useControlled(at, defaultAt, onAtChange);
27
26
  const steps = Children.toArray(children).filter(isValidElement);
28
27
  const count = steps.length;
@@ -42,7 +41,6 @@ export function SheetSteps({ at, reached, defaultAt = 0, onAtChange, reversible
42
41
  shown.current = current;
43
42
  moving.current = performance.now() + MOVE_MS + 100;
44
43
  } // from the very render that changes it: the old field's blur and the keyboard's jump come in that same commit
45
- const press = useRef(null);
46
44
  /* the pile: where the body is scrolled (the two latest done rows in sight), and the glide of its jumps — sheetStepsPile.ts */
47
45
  const { settle, glideNow } = usePile(ref, moving);
48
46
  const go = (i, reason) => {
@@ -119,29 +117,6 @@ export function SheetSteps({ at, reached, defaultAt = 0, onAtChange, reversible
119
117
  setTimeout(() => { if (!typing(document.activeElement) && body)
120
118
  body.scrollTo({ top: 0 }); }, 0); // Tab to the next field keeps it up
121
119
  };
122
- /* an up/down swipe turns the wheel one step — unless it started in content that can still scroll that way */
123
- const down = (e) => {
124
- onPointerDown?.(e);
125
- press.current = e.isPrimary && !e.target.closest('input, textarea, select, .pin, .picker, .slider') ? { x: e.clientX, y: e.clientY } : null;
126
- };
127
- const up = (e) => {
128
- onPointerUp?.(e);
129
- const p = press.current;
130
- press.current = null;
131
- if (!p)
132
- return;
133
- const dy = swipeOf(p, e, 'y', SWIPE_REM * parseFloat(getComputedStyle(document.documentElement).fontSize), 2);
134
- if (!dy)
135
- return;
136
- const body = e.target.closest('.sheet-step__body');
137
- if (body && (dy < 0 ? body.scrollTop + body.clientHeight < body.scrollHeight - 1 : body.scrollTop > 0))
138
- return; // the content's own scroll
139
- const to = current + (dy < 0 ? 1 : -1);
140
- if (reversible && to < count)
141
- go(to, 'swipe'); // a swipe never passes the last step — only its own action or its skip does
142
- else if (dy < 0 && ref.current?.querySelector('.sheet-step--now[data-skippable]'))
143
- go(to, 'skip'); // forward only, and only past a step that may be skipped
144
- };
145
120
  /* typed while the cursor waits in the holder (the fold is playing — or, on an iPhone, the keyboard never followed the cursor into the
146
121
  new step: a focus set by script does not always take the keyboard with it, and every digit landed here and was dropped —
147
122
  timebank, Lh 2026-10-03): what was typed belongs to the open step's first field. Hand it over, and the cursor with it —
@@ -157,7 +132,7 @@ export function SheetSteps({ at, reached, defaultAt = 0, onAtChange, reversible
157
132
  Object.getOwnPropertyDescriptor(proto, 'value').set.call(field, field.value + typed);
158
133
  field.dispatchEvent(new Event('input', { bubbles: true }));
159
134
  };
160
- return (_jsxs("div", { ref: ref, className: cx('sheet-steps', !reversible && 'sheet-steps--locked', className), style: style, onFocus: focusIn, onBlur: focusOut, onPointerDown: down, onPointerUp: up, onPointerCancel: (e) => { onPointerCancel?.(e); press.current = null; }, ...rest, children: [_jsx("input", { ref: holderRef, className: "sheet-steps__holder", "aria-hidden": true, tabIndex: -1, autoComplete: "off", onFocus: () => setParked(true), onBlur: () => setParked(false), onInput: handOver }), _jsx("ol", { className: "sheet-steps__track", children: steps.map((step, i) => (_jsx(StepCtx.Provider, { value: { numbered, i, at: current, furthest: reach, count, reversible, go, park, parked }, children: step }, step.key ?? i))) }), finish != null && ( // always in sight; it takes taps only once every step is done (or skipped)
135
+ return (_jsxs("div", { ref: ref, className: cx('sheet-steps', !reversible && 'sheet-steps--locked', className), style: style, onFocus: focusIn, onBlur: focusOut, ...rest, children: [_jsx("input", { ref: holderRef, className: "sheet-steps__holder", "aria-hidden": true, tabIndex: -1, autoComplete: "off", onFocus: () => setParked(true), onBlur: () => setParked(false), onInput: handOver }), _jsx("ol", { className: "sheet-steps__track", children: steps.map((step, i) => (_jsx(StepCtx.Provider, { value: { numbered, i, at: current, furthest: reach, count, reversible, go, park, parked }, children: step }, step.key ?? i))) }), finish != null && ( // always in sight; it takes taps only once every step is done (or skipped)
161
136
  _jsx("div", { className: "sheet-steps__finish", children: isValidElement(finish) ? cloneElement(finish, { disabled: reach < count || finish.props.disabled }) : finish }))] }));
162
137
  }
163
138
  /* the step's format checks, as one group — see SheetStep */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usableapp/cardds",
3
- "version": "0.7.108",
3
+ "version": "0.7.109",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "description": "card-first mobile design system, React-first: the components in src/ are thin wrappers over the CSS contract (css/*.css stays the only truth); gallery/ shows every story live (npm run dev), tests/ measures the geometry.",