@usableapp/cardds 0.7.88 → 0.7.90

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/dist/index.d.ts CHANGED
@@ -80,6 +80,7 @@ export * from './sticky/StickyDate.js';
80
80
  export * from './sticky/StickyField.js';
81
81
  export * from './sticky/StickyPick.js';
82
82
  export * from './sticky/StickyThread.js';
83
+ export * from './sticky/StickyHeading.js';
83
84
  export * from './sticky/StickyBoard.js';
84
85
  export * from './sticky/StickyPile.js';
85
86
  export * from './sticky/StickyAnswer.js';
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ export * from './sticky/StickyDate.js';
84
84
  export * from './sticky/StickyField.js';
85
85
  export * from './sticky/StickyPick.js';
86
86
  export * from './sticky/StickyThread.js';
87
+ export * from './sticky/StickyHeading.js';
87
88
  export * from './sticky/StickyBoard.js';
88
89
  export * from './sticky/StickyPile.js';
89
90
  export * from './sticky/StickyAnswer.js';
@@ -15,14 +15,21 @@ export interface StampProps extends Omit<ComponentProps<'span'>, 'onClick'> {
15
15
  tilt?: 'left' | 'right';
16
16
  /** the whole stamp is a button: it renders a `<button>` */
17
17
  onClick?: () => void;
18
+ /** the member presses send and the stamp that already stands in the card's head is STRUCK onto its place by itself (shown large and faint, pressed down to its size) — as it comes on, or as this turns true. Use it when a stamp stands where the layout put it (`Card end`, a centre, the album) and sending should strike it; the stamp does not move and it keeps its `tilt`. A stamp laid ON a surface at a place the member chose is `StampOn arrive`. A stamp without `arrive` is not struck. With `onArrived` */
19
+ arrive?: boolean;
20
+ /** an arriving stamp has LANDED (its strike ended; at once where motion is off, and once per arrival) — the app does what follows: sends, closes, sets the card's `leave` */
21
+ onArrived?: () => void;
18
22
  }
19
23
  /**
20
24
  * Stamp — a perforated paper stamp, 4:5: the pictures fill the inner frame on a tone, the words sit on the picture's foot.
21
25
  * Every stamp is ONE size wherever it stands (`--stamp-size`, two taps wide: 96 × 120) — what holds it only places it; a holder narrower than that shows it whole, smaller.
22
26
  * The one small stamp is a stamp stuck on something (`StampOn`). Every inner length follows the stamp's width.
23
27
  * The outer box holds the size and the shadow; the paper inside is the one whose edge is punched (a mask would cut the shadow too).
28
+ * With `arrive` the stamp that stands in its place (the head of a card, `end={<Stamp arrive …/>}`) is struck onto it when the member sends, and `onArrived` tells
29
+ * the app it has landed, so it sends. The strike scales it down from twice its size, so it never moves the layout (it may cover its neighbours for a moment);
30
+ * a `tilt` is kept, the stamp ends at its turn. Afterwards the same stamp stands in the same place with no strike: drop `arrive` or keep it, nothing is struck twice.
24
31
  */
25
- export declare function Stamp({ pictures, tone, name, words, bare, tilt, onClick, className, ...rest }: StampProps): import("react").JSX.Element;
32
+ export declare function Stamp({ pictures, tone, name, words, bare, tilt, onClick, arrive, onArrived, className, onAnimationEnd, ...rest }: StampProps): import("react").JSX.Element;
26
33
  export interface StampPlaceProps extends Omit<ComponentProps<'span'>, 'onClick'> {
27
34
  /** a gap nobody is asked to fill: a paler edge (an album's free places) */
28
35
  faint?: boolean;
@@ -1,16 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cx } from '../cx.js';
3
3
  import { pressable } from '../pressable.js';
4
+ import { useStrike } from './useStrike.js';
4
5
  /**
5
6
  * Stamp — a perforated paper stamp, 4:5: the pictures fill the inner frame on a tone, the words sit on the picture's foot.
6
7
  * Every stamp is ONE size wherever it stands (`--stamp-size`, two taps wide: 96 × 120) — what holds it only places it; a holder narrower than that shows it whole, smaller.
7
8
  * The one small stamp is a stamp stuck on something (`StampOn`). Every inner length follows the stamp's width.
8
9
  * The outer box holds the size and the shadow; the paper inside is the one whose edge is punched (a mask would cut the shadow too).
10
+ * With `arrive` the stamp that stands in its place (the head of a card, `end={<Stamp arrive …/>}`) is struck onto it when the member sends, and `onArrived` tells
11
+ * the app it has landed, so it sends. The strike scales it down from twice its size, so it never moves the layout (it may cover its neighbours for a moment);
12
+ * a `tilt` is kept, the stamp ends at its turn. Afterwards the same stamp stands in the same place with no strike: drop `arrive` or keep it, nothing is struck twice.
9
13
  */
10
- export function Stamp({ pictures, tone = 'custard', name, words, bare, tilt, onClick, className, ...rest }) {
14
+ export function Stamp({ pictures, tone = 'custard', name, words, bare, tilt, onClick, arrive, onArrived, className, onAnimationEnd, ...rest }) {
11
15
  const shown = pictures.slice(0, 4);
16
+ const { struck } = useStrike(arrive, onArrived, onAnimationEnd);
12
17
  const { tap, Box, boxProps } = pressable(onClick, 'span', rest);
13
- return (_jsx(Box, { className: cx('stamp', `stamp--${shown.length}`, bare && 'stamp--bare', tilt && `stamp--tilt-${tilt}`, tap && 'stamp--tap', className), ...boxProps, children: _jsx("span", { className: "stamp__paper", children: _jsxs("span", { className: cx('stamp__pic', `sticky--${tone}`), children: [shown.map((picture, i) => _jsx("span", { className: "stamp__art", "aria-hidden": "true", children: picture }, i)), (name != null || words != null) && (_jsxs("span", { className: "stamp__cap", children: [words != null && _jsx("span", { className: "stamp__words", children: words }), name != null && _jsx("span", { className: "stamp__name", children: name })] }))] }) }) }));
18
+ return (_jsx(Box, { className: cx('stamp', `stamp--${shown.length}`, bare && 'stamp--bare', tilt && `stamp--tilt-${tilt}`, tap && 'stamp--tap', arrive && 'stamp--arrive', className), onAnimationEnd: struck, ...boxProps, children: _jsx("span", { className: "stamp__paper", children: _jsxs("span", { className: cx('stamp__pic', `sticky--${tone}`), children: [shown.map((picture, i) => _jsx("span", { className: "stamp__art", "aria-hidden": "true", children: picture }, i)), (name != null || words != null) && (_jsxs("span", { className: "stamp__cap", children: [words != null && _jsx("span", { className: "stamp__words", children: words }), name != null && _jsx("span", { className: "stamp__name", children: name })] }))] }) }) }));
14
19
  }
15
20
  /**
16
21
  * StampPlace — an empty place for a stamp: a 4:5 frame of round dots the size a Stamp is (`--stamp-size`), a word or an Icon inside.
@@ -24,7 +24,7 @@ export interface StampOnProps extends ComponentProps<'div'> {
24
24
  /** the stamp at the stamp's MAIN size, 96 × 120 (`--stamp-size`), to cover what it lies on; the default is the small size, 48 × 60 (`--stamp-sm-size`), which leaves a note's words readable */
25
25
  main?: boolean;
26
26
  /** the stamp sticks ITSELF: as it comes onto the screen it is struck onto its place (shown large, pressed down to its size). Nobody
27
- * picked it or pressed to stick it — the app shows it when its own move (send) begins. With `onArrived` */
27
+ * picked it or pressed to stick it — the app shows it when its own move (send) begins. With `onArrived`. A stamp that already stands in a card's head or a centre, where the layout put it, is `Stamp arrive` instead */
28
28
  arrive?: boolean;
29
29
  /** an arriving stamp has LANDED (its strike ended; at once where motion is off) — the app does what follows: sends, closes */
30
30
  onArrived?: () => void;
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useContext, useEffect, useRef, useState } from 'react';
3
3
  import { cx } from '../cx.js';
4
- import { reducedMotion, useControlled } from '../state.js';
4
+ import { useControlled } from '../state.js';
5
5
  import { Icon } from '../type/Icon.js';
6
6
  import { word } from '../words.js';
7
7
  import { UnderNotes } from './underNotes.js';
8
+ import { useStrike } from './useStrike.js';
8
9
  const MIDDLE = { x: 0.5, y: 0.5 };
9
10
  const STEP = 0.02; // an arrow key's move: a share of the surface
10
11
  const MOVES = { ArrowLeft: [-STEP, 0], ArrowRight: [STEP, 0], ArrowUp: [0, -STEP], ArrowDown: [0, STEP] };
@@ -18,7 +19,6 @@ function within(el, x, y) {
18
19
  const mx = el.offsetWidth / 2 / box.clientWidth, my = el.offsetHeight / 2 / box.clientHeight;
19
20
  return { x: round(clamp(x, mx, 1 - mx)), y: round(clamp(y, my, 1 - my)) };
20
21
  }
21
- const STRIKE = 'strike'; // css/tokens.css — the strike every mark struck onto paper reads
22
22
  const write = (el, at) => { el.style.setProperty('--stamp-on-x', String(at.x)); el.style.setProperty('--stamp-on-y', String(at.y)); };
23
23
  /**
24
24
  * StampOn — a Stamp (the child) stuck on a surface at the place the member chose. The surface is the nearest positioned box:
@@ -46,7 +46,7 @@ export function StampOn({ at, defaultAt = MIDDLE, turn = 0, onAtChange, label, o
46
46
  const grip = useRef(null);
47
47
  const timer = useRef(undefined);
48
48
  const moves = onAtChange != null && !under;
49
- const [arrived, setArrived] = useState(false);
49
+ const { arrived, struck } = useStrike(arrive, onArrived, onAnimationEnd);
50
50
  const removes = onRemove != null && !under && !(arrive && !arrived);
51
51
  const ask = (next) => { if (next.x !== shown.x || next.y !== shown.y)
52
52
  request(next); };
@@ -115,20 +115,6 @@ export function StampOn({ at, defaultAt = MIDDLE, turn = 0, onAtChange, label, o
115
115
  if (!moves)
116
116
  show();
117
117
  };
118
- const landed = useRef(onArrived);
119
- landed.current = onArrived;
120
- const land = () => { setArrived(true); landed.current?.(); };
121
- useEffect(() => { if (!arrive)
122
- setArrived(false);
123
- else if (reducedMotion()) {
124
- setArrived(true);
125
- landed.current?.();
126
- } }, [arrive]); // no strike to wait for
127
- const struck = (e) => {
128
- onAnimationEnd?.(e);
129
- if (arrive && e.target === e.currentTarget && e.animationName === STRIKE)
130
- land();
131
- };
132
118
  const place = { '--stamp-on-x': shown.x, '--stamp-on-y': shown.y, '--stamp-on-turn': turn, ...style };
133
119
  const own = moves || removes ? { tabIndex: 0, role: 'group', 'aria-label': label, onKeyDown: key } : null;
134
120
  const drag = moves ? { onPointerDown: down, onPointerMove: move, onPointerUp: up, onPointerCancel: up } : null;
@@ -0,0 +1,7 @@
1
+ import { type AnimationEvent } from 'react';
2
+ /** a holder that `arrive`s: has its strike ended? Returns `arrived` (false again once `arrive` turns off) and `struck`, the holder's `onAnimationEnd`
3
+ * (it calls `onAnimationEnd` first). `onArrived` comes once per arrival: when the strike ends, at once where motion is off */
4
+ export declare function useStrike<E extends Element>(arrive: boolean | undefined, onArrived: (() => void) | undefined, onAnimationEnd?: (e: AnimationEvent<E>) => void): {
5
+ arrived: boolean;
6
+ struck: (e: AnimationEvent<E>) => void;
7
+ };
@@ -0,0 +1,20 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { reducedMotion, useLatest } from '../state.js';
3
+ const STRIKE = 'strike'; // css/tokens.css — the strike every mark struck onto paper reads
4
+ /** a holder that `arrive`s: has its strike ended? Returns `arrived` (false again once `arrive` turns off) and `struck`, the holder's `onAnimationEnd`
5
+ * (it calls `onAnimationEnd` first). `onArrived` comes once per arrival: when the strike ends, at once where motion is off */
6
+ export function useStrike(arrive, onArrived, onAnimationEnd) {
7
+ const told = useLatest(onArrived);
8
+ const [arrived, setArrived] = useState(false);
9
+ const land = () => { setArrived(true); told.current?.(); };
10
+ useEffect(() => { if (!arrive)
11
+ setArrived(false);
12
+ else if (reducedMotion())
13
+ land(); }, [arrive]); // no strike to wait for
14
+ const struck = (e) => {
15
+ onAnimationEnd?.(e);
16
+ if (arrive && e.target === e.currentTarget && e.animationName === STRIKE)
17
+ land();
18
+ };
19
+ return { arrived, struck };
20
+ }
@@ -0,0 +1,309 @@
1
+ import type { HTMLAttributes, ReactNode, Ref } from 'react';
2
+ export interface StickyHeadingProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
3
+ /** the element it renders (React 19 passes `ref` as a prop) */
4
+ ref?: Ref<HTMLElement>;
5
+ /** what the notes are about — the subject of the chat, one line; a title too long for the line is cut with an ellipsis */
6
+ title: ReactNode;
7
+ /** a short second thing beside it — when ("Tomorrow 6 pm"); it drops to a line of its own where the room is narrow */
8
+ caption?: ReactNode;
9
+ /** the element: `span` (default) on a note — a note may be a button, and a button holds no heading; `h2` as the first child of a StickyThread, where it is the heading of that thread */
10
+ as?: 'span' | 'h2';
11
+ }
12
+ /**
13
+ * StickyHeading — names what a chat told in notes is about, for as long as that lasts (a job agreed and its time): the subject and a short
14
+ * caption written on whatever it stands on, a dotted rule under it. One line where there is room (the subject, then the caption), two where there is not.
15
+ * Information only: nothing to press, no paper of its own.
16
+ *
17
+ * It stands in two places, both already there, and the notes have no prop for it:
18
+ * - ON A NOTE in the list of chats — in that Sticky's `stuck`: across the top of the note, on its paper. The note's words start under it and show two lines at most, so they never run under the foot; the stamps stuck on the note stay visible.
19
+ * - IN THE ROOM — as the FIRST child of a `StickyThread`: across the thread's top, staying there while the notes run under it, with room before the first note. A `lapped` thread with it leans every note exactly as without it.
20
+ *
21
+ * When the subject ends the app stops rendering it, from both places: the notes are exactly as they were.
22
+ *
23
+ * What a screen reader hears. In the room, `as="h2"`: "title caption, heading level 2" — the sub-heading of that chat, under the screen's h1 (the TopBar's title: who the chat is with), so it is
24
+ * found with the headings rotor. On a note, the default `span`: no heading (a button holds none), the words are part of the note's name, read after the note's words and its foot because the layer
25
+ * that holds things stuck on a note comes after the paper in the page.
26
+ */
27
+ export declare function StickyHeading({ title, caption, as, className, ...rest }: StickyHeadingProps): import("react").DetailedReactHTMLElement<{
28
+ /** the element it renders (React 19 passes `ref` as a prop) */
29
+ ref?: Ref<HTMLElement>;
30
+ translate?: "yes" | "no" | undefined | undefined;
31
+ onChange?: import("react").ChangeEventHandler<HTMLElement, Element> | undefined;
32
+ slot?: string | undefined | undefined;
33
+ style?: import("react").CSSProperties | undefined;
34
+ dir?: string | undefined | undefined;
35
+ children?: ReactNode;
36
+ suppressHydrationWarning?: boolean | undefined | undefined;
37
+ color?: string | undefined | undefined;
38
+ id?: string | undefined | undefined;
39
+ lang?: string | undefined | undefined;
40
+ nonce?: string | undefined | undefined;
41
+ part?: string | undefined | undefined;
42
+ role?: import("react").AriaRole | undefined;
43
+ tabIndex?: number | undefined | undefined;
44
+ "aria-activedescendant"?: string | undefined | undefined;
45
+ "aria-atomic"?: (boolean | "true" | "false") | undefined;
46
+ "aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined | undefined;
47
+ "aria-braillelabel"?: string | undefined | undefined;
48
+ "aria-brailleroledescription"?: string | undefined | undefined;
49
+ "aria-busy"?: (boolean | "true" | "false") | undefined;
50
+ "aria-checked"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
51
+ "aria-colcount"?: number | undefined | undefined;
52
+ "aria-colindex"?: number | undefined | undefined;
53
+ "aria-colindextext"?: string | undefined | undefined;
54
+ "aria-colspan"?: number | undefined | undefined;
55
+ "aria-controls"?: string | undefined | undefined;
56
+ "aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined | undefined;
57
+ "aria-describedby"?: string | undefined | undefined;
58
+ "aria-description"?: string | undefined | undefined;
59
+ "aria-details"?: string | undefined | undefined;
60
+ "aria-disabled"?: (boolean | "true" | "false") | undefined;
61
+ "aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined | undefined;
62
+ "aria-errormessage"?: string | undefined | undefined;
63
+ "aria-expanded"?: (boolean | "true" | "false") | undefined;
64
+ "aria-flowto"?: string | undefined | undefined;
65
+ "aria-grabbed"?: (boolean | "true" | "false") | undefined;
66
+ "aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined | undefined;
67
+ "aria-hidden"?: (boolean | "true" | "false") | undefined;
68
+ "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined | undefined;
69
+ "aria-keyshortcuts"?: string | undefined | undefined;
70
+ "aria-label"?: string | undefined | undefined;
71
+ "aria-labelledby"?: string | undefined | undefined;
72
+ "aria-level"?: number | undefined | undefined;
73
+ "aria-live"?: "off" | "assertive" | "polite" | undefined | undefined;
74
+ "aria-modal"?: (boolean | "true" | "false") | undefined;
75
+ "aria-multiline"?: (boolean | "true" | "false") | undefined;
76
+ "aria-multiselectable"?: (boolean | "true" | "false") | undefined;
77
+ "aria-orientation"?: "horizontal" | "vertical" | undefined | undefined;
78
+ "aria-owns"?: string | undefined | undefined;
79
+ "aria-placeholder"?: string | undefined | undefined;
80
+ "aria-posinset"?: number | undefined | undefined;
81
+ "aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
82
+ "aria-readonly"?: (boolean | "true" | "false") | undefined;
83
+ "aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined | undefined;
84
+ "aria-required"?: (boolean | "true" | "false") | undefined;
85
+ "aria-roledescription"?: string | undefined | undefined;
86
+ "aria-rowcount"?: number | undefined | undefined;
87
+ "aria-rowindex"?: number | undefined | undefined;
88
+ "aria-rowindextext"?: string | undefined | undefined;
89
+ "aria-rowspan"?: number | undefined | undefined;
90
+ "aria-selected"?: (boolean | "true" | "false") | undefined;
91
+ "aria-setsize"?: number | undefined | undefined;
92
+ "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined | undefined;
93
+ "aria-valuemax"?: number | undefined | undefined;
94
+ "aria-valuemin"?: number | undefined | undefined;
95
+ "aria-valuenow"?: number | undefined | undefined;
96
+ "aria-valuetext"?: string | undefined | undefined;
97
+ dangerouslySetInnerHTML?: {
98
+ __html: string | TrustedHTML;
99
+ } | undefined | undefined;
100
+ onCopy?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
101
+ onCopyCapture?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
102
+ onCut?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
103
+ onCutCapture?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
104
+ onPaste?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
105
+ onPasteCapture?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
106
+ onCompositionEnd?: import("react").CompositionEventHandler<HTMLElement> | undefined;
107
+ onCompositionEndCapture?: import("react").CompositionEventHandler<HTMLElement> | undefined;
108
+ onCompositionStart?: import("react").CompositionEventHandler<HTMLElement> | undefined;
109
+ onCompositionStartCapture?: import("react").CompositionEventHandler<HTMLElement> | undefined;
110
+ onCompositionUpdate?: import("react").CompositionEventHandler<HTMLElement> | undefined;
111
+ onCompositionUpdateCapture?: import("react").CompositionEventHandler<HTMLElement> | undefined;
112
+ onFocus?: import("react").FocusEventHandler<HTMLElement> | undefined;
113
+ onFocusCapture?: import("react").FocusEventHandler<HTMLElement> | undefined;
114
+ onBlur?: import("react").FocusEventHandler<HTMLElement> | undefined;
115
+ onBlurCapture?: import("react").FocusEventHandler<HTMLElement> | undefined;
116
+ onChangeCapture?: import("react").ChangeEventHandler<HTMLElement, Element> | undefined;
117
+ onBeforeInput?: import("react").InputEventHandler<HTMLElement> | undefined;
118
+ onBeforeInputCapture?: import("react").InputEventHandler<HTMLElement> | undefined;
119
+ onInput?: import("react").InputEventHandler<HTMLElement> | undefined;
120
+ onInputCapture?: import("react").InputEventHandler<HTMLElement> | undefined;
121
+ onReset?: import("react").ReactEventHandler<HTMLElement> | undefined;
122
+ onResetCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
123
+ onSubmit?: import("react").SubmitEventHandler<HTMLElement> | undefined;
124
+ onSubmitCapture?: import("react").SubmitEventHandler<HTMLElement> | undefined;
125
+ onInvalid?: import("react").ReactEventHandler<HTMLElement> | undefined;
126
+ onInvalidCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
127
+ onLoad?: import("react").ReactEventHandler<HTMLElement> | undefined;
128
+ onLoadCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
129
+ onError?: import("react").ReactEventHandler<HTMLElement> | undefined;
130
+ onErrorCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
131
+ onKeyDown?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
132
+ onKeyDownCapture?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
133
+ onKeyPress?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
134
+ onKeyPressCapture?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
135
+ onKeyUp?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
136
+ onKeyUpCapture?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
137
+ onAbort?: import("react").ReactEventHandler<HTMLElement> | undefined;
138
+ onAbortCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
139
+ onCanPlay?: import("react").ReactEventHandler<HTMLElement> | undefined;
140
+ onCanPlayCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
141
+ onCanPlayThrough?: import("react").ReactEventHandler<HTMLElement> | undefined;
142
+ onCanPlayThroughCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
143
+ onDurationChange?: import("react").ReactEventHandler<HTMLElement> | undefined;
144
+ onDurationChangeCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
145
+ onEmptied?: import("react").ReactEventHandler<HTMLElement> | undefined;
146
+ onEmptiedCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
147
+ onEncrypted?: import("react").ReactEventHandler<HTMLElement> | undefined;
148
+ onEncryptedCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
149
+ onEnded?: import("react").ReactEventHandler<HTMLElement> | undefined;
150
+ onEndedCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
151
+ onLoadedData?: import("react").ReactEventHandler<HTMLElement> | undefined;
152
+ onLoadedDataCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
153
+ onLoadedMetadata?: import("react").ReactEventHandler<HTMLElement> | undefined;
154
+ onLoadedMetadataCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
155
+ onLoadStart?: import("react").ReactEventHandler<HTMLElement> | undefined;
156
+ onLoadStartCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
157
+ onPause?: import("react").ReactEventHandler<HTMLElement> | undefined;
158
+ onPauseCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
159
+ onPlay?: import("react").ReactEventHandler<HTMLElement> | undefined;
160
+ onPlayCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
161
+ onPlaying?: import("react").ReactEventHandler<HTMLElement> | undefined;
162
+ onPlayingCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
163
+ onProgress?: import("react").ReactEventHandler<HTMLElement> | undefined;
164
+ onProgressCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
165
+ onRateChange?: import("react").ReactEventHandler<HTMLElement> | undefined;
166
+ onRateChangeCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
167
+ onSeeked?: import("react").ReactEventHandler<HTMLElement> | undefined;
168
+ onSeekedCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
169
+ onSeeking?: import("react").ReactEventHandler<HTMLElement> | undefined;
170
+ onSeekingCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
171
+ onStalled?: import("react").ReactEventHandler<HTMLElement> | undefined;
172
+ onStalledCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
173
+ onSuspend?: import("react").ReactEventHandler<HTMLElement> | undefined;
174
+ onSuspendCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
175
+ onTimeUpdate?: import("react").ReactEventHandler<HTMLElement> | undefined;
176
+ onTimeUpdateCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
177
+ onVolumeChange?: import("react").ReactEventHandler<HTMLElement> | undefined;
178
+ onVolumeChangeCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
179
+ onWaiting?: import("react").ReactEventHandler<HTMLElement> | undefined;
180
+ onWaitingCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
181
+ onAuxClick?: import("react").MouseEventHandler<HTMLElement> | undefined;
182
+ onAuxClickCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
183
+ onClick?: import("react").MouseEventHandler<HTMLElement> | undefined;
184
+ onClickCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
185
+ onContextMenu?: import("react").MouseEventHandler<HTMLElement> | undefined;
186
+ onContextMenuCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
187
+ onDoubleClick?: import("react").MouseEventHandler<HTMLElement> | undefined;
188
+ onDoubleClickCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
189
+ onDrag?: import("react").DragEventHandler<HTMLElement> | undefined;
190
+ onDragCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
191
+ onDragEnd?: import("react").DragEventHandler<HTMLElement> | undefined;
192
+ onDragEndCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
193
+ onDragEnter?: import("react").DragEventHandler<HTMLElement> | undefined;
194
+ onDragEnterCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
195
+ onDragExit?: import("react").DragEventHandler<HTMLElement> | undefined;
196
+ onDragExitCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
197
+ onDragLeave?: import("react").DragEventHandler<HTMLElement> | undefined;
198
+ onDragLeaveCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
199
+ onDragOver?: import("react").DragEventHandler<HTMLElement> | undefined;
200
+ onDragOverCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
201
+ onDragStart?: import("react").DragEventHandler<HTMLElement> | undefined;
202
+ onDragStartCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
203
+ onDrop?: import("react").DragEventHandler<HTMLElement> | undefined;
204
+ onDropCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
205
+ onMouseDown?: import("react").MouseEventHandler<HTMLElement> | undefined;
206
+ onMouseDownCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
207
+ onMouseEnter?: import("react").MouseEventHandler<HTMLElement> | undefined;
208
+ onMouseLeave?: import("react").MouseEventHandler<HTMLElement> | undefined;
209
+ onMouseMove?: import("react").MouseEventHandler<HTMLElement> | undefined;
210
+ onMouseMoveCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
211
+ onMouseOut?: import("react").MouseEventHandler<HTMLElement> | undefined;
212
+ onMouseOutCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
213
+ onMouseOver?: import("react").MouseEventHandler<HTMLElement> | undefined;
214
+ onMouseOverCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
215
+ onMouseUp?: import("react").MouseEventHandler<HTMLElement> | undefined;
216
+ onMouseUpCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
217
+ onSelect?: import("react").ReactEventHandler<HTMLElement> | undefined;
218
+ onSelectCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
219
+ onTouchCancel?: import("react").TouchEventHandler<HTMLElement> | undefined;
220
+ onTouchCancelCapture?: import("react").TouchEventHandler<HTMLElement> | undefined;
221
+ onTouchEnd?: import("react").TouchEventHandler<HTMLElement> | undefined;
222
+ onTouchEndCapture?: import("react").TouchEventHandler<HTMLElement> | undefined;
223
+ onTouchMove?: import("react").TouchEventHandler<HTMLElement> | undefined;
224
+ onTouchMoveCapture?: import("react").TouchEventHandler<HTMLElement> | undefined;
225
+ onTouchStart?: import("react").TouchEventHandler<HTMLElement> | undefined;
226
+ onTouchStartCapture?: import("react").TouchEventHandler<HTMLElement> | undefined;
227
+ onPointerDown?: import("react").PointerEventHandler<HTMLElement> | undefined;
228
+ onPointerDownCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
229
+ onPointerMove?: import("react").PointerEventHandler<HTMLElement> | undefined;
230
+ onPointerMoveCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
231
+ onPointerUp?: import("react").PointerEventHandler<HTMLElement> | undefined;
232
+ onPointerUpCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
233
+ onPointerCancel?: import("react").PointerEventHandler<HTMLElement> | undefined;
234
+ onPointerCancelCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
235
+ onPointerEnter?: import("react").PointerEventHandler<HTMLElement> | undefined;
236
+ onPointerLeave?: import("react").PointerEventHandler<HTMLElement> | undefined;
237
+ onPointerOver?: import("react").PointerEventHandler<HTMLElement> | undefined;
238
+ onPointerOverCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
239
+ onPointerOut?: import("react").PointerEventHandler<HTMLElement> | undefined;
240
+ onPointerOutCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
241
+ onGotPointerCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
242
+ onGotPointerCaptureCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
243
+ onLostPointerCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
244
+ onLostPointerCaptureCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
245
+ onScroll?: import("react").UIEventHandler<HTMLElement> | undefined;
246
+ onScrollCapture?: import("react").UIEventHandler<HTMLElement> | undefined;
247
+ onScrollEnd?: import("react").UIEventHandler<HTMLElement> | undefined;
248
+ onScrollEndCapture?: import("react").UIEventHandler<HTMLElement> | undefined;
249
+ onWheel?: import("react").WheelEventHandler<HTMLElement> | undefined;
250
+ onWheelCapture?: import("react").WheelEventHandler<HTMLElement> | undefined;
251
+ onAnimationStart?: import("react").AnimationEventHandler<HTMLElement> | undefined;
252
+ onAnimationStartCapture?: import("react").AnimationEventHandler<HTMLElement> | undefined;
253
+ onAnimationEnd?: import("react").AnimationEventHandler<HTMLElement> | undefined;
254
+ onAnimationEndCapture?: import("react").AnimationEventHandler<HTMLElement> | undefined;
255
+ onAnimationIteration?: import("react").AnimationEventHandler<HTMLElement> | undefined;
256
+ onAnimationIterationCapture?: import("react").AnimationEventHandler<HTMLElement> | undefined;
257
+ onToggle?: import("react").ToggleEventHandler<HTMLElement> | undefined;
258
+ onBeforeToggle?: import("react").ToggleEventHandler<HTMLElement> | undefined;
259
+ onTransitionCancel?: import("react").TransitionEventHandler<HTMLElement> | undefined;
260
+ onTransitionCancelCapture?: import("react").TransitionEventHandler<HTMLElement> | undefined;
261
+ onTransitionEnd?: import("react").TransitionEventHandler<HTMLElement> | undefined;
262
+ onTransitionEndCapture?: import("react").TransitionEventHandler<HTMLElement> | undefined;
263
+ onTransitionRun?: import("react").TransitionEventHandler<HTMLElement> | undefined;
264
+ onTransitionRunCapture?: import("react").TransitionEventHandler<HTMLElement> | undefined;
265
+ onTransitionStart?: import("react").TransitionEventHandler<HTMLElement> | undefined;
266
+ onTransitionStartCapture?: import("react").TransitionEventHandler<HTMLElement> | undefined;
267
+ defaultChecked?: boolean | undefined | undefined;
268
+ defaultValue?: string | number | readonly string[] | undefined;
269
+ suppressContentEditableWarning?: boolean | undefined | undefined;
270
+ accessKey?: string | undefined | undefined;
271
+ autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | undefined | (string & {}) | undefined;
272
+ autoFocus?: boolean | undefined | undefined;
273
+ contentEditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
274
+ contextMenu?: string | undefined | undefined;
275
+ draggable?: (boolean | "true" | "false") | undefined;
276
+ enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
277
+ hidden?: boolean | undefined | undefined;
278
+ spellCheck?: (boolean | "true" | "false") | undefined;
279
+ radioGroup?: string | undefined | undefined;
280
+ about?: string | undefined | undefined;
281
+ content?: string | undefined | undefined;
282
+ datatype?: string | undefined | undefined;
283
+ inlist?: any;
284
+ prefix?: string | undefined | undefined;
285
+ property?: string | undefined | undefined;
286
+ rel?: string | undefined | undefined;
287
+ resource?: string | undefined | undefined;
288
+ rev?: string | undefined | undefined;
289
+ typeof?: string | undefined | undefined;
290
+ vocab?: string | undefined | undefined;
291
+ autoCorrect?: string | undefined | undefined;
292
+ autoSave?: string | undefined | undefined;
293
+ itemProp?: string | undefined | undefined;
294
+ itemScope?: boolean | undefined | undefined;
295
+ itemType?: string | undefined | undefined;
296
+ itemID?: string | undefined | undefined;
297
+ itemRef?: string | undefined | undefined;
298
+ results?: number | undefined | undefined;
299
+ security?: string | undefined | undefined;
300
+ unselectable?: "on" | "off" | undefined | undefined;
301
+ popover?: "" | "auto" | "manual" | "hint" | undefined | undefined;
302
+ popoverTargetAction?: "toggle" | "show" | "hide" | undefined | undefined;
303
+ popoverTarget?: string | undefined | undefined;
304
+ inert?: boolean | undefined | undefined;
305
+ inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined | undefined;
306
+ is?: string | undefined | undefined;
307
+ exportparts?: string | undefined | undefined;
308
+ className: string;
309
+ }, HTMLElement>;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createElement } from 'react';
3
+ import { cx } from '../cx.js';
4
+ /**
5
+ * StickyHeading — names what a chat told in notes is about, for as long as that lasts (a job agreed and its time): the subject and a short
6
+ * caption written on whatever it stands on, a dotted rule under it. One line where there is room (the subject, then the caption), two where there is not.
7
+ * Information only: nothing to press, no paper of its own.
8
+ *
9
+ * It stands in two places, both already there, and the notes have no prop for it:
10
+ * - ON A NOTE in the list of chats — in that Sticky's `stuck`: across the top of the note, on its paper. The note's words start under it and show two lines at most, so they never run under the foot; the stamps stuck on the note stay visible.
11
+ * - IN THE ROOM — as the FIRST child of a `StickyThread`: across the thread's top, staying there while the notes run under it, with room before the first note. A `lapped` thread with it leans every note exactly as without it.
12
+ *
13
+ * When the subject ends the app stops rendering it, from both places: the notes are exactly as they were.
14
+ *
15
+ * What a screen reader hears. In the room, `as="h2"`: "title caption, heading level 2" — the sub-heading of that chat, under the screen's h1 (the TopBar's title: who the chat is with), so it is
16
+ * found with the headings rotor. On a note, the default `span`: no heading (a button holds none), the words are part of the note's name, read after the note's words and its foot because the layer
17
+ * that holds things stuck on a note comes after the paper in the page.
18
+ */
19
+ export function StickyHeading({ title, caption, as = 'span', className, ...rest }) {
20
+ return createElement(as, { className: cx('sticky-heading', className), ...rest }, _jsx("span", { className: "sticky-heading__title", children: title }), caption != null && _jsx("span", { className: "sticky-heading__caption", children: caption }));
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usableapp/cardds",
3
- "version": "0.7.88",
3
+ "version": "0.7.90",
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.",