@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.
- package/CHANGELOG.md +9 -0
- package/dist/catalog.json +1 -1
- package/dist/sheets/SheetSteps.d.ts +6 -6
- package/dist/sheets/SheetSteps.js +3 -28
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComponentProps, ReactNode } from 'react';
|
|
2
|
-
/** what asked for the move: a `tap` on another step,
|
|
3
|
-
export type SheetStepsReason = 'tap' | '
|
|
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
|
|
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
|
|
23
|
-
* false (default): only forward — the done steps stay in the pile, never opened again
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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.
|
|
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.",
|