@cahyo-dimas/freeday 1.24.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 +70 -0
- package/COMPONENTS.md +13 -0
- package/README.id.md +1 -1
- package/README.md +1 -1
- package/dist/freeday-upload.js +47 -5
- package/dist/freeday.js +47 -5
- package/docs/getting-started.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,76 @@
|
|
|
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
|
+
|
|
46
|
+
## [1.25.0] — 2026-08-13
|
|
47
|
+
Improvement note #42, found while adopting 1.24.0 — the other half of the same integration.
|
|
48
|
+
### Fixed
|
|
49
|
+
- **`fdy-upload-remove` now fires on the dropzone**, the same element as `fdy-upload-add`. It was
|
|
50
|
+
dispatched on the *row*, which lives in the file list — and the kit's own markup contract puts that
|
|
51
|
+
list as a **sibling** of the dropzone, so the event never bubbled through the zone. A consumer
|
|
52
|
+
following the documentation got `add` and never got `remove`: no error, right event name, right
|
|
53
|
+
element, and the other event on that element working. Their state kept a file the user had already
|
|
54
|
+
taken away.
|
|
55
|
+
The kit's own header comment was the source of the mistake — it said both events are emitted "on the
|
|
56
|
+
dropzone" while the code dispatched one of them somewhere else. It now states the target for each,
|
|
57
|
+
and why (a row in a sibling list can never reach the zone). `COMPONENTS.md` says it once beside the
|
|
58
|
+
row state table, with the removal listener in the worked example.
|
|
59
|
+
### Notes on the shape of the fix
|
|
60
|
+
- The report proposed dispatching on **both** the row and the zone, for backward compatibility. Not
|
|
61
|
+
taken, and the guard proves why: when the file list is **nested inside** the dropzone — which
|
|
62
|
+
`data-filelist` permits — the row already bubbles through the zone, so a second dispatch makes a
|
|
63
|
+
plain zone listener fire **twice** per removal. Firing on both also leaves the pair asymmetric (one
|
|
64
|
+
`add`, two `remove`s for anyone delegating on a common ancestor), which is the same class of silent
|
|
65
|
+
bug this note is about. One canonical target is the honest fix.
|
|
66
|
+
- Dispatching on the zone also fixes the **listless** case from 1.24.0: a row that was never attached
|
|
67
|
+
to the document bubbles to nothing at all, so its × was previously unobservable.
|
|
68
|
+
- *Migration:* a consumer that worked around the old behaviour by listening on the file list must
|
|
69
|
+
move that listener to the dropzone. That position was never documented — it was the bug.
|
|
70
|
+
### Added — guards
|
|
71
|
+
- `browser/upload-states.mjs` gains a second spec: removal fires on the dropzone **exactly once**, in
|
|
72
|
+
both layouts (list as sibling, list nested inside the zone), and delegation on a shared ancestor
|
|
73
|
+
sees no duplicates. Mutation-checked against *both* rejected designs — reverting to the row target
|
|
74
|
+
fails it, and so does the report's dispatch-on-both.
|
|
75
|
+
|
|
6
76
|
## [1.24.0] — 2026-08-13
|
|
7
77
|
Improvement note #41: the upload row had no "chosen, not yet sent" state, so every consumer-driven
|
|
8
78
|
integration showed a transfer that had not started.
|
package/COMPONENTS.md
CHANGED
|
@@ -482,6 +482,10 @@ Click/Enter opens the file dialog; drop works too. Needs `freeday-upload.js`.
|
|
|
482
482
|
— the kit fakes a transfer to `done()`; never set it in an app)
|
|
483
483
|
- A11y: the dropzone is `role="button" tabindex="0"` + `aria-label`.
|
|
484
484
|
|
|
485
|
+
**Both events fire on the dropzone** — `fdy-upload-add` *and* `fdy-upload-remove`. One listener, one
|
|
486
|
+
element. (The file list may be anywhere in the document; nothing is dispatched on the row, because a
|
|
487
|
+
row in a sibling list would never bubble through the zone.)
|
|
488
|
+
|
|
485
489
|
**The row is yours to drive.** `fdy-upload-add` carries `detail.row`, a small state machine over the
|
|
486
490
|
rendered `.fdy-file`. A dropped file **rests** — it shows its size and nothing else — until you say a
|
|
487
491
|
transfer started; the kit never claims one it is not performing.
|
|
@@ -491,11 +495,17 @@ transfer started; the kit never claims one it is not performing.
|
|
|
491
495
|
| *(initial)* | **rest** — chosen, not sent. Size only, no progress bar. |
|
|
492
496
|
| `.uploading()` | in flight — adds the progress bar |
|
|
493
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…*) |
|
|
494
499
|
| `.done()` | success — drops the bar, `.fdy-file--success` |
|
|
495
500
|
| `.fail(msg)` | error — drops the bar, `.fdy-file--error`, `msg` replaces the sub-line |
|
|
496
501
|
| `.ready()` | back to **rest** (e.g. after a failed attempt the user will retry) |
|
|
497
502
|
| `.el` | the row element |
|
|
498
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
|
+
|
|
499
509
|
```js
|
|
500
510
|
zone.addEventListener('fdy-upload-add', (e) => {
|
|
501
511
|
if (e.detail.rejected) return; // the kit already rendered the reason
|
|
@@ -503,9 +513,12 @@ zone.addEventListener('fdy-upload-add', (e) => {
|
|
|
503
513
|
submitBtn.onclick = async () => {
|
|
504
514
|
row.uploading();
|
|
505
515
|
await send(file, (pct) => row.setProgress(pct));
|
|
516
|
+
row.waiting('Membaca dokumen…'); // bytes gone, server still working
|
|
517
|
+
await serverFinished();
|
|
506
518
|
row.done();
|
|
507
519
|
};
|
|
508
520
|
});
|
|
521
|
+
zone.addEventListener('fdy-upload-remove', (e) => forget(e.detail.file)); // same element
|
|
509
522
|
```
|
|
510
523
|
|
|
511
524
|
**Bring your own row:** omit the file list entirely (no `data-filelist`, and no `.fdy-filelist`
|
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
|
@@ -15,8 +15,15 @@
|
|
|
15
15
|
* </div>
|
|
16
16
|
* <div class="fdy-filelist" id="dz-list"></div>
|
|
17
17
|
*
|
|
18
|
-
* Emits bubbling CustomEvents on the dropzone
|
|
19
|
-
*
|
|
18
|
+
* Emits bubbling CustomEvents — BOTH on the dropzone element, which is the one target a consumer
|
|
19
|
+
* needs to listen on:
|
|
20
|
+
* "fdy-upload-add" {file, rejected, reason, row}
|
|
21
|
+
* "fdy-upload-remove" {file}
|
|
22
|
+
* `row` is the state machine over the rendered .fdy-file:
|
|
23
|
+
* .ready() (rest — where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
24
|
+
* .waiting(label) (sent, awaiting the server) / .done() / .fail(msg) / .el
|
|
25
|
+
* Note the file list is a SIBLING of the dropzone above, so nothing dispatched on a row would ever
|
|
26
|
+
* bubble through the zone — which is why removal fires on the zone and not on the row.
|
|
20
27
|
*/
|
|
21
28
|
(function () {
|
|
22
29
|
'use strict';
|
|
@@ -43,7 +50,7 @@
|
|
|
43
50
|
});
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
function makeRow(file) {
|
|
53
|
+
function makeRow(file, zone) {
|
|
47
54
|
var el = document.createElement('div');
|
|
48
55
|
el.className = 'fdy-file';
|
|
49
56
|
var icon = document.createElement('span');
|
|
@@ -65,7 +72,14 @@
|
|
|
65
72
|
remove.setAttribute('aria-label', 'Hapus ' + file.name);
|
|
66
73
|
remove.innerHTML = '×';
|
|
67
74
|
remove.addEventListener('click', function () {
|
|
68
|
-
|
|
75
|
+
/* Dispatched on the ZONE, not on the row — the same target as fdy-upload-add, so one listener
|
|
76
|
+
on the dropzone gets both. The row lives in the file list, which the kit's own markup
|
|
77
|
+
contract puts as a SIBLING of the dropzone, so a row event never bubbles through the zone:
|
|
78
|
+
a consumer following the docs saw `add` arrive and `remove` never fire, with no error.
|
|
79
|
+
Firing on both would look safer, but it makes the pair asymmetric — one `add` and, for
|
|
80
|
+
anyone delegating on a common ancestor, two `remove`s — which is its own silent bug. It
|
|
81
|
+
also fixes the listless case: a row that was never attached bubbles to nothing at all. */
|
|
82
|
+
zone.dispatchEvent(new CustomEvent('fdy-upload-remove', { bubbles: true, detail: { file: file } }));
|
|
69
83
|
el.remove();
|
|
70
84
|
});
|
|
71
85
|
el.appendChild(icon);
|
|
@@ -93,6 +107,13 @@
|
|
|
93
107
|
function dropProgress() {
|
|
94
108
|
if (progressWrap) { progressWrap.remove(); progressWrap = null; bar = null; progressEl = null; }
|
|
95
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
|
+
}
|
|
96
117
|
return {
|
|
97
118
|
el: el,
|
|
98
119
|
/* The state a row starts in: chosen, not yet sent. The size alone — it makes no claim about
|
|
@@ -110,13 +131,34 @@
|
|
|
110
131
|
icon.innerHTML = FILE_ICON;
|
|
111
132
|
sub.textContent = fmtSize(file.size) + ' · Mengunggah…';
|
|
112
133
|
ensureProgress();
|
|
134
|
+
determinate();
|
|
113
135
|
},
|
|
114
136
|
setProgress: function (pct) {
|
|
115
137
|
ensureProgress();
|
|
138
|
+
determinate();
|
|
116
139
|
var v = Math.max(0, Math.min(100, pct));
|
|
117
140
|
bar.style.width = v + '%';
|
|
118
141
|
progressEl.setAttribute('aria-valuenow', String(Math.round(v)));
|
|
119
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
|
+
},
|
|
120
162
|
done: function () {
|
|
121
163
|
el.classList.add('fdy-file--success');
|
|
122
164
|
el.classList.remove('fdy-file--error');
|
|
@@ -191,7 +233,7 @@
|
|
|
191
233
|
var reason = null;
|
|
192
234
|
if (!accepts(file, acceptAttr)) reason = 'Tipe berkas tidak didukung.';
|
|
193
235
|
else if (maxSize && file.size > maxSize) reason = 'Ukuran melebihi batas (' + fmtSize(maxSize) + ').';
|
|
194
|
-
var row = makeRow(file);
|
|
236
|
+
var row = makeRow(file, zone);
|
|
195
237
|
if (list) list.appendChild(row.el);
|
|
196
238
|
if (reason) {
|
|
197
239
|
row.fail(reason);
|
package/dist/freeday.js
CHANGED
|
@@ -3590,8 +3590,15 @@
|
|
|
3590
3590
|
* </div>
|
|
3591
3591
|
* <div class="fdy-filelist" id="dz-list"></div>
|
|
3592
3592
|
*
|
|
3593
|
-
* Emits bubbling CustomEvents on the dropzone
|
|
3594
|
-
*
|
|
3593
|
+
* Emits bubbling CustomEvents — BOTH on the dropzone element, which is the one target a consumer
|
|
3594
|
+
* needs to listen on:
|
|
3595
|
+
* "fdy-upload-add" {file, rejected, reason, row}
|
|
3596
|
+
* "fdy-upload-remove" {file}
|
|
3597
|
+
* `row` is the state machine over the rendered .fdy-file:
|
|
3598
|
+
* .ready() (rest — where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
3599
|
+
* .waiting(label) (sent, awaiting the server) / .done() / .fail(msg) / .el
|
|
3600
|
+
* Note the file list is a SIBLING of the dropzone above, so nothing dispatched on a row would ever
|
|
3601
|
+
* bubble through the zone — which is why removal fires on the zone and not on the row.
|
|
3595
3602
|
*/
|
|
3596
3603
|
(function () {
|
|
3597
3604
|
'use strict';
|
|
@@ -3618,7 +3625,7 @@
|
|
|
3618
3625
|
});
|
|
3619
3626
|
}
|
|
3620
3627
|
|
|
3621
|
-
function makeRow(file) {
|
|
3628
|
+
function makeRow(file, zone) {
|
|
3622
3629
|
var el = document.createElement('div');
|
|
3623
3630
|
el.className = 'fdy-file';
|
|
3624
3631
|
var icon = document.createElement('span');
|
|
@@ -3640,7 +3647,14 @@
|
|
|
3640
3647
|
remove.setAttribute('aria-label', 'Hapus ' + file.name);
|
|
3641
3648
|
remove.innerHTML = '×';
|
|
3642
3649
|
remove.addEventListener('click', function () {
|
|
3643
|
-
|
|
3650
|
+
/* Dispatched on the ZONE, not on the row — the same target as fdy-upload-add, so one listener
|
|
3651
|
+
on the dropzone gets both. The row lives in the file list, which the kit's own markup
|
|
3652
|
+
contract puts as a SIBLING of the dropzone, so a row event never bubbles through the zone:
|
|
3653
|
+
a consumer following the docs saw `add` arrive and `remove` never fire, with no error.
|
|
3654
|
+
Firing on both would look safer, but it makes the pair asymmetric — one `add` and, for
|
|
3655
|
+
anyone delegating on a common ancestor, two `remove`s — which is its own silent bug. It
|
|
3656
|
+
also fixes the listless case: a row that was never attached bubbles to nothing at all. */
|
|
3657
|
+
zone.dispatchEvent(new CustomEvent('fdy-upload-remove', { bubbles: true, detail: { file: file } }));
|
|
3644
3658
|
el.remove();
|
|
3645
3659
|
});
|
|
3646
3660
|
el.appendChild(icon);
|
|
@@ -3668,6 +3682,13 @@
|
|
|
3668
3682
|
function dropProgress() {
|
|
3669
3683
|
if (progressWrap) { progressWrap.remove(); progressWrap = null; bar = null; progressEl = null; }
|
|
3670
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
|
+
}
|
|
3671
3692
|
return {
|
|
3672
3693
|
el: el,
|
|
3673
3694
|
/* The state a row starts in: chosen, not yet sent. The size alone — it makes no claim about
|
|
@@ -3685,13 +3706,34 @@
|
|
|
3685
3706
|
icon.innerHTML = FILE_ICON;
|
|
3686
3707
|
sub.textContent = fmtSize(file.size) + ' · Mengunggah…';
|
|
3687
3708
|
ensureProgress();
|
|
3709
|
+
determinate();
|
|
3688
3710
|
},
|
|
3689
3711
|
setProgress: function (pct) {
|
|
3690
3712
|
ensureProgress();
|
|
3713
|
+
determinate();
|
|
3691
3714
|
var v = Math.max(0, Math.min(100, pct));
|
|
3692
3715
|
bar.style.width = v + '%';
|
|
3693
3716
|
progressEl.setAttribute('aria-valuenow', String(Math.round(v)));
|
|
3694
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
|
+
},
|
|
3695
3737
|
done: function () {
|
|
3696
3738
|
el.classList.add('fdy-file--success');
|
|
3697
3739
|
el.classList.remove('fdy-file--error');
|
|
@@ -3766,7 +3808,7 @@
|
|
|
3766
3808
|
var reason = null;
|
|
3767
3809
|
if (!accepts(file, acceptAttr)) reason = 'Tipe berkas tidak didukung.';
|
|
3768
3810
|
else if (maxSize && file.size > maxSize) reason = 'Ukuran melebihi batas (' + fmtSize(maxSize) + ').';
|
|
3769
|
-
var row = makeRow(file);
|
|
3811
|
+
var row = makeRow(file, zone);
|
|
3770
3812
|
if (list) list.appendChild(row.el);
|
|
3771
3813
|
if (reason) {
|
|
3772
3814
|
row.fail(reason);
|
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`)
|