@cahyo-dimas/freeday 1.25.0 → 1.26.0
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 +40 -0
- package/COMPONENTS.md +8 -0
- package/README.id.md +1 -1
- package/README.md +1 -1
- package/dist/freeday-upload.js +30 -2
- package/dist/freeday.js +30 -2
- package/docs/getting-started.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,46 @@
|
|
|
3
3
|
Semua perubahan penting dicatat di sini. Format longgar mengikuti
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/); tiap versi = git tag.
|
|
5
5
|
|
|
6
|
+
## [1.26.0] — 2026-08-14
|
|
7
|
+
Improvement note #43, found while chasing a "the upload is stuck" report on a 626 KB PDF: the
|
|
8
|
+
transfer took about a second, the server then spent nearly a minute reading the document.
|
|
9
|
+
### Added
|
|
10
|
+
- **`row.waiting(label)`** — the state between `setProgress` and `done`: the bytes are gone, the
|
|
11
|
+
server has not answered. The row's only long-running state was named after the *transfer*, so it
|
|
12
|
+
kept saying "Mengunggah…" for the whole minute of server-side work — and `setProgress(100)` made it
|
|
13
|
+
worse, because a full bar that then sits still is the most convincing "hung" signal a UI can
|
|
14
|
+
produce. There was no way out within the row's API: `done()` claims success, `fail()` claims an
|
|
15
|
+
error, `ready()` walks backwards. Consumers were rendering a second status line outside the row and
|
|
16
|
+
leaving the row to contradict it.
|
|
17
|
+
The bar goes **indeterminate** and drops `aria-valuenow` — a progressbar with no value is exactly
|
|
18
|
+
what ARIA calls indeterminate, which is the contract `COMPONENTS.md` already stated for
|
|
19
|
+
`.fdy-progress`. The label is the consumer's, because only they know what the server is doing
|
|
20
|
+
(`Membaca PDF…`, `Memindai…`); it falls back to `Menunggu server…`.
|
|
21
|
+
- `COMPONENTS.md` gains the state in the row table plus the sentence that would have saved the
|
|
22
|
+
round-trip: **if your request outlives the transfer, drive `waiting()`**.
|
|
23
|
+
### Notes on the shape of the fix
|
|
24
|
+
- The report's patch would have shipped the symptom it set out to remove. It put the modifier on the
|
|
25
|
+
**bar** (`.fdy-progress--indeterminate` styles `.fdy-progress__bar`, so it belongs on the
|
|
26
|
+
container — on the bar it matches nothing) and then set an inline `width:100%`, which beats the
|
|
27
|
+
modifier's own width anyway. Both mistakes render a full, frozen bar. The note also hedged that
|
|
28
|
+
`.fdy-progress--indeterminate` might not exist; it has all along.
|
|
29
|
+
- **Leaving the state needs more care than entering it.** `.fdy-progress__bar` is a plain block div:
|
|
30
|
+
with no width it fills its track. So `uploading()`/`setProgress()` restore an explicit width when
|
|
31
|
+
they clear the modifier, or a retried row paints a *full* bar while meaning 0%. `done()`, `fail()`
|
|
32
|
+
and `ready()` need no counterpart — they drop the progress element outright, modifier and all
|
|
33
|
+
(contrary to the note, which expected a line in each).
|
|
34
|
+
- **No `.fdy-file--waiting` class.** `uploading` has none either; only `--success`/`--error` do,
|
|
35
|
+
because they carry colour. A documented class with no rule is markup that looks like it does
|
|
36
|
+
something.
|
|
37
|
+
- Under `prefers-reduced-motion: reduce` the kit's indeterminate treatment is a dimmed **full** bar
|
|
38
|
+
(no animation left to carry the meaning) — pre-existing behaviour for every indeterminate progress,
|
|
39
|
+
not introduced here. For those users the honest signal is the label, not the bar.
|
|
40
|
+
### Added — guards
|
|
41
|
+
- `browser/upload-states.mjs` gains a third spec, measuring what the **engine renders** rather than
|
|
42
|
+
what the source declares: both ways to get this wrong are invisible in a code read. Mutation-checked
|
|
43
|
+
against five defects, including the report's own two — modifier-on-bar, inline `width:100%`, no
|
|
44
|
+
width restored on return, `aria-valuenow` kept, and label ignored.
|
|
45
|
+
|
|
6
46
|
## [1.25.0] — 2026-08-13
|
|
7
47
|
Improvement note #42, found while adopting 1.24.0 — the other half of the same integration.
|
|
8
48
|
### Fixed
|
package/COMPONENTS.md
CHANGED
|
@@ -495,11 +495,17 @@ transfer started; the kit never claims one it is not performing.
|
|
|
495
495
|
| *(initial)* | **rest** — chosen, not sent. Size only, no progress bar. |
|
|
496
496
|
| `.uploading()` | in flight — adds the progress bar |
|
|
497
497
|
| `.setProgress(pct)` | moves the bar (0–100) |
|
|
498
|
+
| `.waiting(label)` | **sent, waiting on the server** — indeterminate bar, no `aria-valuenow`; `label` is yours (default *Menunggu server…*) |
|
|
498
499
|
| `.done()` | success — drops the bar, `.fdy-file--success` |
|
|
499
500
|
| `.fail(msg)` | error — drops the bar, `.fdy-file--error`, `msg` replaces the sub-line |
|
|
500
501
|
| `.ready()` | back to **rest** (e.g. after a failed attempt the user will retry) |
|
|
501
502
|
| `.el` | the row element |
|
|
502
503
|
|
|
504
|
+
**If your request outlives the transfer, drive `waiting()`.** Server-side work after the last byte —
|
|
505
|
+
OCR, extraction, virus scanning, transcoding — is not uploading, and `setProgress(100)` left standing
|
|
506
|
+
is read as a hang. `waiting()` is the state for it: the label says what the server is doing, and the
|
|
507
|
+
bar stops claiming a percentage it no longer has.
|
|
508
|
+
|
|
503
509
|
```js
|
|
504
510
|
zone.addEventListener('fdy-upload-add', (e) => {
|
|
505
511
|
if (e.detail.rejected) return; // the kit already rendered the reason
|
|
@@ -507,6 +513,8 @@ zone.addEventListener('fdy-upload-add', (e) => {
|
|
|
507
513
|
submitBtn.onclick = async () => {
|
|
508
514
|
row.uploading();
|
|
509
515
|
await send(file, (pct) => row.setProgress(pct));
|
|
516
|
+
row.waiting('Membaca dokumen…'); // bytes gone, server still working
|
|
517
|
+
await serverFinished();
|
|
510
518
|
row.done();
|
|
511
519
|
};
|
|
512
520
|
});
|
package/README.id.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
> **Lebih banyak _free day_ buat dev — UI kit-nya sudah siap pakai.**
|
|
6
6
|
|
|
7
7
|
[](https://cahyo-dimas.github.io/freeday-ui-kit/)
|
|
8
|
-
[](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v1.26.0)
|
|
9
9
|
|
|
10
10
|
UI KIT yang token-driven & framework-agnostic — satu sumber kebenaran untuk warna, tipografi,
|
|
11
11
|
spasi, dan komponen. Blueprint: `docs/superpowers/specs/2026-07-21-freeday-ui-kit-design.md`.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
> **More free days for devs — the UI kit is ready to use.**
|
|
6
6
|
|
|
7
7
|
[](https://cahyo-dimas.github.io/freeday-ui-kit/)
|
|
8
|
-
[](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v1.26.0)
|
|
9
9
|
|
|
10
10
|
A token-driven, framework-agnostic UI kit — one source of truth for color, typography,
|
|
11
11
|
spacing, and components. Blueprint: `docs/superpowers/specs/2026-07-21-freeday-ui-kit-design.md`.
|
package/dist/freeday-upload.js
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
* "fdy-upload-add" {file, rejected, reason, row}
|
|
21
21
|
* "fdy-upload-remove" {file}
|
|
22
22
|
* `row` is the state machine over the rendered .fdy-file:
|
|
23
|
-
* .ready() (rest — where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
24
|
-
* .fail(msg) / .el
|
|
23
|
+
* .ready() (rest — where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
24
|
+
* .waiting(label) (sent, awaiting the server) / .done() / .fail(msg) / .el
|
|
25
25
|
* Note the file list is a SIBLING of the dropzone above, so nothing dispatched on a row would ever
|
|
26
26
|
* bubble through the zone — which is why removal fires on the zone and not on the row.
|
|
27
27
|
*/
|
|
@@ -107,6 +107,13 @@
|
|
|
107
107
|
function dropProgress() {
|
|
108
108
|
if (progressWrap) { progressWrap.remove(); progressWrap = null; bar = null; progressEl = null; }
|
|
109
109
|
}
|
|
110
|
+
/* Back to a measured bar. While the indeterminate modifier is on it owns the bar's width, so an
|
|
111
|
+
explicit one has to be restored when it comes off: .fdy-progress__bar is a plain block div, and
|
|
112
|
+
with no width at all it fills the track — a full bar, which is the opposite of what 0% means. */
|
|
113
|
+
function determinate() {
|
|
114
|
+
progressEl.classList.remove('fdy-progress--indeterminate');
|
|
115
|
+
if (!bar.style.width) bar.style.width = '0%';
|
|
116
|
+
}
|
|
110
117
|
return {
|
|
111
118
|
el: el,
|
|
112
119
|
/* The state a row starts in: chosen, not yet sent. The size alone — it makes no claim about
|
|
@@ -124,13 +131,34 @@
|
|
|
124
131
|
icon.innerHTML = FILE_ICON;
|
|
125
132
|
sub.textContent = fmtSize(file.size) + ' · Mengunggah…';
|
|
126
133
|
ensureProgress();
|
|
134
|
+
determinate();
|
|
127
135
|
},
|
|
128
136
|
setProgress: function (pct) {
|
|
129
137
|
ensureProgress();
|
|
138
|
+
determinate();
|
|
130
139
|
var v = Math.max(0, Math.min(100, pct));
|
|
131
140
|
bar.style.width = v + '%';
|
|
132
141
|
progressEl.setAttribute('aria-valuenow', String(Math.round(v)));
|
|
133
142
|
},
|
|
143
|
+
/* The bytes are gone and the server has not answered yet — extraction, scanning, transcoding.
|
|
144
|
+
"Mengunggah…" turns false the moment the last byte leaves, and a determinate bar parked at
|
|
145
|
+
100% is the most convincing "hung" signal a UI can produce, so this state reports no
|
|
146
|
+
percentage: the bar goes indeterminate and the label belongs to the consumer, because only
|
|
147
|
+
they know what the server is doing ("Membaca PDF…", "Memindai…"). done()/fail()/ready() need
|
|
148
|
+
no counterpart here — they drop the progress element outright, modifier and all. */
|
|
149
|
+
waiting: function (label) {
|
|
150
|
+
el.classList.remove('fdy-file--error', 'fdy-file--success');
|
|
151
|
+
icon.innerHTML = FILE_ICON;
|
|
152
|
+
sub.textContent = fmtSize(file.size) + ' · ' + (label || 'Menunggu server…');
|
|
153
|
+
ensureProgress();
|
|
154
|
+
progressEl.classList.add('fdy-progress--indeterminate');
|
|
155
|
+
/* The modifier styles .fdy-progress__bar, so it must sit on the CONTAINER, and the inline
|
|
156
|
+
width setProgress wrote has to go — an inline style beats any rule the modifier brings.
|
|
157
|
+
aria-valuenow goes with it: a progressbar with no value is precisely what ARIA calls
|
|
158
|
+
indeterminate, which is the contract COMPONENTS.md already states for this component. */
|
|
159
|
+
bar.style.width = '';
|
|
160
|
+
progressEl.removeAttribute('aria-valuenow');
|
|
161
|
+
},
|
|
134
162
|
done: function () {
|
|
135
163
|
el.classList.add('fdy-file--success');
|
|
136
164
|
el.classList.remove('fdy-file--error');
|
package/dist/freeday.js
CHANGED
|
@@ -3595,8 +3595,8 @@
|
|
|
3595
3595
|
* "fdy-upload-add" {file, rejected, reason, row}
|
|
3596
3596
|
* "fdy-upload-remove" {file}
|
|
3597
3597
|
* `row` is the state machine over the rendered .fdy-file:
|
|
3598
|
-
* .ready() (rest — where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
3599
|
-
* .fail(msg) / .el
|
|
3598
|
+
* .ready() (rest — where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
3599
|
+
* .waiting(label) (sent, awaiting the server) / .done() / .fail(msg) / .el
|
|
3600
3600
|
* Note the file list is a SIBLING of the dropzone above, so nothing dispatched on a row would ever
|
|
3601
3601
|
* bubble through the zone — which is why removal fires on the zone and not on the row.
|
|
3602
3602
|
*/
|
|
@@ -3682,6 +3682,13 @@
|
|
|
3682
3682
|
function dropProgress() {
|
|
3683
3683
|
if (progressWrap) { progressWrap.remove(); progressWrap = null; bar = null; progressEl = null; }
|
|
3684
3684
|
}
|
|
3685
|
+
/* Back to a measured bar. While the indeterminate modifier is on it owns the bar's width, so an
|
|
3686
|
+
explicit one has to be restored when it comes off: .fdy-progress__bar is a plain block div, and
|
|
3687
|
+
with no width at all it fills the track — a full bar, which is the opposite of what 0% means. */
|
|
3688
|
+
function determinate() {
|
|
3689
|
+
progressEl.classList.remove('fdy-progress--indeterminate');
|
|
3690
|
+
if (!bar.style.width) bar.style.width = '0%';
|
|
3691
|
+
}
|
|
3685
3692
|
return {
|
|
3686
3693
|
el: el,
|
|
3687
3694
|
/* The state a row starts in: chosen, not yet sent. The size alone — it makes no claim about
|
|
@@ -3699,13 +3706,34 @@
|
|
|
3699
3706
|
icon.innerHTML = FILE_ICON;
|
|
3700
3707
|
sub.textContent = fmtSize(file.size) + ' · Mengunggah…';
|
|
3701
3708
|
ensureProgress();
|
|
3709
|
+
determinate();
|
|
3702
3710
|
},
|
|
3703
3711
|
setProgress: function (pct) {
|
|
3704
3712
|
ensureProgress();
|
|
3713
|
+
determinate();
|
|
3705
3714
|
var v = Math.max(0, Math.min(100, pct));
|
|
3706
3715
|
bar.style.width = v + '%';
|
|
3707
3716
|
progressEl.setAttribute('aria-valuenow', String(Math.round(v)));
|
|
3708
3717
|
},
|
|
3718
|
+
/* The bytes are gone and the server has not answered yet — extraction, scanning, transcoding.
|
|
3719
|
+
"Mengunggah…" turns false the moment the last byte leaves, and a determinate bar parked at
|
|
3720
|
+
100% is the most convincing "hung" signal a UI can produce, so this state reports no
|
|
3721
|
+
percentage: the bar goes indeterminate and the label belongs to the consumer, because only
|
|
3722
|
+
they know what the server is doing ("Membaca PDF…", "Memindai…"). done()/fail()/ready() need
|
|
3723
|
+
no counterpart here — they drop the progress element outright, modifier and all. */
|
|
3724
|
+
waiting: function (label) {
|
|
3725
|
+
el.classList.remove('fdy-file--error', 'fdy-file--success');
|
|
3726
|
+
icon.innerHTML = FILE_ICON;
|
|
3727
|
+
sub.textContent = fmtSize(file.size) + ' · ' + (label || 'Menunggu server…');
|
|
3728
|
+
ensureProgress();
|
|
3729
|
+
progressEl.classList.add('fdy-progress--indeterminate');
|
|
3730
|
+
/* The modifier styles .fdy-progress__bar, so it must sit on the CONTAINER, and the inline
|
|
3731
|
+
width setProgress wrote has to go — an inline style beats any rule the modifier brings.
|
|
3732
|
+
aria-valuenow goes with it: a progressbar with no value is precisely what ARIA calls
|
|
3733
|
+
indeterminate, which is the contract COMPONENTS.md already states for this component. */
|
|
3734
|
+
bar.style.width = '';
|
|
3735
|
+
progressEl.removeAttribute('aria-valuenow');
|
|
3736
|
+
},
|
|
3709
3737
|
done: function () {
|
|
3710
3738
|
el.classList.add('fdy-file--success');
|
|
3711
3739
|
el.classList.remove('fdy-file--error');
|
package/docs/getting-started.md
CHANGED
|
@@ -184,7 +184,7 @@ live docs also have a copy button per component.
|
|
|
184
184
|
```bash
|
|
185
185
|
npm i @cahyo-dimas/freeday
|
|
186
186
|
```
|
|
187
|
-
Lands in `package.json` as `"@cahyo-dimas/freeday": "^1.
|
|
187
|
+
Lands in `package.json` as `"@cahyo-dimas/freeday": "^1.26.0"` (public npm package). `dist/` is
|
|
188
188
|
committed and published → no build step; `npm ci` runs without auth.
|
|
189
189
|
|
|
190
190
|
### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
|