@joewinke/jatui 0.34.1 → 0.34.2
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/components/review/MediaZoomLightbox.svelte +8 -1
- package/dist/components/review/ReviewNote.svelte +37 -16
- package/dist/components/review/ReviewNote.svelte.d.ts +4 -0
- package/dist/components/review/VerdictControl.svelte +31 -14
- package/dist/components/review/VerdictControl.svelte.d.ts +4 -0
- package/package.json +1 -1
|
@@ -613,7 +613,14 @@
|
|
|
613
613
|
>
|
|
614
614
|
</div>
|
|
615
615
|
{#if showHint}
|
|
616
|
-
|
|
616
|
+
<!-- Keys are useless on a touch screen: hidden on coarse pointers
|
|
617
|
+
(the checklist block's footer hint hides the same way,
|
|
618
|
+
mos-lsmve.16 F7 — the 4-line wrap pushed the note below the
|
|
619
|
+
fold). -->
|
|
620
|
+
<p
|
|
621
|
+
class="w-full text-2xs text-base-content/60 [@media(pointer:coarse)]:hidden"
|
|
622
|
+
data-media-zoom-hint>{hint}</p
|
|
623
|
+
>
|
|
617
624
|
{/if}
|
|
618
625
|
</div>
|
|
619
626
|
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
marked `opensNote` (Reject / Needs work / Fail…). It does NOT open on
|
|
7
7
|
mount, on a repeat of the same verdict, or when the round changes.
|
|
8
8
|
- The note is optional: under an EMPTY note on a note-worthy verdict sits
|
|
9
|
-
the soft NOTE_HINT — never a validation block.
|
|
9
|
+
the soft NOTE_HINT — never a validation block. The hint never claims the
|
|
10
|
+
verdict was saved when it was not: a `saveFailed` host (its own save
|
|
11
|
+
state) or this note's own failed save silences it (mos-lsmve.16 F6).
|
|
10
12
|
- Auto-grows with its content.
|
|
11
13
|
- Per-note save state from `onSave`: "Saving…" → "Saved ✓", or
|
|
12
14
|
"Not saved" + Retry when it throws/rejects. Saves are debounced while
|
|
@@ -16,7 +18,12 @@
|
|
|
16
18
|
discard.
|
|
17
19
|
- `round` keys the note to the current round: a new round resets the save
|
|
18
20
|
state, drops any pending save for the old round, and collapses the note
|
|
19
|
-
if the host gives it an empty value.
|
|
21
|
+
if the host gives it an empty value. The round effect is DECLARED BEFORE
|
|
22
|
+
the verdict effect on purpose (mos-lsmve.16 F9): when a round change and
|
|
23
|
+
a note-worthy verdict change land in the same flush, Svelte runs
|
|
24
|
+
instance effects in declaration order, so the verdict's open runs LAST
|
|
25
|
+
and survives the round's collapse — the reviewer is looking at a note
|
|
26
|
+
for the verdict they just cast, and it must not blink away.
|
|
20
27
|
-->
|
|
21
28
|
<script module lang="ts">
|
|
22
29
|
/** The soft inline hint under an EMPTY note field on a note-worthy verdict. */
|
|
@@ -42,6 +49,10 @@
|
|
|
42
49
|
options?: readonly VerdictOption[];
|
|
43
50
|
/** Current round key (version, retestSince…). */
|
|
44
51
|
round?: string | number | null;
|
|
52
|
+
/** The host's own save failed (e.g. the verdict's save): the hint must
|
|
53
|
+
* not claim the verdict is saved while the block shows "Not saved"
|
|
54
|
+
* (mos-lsmve.16 F6). */
|
|
55
|
+
saveFailed?: boolean;
|
|
45
56
|
/** Persist the note. Throw / reject to show "Not saved". */
|
|
46
57
|
onSave?: (text: string, ctx: { round: string | number | null }) => void | Promise<void>;
|
|
47
58
|
/** Every keystroke, before the debounce. */
|
|
@@ -66,6 +77,7 @@
|
|
|
66
77
|
verdict = null,
|
|
67
78
|
options = [],
|
|
68
79
|
round = null,
|
|
80
|
+
saveFailed = false,
|
|
69
81
|
onSave,
|
|
70
82
|
onInput,
|
|
71
83
|
debounceMs = 600,
|
|
@@ -88,20 +100,12 @@
|
|
|
88
100
|
|
|
89
101
|
const noteWorthy = $derived(!!verdict && options.some((o) => o.id === verdict && o.opensNote));
|
|
90
102
|
|
|
91
|
-
// Open + focus when the verdict CHANGES to a note-worthy one. The first run
|
|
92
|
-
// only records the starting verdict, so a saved state never pops open.
|
|
93
|
-
let lastVerdict: string | null | undefined = undefined;
|
|
94
|
-
$effect(() => {
|
|
95
|
-
const v = verdict;
|
|
96
|
-
const prev = lastVerdict;
|
|
97
|
-
lastVerdict = v;
|
|
98
|
-
if (prev === undefined || v === prev || !v) return;
|
|
99
|
-
if (!untrack(() => options.some((o) => o.id === v && o.opensNote))) return;
|
|
100
|
-
open = true;
|
|
101
|
-
tick().then(() => textarea?.focus());
|
|
102
|
-
});
|
|
103
|
-
|
|
104
103
|
// A new round: forget the old round's save state and pending save.
|
|
104
|
+
// DECLARED BEFORE the verdict effect below (mos-lsmve.16 F9): instance
|
|
105
|
+
// effects run in declaration order, so when a round change and a
|
|
106
|
+
// note-worthy verdict change land in the same flush, the round's
|
|
107
|
+
// collapse happens FIRST and the verdict's open survives — the note
|
|
108
|
+
// never blinks away under the verdict the reviewer just cast.
|
|
105
109
|
let lastRound: string | number | null | undefined = undefined;
|
|
106
110
|
$effect(() => {
|
|
107
111
|
const r = round;
|
|
@@ -115,6 +119,19 @@
|
|
|
115
119
|
if (!untrack(() => value)) open = false;
|
|
116
120
|
});
|
|
117
121
|
|
|
122
|
+
// Open + focus when the verdict CHANGES to a note-worthy one. The first run
|
|
123
|
+
// only records the starting verdict, so a saved state never pops open.
|
|
124
|
+
let lastVerdict: string | null | undefined = undefined;
|
|
125
|
+
$effect(() => {
|
|
126
|
+
const v = verdict;
|
|
127
|
+
const prev = lastVerdict;
|
|
128
|
+
lastVerdict = v;
|
|
129
|
+
if (prev === undefined || v === prev || !v) return;
|
|
130
|
+
if (!untrack(() => options.some((o) => o.id === v && o.opensNote))) return;
|
|
131
|
+
open = true;
|
|
132
|
+
tick().then(() => textarea?.focus());
|
|
133
|
+
});
|
|
134
|
+
|
|
118
135
|
// Auto-grow: reset, then fit the content.
|
|
119
136
|
$effect(() => {
|
|
120
137
|
void value;
|
|
@@ -206,7 +223,11 @@
|
|
|
206
223
|
onblur={flush}
|
|
207
224
|
onkeydown={onKeydown}
|
|
208
225
|
></textarea>
|
|
209
|
-
|
|
226
|
+
<!-- The hint asserts the verdict was saved — it must not say that
|
|
227
|
+
while a save has failed (the host's own, or this note's own
|
|
228
|
+
retryable one): the alert above is saying the opposite
|
|
229
|
+
(mos-lsmve.16 F6). -->
|
|
230
|
+
{#if noteWorthy && !value.trim() && saveState !== 'error' && !saveFailed}
|
|
210
231
|
<p class="text-xs text-base-content/60">{NOTE_HINT}</p>
|
|
211
232
|
{/if}
|
|
212
233
|
</div>
|
|
@@ -13,6 +13,10 @@ interface Props {
|
|
|
13
13
|
options?: readonly VerdictOption[];
|
|
14
14
|
/** Current round key (version, retestSince…). */
|
|
15
15
|
round?: string | number | null;
|
|
16
|
+
/** The host's own save failed (e.g. the verdict's save): the hint must
|
|
17
|
+
* not claim the verdict is saved while the block shows "Not saved"
|
|
18
|
+
* (mos-lsmve.16 F6). */
|
|
19
|
+
saveFailed?: boolean;
|
|
16
20
|
/** Persist the note. Throw / reject to show "Not saved". */
|
|
17
21
|
onSave?: (text: string, ctx: {
|
|
18
22
|
round: string | number | null;
|
|
@@ -13,12 +13,17 @@
|
|
|
13
13
|
- One-key hotkeys from `options[].key` (and `u` = Undo) while focus is in
|
|
14
14
|
the control. Handled keys are preventDefault-ed so a page-level
|
|
15
15
|
reviewKeys() handler skips them.
|
|
16
|
+
- `trailing` renders the row's trailing actions (a Note toggle…) in ONE
|
|
17
|
+
non-breaking group with Undo, so a narrow card wraps the whole group —
|
|
18
|
+
the note button never strands alone on its own row (mos-lsmve.16 F7:
|
|
19
|
+
it used to wrap alone, below Undo).
|
|
16
20
|
- Sizes: `sm` (cards, default) and `xs` (inline strips).
|
|
17
21
|
|
|
18
22
|
The `options` array is the same one ReviewNote (`opensNote`) and
|
|
19
23
|
reviewKeys (`key`) take.
|
|
20
24
|
-->
|
|
21
25
|
<script lang="ts">
|
|
26
|
+
import type { Snippet } from 'svelte';
|
|
22
27
|
import type { VerdictOption, VerdictTone } from '../../actions/reviewKeys';
|
|
23
28
|
|
|
24
29
|
interface Props {
|
|
@@ -35,6 +40,9 @@
|
|
|
35
40
|
undoLabel?: string;
|
|
36
41
|
/** Show each option's hotkey as a keycap inside its button. */
|
|
37
42
|
showKeys?: boolean;
|
|
43
|
+
/** Trailing actions (Note toggle…), kept in one non-breaking group
|
|
44
|
+
* with Undo so the wrap boundary falls before the pair. */
|
|
45
|
+
trailing?: Snippet;
|
|
38
46
|
/** Local hotkeys while focus is inside the control. Default true. */
|
|
39
47
|
hotkeys?: boolean;
|
|
40
48
|
disabled?: boolean;
|
|
@@ -50,6 +58,7 @@
|
|
|
50
58
|
label = 'Verdict',
|
|
51
59
|
undoLabel = 'Undo',
|
|
52
60
|
showKeys = false,
|
|
61
|
+
trailing = undefined,
|
|
53
62
|
hotkeys = true,
|
|
54
63
|
disabled = false,
|
|
55
64
|
class: className = ''
|
|
@@ -145,19 +154,27 @@
|
|
|
145
154
|
</button>
|
|
146
155
|
{/each}
|
|
147
156
|
</div>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
157
|
+
<!-- Undo + trailing actions in one ATOMIC span: the row's wrap boundary
|
|
158
|
+
falls before the pair, so Undo and whatever trails it (the Note
|
|
159
|
+
toggle) always sit on the same line (mos-lsmve.16 F7). -->
|
|
160
|
+
{#if value && onUndo || trailing}
|
|
161
|
+
<span class="flex items-center gap-2">
|
|
162
|
+
{#if value && onUndo}
|
|
163
|
+
<button
|
|
164
|
+
type="button"
|
|
165
|
+
class="btn btn-ghost {sizeClass} gap-1 px-2 text-base-content/75 hover:text-base-content"
|
|
166
|
+
aria-label="{undoLabel} — {label}"
|
|
167
|
+
aria-keyshortcuts="u"
|
|
168
|
+
{disabled}
|
|
169
|
+
onclick={() => onUndo?.()}
|
|
170
|
+
>
|
|
171
|
+
<svg viewBox="0 0 24 24" class="size-3.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
172
|
+
<path d="M9 14 4 9l5-5" /><path d="M4 9h10.5a5.5 5.5 0 0 1 0 11H11" />
|
|
173
|
+
</svg>
|
|
174
|
+
{undoLabel}
|
|
175
|
+
</button>
|
|
176
|
+
{/if}
|
|
177
|
+
{#if trailing}{@render trailing()}{/if}
|
|
178
|
+
</span>
|
|
162
179
|
{/if}
|
|
163
180
|
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
import type { VerdictOption } from '../../actions/reviewKeys';
|
|
2
3
|
interface Props {
|
|
3
4
|
options: readonly VerdictOption[];
|
|
@@ -13,6 +14,9 @@ interface Props {
|
|
|
13
14
|
undoLabel?: string;
|
|
14
15
|
/** Show each option's hotkey as a keycap inside its button. */
|
|
15
16
|
showKeys?: boolean;
|
|
17
|
+
/** Trailing actions (Note toggle…), kept in one non-breaking group
|
|
18
|
+
* with Undo so the wrap boundary falls before the pair. */
|
|
19
|
+
trailing?: Snippet;
|
|
16
20
|
/** Local hotkeys while focus is inside the control. Default true. */
|
|
17
21
|
hotkeys?: boolean;
|
|
18
22
|
disabled?: boolean;
|