@usableapp/cardds 0.7.84 → 0.7.86
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 +21 -0
- package/README.md +22 -1
- package/css/actions.css +12 -5
- package/css/stamp.css +47 -0
- package/css/sticky.css +44 -1
- package/dist/cardds.css +103 -6
- package/dist/catalog.json +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/stamp/StampOn.d.ts +26 -2
- package/dist/stamp/StampOn.js +82 -14
- package/dist/stamp/underNotes.d.ts +3 -0
- package/dist/stamp/underNotes.js +4 -0
- package/dist/sticky/Sticky.d.ts +2 -2
- package/dist/sticky/StickyPile.d.ts +20 -0
- package/dist/sticky/StickyPile.js +31 -0
- package/dist/words.d.ts +1 -1
- package/dist/words.js +2 -2
- package/lint/rules.mjs +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,7 @@ export * from './sticky/StickyField.js';
|
|
|
81
81
|
export * from './sticky/StickyPick.js';
|
|
82
82
|
export * from './sticky/StickyThread.js';
|
|
83
83
|
export * from './sticky/StickyBoard.js';
|
|
84
|
+
export * from './sticky/StickyPile.js';
|
|
84
85
|
export * from './sticky/StickyAnswer.js';
|
|
85
86
|
export * from './stamp/Stamp.js';
|
|
86
87
|
export * from './stamp/StampBook.js';
|
package/dist/index.js
CHANGED
|
@@ -85,6 +85,7 @@ export * from './sticky/StickyField.js';
|
|
|
85
85
|
export * from './sticky/StickyPick.js';
|
|
86
86
|
export * from './sticky/StickyThread.js';
|
|
87
87
|
export * from './sticky/StickyBoard.js';
|
|
88
|
+
export * from './sticky/StickyPile.js';
|
|
88
89
|
export * from './sticky/StickyAnswer.js';
|
|
89
90
|
export * from './stamp/Stamp.js';
|
|
90
91
|
export * from './stamp/StampBook.js';
|
package/dist/stamp/StampOn.d.ts
CHANGED
|
@@ -15,12 +15,36 @@ export interface StampOnProps extends ComponentProps<'div'> {
|
|
|
15
15
|
onAtChange?: (at: StampAt) => void;
|
|
16
16
|
/** a draggable stamp's accessible name ("แสตมป์ของต้น") */
|
|
17
17
|
label?: string;
|
|
18
|
+
/** the stamp can be TAKEN OFF: when a drag ends, an arrow key moves it, or (on a stamp that cannot be dragged) it is pressed, a cross stands on its top right corner for `removeFor` ms; a press on the cross — or Delete / Backspace on the focused stamp — ASKS. The app owns what is stuck (as it owns `at`): it takes the `StampOn` away. Without it nothing changes: no cross, the stamp is dragged as before and cannot be taken off. A press or a drag on a removable stamp never reaches what lies under it */
|
|
19
|
+
onRemove?: () => void;
|
|
20
|
+
/** the cross's accessible name (default: cardds's own word, "Remove" / "เอาออก" by the page's language) */
|
|
21
|
+
removeLabel?: string;
|
|
22
|
+
/** how long the cross stands after the stamp was touched, in ms (default 3000). It stays while it holds the keyboard focus; a new drag takes it away until the drag ends */
|
|
23
|
+
removeFor?: number;
|
|
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
|
+
main?: boolean;
|
|
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` */
|
|
28
|
+
arrive?: boolean;
|
|
29
|
+
/** an arriving stamp has LANDED (its strike ended; at once where motion is off) — the app does what follows: sends, closes */
|
|
30
|
+
onArrived?: () => void;
|
|
18
31
|
}
|
|
19
32
|
/**
|
|
20
33
|
* StampOn — a Stamp (the child) stuck on a surface at the place the member chose. The surface is the nearest positioned box:
|
|
21
34
|
* a Sticky's `stuck`, a postcard's face. `at` is the stamp's middle as a share of that surface, so the note reads the same at any size.
|
|
22
|
-
* The stamp is the SMALL size: 48 × 60 on any surface, whatever the surface's size (`--stamp-sm-size`, one tap wide)
|
|
35
|
+
* The stamp is the SMALL size: 48 × 60 on any surface, whatever the surface's size (`--stamp-sm-size`, one tap wide); `main` makes it the
|
|
36
|
+
* stamp's main size, 96 × 120, to cover what it lies on.
|
|
23
37
|
* With `onAtChange` the member drags it (finger or mouse; the arrow keys move it too): while the finger holds, the stamp writes its
|
|
24
38
|
* own place, and on letting go it ASKS — the app owns `at`. It never leaves the surface.
|
|
39
|
+
* With `onRemove` it can be taken off: once touched (a drag let go, an arrow key, or a press on a stamp that cannot be dragged) a cross
|
|
40
|
+
* stands on its top right corner for `removeFor` ms; a press on the cross, or Delete / Backspace on the focused stamp, ASKS, and the app
|
|
41
|
+
* takes the stamp away. `onRemove` without `onAtChange` is a stamp that stays where it is and is taken off: a press shows the cross
|
|
42
|
+
* (a warning instead would leave the prop dead, since the cross needs a touch to appear). A press or a drag on a removable stamp or its
|
|
43
|
+
* cross stops there, so a pressable card or pile under it is not pressed. The cross stays upright whatever the stamp's `turn`: it is a
|
|
44
|
+
* button, and its 44 × 44 touch target round the 24 disc should be square to the screen.
|
|
45
|
+
* Inside a `StickyPile`, a stamp on a note under the top one is inert: no drag, no cross, no focus.
|
|
46
|
+
* With `arrive` it sticks itself: struck onto its place as it comes on, and `onArrived` tells the app it has landed. A stamp that is still
|
|
47
|
+
* arriving cannot be taken off: no cross, no focus, no Delete until it has landed (it is not stuck yet). `arrive` with `main` strikes
|
|
48
|
+
* the main size, 96 × 120, onto its place.
|
|
25
49
|
*/
|
|
26
|
-
export declare function StampOn({ at, defaultAt, turn, onAtChange, label, className, style, children, ...rest }: StampOnProps): import("react").JSX.Element;
|
|
50
|
+
export declare function StampOn({ at, defaultAt, turn, onAtChange, label, onRemove, removeLabel, removeFor, main, arrive, onArrived, className, style, onAnimationEnd, children, ...rest }: StampOnProps): import("react").JSX.Element;
|
package/dist/stamp/StampOn.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRef, useState } from 'react';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useContext, useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { cx } from '../cx.js';
|
|
4
|
-
import { useControlled } from '../state.js';
|
|
4
|
+
import { reducedMotion, useControlled } from '../state.js';
|
|
5
|
+
import { Icon } from '../type/Icon.js';
|
|
6
|
+
import { word } from '../words.js';
|
|
7
|
+
import { UnderNotes } from './underNotes.js';
|
|
5
8
|
const MIDDLE = { x: 0.5, y: 0.5 };
|
|
6
9
|
const STEP = 0.02; // an arrow key's move: a share of the surface
|
|
7
10
|
const MOVES = { ArrowLeft: [-STEP, 0], ArrowRight: [STEP, 0], ArrowUp: [0, -STEP], ArrowDown: [0, STEP] };
|
|
@@ -15,21 +18,42 @@ function within(el, x, y) {
|
|
|
15
18
|
const mx = el.offsetWidth / 2 / box.clientWidth, my = el.offsetHeight / 2 / box.clientHeight;
|
|
16
19
|
return { x: round(clamp(x, mx, 1 - mx)), y: round(clamp(y, my, 1 - my)) };
|
|
17
20
|
}
|
|
21
|
+
const STRIKE = 'stamp-on-strike'; // css/stamp.css
|
|
18
22
|
const write = (el, at) => { el.style.setProperty('--stamp-on-x', String(at.x)); el.style.setProperty('--stamp-on-y', String(at.y)); };
|
|
19
23
|
/**
|
|
20
24
|
* StampOn — a Stamp (the child) stuck on a surface at the place the member chose. The surface is the nearest positioned box:
|
|
21
25
|
* a Sticky's `stuck`, a postcard's face. `at` is the stamp's middle as a share of that surface, so the note reads the same at any size.
|
|
22
|
-
* The stamp is the SMALL size: 48 × 60 on any surface, whatever the surface's size (`--stamp-sm-size`, one tap wide)
|
|
26
|
+
* The stamp is the SMALL size: 48 × 60 on any surface, whatever the surface's size (`--stamp-sm-size`, one tap wide); `main` makes it the
|
|
27
|
+
* stamp's main size, 96 × 120, to cover what it lies on.
|
|
23
28
|
* With `onAtChange` the member drags it (finger or mouse; the arrow keys move it too): while the finger holds, the stamp writes its
|
|
24
29
|
* own place, and on letting go it ASKS — the app owns `at`. It never leaves the surface.
|
|
30
|
+
* With `onRemove` it can be taken off: once touched (a drag let go, an arrow key, or a press on a stamp that cannot be dragged) a cross
|
|
31
|
+
* stands on its top right corner for `removeFor` ms; a press on the cross, or Delete / Backspace on the focused stamp, ASKS, and the app
|
|
32
|
+
* takes the stamp away. `onRemove` without `onAtChange` is a stamp that stays where it is and is taken off: a press shows the cross
|
|
33
|
+
* (a warning instead would leave the prop dead, since the cross needs a touch to appear). A press or a drag on a removable stamp or its
|
|
34
|
+
* cross stops there, so a pressable card or pile under it is not pressed. The cross stays upright whatever the stamp's `turn`: it is a
|
|
35
|
+
* button, and its 44 × 44 touch target round the 24 disc should be square to the screen.
|
|
36
|
+
* Inside a `StickyPile`, a stamp on a note under the top one is inert: no drag, no cross, no focus.
|
|
37
|
+
* With `arrive` it sticks itself: struck onto its place as it comes on, and `onArrived` tells the app it has landed. A stamp that is still
|
|
38
|
+
* arriving cannot be taken off: no cross, no focus, no Delete until it has landed (it is not stuck yet). `arrive` with `main` strikes
|
|
39
|
+
* the main size, 96 × 120, onto its place.
|
|
25
40
|
*/
|
|
26
|
-
export function StampOn({ at, defaultAt = MIDDLE, turn = 0, onAtChange, label, className, style, children, ...rest }) {
|
|
41
|
+
export function StampOn({ at, defaultAt = MIDDLE, turn = 0, onAtChange, label, onRemove, removeLabel, removeFor = 3000, main, arrive, onArrived, className, style, onAnimationEnd, children, ...rest }) {
|
|
42
|
+
const under = useContext(UnderNotes);
|
|
27
43
|
const [shown, request] = useControlled(at, defaultAt, onAtChange);
|
|
28
44
|
const [held, setHeld] = useState(false);
|
|
45
|
+
const [cross, setCross] = useState(false);
|
|
29
46
|
const grip = useRef(null);
|
|
30
|
-
const
|
|
47
|
+
const timer = useRef(undefined);
|
|
48
|
+
const moves = onAtChange != null && !under;
|
|
49
|
+
const [arrived, setArrived] = useState(false);
|
|
50
|
+
const removes = onRemove != null && !under && !(arrive && !arrived);
|
|
31
51
|
const ask = (next) => { if (next.x !== shown.x || next.y !== shown.y)
|
|
32
52
|
request(next); };
|
|
53
|
+
const stop = () => clearTimeout(timer.current);
|
|
54
|
+
const show = () => { if (!removes)
|
|
55
|
+
return; stop(); setCross(true); timer.current = setTimeout(() => setCross(false), removeFor); };
|
|
56
|
+
useEffect(() => stop, []);
|
|
33
57
|
const down = (e) => {
|
|
34
58
|
const el = e.currentTarget, box = el.offsetParent;
|
|
35
59
|
if (!box || e.button !== 0)
|
|
@@ -38,6 +62,8 @@ export function StampOn({ at, defaultAt = MIDDLE, turn = 0, onAtChange, label, c
|
|
|
38
62
|
grip.current = { dx: (e.clientX - r.left - box.clientLeft) / box.clientWidth - shown.x, dy: (e.clientY - r.top - box.clientTop) / box.clientHeight - shown.y, next: shown };
|
|
39
63
|
el.setPointerCapture(e.pointerId);
|
|
40
64
|
setHeld(true);
|
|
65
|
+
stop();
|
|
66
|
+
setCross(false); // a new drag takes the cross away until it ends
|
|
41
67
|
};
|
|
42
68
|
const move = (e) => {
|
|
43
69
|
const el = e.currentTarget, box = el.offsetParent, g = grip.current;
|
|
@@ -54,17 +80,59 @@ export function StampOn({ at, defaultAt = MIDDLE, turn = 0, onAtChange, label, c
|
|
|
54
80
|
grip.current = null;
|
|
55
81
|
setHeld(false);
|
|
56
82
|
write(e.currentTarget, shown); // back to what the app has: if it takes the new place, the next render writes it
|
|
57
|
-
if (e.type === 'pointerup')
|
|
83
|
+
if (e.type === 'pointerup') {
|
|
58
84
|
ask(g.next);
|
|
85
|
+
show();
|
|
86
|
+
}
|
|
59
87
|
};
|
|
60
88
|
const key = (e) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
89
|
+
if (e.target !== e.currentTarget)
|
|
90
|
+
return; // the cross has its own keys
|
|
91
|
+
const by = moves ? MOVES[e.key] : undefined;
|
|
92
|
+
if (by) {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
e.stopPropagation();
|
|
95
|
+
ask(within(e.currentTarget, shown.x + by[0], shown.y + by[1]));
|
|
96
|
+
show();
|
|
97
|
+
}
|
|
98
|
+
else if (removes && (e.key === 'Delete' || e.key === 'Backspace')) {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
e.stopPropagation();
|
|
101
|
+
stop();
|
|
102
|
+
setCross(false);
|
|
103
|
+
onRemove();
|
|
104
|
+
}
|
|
105
|
+
else if (removes && !moves && (e.key === 'Enter' || e.key === ' ')) {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
e.stopPropagation();
|
|
108
|
+
show();
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
// nothing that starts on a removable stamp or its cross reaches what lies under it: a click is the end of a press, and a drag's end is one too
|
|
112
|
+
const click = (e) => {
|
|
113
|
+
e.stopPropagation();
|
|
114
|
+
rest.onClick?.(e);
|
|
115
|
+
if (!moves)
|
|
116
|
+
show();
|
|
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();
|
|
66
131
|
};
|
|
67
132
|
const place = { '--stamp-on-x': shown.x, '--stamp-on-y': shown.y, '--stamp-on-turn': turn, ...style };
|
|
68
|
-
const
|
|
69
|
-
|
|
133
|
+
const own = moves || removes ? { tabIndex: 0, role: 'group', 'aria-label': label, onKeyDown: key } : null;
|
|
134
|
+
const drag = moves ? { onPointerDown: down, onPointerMove: move, onPointerUp: up, onPointerCancel: up } : null;
|
|
135
|
+
const guard = removes ? { onClick: click } : null;
|
|
136
|
+
return (_jsxs("div", { className: cx('stamp-on', moves && 'stamp-on--move', main && 'stamp-on--main', arrive && 'stamp-on--arrive', held && 'is-held', className), style: place, onAnimationEnd: struck, ...rest, ...own, ...drag, ...guard, inert: under || undefined, children: [children, removes && cross && (_jsx("button", { type: "button", className: "stamp-on__remove", "aria-label": removeLabel ?? word('remove'), onPointerDown: (e) => e.stopPropagation(), onClick: (e) => { e.stopPropagation(); stop(); setCross(false); onRemove(); }, onFocus: stop, onBlur: () => { if (!grip.current)
|
|
137
|
+
show(); }, children: _jsx("span", { className: "stamp-on__remove-disc", children: _jsx(Icon, { name: "x" }) }) }))] }));
|
|
70
138
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** set by a holder whose notes lie one under another (`StickyPile`): a stamp on a note that only shows what peeks out is inert — no drag, no cross, no focus.
|
|
2
|
+
* Not part of the package's API: a holder writes it, `StampOn` reads it. */
|
|
3
|
+
export declare const UnderNotes: import("react").Context<boolean>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
/** set by a holder whose notes lie one under another (`StickyPile`): a stamp on a note that only shows what peeks out is inert — no drag, no cross, no focus.
|
|
3
|
+
* Not part of the package's API: a holder writes it, `StampOn` reads it. */
|
|
4
|
+
export const UnderNotes = createContext(false);
|
package/dist/sticky/Sticky.d.ts
CHANGED
|
@@ -13,13 +13,13 @@ export interface StickyProps extends Omit<ComponentProps<'div'>, 'onClick'> {
|
|
|
13
13
|
head?: ReactNode;
|
|
14
14
|
/** a StickyDate at the end of the head — the note's top-right corner */
|
|
15
15
|
date?: ReactNode;
|
|
16
|
-
/** turned a little on its middle (the one note of a start screen); a note is straight otherwise, unless its holder turns it (`StickyBoard`, `StickyThread lapped`) */
|
|
16
|
+
/** turned a little on its middle (the one note of a start screen); a note is straight otherwise, unless its holder turns it (`StickyBoard`, `StickyPile`, `StickyThread lapped`) */
|
|
17
17
|
tilt?: 'left' | 'right';
|
|
18
18
|
/** a small muted line at the bottom ("From …") */
|
|
19
19
|
foot?: ReactNode;
|
|
20
20
|
/** a control (an IconBtn) standing on the bottom-right corner, half outside the note; the note then has no fold */
|
|
21
21
|
corner?: ReactNode;
|
|
22
|
-
/** things stuck ON the note, each placing itself over the paper (a `StampOn`) — a layer the size of the note, in front of the words */
|
|
22
|
+
/** things stuck ON the note, each placing itself over the paper (a `StampOn`: the small stamp, or `main` for the main size) — a layer the size of the note, in front of the words */
|
|
23
23
|
stuck?: ReactNode;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ComponentProps } from 'react';
|
|
2
|
+
export interface StickyPileProps extends Omit<ComponentProps<'div'>, 'onClick'> {
|
|
3
|
+
/** the whole pile is ONE button: it renders a `<button>` over the notes and says that it was pressed — one note or many, and where the press goes, is the app's. Without it the pile only shows. A `Sticky` inside with an `onClick` of its own is misuse (two buttons for one pile): the pile warns once */
|
|
4
|
+
onClick?: () => void;
|
|
5
|
+
/** the pile's accessible name, REQUIRED with `onClick` (the notes' words are not read by the button): say how many and from whom, in the app's words. Warns once without it */
|
|
6
|
+
label?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* StickyPile — ONE to SIX `Sticky` notes lying one over another, as the newest notes of a feed do. The FIRST child is on top (the app passes
|
|
10
|
+
* the newest first); each note under it is shifted a step right and down, so a strip of it peeks out (`--sticky-pile-step-x`, 24, and
|
|
11
|
+
* `--sticky-pile-step-y`, 8) and turned a little by the same four angles as `StickyBoard` (−3° · 1.5° · 2.75° · −1.75°, by the child's place,
|
|
12
|
+
* so the top note's turn is the same whether the pile holds one note or six). Only the top note draws its `foot`: a cut time under another note is
|
|
13
|
+
* noise. A pile of one is that note, centred, with its foot. A seventh note is not drawn (a warning, once).
|
|
14
|
+
* The pile is centred in its column and never wider than it: a column too narrow for the steps shortens them (a lone note's width is the least it takes).
|
|
15
|
+
* It knows nothing of a feed or a home: in a Home's `base` it stands above a `CardFeed`, and the app writes both.
|
|
16
|
+
* With `onClick` the whole pile is one control with a name (`label`): one Tab stop, a ring round the pile, dimmed while it is held.
|
|
17
|
+
* A `StampOn` on the TOP note keeps its own touch — dragging it, or pressing its cross, never presses the pile — and the stamps on the notes under it are inert
|
|
18
|
+
* (no drag, no cross, no focus: they only show what peeks out), whatever the app passed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function StickyPile({ onClick, label, className, style, children, ...rest }: StickyPileProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Children, isValidElement } from 'react';
|
|
3
|
+
import { cx } from '../cx.js';
|
|
4
|
+
import { warnOnce } from '../state.js';
|
|
5
|
+
import { UnderNotes } from '../stamp/underNotes.js';
|
|
6
|
+
/** most notes a pile draws; a seventh is dropped, as a Stamp drops a fifth picture */
|
|
7
|
+
const MOST = 6;
|
|
8
|
+
/**
|
|
9
|
+
* StickyPile — ONE to SIX `Sticky` notes lying one over another, as the newest notes of a feed do. The FIRST child is on top (the app passes
|
|
10
|
+
* the newest first); each note under it is shifted a step right and down, so a strip of it peeks out (`--sticky-pile-step-x`, 24, and
|
|
11
|
+
* `--sticky-pile-step-y`, 8) and turned a little by the same four angles as `StickyBoard` (−3° · 1.5° · 2.75° · −1.75°, by the child's place,
|
|
12
|
+
* so the top note's turn is the same whether the pile holds one note or six). Only the top note draws its `foot`: a cut time under another note is
|
|
13
|
+
* noise. A pile of one is that note, centred, with its foot. A seventh note is not drawn (a warning, once).
|
|
14
|
+
* The pile is centred in its column and never wider than it: a column too narrow for the steps shortens them (a lone note's width is the least it takes).
|
|
15
|
+
* It knows nothing of a feed or a home: in a Home's `base` it stands above a `CardFeed`, and the app writes both.
|
|
16
|
+
* With `onClick` the whole pile is one control with a name (`label`): one Tab stop, a ring round the pile, dimmed while it is held.
|
|
17
|
+
* A `StampOn` on the TOP note keeps its own touch — dragging it, or pressing its cross, never presses the pile — and the stamps on the notes under it are inert
|
|
18
|
+
* (no drag, no cross, no focus: they only show what peeks out), whatever the app passed.
|
|
19
|
+
*/
|
|
20
|
+
export function StickyPile({ onClick, label, className, style, children, ...rest }) {
|
|
21
|
+
const all = Children.toArray(children).filter((c) => isValidElement(c));
|
|
22
|
+
if (all.length > MOST)
|
|
23
|
+
warnOnce('sticky-pile-most', `cardds StickyPile: ${all.length} notes — at most six are drawn; show the rest elsewhere`);
|
|
24
|
+
const notes = all.slice(0, MOST);
|
|
25
|
+
const tap = onClick != null;
|
|
26
|
+
if (tap && label == null)
|
|
27
|
+
warnOnce('sticky-pile-label', 'cardds StickyPile: onClick without a label — the pile is one button and the notes\' words are not its name');
|
|
28
|
+
if (tap && notes.some((n) => n.props.onClick != null))
|
|
29
|
+
warnOnce('sticky-pile-tap', 'cardds StickyPile: a Sticky with its own onClick inside a pile that has one — two buttons for one pile; give the pile the onClick');
|
|
30
|
+
return (_jsx("div", { className: cx('sticky-pile', tap && 'sticky-pile--tap', className), style: { '--_n': notes.length, ...style }, ...rest, children: _jsxs("div", { className: "sticky-pile__notes", children: [notes.map((note, i) => _jsx(UnderNotes.Provider, { value: i > 0, children: note }, note.key)), tap && _jsx("button", { type: "button", className: "sticky-pile__press", "aria-label": label, onClick: onClick })] }) }));
|
|
31
|
+
}
|
package/dist/words.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type WordKey = 'resize' | 'back' | 'next' | 'previous' | 'skip' | 'digit' | 'progress' | 'image' | 'mood' | 'photos' | 'voiceNote' | 'play' | 'pause' | 'to' | 'turnToBack' | 'turnToPicture' | 'message' | 'address' | 'toMessage' | 'toAddress' | 'stampOn' | 'stampOff' | 'stamps' | 'unread';
|
|
1
|
+
export type WordKey = 'resize' | 'back' | 'next' | 'previous' | 'skip' | 'digit' | 'progress' | 'image' | 'mood' | 'photos' | 'voiceNote' | 'play' | 'pause' | 'to' | 'turnToBack' | 'turnToPicture' | 'message' | 'address' | 'toMessage' | 'toAddress' | 'stampOn' | 'stampOff' | 'stamps' | 'unread' | 'remove';
|
|
2
2
|
type Words = Record<WordKey, string>;
|
|
3
3
|
/** add or retune the words for a language (`'lo'`, or `'th'` to change one) — once, at app start. Missing words fall back to English. */
|
|
4
4
|
export declare function setWords(lang: string, words: Partial<Words>): void;
|
package/dist/words.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const sets = {
|
|
2
2
|
en: { resize: 'Resize', back: 'Back', next: 'Next', previous: 'Previous', skip: 'Skip', digit: 'Digit', progress: 'Progress', image: 'Image', mood: 'Mood', photos: 'Photos', voiceNote: 'Voice note',
|
|
3
|
-
play: 'Play', pause: 'Pause', to: 'To', turnToBack: 'See the back', turnToPicture: 'See the picture', message: 'Message', address: 'Address', toMessage: 'Back to the message', toAddress: 'On to the address', stampOn: 'Stick a stamp', stampOff: 'Take the stamp off', stamps: 'Choose a stamp', unread: 'Unread' },
|
|
3
|
+
play: 'Play', pause: 'Pause', to: 'To', turnToBack: 'See the back', turnToPicture: 'See the picture', message: 'Message', address: 'Address', toMessage: 'Back to the message', toAddress: 'On to the address', stampOn: 'Stick a stamp', stampOff: 'Take the stamp off', stamps: 'Choose a stamp', unread: 'Unread', remove: 'Remove' },
|
|
4
4
|
th: { resize: 'ปรับความสูง', back: 'กลับ', next: 'ถัดไป', previous: 'ก่อนหน้า', skip: 'ข้าม', digit: 'หลักที่', progress: 'ความคืบหน้า', image: 'รูปภาพ', mood: 'อารมณ์', photos: 'รูปภาพ', voiceNote: 'ข้อความเสียง',
|
|
5
|
-
play: 'ฟังเสียง', pause: 'หยุดเสียง', to: 'ถึง', turnToBack: 'พลิกดูด้านหลัง', turnToPicture: 'พลิกดูรูป', message: 'ข้อความ', address: 'จ่าหน้า', toMessage: 'เลื่อนกลับไปข้อความ', toAddress: 'เลื่อนไปจ่าหน้า', stampOn: 'ติดแสตมป์', stampOff: 'แกะแสตมป์ออก', stamps: 'เลือกแสตมป์', unread: 'ยังไม่ได้อ่าน' },
|
|
5
|
+
play: 'ฟังเสียง', pause: 'หยุดเสียง', to: 'ถึง', turnToBack: 'พลิกดูด้านหลัง', turnToPicture: 'พลิกดูรูป', message: 'ข้อความ', address: 'จ่าหน้า', toMessage: 'เลื่อนกลับไปข้อความ', toAddress: 'เลื่อนไปจ่าหน้า', stampOn: 'ติดแสตมป์', stampOff: 'แกะแสตมป์ออก', stamps: 'เลือกแสตมป์', unread: 'ยังไม่ได้อ่าน', remove: 'เอาออก' },
|
|
6
6
|
};
|
|
7
7
|
/** add or retune the words for a language (`'lo'`, or `'th'` to change one) — once, at app start. Missing words fall back to English. */
|
|
8
8
|
export function setWords(lang, words) {
|
package/lint/rules.mjs
CHANGED
|
@@ -12,7 +12,7 @@ const CSS_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'css');
|
|
|
12
12
|
/** the props whose default is cardds's own word (src/words.ts), per component — a `label` that is a control's required accessible name (IconBtn, Fab, Pin…) is not here */
|
|
13
13
|
export const CONTROL_NAMES = {
|
|
14
14
|
Sheet: ['handle'], CallSheet: ['handle'], StackSheet: ['backLabel'], Home: ['backLabel'], Walkthrough: ['backLabel', 'nextLabel'],
|
|
15
|
-
Calendar: ['prevLabel', 'nextLabel'], SheetStep: ['skipLabel'], Sticky: ['unreadLabel'], PostLetter: ['backLabel', 'pictureLabel', 'playLabel', 'pauseLabel', 'toLabel'],
|
|
15
|
+
Calendar: ['prevLabel', 'nextLabel'], SheetStep: ['skipLabel'], Sticky: ['unreadLabel'], StampOn: ['removeLabel'], PostLetter: ['backLabel', 'pictureLabel', 'playLabel', 'pauseLabel', 'toLabel'],
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const lineOf = (text, idx) => text.slice(0, idx).split('\n').length;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usableapp/cardds",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.86",
|
|
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.",
|