@usableapp/cardds 0.7.15 → 0.7.16
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 +26 -0
- package/README.md +6 -8
- package/css/actions.css +24 -1
- package/css/base.css +2 -2
- package/css/card.css +7 -13
- package/css/choice.css +2 -2
- package/css/journey.css +3 -3
- package/css/layover.css +5 -17
- package/css/media.css +4 -5
- package/css/numbers.css +4 -3
- package/css/people.css +3 -2
- package/css/sheet.css +4 -6
- package/css/stack.css +6 -10
- package/css/step.css +3 -5
- package/css/theme.template.css +2 -1
- package/css/tokens.css +4 -1
- package/dist/actions/Dropdown.js +4 -5
- package/dist/cardds.css +71 -70
- package/dist/cards/Card.d.ts +1 -1
- package/dist/choice/Calendar.js +3 -5
- package/dist/choice/Mood.d.ts +10 -5
- package/dist/choice/Mood.js +4 -2
- package/dist/forms/Pin.js +7 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lists/Row.d.ts +2 -1
- package/dist/lists/Row.js +2 -2
- package/dist/media/Postcard.js +3 -5
- package/dist/numbers/Picker.js +4 -5
- package/dist/numbers/Track.d.ts +4 -2
- package/dist/numbers/Track.js +3 -3
- package/dist/people/AvatarPick.d.ts +3 -1
- package/dist/people/AvatarPick.js +2 -2
- package/dist/scaffold/Centre.d.ts +1 -1
- package/dist/scaffold/Centre.js +1 -1
- package/dist/scaffold/Screen.js +3 -4
- package/dist/scaffold/TopBar.js +3 -5
- package/dist/sheets/Sheet.js +5 -7
- package/dist/sheets/SheetStack.js +4 -5
- package/dist/sheets/SheetSteps.js +5 -6
- package/dist/sheets/StackSheet.js +4 -5
- package/dist/state.d.ts +7 -0
- package/dist/state.js +17 -0
- package/dist/templates/CallSheet.js +3 -4
- package/dist/templates/Home.js +3 -4
- package/dist/templates/Splash.js +2 -1
- package/dist/templates/Walkthrough.js +4 -5
- package/dist/type/Icon.js +3 -5
- package/dist/type/textScale.d.ts +6 -0
- package/dist/type/textScale.js +12 -0
- package/package.json +1 -1
- package/dist/layover/Deck.d.ts +0 -9
- package/dist/layover/Deck.js +0 -10
package/dist/cardds.css
CHANGED
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
--btn-h: calc(var(--tap-base) * 13 / 12); /* 52 button height */
|
|
90
90
|
--btn-xl-h: calc(var(--tap-base) * 11 / 6); /* 88 the one giant CTA */
|
|
91
91
|
--chip-h: calc(var(--tap-base) * 19 / 24); /* 38 chip height */
|
|
92
|
-
--bar-h: calc(var(--tap-base) * 4 / 3); /* 64 float bar, day cell
|
|
92
|
+
--bar-h: calc(var(--tap-base) * 4 / 3); /* 64 float bar, day cell */
|
|
93
93
|
--header-h: calc(var(--tap-base) * 5 / 4); /* 60 top bar — .topbar and .appbar alike */
|
|
94
94
|
--card-pad-rest: var(--sp-5); /* 20 a card's padding before any variant — .card publishes it as --card-pad; the sheet's peek strip is built from it where no card is in scope */
|
|
95
95
|
--card-gap-rest: var(--sp-4); /* 16 …and its gap */
|
|
@@ -136,6 +136,9 @@
|
|
|
136
136
|
--motion-sheet: 0.35s;
|
|
137
137
|
--motion-slide: 0.22s; /* a centred card sliding out and the next in (card.css) — quick, a page turn (Lh, 2026-09-12) */
|
|
138
138
|
--motion-flip: 0.45s; /* a postcard turning over (media.css) — slow enough to read as a card in the hand */
|
|
139
|
+
--dim: 0.7; /* secondary words dimmed on a ground whose muted ink does not fit (the dark card --3, a drawer, a tile's sub line): ONE share for all */
|
|
140
|
+
--motion-press: 0.12s; /* a control giving under the finger and springing back (actions.css PRESS) */
|
|
141
|
+
--press-scale: 0.96; /* how far it gives — a share of its size */
|
|
139
142
|
|
|
140
143
|
/* ---- typography: one scale, rem so it follows the root ---- */
|
|
141
144
|
--font-ui: "PK Nonthaburi", -apple-system, system-ui, sans-serif; /* fonts.css ships it; the rest is the fallback while it loads */
|
|
@@ -439,7 +442,7 @@
|
|
|
439
442
|
the member wants to copy), and inputs are untouched. Native buttons and
|
|
440
443
|
links are matched by element; the rest by the class that makes them one */
|
|
441
444
|
button, [role="button"], [role="tab"], [role="option"], [role="menuitem"], a.btn, a.icon-btn, a.chip, label.chip, .chip--pick,
|
|
442
|
-
.segment__item, .day, .calendar__day, .mood__opt, .pin__cell, .slider, .picker__item, .float-bar__item, .menu__item, .sheet__handle, .
|
|
445
|
+
.segment__item, .day, .calendar__day, .mood__opt, .pin__cell, .slider, .picker__item, .float-bar__item, .menu__item, .sheet__handle, .track__step, .pager__at {
|
|
443
446
|
user-select: none;
|
|
444
447
|
-webkit-user-select: none;
|
|
445
448
|
-webkit-touch-callout: none;
|
|
@@ -532,7 +535,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
532
535
|
|
|
533
536
|
.t-muted { color: var(--on-surface-muted); }
|
|
534
537
|
.card .t-muted { color: var(--card-muted); } /* muted follows the card's ink, not the surface's */
|
|
535
|
-
.card--3 .t-muted { color: inherit; opacity:
|
|
538
|
+
.card--3 .t-muted { color: inherit; opacity: var(--dim); } /* card-muted is tuned for light cards; dim instead on the dark slot */
|
|
536
539
|
.t-caps { text-transform: uppercase; }
|
|
537
540
|
.grow { flex: 1; min-width: 0; } /* the child that takes the remaining row space */
|
|
538
541
|
/* read, not shown (Lh 2026-09-23): words a screen reader needs — a page's h1 the design leaves out — kept in the page, taking no room.
|
|
@@ -748,8 +751,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
748
751
|
one is there, and the screen's gap), or twice the room below it (the
|
|
749
752
|
bar's reserve) — whichever is less. --_lead is the title bar's height
|
|
750
753
|
only on a screen that HAS one: without a .topbar/.appbar before it the
|
|
751
|
-
block keeps that room (a
|
|
752
|
-
under it, need it on a short phone; Lh, 2026-09-11). */
|
|
754
|
+
block keeps that room (a card and its pager under it needs it on a short phone; Lh, 2026-09-11). */
|
|
753
755
|
--_lead: 0rem;
|
|
754
756
|
max-height: calc(2 * min(var(--screen-centre) - var(--_lead) - var(--screen-gap), 100% - var(--screen-centre) - var(--bar-reserve)));
|
|
755
757
|
overflow-y: auto;
|
|
@@ -760,9 +762,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
760
762
|
|
|
761
763
|
/* ---- centred block: the same place, for one thing to read that is not a
|
|
762
764
|
lone card — an empty state, a card with its quiet second button and the
|
|
763
|
-
pager under it
|
|
764
|
-
edge and keeps the screen's inset as padding,
|
|
765
|
-
overhang and clip at the screen's edges (correct — the fold visual), and
|
|
765
|
+
pager under it. The block runs edge to
|
|
766
|
+
edge and keeps the screen's inset as padding, and
|
|
766
767
|
its children stack at the screen's own rhythm (Lh, 2026-09-11):
|
|
767
768
|
<div class="screen screen--fill"><header class="topbar">…</header>
|
|
768
769
|
<div class="screen__centre"><article class="card">…</article><button class="btn btn--quiet btn--block">…</button></div>
|
|
@@ -835,14 +836,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
835
836
|
animation-timing-function: ease-out;
|
|
836
837
|
mix-blend-mode: normal;
|
|
837
838
|
}
|
|
838
|
-
:root[data-slide="next"]::view-transition-old(centre-card-out) { animation-name: centre-card-out-left; }
|
|
839
|
-
:root[data-slide="next"]::view-transition-new(centre-card-in) { animation-name: centre-card-in-right; }
|
|
840
|
-
:root[data-slide="back"]::view-transition-old(centre-card-out) { animation-name: centre-card-out-right; }
|
|
841
|
-
:root[data-slide="back"]::view-transition-new(centre-card-in) { animation-name: centre-card-in-left; }
|
|
842
|
-
:root[data-slide="next"]::view-transition-old(screen-out) { animation-name: centre-card-out-left; }
|
|
843
|
-
:root[data-slide="next"]::view-transition-new(screen-in) { animation-name: centre-card-in-right; }
|
|
844
|
-
:root[data-slide="back"]::view-transition-old(screen-out) { animation-name: centre-card-out-right; }
|
|
845
|
-
:root[data-slide="back"]::view-transition-new(screen-in) { animation-name: centre-card-in-left; }
|
|
839
|
+
:root[data-slide="next"]::view-transition-old(centre-card-out), :root[data-slide="next"]::view-transition-old(screen-out) { animation-name: centre-card-out-left; }
|
|
840
|
+
:root[data-slide="next"]::view-transition-new(centre-card-in), :root[data-slide="next"]::view-transition-new(screen-in) { animation-name: centre-card-in-right; }
|
|
841
|
+
:root[data-slide="back"]::view-transition-old(centre-card-out), :root[data-slide="back"]::view-transition-old(screen-out) { animation-name: centre-card-out-right; }
|
|
842
|
+
:root[data-slide="back"]::view-transition-new(centre-card-in), :root[data-slide="back"]::view-transition-new(screen-in) { animation-name: centre-card-in-left; }
|
|
846
843
|
@keyframes centre-card-out-left { to { translate: -100% 0; opacity: 0; } }
|
|
847
844
|
@keyframes centre-card-out-right { to { translate: 100% 0; opacity: 0; } }
|
|
848
845
|
@keyframes centre-card-in-right { from { translate: 100% 0; opacity: 0; } }
|
|
@@ -1149,9 +1146,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1149
1146
|
padding-left: 0;
|
|
1150
1147
|
transition: padding-left 0.35s ease;
|
|
1151
1148
|
}
|
|
1152
|
-
.sheet-stack--tap > .card.is-open > .card__head {
|
|
1153
|
-
padding-left: calc(var(--tap-sm) + var(--sp-3));
|
|
1154
|
-
}
|
|
1155
1149
|
.sheet-stack--tap .sheet-stack__back {
|
|
1156
1150
|
position: absolute;
|
|
1157
1151
|
left: 0;
|
|
@@ -1160,6 +1154,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
1160
1154
|
opacity: 0;
|
|
1161
1155
|
visibility: hidden;
|
|
1162
1156
|
transition: opacity 0.35s ease, visibility 0.35s;
|
|
1157
|
+
/* SMALL (see "the back button, SMALL" below) */
|
|
1158
|
+
width: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
|
|
1159
|
+
height: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
|
|
1160
|
+
min-width: 0;
|
|
1161
|
+
min-height: 0;
|
|
1162
|
+
padding: 0;
|
|
1163
1163
|
}
|
|
1164
1164
|
.sheet-stack--tap > .card.is-open .sheet-stack__back {
|
|
1165
1165
|
opacity: 1;
|
|
@@ -1219,13 +1219,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1219
1219
|
tappable"): a SMALL DISC — its ring kept, 32px instead of 44, the chevron in it at 20 — so the head gives it 32px + a gap
|
|
1220
1220
|
(was 56px). Its tap target stays 44px: an invisible ::after around the disc. ---- */
|
|
1221
1221
|
.sheet-stack--tap > .card.is-open > .card__head { padding-left: var(--stack-back-room, calc(var(--stack-back-size, calc(var(--tap-base) * 2 / 3)) + var(--sp-2))); }
|
|
1222
|
-
.sheet-stack--tap .sheet-stack__back {
|
|
1223
|
-
width: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
|
|
1224
|
-
height: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
|
|
1225
|
-
min-width: 0;
|
|
1226
|
-
min-height: 0;
|
|
1227
|
-
padding: 0;
|
|
1228
|
-
}
|
|
1229
1222
|
.sheet-stack--tap .sheet-stack__back > .icon { width: var(--stack-back-icon, calc(var(--tap-base) * 13 / 24)); height: var(--stack-back-icon, calc(var(--tap-base) * 13 / 24)); flex: none; } /* 26: the chevron fills the small disc (Lh 2026-09-23) */
|
|
1230
1223
|
.sheet-stack--tap .sheet-stack__back::after { content: ""; position: absolute; inset: calc((var(--stack-back-size, calc(var(--tap-base) * 2 / 3)) - var(--tap-sm)) / 2); }
|
|
1231
1224
|
|
|
@@ -1619,9 +1612,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1619
1612
|
bottom of the body, each its title line. Every step has a thin rule on top. A step opens and folds as a 1fr ↔ 0fr grid row, so the pile rises and
|
|
1620
1613
|
falls — no height is written. Nothing fades: done and next read at full ink. */
|
|
1621
1614
|
.sheet__body:has(> .sheet-steps) { display: flex; flex-direction: column; }
|
|
1622
|
-
.sheet-steps { flex-shrink: 0; min-height: calc(100% + var(--kb, 0rem)); /* the keyboard's height more: the pile of next steps stays under the keyboard, it never rises with it */ display: flex; flex-direction: column; touch-action: pan-y; }
|
|
1615
|
+
.sheet-steps { flex-shrink: 0; min-height: calc(100% + var(--kb, 0rem)); /* the keyboard's height more: the pile of next steps stays under the keyboard, it never rises with it */ display: flex; flex-direction: column; touch-action: pan-y; position: relative; }
|
|
1623
1616
|
.sheet-steps__holder { position: absolute; top: 0; left: 0; width: 1rem; height: 1rem; padding: 0; border: 0; opacity: 0; pointer-events: none; font-size: 1rem; } /* holds the cursor (and the phone's keyboard) while a step folds; font 1rem = the browser's own size, so iOS does not zoom to it */
|
|
1624
|
-
.sheet-steps { position: relative; }
|
|
1625
1617
|
.sheet-steps__track { list-style: none; margin: 0; padding: 0; flex: 1; display: flex; flex-direction: column; }
|
|
1626
1618
|
|
|
1627
1619
|
.sheet-step {
|
|
@@ -1639,23 +1631,22 @@ button { font: inherit; cursor: pointer; }
|
|
|
1639
1631
|
display: flex; align-items: center; gap: var(--sp-3); min-height: var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)); /* THIN: 32px, a summary line, not a button (Lh 2026-09-25) — the tap stays 44, see __go */
|
|
1640
1632
|
padding-inline: var(--sheet-step-bar-pad-x, var(--sp-3)); border-radius: var(--sheet-step-bar-radius, var(--r-chip));
|
|
1641
1633
|
background: var(--sheet-step-bar-bg, var(--ink-tint)); color: var(--sheet-step-bar-ink, inherit);
|
|
1642
|
-
border-top: var(--sheet-step-border-w,
|
|
1634
|
+
border-top: var(--sheet-step-border-w, 0rem) solid var(--sheet-step-border, currentColor); /* a rule instead of the bar: set this, the bar's bg transparent, radius 0 */
|
|
1643
1635
|
}
|
|
1644
|
-
.sheet-step__summary { color: inherit; opacity: 0.8; }
|
|
1645
1636
|
.sheet-step__title { font: var(--sheet-step-title-font, var(--type-label)); margin: 0; flex: 0 1 auto; min-width: 0; outline: none; /* the label size, not a body title: a pile of rows must stay small (Lh 2026-09-24) */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1646
1637
|
.sheet-step__num { /* `numbered`: the step's number in a disc BEFORE its title (Lh 2026-09-25) — a white disc on the bar, the number in the ink */
|
|
1647
1638
|
flex: none; display: grid; place-items: center; inline-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2)); block-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2));
|
|
1648
1639
|
border-radius: 50%; font: var(--sheet-step-num-font, var(--type-caption)); font-variant-numeric: tabular-nums;
|
|
1649
1640
|
background: var(--sheet-step-num-bg, var(--card-bg)); color: var(--sheet-step-num-ink, inherit);
|
|
1650
1641
|
}
|
|
1651
|
-
.sheet-step__summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1642
|
+
.sheet-step__summary { color: inherit; opacity: 0.8; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1652
1643
|
.sheet-step__check { margin-inline-start: auto; }
|
|
1653
1644
|
.sheet-step__body { min-height: 0; overflow: clip; overflow-clip-margin: var(--sp-1); }
|
|
1654
1645
|
.sheet-step__inner { display: grid; gap: var(--card-gap); padding-block: var(--card-gap); }
|
|
1655
1646
|
.sheet-step__inner > * { min-width: 0; }
|
|
1656
1647
|
.sheet-step__inner > .link { justify-self: center; } /* a link of its own row in a step (skip, "change number", "set PIN again"): centred under the step, as the skip (Lh 2026-09-25) */
|
|
1657
1648
|
.sheet-step:not(.sheet-step--now) > .sheet-step__body { visibility: hidden; transition: visibility 0s linear var(--motion-sheet); }
|
|
1658
|
-
.sheet-step__go { position: absolute; inset: min(
|
|
1649
|
+
.sheet-step__go { position: absolute; inset: min(0rem, (var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)) - var(--tap-sm)) / 2) 0 auto; min-height: max(100%, var(--tap-sm)); /* a thin bar still takes a 44px tap */ margin: 0; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
|
1659
1650
|
/* the last step's finish, when the app gives one: under the steps */
|
|
1660
1651
|
.sheet-steps__finish { display: grid; padding-top: var(--card-gap); }
|
|
1661
1652
|
@media (prefers-reduced-motion: reduce) { .sheet-step { transition: none; } }
|
|
@@ -1755,8 +1746,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1755
1746
|
.step:has(> .sheet--peek) > :is(.page, .pages) { padding-bottom: var(--sheet-peek); }
|
|
1756
1747
|
.step > :is(.page, .pages) {
|
|
1757
1748
|
flex: 0 1 auto;
|
|
1758
|
-
}
|
|
1759
|
-
.step > :is(.page, .pages) {
|
|
1760
1749
|
min-height: 0;
|
|
1761
1750
|
margin: var(--_lead, 0) var(--screen-pad) 0; /* the screen's gap, moved in here from above the step; the inset, taken back from the bleed */
|
|
1762
1751
|
overflow-y: auto;
|
|
@@ -1768,12 +1757,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
1768
1757
|
padding above AND below — so the words begin near the middle and, when a big text size runs them long, scroll up clear.
|
|
1769
1758
|
The walk shows no scrollbar. */
|
|
1770
1759
|
.step > .pages { margin: 0; scrollbar-width: none; touch-action: pan-y; } /* pan-y: the walk scrolls itself up and down; a sideways swipe is the Walkthrough's (it turns the page) */ /* the scrolling box is the whole content: from the step's top edge, edge to edge — no margin (Lh 2026-09-23) */
|
|
1771
|
-
.step > .pages > .page { padding-inline: var(--screen-pad); } /* the words keep the inset INSIDE it */
|
|
1772
|
-
.step > .pages > .page { margin-top: var(--_lead, 0rem); } /* the lead is each PAGE's margin (15% with no top, the screen's gap under a bar): the words start there and scroll up to the box's edge */
|
|
1773
|
-
.step > .pages::-webkit-scrollbar { display: none; }
|
|
1774
1760
|
.step > .pages > .page {
|
|
1761
|
+
padding-inline: var(--screen-pad); /* the words keep the inset INSIDE it */
|
|
1762
|
+
margin-top: var(--_lead, 0rem); /* the lead is each PAGE's margin (15% with no top, the screen's gap under a bar): the words start there and scroll up to the box's edge */
|
|
1775
1763
|
padding-bottom: var(--bottom-h, 0rem); /* exactly the bottom layer (measured by Screen): a page scrolls ONLY when the layer would cover its words, and then just far enough to clear them — at 100% nothing scrolls (Lh 2026-09-23) */
|
|
1776
1764
|
}
|
|
1765
|
+
.step > .pages::-webkit-scrollbar { display: none; }
|
|
1777
1766
|
|
|
1778
1767
|
/* ============================================================
|
|
1779
1768
|
TOP + CONTENT in one flex column; BOTTOM is a LAYER over them (Lh 2026-09-23, second call — replaces "the bar is a row"):
|
|
@@ -1944,7 +1933,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1944
1933
|
.route__point--end .route__code::after { content: none; }
|
|
1945
1934
|
.route__time { font: var(--route-time-font, var(--type-body)); font-variant-numeric: tabular-nums; }
|
|
1946
1935
|
.route__place { font: var(--route-place-font, var(--type-caption)); color: var(--route-place-ink, var(--card-muted)); }
|
|
1947
|
-
.card--3 .route__place { color: inherit; opacity:
|
|
1936
|
+
.card--3 .route__place { color: inherit; opacity: var(--dim); }
|
|
1948
1937
|
|
|
1949
1938
|
.route__via { grid-column: 2; grid-row: 2; align-self: center; display: flex; align-items: center; opacity: 0.65; font-size: var(--route-via-font-size, var(--fs-body)); }
|
|
1950
1939
|
|
|
@@ -1958,7 +1947,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1958
1947
|
background: var(--tile-badge-bg, var(--card-3-bg));
|
|
1959
1948
|
color: var(--tile-badge-ink, var(--card-3-ink));
|
|
1960
1949
|
}
|
|
1961
|
-
.tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity:
|
|
1950
|
+
.tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity: var(--dim); }
|
|
1962
1951
|
.tile-badge__value { font: 800 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
|
|
1963
1952
|
|
|
1964
1953
|
/* plain: no fill — a date box beside a title (Jun / 18) */
|
|
@@ -1976,7 +1965,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1976
1965
|
font: var(--note-row-font, var(--type-caption));
|
|
1977
1966
|
color: var(--note-row-ink, var(--card-muted));
|
|
1978
1967
|
}
|
|
1979
|
-
.card--3 .note-row { color: inherit; opacity:
|
|
1968
|
+
.card--3 .note-row { color: inherit; opacity: var(--dim); }
|
|
1980
1969
|
|
|
1981
1970
|
/* ---- css/actions.css — topbar, chips, buttons, CTA pack ---- */
|
|
1982
1971
|
/* ============================================================
|
|
@@ -2053,7 +2042,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2053
2042
|
border-radius: var(--appbar-who-radius, var(--r-chip));
|
|
2054
2043
|
-webkit-tap-highlight-color: transparent;
|
|
2055
2044
|
}
|
|
2056
|
-
.appbar__who:focus-visible { outline: var(--focus); outline-offset:
|
|
2045
|
+
.appbar__who:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
2057
2046
|
.appbar__title, .appbar__sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.35; } /* clipped lines: room for Thai marks */
|
|
2058
2047
|
.appbar__title { font: var(--type-label); }
|
|
2059
2048
|
.appbar__sub { font: var(--type-caption); color: var(--on-surface-muted); }
|
|
@@ -2315,6 +2304,29 @@ button { font: inherit; cursor: pointer; }
|
|
|
2315
2304
|
.action-note__words { display: block; } /* ONE run of words: an icon inside sits in the line, the spaces around it kept */
|
|
2316
2305
|
.action-note__words > .icon { display: inline-block; vertical-align: -0.2em; width: 1.25em; height: 1.25em; margin-inline: 0.15em; color: var(--action-note-icon-ink, inherit); } /* a project may colour the icon apart from the words — e.g. the system blue of the control it points at (Lh 2026-09-24) */
|
|
2317
2306
|
|
|
2307
|
+
/* PRESS (Lh 2026-09-25): every CONTROL answers a tap — it gives under the finger and springs back, its choice fill fades in;
|
|
2308
|
+
INFORMATION never does (no pointer, no press). One list, zero specificity: a component's own rule always wins. */
|
|
2309
|
+
:where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item,
|
|
2310
|
+
.calendar__day, .float-bar__item, .avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn, .appbar__who, .menu__item) {
|
|
2311
|
+
cursor: pointer;
|
|
2312
|
+
touch-action: manipulation;
|
|
2313
|
+
transition: scale var(--motion-press) ease-out;
|
|
2314
|
+
}
|
|
2315
|
+
/* a CHOICE's fill fades in as it is picked (a button's colour never changes on a tap, so it does not fade) */
|
|
2316
|
+
:where(.chip--pick, .tile--pick, .day, .segment__item, .calendar__day, .float-bar__item, .menu__item) {
|
|
2317
|
+
transition: scale var(--motion-press) ease-out, background-color var(--motion-press) ease-out, color var(--motion-press) ease-out, border-color var(--motion-press) ease-out;
|
|
2318
|
+
}
|
|
2319
|
+
:where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item,
|
|
2320
|
+
.calendar__day, .float-bar__item, .avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn, .appbar__who, .menu__item):active:not(:disabled, [aria-disabled="true"]) {
|
|
2321
|
+
scale: var(--press-scale);
|
|
2322
|
+
}
|
|
2323
|
+
:where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, a.tile, button.tile, .day, .segment__item, .calendar__day, .float-bar__item,
|
|
2324
|
+
.avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn):focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
2325
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2326
|
+
:where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item,
|
|
2327
|
+
.calendar__day, .float-bar__item, .avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn, .appbar__who, .menu__item):active { scale: none; }
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2318
2330
|
/* ---- css/numbers.css — ring, track, band, dot grid, picker, badge, bars ---- */
|
|
2319
2331
|
/* ============================================================
|
|
2320
2332
|
cardds/numbers.css — big numbers inside cards:
|
|
@@ -2351,7 +2363,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2351
2363
|
}
|
|
2352
2364
|
.ring__value { font: var(--ring-value-font, var(--type-stat)); letter-spacing: var(--track-display); }
|
|
2353
2365
|
.ring__label { font: var(--ring-label-font, var(--type-caption)); color: var(--ring-label-ink, var(--card-muted)); }
|
|
2354
|
-
.card--3 .ring__label { color: inherit; opacity:
|
|
2366
|
+
.card--3 .ring__label { color: inherit; opacity: var(--dim); }
|
|
2355
2367
|
.ring--sm { --_size: var(--avatar-xl); --_w: var(--sp-3); }
|
|
2356
2368
|
.ring--sm .ring__value { font: 800 var(--fs-2xl)/1 var(--font-display); }
|
|
2357
2369
|
.ring--xs { --_size: var(--tap-sm); --_w: var(--dot-xs); } /* inline counter (step 3 of 5) */
|
|
@@ -2404,6 +2416,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2404
2416
|
background: color-mix(in srgb, var(--_ink) 30%, transparent);
|
|
2405
2417
|
color: var(--_ink);
|
|
2406
2418
|
}
|
|
2419
|
+
.track__step > .track__dot { display: grid; place-items: center; padding: 0; font: var(--track-dot-font, var(--type-title)); font-variant-numeric: tabular-nums; } /* a number in the round (TrackStep num) */
|
|
2407
2420
|
.track__step.is-done > .icon,
|
|
2408
2421
|
.track__step.is-done > .track__dot { background: var(--accent); color: var(--on-accent); }
|
|
2409
2422
|
|
|
@@ -2425,7 +2438,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2425
2438
|
}
|
|
2426
2439
|
.band__num small { font-size: 0.5em; font-weight: 700; margin-right: 0.1em; } /* unit or % */
|
|
2427
2440
|
.band__foot { grid-area: foot; font: var(--band-foot-font, var(--type-caption)); color: var(--band-foot-ink, var(--card-muted)); }
|
|
2428
|
-
.card--3 .band__foot { color: inherit; opacity:
|
|
2441
|
+
.card--3 .band__foot { color: inherit; opacity: var(--dim); }
|
|
2429
2442
|
/* band stack: ONE card boundary; bands are frameless inside it, a hairline
|
|
2430
2443
|
between each. Bands may still carry slot tints (card--2 / card--3). */
|
|
2431
2444
|
.band-stack {
|
|
@@ -2524,7 +2537,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2524
2537
|
}
|
|
2525
2538
|
.picker::after { left: auto; right: calc(100% / 3); }
|
|
2526
2539
|
.picker__unit { font: var(--picker-unit-font, var(--type-caption)); color: var(--picker-unit-ink, var(--card-muted)); }
|
|
2527
|
-
.card--3 .picker__unit { color: inherit; opacity:
|
|
2540
|
+
.card--3 .picker__unit { color: inherit; opacity: var(--dim); }
|
|
2528
2541
|
/* the scrolled-to item lights up by position — no JS */
|
|
2529
2542
|
@supports (animation-timeline: view(x)) {
|
|
2530
2543
|
.picker__item { opacity: 0.3; animation: picker-focus linear both; animation-timeline: view(x); }
|
|
@@ -2625,7 +2638,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2625
2638
|
.avatar--xl { --_size: var(--avatar-xl); } /* sheet head icon ring */
|
|
2626
2639
|
/* outline avatar: no fill, an edge — icons in a ring, "add" slots */
|
|
2627
2640
|
.avatar--outline { background: transparent; color: inherit; border: var(--border-w) solid var(--avatar-outline-border, var(--outline)); }
|
|
2628
|
-
.avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline));
|
|
2641
|
+
.avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline)); }
|
|
2629
2642
|
/* accent avatar: the "+7" tail, the selected person */
|
|
2630
2643
|
.avatar--on { background: var(--avatar-on-bg, var(--accent)); color: var(--avatar-on-ink, var(--on-accent)); }
|
|
2631
2644
|
/* halo: faint concentric rings behind a hero avatar (decorative) */
|
|
@@ -2670,7 +2683,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2670
2683
|
}
|
|
2671
2684
|
.avatar-pick__name { font: var(--avatar-pick-name-font, var(--type-label)); font-size: var(--avatar-pick-name-font-size, var(--fs-sm)); }
|
|
2672
2685
|
.avatar-pick__role { font: var(--avatar-pick-role-font, var(--type-caption)); color: var(--avatar-pick-role-ink, var(--card-muted)); margin-top: calc(-1 * var(--sp-1)); }
|
|
2673
|
-
.
|
|
2686
|
+
.avatar-pick__item[aria-pressed="true"] > .avatar { background: var(--avatar-pick-on-bg, var(--accent)); color: var(--avatar-pick-on-ink, var(--on-accent)); border-color: transparent; } /* the picked person */
|
|
2687
|
+
.card--3 .avatar-pick__role { color: inherit; opacity: var(--dim); }
|
|
2674
2688
|
|
|
2675
2689
|
/* ---- css/choice.css — check, toggle, toggle chip, chip grid, days, calendar, mood, pin, composer ---- */
|
|
2676
2690
|
/* ============================================================
|
|
@@ -2814,7 +2828,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2814
2828
|
.calendar__head { display: flex; align-items: center; justify-content: space-between; gap: var(--calendar-head-gap, var(--sp-2)); }
|
|
2815
2829
|
.calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--calendar-grid-gap, var(--sp-1)); justify-items: center; }
|
|
2816
2830
|
.calendar__wd { font: var(--calendar-wd-font, var(--type-caption)); color: var(--calendar-wd-ink, var(--card-muted)); padding-block: var(--calendar-wd-pad-y, var(--sp-2)); }
|
|
2817
|
-
.card--3 .calendar__wd { color: inherit; opacity:
|
|
2831
|
+
.card--3 .calendar__wd { color: inherit; opacity: var(--dim); }
|
|
2818
2832
|
.calendar__day {
|
|
2819
2833
|
width: 100%;
|
|
2820
2834
|
max-width: var(--calendar-day-max-w, var(--tap));
|
|
@@ -2917,7 +2931,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2917
2931
|
.composer__input::placeholder { color: inherit; opacity: 0.4; }
|
|
2918
2932
|
.composer__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--composer-bar-gap, var(--sp-3)); }
|
|
2919
2933
|
.composer__hint { font: var(--composer-hint-font, var(--type-caption)); color: var(--composer-hint-ink, var(--card-muted)); }
|
|
2920
|
-
.card--3 .composer__hint { color: inherit; opacity:
|
|
2934
|
+
.card--3 .composer__hint { color: inherit; opacity: var(--dim); }
|
|
2921
2935
|
|
|
2922
2936
|
/* ---- slider: one value along a track, a thumb-size knob ----
|
|
2923
2937
|
<label class="slider"><span class="slider__label">ขนาดตัวหนังสือ</span><input class="slider__range" type="range" min="100" max="160" step="15" aria-label="ขนาดตัวหนังสือ"></label>
|
|
@@ -3260,7 +3274,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3260
3274
|
.quote__text { margin: 0; font: var(--quote-text-font, var(--type-h1)); letter-spacing: var(--track-h1); text-wrap: balance; }
|
|
3261
3275
|
.quote--sm .quote__text { font: var(--type-h2); letter-spacing: var(--track-h2); }
|
|
3262
3276
|
.quote__by { font: var(--quote-by-font, var(--type-caption)); color: var(--quote-by-ink, var(--card-muted)); }
|
|
3263
|
-
.card--3 .quote__by { color: inherit; opacity:
|
|
3277
|
+
.card--3 .quote__by { color: inherit; opacity: var(--dim); }
|
|
3264
3278
|
|
|
3265
3279
|
/* ---- tile grid: big square tiles — icon top, label bottom ----
|
|
3266
3280
|
<div class="tile-grid"><button class="tile"><svg class="icon"/><span class="tile__label">Light</span></button>
|
|
@@ -3286,11 +3300,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
3286
3300
|
font: inherit;
|
|
3287
3301
|
text-align: left;
|
|
3288
3302
|
position: relative;
|
|
3289
|
-
cursor: pointer;
|
|
3290
3303
|
}
|
|
3291
3304
|
.tile__label { font: var(--tile-label-font, var(--type-title)); }
|
|
3292
3305
|
.tile__value { font: var(--tile-value-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
|
|
3293
|
-
.tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity:
|
|
3306
|
+
.tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity: var(--dim); }
|
|
3294
3307
|
.tile__text { display: grid; gap: var(--tile-text-gap, var(--sp-0)); }
|
|
3295
3308
|
.tile--on { background: var(--tile-on-bg, var(--accent)); color: var(--tile-on-ink, var(--on-accent)); border-color: transparent; }
|
|
3296
3309
|
.tile--pick > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
@@ -3298,7 +3311,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3298
3311
|
.tile--pick:has(:focus-visible) { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
3299
3312
|
.tile--wide { grid-column: 1 / -1; }
|
|
3300
3313
|
/* stat: READ, not pressed — no frame of its own inside a card (a frame says "tap me"): icon · value · words on the card's ground */
|
|
3301
|
-
.tile--stat { border-color: transparent; background: none; padding-inline: 0;
|
|
3314
|
+
.tile--stat { border-color: transparent; background: none; padding-inline: 0; }
|
|
3302
3315
|
.tile--stat { min-height: var(--tile-stat-min-h, var(--avatar-xl)); gap: var(--tile-stat-gap, var(--sp-3)); grid-template-columns: auto 1fr; align-items: center; }
|
|
3303
3316
|
/* upload: dashed, centred plus + label */
|
|
3304
3317
|
.tile--upload {
|
|
@@ -3322,7 +3335,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3322
3335
|
the corner read as a notch with a line through it rather than a lifted flap.
|
|
3323
3336
|
The COLOUR goes transparent rather than the border going to 0, so the box
|
|
3324
3337
|
keeps its geometry and a fold card still measures the same as the cards
|
|
3325
|
-
beside it
|
|
3338
|
+
beside it. */
|
|
3326
3339
|
.card--fold {
|
|
3327
3340
|
border-color: transparent;
|
|
3328
3341
|
border-bottom-right-radius: 0;
|
|
@@ -3499,7 +3512,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3499
3512
|
/* ============================================================
|
|
3500
3513
|
cardds/layover.css — cards that lay OVER other content:
|
|
3501
3514
|
Modal (ask) · popover menu · expanding banner · float bar ·
|
|
3502
|
-
callout
|
|
3515
|
+
callout. The sheet (peek / half / full / page)
|
|
3503
3516
|
lives in sheet.css; these compose with .sheet-stage.
|
|
3504
3517
|
============================================================ */
|
|
3505
3518
|
|
|
@@ -3570,7 +3583,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3570
3583
|
--accent: var(--card-3-ink); /* the one solid button inverts on the dark drawer */
|
|
3571
3584
|
--on-accent: var(--card-3-bg);
|
|
3572
3585
|
}
|
|
3573
|
-
.modal__drawer .t-caption { opacity:
|
|
3586
|
+
.modal__drawer .t-caption { opacity: var(--dim); }
|
|
3574
3587
|
.modal__drawer .icon-btn { background: transparent; color: inherit; border-color: var(--ink-rail); }
|
|
3575
3588
|
.modal__drawer .icon-btn--invert { background: var(--accent); color: var(--on-accent); border-color: transparent; width: var(--tap-lg); height: var(--tap-lg); }
|
|
3576
3589
|
@media (prefers-reduced-motion: no-preference) {
|
|
@@ -3642,7 +3655,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3642
3655
|
cursor: pointer;
|
|
3643
3656
|
}
|
|
3644
3657
|
.menu__item:hover, .menu__item:focus-visible { background: var(--ink-tint); outline: none; }
|
|
3645
|
-
.menu__item[aria-current]
|
|
3658
|
+
.menu__item[aria-current] { background: var(--accent); color: var(--on-accent); }
|
|
3646
3659
|
@supports (anchor-name: --a) {
|
|
3647
3660
|
.menu[popover] {
|
|
3648
3661
|
position-area: block-end span-inline-end;
|
|
@@ -3753,8 +3766,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3753
3766
|
}
|
|
3754
3767
|
/* aria-current="false" is the correct way to mark the OTHER tabs, so the
|
|
3755
3768
|
bare attribute selector lights every one of them up */
|
|
3756
|
-
.float-bar__item[aria-current]:not([aria-current="false"])
|
|
3757
|
-
.float-bar__item.is-on { background: var(--accent); color: var(--on-accent); opacity: 1; }
|
|
3769
|
+
.float-bar__item[aria-current]:not([aria-current="false"]) { background: var(--accent); color: var(--on-accent); opacity: 1; }
|
|
3758
3770
|
.float-bar > .icon-btn { width: var(--bar-h); height: var(--bar-h); box-shadow: var(--shadow-float); }
|
|
3759
3771
|
|
|
3760
3772
|
/* ---- callout: a small card with a tail — over a map, a media, a mascot ----
|
|
@@ -3788,18 +3800,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3788
3800
|
border-left: 0;
|
|
3789
3801
|
}
|
|
3790
3802
|
.callout__title { font: var(--callout-title-font, var(--type-title)); }
|
|
3791
|
-
.callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity:
|
|
3792
|
-
/* ---- deck: swipe cards; the last child is on top ----
|
|
3793
|
-
<div class="deck"><article class="card">…</article><article class="card">…</article><article class="card card--fold">front</article></div>
|
|
3794
|
-
<div class="deck__actions"><button class="icon-btn">×</button><button class="icon-btn icon-btn--invert">✓</button></div> */
|
|
3795
|
-
.deck { display: grid; place-items: center; padding: var(--deck-pad-top, var(--sp-4)) var(--deck-pad-x, var(--sp-4)) var(--deck-pad-bottom, var(--sp-6)); }
|
|
3796
|
-
.deck > .card { grid-area: 1 / 1; width: min(100%, 20rem); min-height: 12.5rem; transition: rotate 0.3s ease, translate 0.3s ease; }
|
|
3797
|
-
.deck > .card:nth-last-child(2) { rotate: -4deg; translate: calc(-1 * var(--sp-2)) var(--sp-2); opacity: 0.85; }
|
|
3798
|
-
.deck > .card:nth-last-child(3) { rotate: 5deg; translate: var(--sp-3) var(--sp-4); opacity: 0.6; }
|
|
3799
|
-
.deck > .card:nth-last-child(n + 4) { display: none; }
|
|
3800
|
-
.deck__actions { display: flex; justify-content: center; gap: var(--deck-actions-gap, var(--sp-8)); }
|
|
3801
|
-
.deck__actions > .icon-btn { width: var(--bar-h); height: var(--bar-h); }
|
|
3802
|
-
|
|
3803
|
+
.callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity: var(--dim); }
|
|
3803
3804
|
/* ---- splash: the app's mark at the human centre while it boots (Lh 2026-09-24) ----
|
|
3804
3805
|
<section class="screen screen--fill splash"><div class="splash__group"><div class="splash__mark">{the app's lottie/img}</div>
|
|
3805
3806
|
<p class="splash__text">…</p></div></section>
|
package/dist/cards/Card.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface CardProps extends ComponentProps<'article'> {
|
|
|
8
8
|
row?: boolean;
|
|
9
9
|
/** the image IS the card: a Placeholder or img first, text over a scrim */
|
|
10
10
|
cover?: boolean;
|
|
11
|
-
/** a sticky-note corner, no border (
|
|
11
|
+
/** a sticky-note corner, no border (quotes) */
|
|
12
12
|
fold?: boolean;
|
|
13
13
|
/** one card at the human centre of a `Screen fill` (the introduction screens) */
|
|
14
14
|
centre?: boolean;
|
package/dist/choice/Calendar.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useControlled } from '../state.js';
|
|
3
3
|
import { cx } from '../cx.js';
|
|
4
4
|
import { IconBtn } from '../actions/IconBtn.js';
|
|
5
5
|
/** Calendar — a month grid of 44px round cells: one accent day, optional marks; prev/next in the head. It shows ONE month and knows no dates:
|
|
6
6
|
* the app owns which month (`title`, `days`, `start` + `onPrev` / `onNext`) and which day (`selected` + `onSelect`, or `defaultSelected`). */
|
|
7
7
|
export function Calendar({ title, days = 30, start = 1, selected, defaultSelected, onSelect, onPrev, onNext, prevLabel = 'Previous', nextLabel = 'Next', marks = [], marks2 = [], disabled = [], weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], nav, className, ...rest }) {
|
|
8
|
-
const [
|
|
9
|
-
const
|
|
10
|
-
const pick = (d) => { if (selected === undefined)
|
|
11
|
-
setInner(d); onSelect?.(d); };
|
|
8
|
+
const [day, setOwn] = useControlled(selected, defaultSelected);
|
|
9
|
+
const pick = (d) => { setOwn(d); onSelect?.(d); };
|
|
12
10
|
return (_jsxs("div", { className: cx('calendar', className), ...rest, children: [_jsxs("div", { className: "calendar__head", children: [_jsx("span", { className: "t-title", children: title }), nav ?? (_jsxs("span", { className: "icon-row", children: [_jsx(IconBtn, { sm: true, icon: "chevron-left", label: prevLabel, onClick: onPrev }), _jsx(IconBtn, { sm: true, icon: "chevron-right", label: nextLabel, onClick: onNext })] }))] }), _jsxs("div", { className: "calendar__grid", children: [weekdays.map((d) => _jsx("span", { className: "calendar__wd", children: d }, d)), Array.from({ length: days }, (_, i) => i + 1).map((d) => (_jsx("button", { type: "button", className: cx('calendar__day', marks.includes(d) && 'calendar__day--mark', marks2.includes(d) && 'calendar__day--mark2'), "aria-selected": d === day ? 'true' : undefined, onClick: () => pick(d), disabled: disabled.includes(d), style: d === 1 ? { '--start': start } : undefined, children: d }, d)))] })] }));
|
|
13
11
|
}
|
package/dist/choice/Mood.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
|
-
|
|
2
|
+
type MoodValue = 1 | 2 | 3 | 4 | 5;
|
|
3
|
+
export interface MoodProps extends Omit<ComponentProps<'fieldset'>, 'onChange' | 'defaultValue'> {
|
|
3
4
|
/** the radio group name */
|
|
4
5
|
name?: string;
|
|
5
|
-
/** the picked dot, 1 (worst) to 5 (best) */
|
|
6
|
-
value?:
|
|
6
|
+
/** CONTROLLED: the picked dot, 1 (worst) to 5 (best) — with `onChange`; or `defaultValue` */
|
|
7
|
+
value?: MoodValue;
|
|
8
|
+
/** UNCONTROLLED: the dot picked on arrival; the Mood then keeps its own */
|
|
9
|
+
defaultValue?: MoodValue;
|
|
7
10
|
label?: string;
|
|
8
|
-
|
|
11
|
+
/** the dot the member tapped */
|
|
12
|
+
onChange?: (value: MoodValue) => void;
|
|
9
13
|
}
|
|
10
14
|
/** Mood — five dots in a pill from bad to good; the picked one grows. */
|
|
11
|
-
export declare function Mood({ name, value, label, onChange, className, ...rest }: MoodProps): import("react").JSX.Element;
|
|
15
|
+
export declare function Mood({ name, value, defaultValue, label, onChange, className, ...rest }: MoodProps): import("react").JSX.Element;
|
|
16
|
+
export {};
|
package/dist/choice/Mood.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../cx.js';
|
|
3
|
+
import { useControlled } from '../state.js';
|
|
3
4
|
/** Mood — five dots in a pill from bad to good; the picked one grows. */
|
|
4
|
-
export function Mood({ name = 'mood', value, label = 'Mood', onChange, className, ...rest }) {
|
|
5
|
-
|
|
5
|
+
export function Mood({ name = 'mood', value, defaultValue, label = 'Mood', onChange, className, ...rest }) {
|
|
6
|
+
const [picked, setOwn] = useControlled(value, defaultValue);
|
|
7
|
+
return (_jsx("fieldset", { className: cx('mood', className), "aria-label": label, ...rest, children: [1, 2, 3, 4, 5].map((n) => (_jsxs("label", { className: "mood__opt", children: [_jsx("input", { type: "radio", name: name, value: n, checked: n === picked, onChange: () => { setOwn(n); onChange?.(n); } }), _jsx("span", { className: "mood__dot" })] }, n))) }));
|
|
6
8
|
}
|
package/dist/forms/Pin.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useControlled } from '../state.js';
|
|
3
4
|
import { cx } from '../cx.js';
|
|
4
5
|
import { useCheck } from './check.js';
|
|
5
6
|
const RESET_MS = 900; // a code that does not match: red + shake this long, then the cells empty for a new try
|
|
@@ -11,12 +12,12 @@ const RESET_MS = 900; // a code that does not match: red + shake this long, then
|
|
|
11
12
|
*/
|
|
12
13
|
export function Pin({ length = 6, value, defaultValue = '', label = 'Digit', onChange, autoFocus, error, match, formatError, secret, className, onAnimationEnd, ...rest }) {
|
|
13
14
|
const ref = useRef(null);
|
|
14
|
-
const [
|
|
15
|
-
const code =
|
|
15
|
+
const [typed, setOwn] = useControlled(value, defaultValue);
|
|
16
|
+
const code = typed.replace(/\D/g, '').slice(0, length);
|
|
16
17
|
const cell = (i) => ref.current?.querySelectorAll('.pin__cell')[i];
|
|
17
18
|
/* a miss — a match that failed, or the app's error: shake, red, then the cells empty for a new try on the same step */
|
|
18
|
-
const latest = useRef({
|
|
19
|
-
latest.current = {
|
|
19
|
+
const latest = useRef({ setOwn, onChange, error });
|
|
20
|
+
latest.current = { setOwn, onChange, error };
|
|
20
21
|
const miss = () => {
|
|
21
22
|
restart.current = true;
|
|
22
23
|
setShake(true);
|
|
@@ -25,8 +26,7 @@ export function Pin({ length = 6, value, defaultValue = '', label = 'Digit', onC
|
|
|
25
26
|
setBad(false);
|
|
26
27
|
setCleared(latest.current.error);
|
|
27
28
|
restart.current = false;
|
|
28
|
-
|
|
29
|
-
setInner('');
|
|
29
|
+
latest.current.setOwn('');
|
|
30
30
|
latest.current.onChange?.('', false);
|
|
31
31
|
cell(0)?.focus();
|
|
32
32
|
}, RESET_MS);
|
|
@@ -46,8 +46,7 @@ export function Pin({ length = 6, value, defaultValue = '', label = 'Digit', onC
|
|
|
46
46
|
said.current = true;
|
|
47
47
|
miss();
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
setInner(next);
|
|
49
|
+
setOwn(next);
|
|
51
50
|
if (next !== code)
|
|
52
51
|
onChange?.(next, next.length === length);
|
|
53
52
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { cx } from './cx.js';
|
|
|
4
4
|
export * from './type/Icon.js';
|
|
5
5
|
export { ICON_NAMES } from './type/icons.js';
|
|
6
6
|
export * from './type/Text.js';
|
|
7
|
+
export { setTextScale } from './type/textScale.js';
|
|
7
8
|
export * from './scaffold/Screen.js';
|
|
8
9
|
export * from './scaffold/AppBar.js';
|
|
9
10
|
export * from './scaffold/TopBar.js';
|
|
@@ -72,7 +73,6 @@ export * from './media/Tile.js';
|
|
|
72
73
|
export * from './media/Mosaic.js';
|
|
73
74
|
export * from './layover/Banner.js';
|
|
74
75
|
export * from './layover/Callout.js';
|
|
75
|
-
export * from './layover/Deck.js';
|
|
76
76
|
export * from './journey/Route.js';
|
|
77
77
|
export * from './journey/TileBadge.js';
|
|
78
78
|
export * from './journey/NoteRow.js';
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export { cx } from './cx.js';
|
|
|
8
8
|
export * from './type/Icon.js';
|
|
9
9
|
export { ICON_NAMES } from './type/icons.js';
|
|
10
10
|
export * from './type/Text.js';
|
|
11
|
+
export { setTextScale } from './type/textScale.js';
|
|
11
12
|
export * from './scaffold/Screen.js';
|
|
12
13
|
export * from './scaffold/AppBar.js';
|
|
13
14
|
export * from './scaffold/TopBar.js';
|
|
@@ -76,7 +77,6 @@ export * from './media/Tile.js';
|
|
|
76
77
|
export * from './media/Mosaic.js';
|
|
77
78
|
export * from './layover/Banner.js';
|
|
78
79
|
export * from './layover/Callout.js';
|
|
79
|
-
export * from './layover/Deck.js';
|
|
80
80
|
export * from './journey/Route.js';
|
|
81
81
|
export * from './journey/TileBadge.js';
|
|
82
82
|
export * from './journey/NoteRow.js';
|
package/dist/lists/Row.d.ts
CHANGED
|
@@ -41,7 +41,8 @@ export interface RowProps extends Omit<ComponentProps<'div'>, 'title'> {
|
|
|
41
41
|
*/
|
|
42
42
|
export declare function Row({ lead, leadIcon, title, meta, body, end, endValue, endCaption, lg, done, as, href, more, open, defaultOpen, onOpenChange, className, children, ...rest }: RowProps): import("react").JSX.Element;
|
|
43
43
|
export interface RowsProps extends ComponentProps<'div'> {
|
|
44
|
-
/** the group's name, above its rows (Today · Tomorrow · Later): a long list is GROUPED, not spaced out — several Rows one after another
|
|
44
|
+
/** the group's name, above its rows (Today · Tomorrow · Later): a long list is GROUPED, not spaced out — several Rows one after another.
|
|
45
|
+
* Words = the small label; an ELEMENT (`<Text level="title" as="h3">`) is used as the heading itself — the group is labelled by it. */
|
|
45
46
|
heading?: ReactNode;
|
|
46
47
|
}
|
|
47
48
|
/** Rows — a list of Rows separated by hairlines; `heading` names the group (a `role="group"` labelled by it). */
|
package/dist/lists/Row.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { createElement, useId } from 'react';
|
|
2
|
+
import { cloneElement, createElement, isValidElement, useId } from 'react';
|
|
3
3
|
import { cx } from '../cx.js';
|
|
4
4
|
import { Icon } from '../type/Icon.js';
|
|
5
5
|
/**
|
|
@@ -22,5 +22,5 @@ export function Row({ lead, leadIcon, title, meta, body, end, endValue, endCapti
|
|
|
22
22
|
export function Rows({ heading, className, children, ...rest }) {
|
|
23
23
|
const id = useId();
|
|
24
24
|
const named = heading != null;
|
|
25
|
-
return (_jsxs("div", { className: cx('rows', className), role: named ? 'group' : undefined, "aria-labelledby": named ? id : undefined, ...rest, children: [named && _jsx("span", { className: "rows__head", id: id, children: heading }), children] }));
|
|
25
|
+
return (_jsxs("div", { className: cx('rows', className), role: named ? 'group' : undefined, "aria-labelledby": named ? id : undefined, ...rest, children: [named && (isValidElement(heading) ? cloneElement(heading, { id }) : _jsx("span", { className: "rows__head", id: id, children: heading })), children] }));
|
|
26
26
|
}
|
package/dist/media/Postcard.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useControlled } from '../state.js';
|
|
3
3
|
import { cx } from '../cx.js';
|
|
4
4
|
import { IconBtn } from '../actions/IconBtn.js';
|
|
5
5
|
/**
|
|
@@ -9,10 +9,8 @@ import { IconBtn } from '../actions/IconBtn.js';
|
|
|
9
9
|
* Who else liked it, a voice note, a reply go OUTSIDE it — a Row under it (`Row more` expands the list).
|
|
10
10
|
*/
|
|
11
11
|
export function Postcard({ photo, alt = '', name, line, caption, voice, flipped, defaultFlipped = false, onFlip, liked, onLike, likeLabel = 'ถูกใจ', frontLabel = 'พลิกดูด้านหลัง', backLabel = 'พลิกดูด้านหน้า', className, ...rest }) {
|
|
12
|
-
const [
|
|
13
|
-
const
|
|
14
|
-
const turn = () => { if (flipped === undefined)
|
|
15
|
-
setInner(!back); onFlip?.(!back); };
|
|
12
|
+
const [back, setOwn] = useControlled(flipped, defaultFlipped);
|
|
13
|
+
const turn = () => { setOwn(!back); onFlip?.(!back); };
|
|
16
14
|
const words = (_jsxs("div", { className: "postcard__words", children: [_jsx("span", { className: "t-h1", children: name }), line != null && _jsx("span", { className: "t-body", children: line })] }));
|
|
17
15
|
return (_jsxs("div", { className: cx('postcard', back && 'is-flipped', className), ...rest, children: [_jsxs("div", { className: "postcard__face postcard__front", children: [_jsx("button", { className: "postcard__turn", type: "button", "aria-label": frontLabel, onClick: turn, children: photo ? _jsx("img", { src: photo, alt: alt }) : alt ? _jsx("div", { className: "ph", role: "img", "aria-label": alt }) : _jsx("div", { className: "ph" }) }), _jsxs("div", { className: "postcard__band", children: [words, (liked !== undefined || onLike) && _jsx(IconBtn, { invert: true, icon: "heart", label: likeLabel, "aria-pressed": !!liked, onClick: () => onLike?.(!liked) })] })] }), _jsxs("div", { className: "postcard__face postcard__back", children: [_jsxs("button", { className: "postcard__turn", type: "button", "aria-label": backLabel, onClick: turn, children: [words, caption != null && _jsx("p", { className: "postcard__caption", children: caption })] }), voice != null && _jsx("div", { className: "postcard__voice", children: voice })] })] }));
|
|
18
16
|
}
|