@cahyo-dimas/freeday 3.7.0 → 3.9.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 +197 -0
- package/COMPONENTS.md +214 -18
- package/README.id.md +4 -4
- package/README.md +4 -3
- package/adapters/blazor/FdyDatepicker.razor +3 -1
- package/adapters/blazor/FdyDatepicker.razor.cs +36 -1
- package/adapters/blazor/FdyMonthpicker.razor +29 -0
- package/adapters/blazor/FdyMonthpicker.razor.cs +102 -0
- package/adapters/blazor/FdyTable.razor +6 -1
- package/adapters/blazor/FdyTable.razor.cs +33 -0
- package/adapters/blazor/FdyTableFooter.razor +6 -6
- package/adapters/blazor/FdyTableFooter.razor.cs +36 -0
- package/adapters/core/month-grid.d.ts +116 -0
- package/adapters/core/month-grid.js +234 -0
- package/adapters/react/components/FdyDatepicker.tsx +164 -166
- package/adapters/react/components/FdyMonthpicker.tsx +400 -0
- package/adapters/react/components/FdyTable.tsx +38 -2
- package/adapters/react/components/FdyTableFilter.tsx +13 -1
- package/adapters/react/components/FdyTableFooter.tsx +37 -6
- package/adapters/react/index.d.ts +6 -2
- package/adapters/react/index.js +1 -0
- package/adapters/react/usePopover.ts +54 -9
- package/adapters/vue/components/FdyDatepicker.vue +158 -160
- package/adapters/vue/components/FdyMonthpicker.vue +387 -0
- package/adapters/vue/components/FdyTable.vue +33 -1
- package/adapters/vue/components/FdyTableFilter.vue +13 -1
- package/adapters/vue/components/FdyTableFooter.vue +39 -6
- package/adapters/vue/index.d.ts +16 -0
- package/adapters/vue/index.js +1 -0
- package/adapters/vue/usePopover.ts +53 -10
- package/dist/freeday-datepicker.js +68 -14
- package/dist/freeday-menu.js +15 -1
- package/dist/freeday-popover.js +33 -11
- package/dist/freeday-table.js +64 -5
- package/dist/freeday-tabs.js +21 -2
- package/dist/freeday.bundle.css +50 -2
- package/dist/freeday.css +50 -2
- package/dist/freeday.js +201 -33
- package/docs/agent-onboarding.md +3 -3
- package/docs/getting-started.md +6 -6
- package/docs/integrations.md +22 -4
- package/package.json +2 -2
- package/src/components/table.css +19 -2
- package/src/components/tabs.css +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,203 @@ benar. Perubahan seperti itu ditulis di bawah `### Changed: BREAKING (types)`
|
|
|
9
9
|
lama → tipe baru, dan cara menyempitkannya — bukan di bawah `### Added`, betapapun aditifnya dari
|
|
10
10
|
sisi kit.
|
|
11
11
|
|
|
12
|
+
## [3.9.0] - 2026-10-23
|
|
13
|
+
|
|
14
|
+
**Rilis ini dimulai sebagai tiga penambahan dan berakhir dengan lima cacat, dan yang menemukan
|
|
15
|
+
kelimanya adalah penambahan itu sendiri.** Pemiliknya memutuskan batas antara kit dan app yang
|
|
16
|
+
memakainya ditentukan oleh satu pertanyaan: apakah sesuatu bisa diadaptasi universal. Di bawah
|
|
17
|
+
ukuran itu beberapa hal yang sebelumnya dicatat sebagai "selera pemakai" atau diparkir menunggu
|
|
18
|
+
pemanggil kedua ternyata pekerjaan kit, dan menutupnya membuka apa yang ada di belakangnya.
|
|
19
|
+
|
|
20
|
+
Satu algoritma hidup di **empat salinan**. `#079` di 3.8.0 memperbaiki posisi panel popover di satu
|
|
21
|
+
di antaranya, dan tiga butir rilis ini adalah akibat langsungnya: salinan bertipe membawa cacat yang
|
|
22
|
+
sama untuk delapan komponen, salinan keempat di enhancer tabel memecah paritas begitu salinan
|
|
23
|
+
bertipe dibetulkan, dan memindahkan panel filter ke tempat yang benar membuka dua hal lagi. Satu di
|
|
24
|
+
antaranya sudah terkirim ke pengguna sejak lama tanpa ada yang melaporkannya: panel filter pada
|
|
25
|
+
tabel di dalam modal terbuka, tergambar, lalu menolak segala masukan.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **Cacat urutan `#079` ada di salinan bertipe juga** (`#081`), dan di sana ia kena delapan
|
|
29
|
+
komponen: `FdyDatepicker`, `FdyAutocomplete`, `FdyCascade` dan `FdyCombo`, masing-masing di Vue
|
|
30
|
+
dan React. `adapters/vue/usePopover.ts` dan padanannya di React membaca rect pemicu sebagai hal
|
|
31
|
+
pertama, sama seperti jalur mentah sebelum 3.8.0. Terukur: panel mendarat 741 lawan pemicu 756,
|
|
32
|
+
selisih tepat satu lebar scrollbar klasik. `FdyTableFilter` lolos, dan sebabnya menarik:
|
|
33
|
+
`.fdy-filter` satu-satunya panel kit yang `position:fixed`, jadi dokumen tidak pernah tumbuh dan
|
|
34
|
+
pemicunya tidak pernah bergeser.
|
|
35
|
+
- **Panel filter pada tabel di dalam modal tidak bisa diisi** (`#082`). `showModal()` membuat
|
|
36
|
+
segalanya di luar subtree dialog `inert`, dan panel jalur mentah menempel ke `document.body`,
|
|
37
|
+
yaitu di luar itu. Panelnya terbuka, tergambar, lalu menolak fokus dan klik: `focus()` milik
|
|
38
|
+
enhancer sendiri jadi no-op dan `elementFromPoint` di tengah panel mengembalikan sel tabel di
|
|
39
|
+
belakangnya. Rect tidak bisa melihat ini, jadi yang menemukannya hit test. Panelnya kini anak
|
|
40
|
+
`<th>`-nya sendiri seperti `FdyTableFilter` bertipe, dan salinan keempat logika posisi yang
|
|
41
|
+
memosisikannya ikut terhapus.
|
|
42
|
+
- **Panel filter mewarisi tipografi sel header** (`#083`). Dari sepuluh deklarasi `.fdy-table th`,
|
|
43
|
+
tujuh diwariskan, dan empat sudah ditimpa sejak terbit. Yang lolos: `white-space:nowrap`, yang
|
|
44
|
+
membuat nilai enum panjang melebar sampai `max-width` alih-alih membungkus, plus `font-size` dan
|
|
45
|
+
`text-align`. Sumber kedua dari sel yang sama: `.fdy-table__num` menambahkan
|
|
46
|
+
`font-family:var(--font-mono)`, jadi judul panel tercetak dengan huruf data.
|
|
47
|
+
- **Panel filter jalur merosot terkurung konteks penumpukan sel header** (`#084`).
|
|
48
|
+
`.fdy-table--sticky-head thead th` adalah `position:sticky;z-index:2`, yang membuat konteks
|
|
49
|
+
penumpukan, jadi `z-index:130` milik panel diurutkan di dalam angka 2 itu. Jalur popover lolos
|
|
50
|
+
sebab top layer tidak peduli konteks penumpukan, tetapi Popover API baru ada di Chrome 114,
|
|
51
|
+
Safari 17 dan Firefox 125 sementara lantai dukungan kit Chrome 105, Safari 15.4 dan Firefox 121.
|
|
52
|
+
Terukur di jalur merosot: panel tumpang tindih 224x49px dengan `.fdy-bottomnav`, dan 49px itu
|
|
53
|
+
persis tombol Reset dan Close. Panelnya kini dipasang di akar tabel dan diposisikan lewat
|
|
54
|
+
`place()`. Menaikkan angkanya tidak akan menolong, sebab angka 2 itu yang menahan header beku di
|
|
55
|
+
atas sel badan beku dan menaikkannya di atas 50 akan mencetak seluruh baris header di atas pita
|
|
56
|
+
bawah.
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
- **`FdyTable` meneruskan keenam prop teks pita kakinya** (`#080`), di ketiga adapter. 3.8.0 memberi
|
|
60
|
+
`FdyTableFooter` prop itu, tetapi tabel merender pita kakinya sendiri tanpa meneruskannya, jadi
|
|
61
|
+
app non-Inggris yang memakai pita kaki bawaan tetap Inggris dan jalan keluarnya satu-satunya
|
|
62
|
+
`pager={false}` plus merender pita kakinya sendiri. Prop datar, bukan objek, sebab penjaga
|
|
63
|
+
dokumentasi membaca nama prop satu per satu dan sebuah objek akan menyembunyikan isinya dari
|
|
64
|
+
penjaga itu.
|
|
65
|
+
- **Perataan untuk popover bertipe** (`#081`). `align: 'start' | 'end'`, nama yang sama dengan jalur
|
|
66
|
+
mentah, sebagai argumen posisi keempat `usePopover`. Objek opsi ditolak sebab literal objek di
|
|
67
|
+
`useEffect` React punya identitas baru setiap render sementara primitif masuk ke deps tanpa
|
|
68
|
+
menuntut `useMemo` dari pemanggil. Yang memilih perataan komponennya sendiri, jadi `FdyTable`
|
|
69
|
+
tidak bertambah prop: `FdyTableFilter` menulis `'end'` karena ia memang selalu duduk di header
|
|
70
|
+
kolom.
|
|
71
|
+
- **`matchWidth` di jalur bertipe** (`#082`). Adapter selalu menulis `minWidth` selebar pemicu, dan
|
|
72
|
+
untuk corong filter 24px nilai inline itu mengalahkan `min-width:14rem` di stylesheet: panel
|
|
73
|
+
terukur 206px alih-alih 224. Yang lebarnya bergerak hanya `FdyTableFilter`, jadi hanya filter yang
|
|
74
|
+
mengirim `matchWidth:false`. Keempat panel lain memang harus tetap memakai bawaannya, dan
|
|
75
|
+
alasannya mekanisme yang sama dengan `#079`: ketiga listbox `min-width:100%`, dan di top layer
|
|
76
|
+
`100%` itu lebar viewport.
|
|
77
|
+
- **`loadingText` di `FdyTable` Vue dan React** (`#085`). Blazor punya `LoadingText` sejak awal,
|
|
78
|
+
kedua adapter lain menulis `Loading…` sebagai literal di templat. Sapuan yang menemukannya juga
|
|
79
|
+
mencatat dua belas string di panel filter kolom dan satu nama aksesibel di `FdyCfl` yang masih
|
|
80
|
+
tertutup di ketiga adapter sekaligus; keduanya tercatat dengan nomor baris untuk rilis berikutnya.
|
|
81
|
+
- **`FdyMonthpicker`** (`#086`), di ketiga adapter. 3.8.0 menambahkan `mode="month"` di
|
|
82
|
+
`FdyDatepicker`, dan bentuk itu membuat satu prop membawa dua format dengan prop lain yang memilih
|
|
83
|
+
format mana. Komponen tersendiri menutupnya dengan hitungan prop, bukan dengan alias tipe: tidak
|
|
84
|
+
ada `mode` yang bisa mengubah arti `modelValue`. Gridnya diangkat ke `adapters/core/month-grid.js`
|
|
85
|
+
dan dipakai balik oleh `FdyDatepicker`, jadi implementasi grid bulan di kit tetap dua, bukan tiga;
|
|
86
|
+
kedua datepicker menyusut 87 dan 82 baris. Jalur mentah tidak diubah dan `data-mode="month"` tidak
|
|
87
|
+
ditandai usang, sebab atribut string tidak punya tipe yang bisa berbohong soal isi `data-value`.
|
|
88
|
+
|
|
89
|
+
### Deprecated
|
|
90
|
+
- **`mode` di `FdyDatepicker`** (`#086`), bersama `FdyDatepickerMode.Month` dan parameter `Mode` di
|
|
91
|
+
Blazor. Tetap bekerja persis seperti di 3.8.0, dan ada uji paritas sel demi sel yang menjaga janji
|
|
92
|
+
itu. Dihapus di 4.0. Penandaannya lewat tipe dan bukan prosa, satu mekanisme per stack
|
|
93
|
+
(`@deprecated` di Vue dan React, `[Obsolete]` di Blazor), masing-masing menyebut penggantinya, dan
|
|
94
|
+
ada uji yang menuntut nama pengganti itu tetap ada di sana. Repo ini belum pernah memensiunkan apa
|
|
95
|
+
pun, jadi ini presedennya.
|
|
96
|
+
|
|
97
|
+
### Catatan: lencana netral sudah ada
|
|
98
|
+
Catatan app yang jadi sumber 3.8.0 dan 3.9.0 meminta `fdy-badge--neutral` untuk status yang tidak
|
|
99
|
+
boleh terbaca seperti aksi. Kelas itu tidak dibuat, sebab `.fdy-badge--outline` sudah menjawabnya:
|
|
100
|
+
tintanya 7.46:1 di tema terang dan 7.28:1 di gelap terhadap permukaan, jadi ia sudah lewat gerbang
|
|
101
|
+
kontras pada ambang teks untuk ketiga permukaan. Yang ditambahkan dokumentasi yang mengatakannya,
|
|
102
|
+
plus penjaga yang mengikat dokumen itu ke deklarasinya. Separuh usul yang lain, menjadikan netral
|
|
103
|
+
sebagai **bawaan** `.fdy-badge`, tetap milik 4.0 sebab ia mengubah tampilan setiap lencana yang
|
|
104
|
+
sudah terkirim.
|
|
105
|
+
|
|
106
|
+
### Catatan: dokumen yang menjelaskan varian yang tidak bekerja
|
|
107
|
+
`COMPONENTS.md` menulis bahwa `.fdy-table__num` meratakan ke kanan dan memasangnya di `<th>` pada
|
|
108
|
+
dua contoh. Kelas itu berspesifisitas `(0,1,0)` sementara `.fdy-table th` `(0,2,0)`, jadi di sel
|
|
109
|
+
header huruf data dan angka tabularnya sampai tetapi `text-align:left` dari aturan header yang
|
|
110
|
+
menang. Keduanya lahir di commit yang sama pada Juli dan kalimat dokumennya baru datang pada
|
|
111
|
+
Agustus, tidak ada uji yang pernah menegaskan perataan header lewat kelas itu, dan docs hidup kit
|
|
112
|
+
mengirim bentuk itu di delapan tempat dengan header rata kiri sejak saat itu. Jadi yang dibetulkan
|
|
113
|
+
dokumennya: perilakunya dibiarkan, sebab mengubahnya menggeser setiap header kolom angka yang sudah
|
|
114
|
+
terkirim.
|
|
115
|
+
|
|
116
|
+
## [3.8.0] - 2026-10-23
|
|
117
|
+
|
|
118
|
+
**Empat penambahan dari catatan yang sama, dan tiga di antaranya ternyata sudah separuh ada di
|
|
119
|
+
kit.** App ronda yang menemukan Safari 15 di 3.7.0 menulis delapan butir; dua di antaranya cacat,
|
|
120
|
+
dan keduanya sudah terbit. Empat dari yang tersisa penambahan, dan keempatnya lebih murah daripada
|
|
121
|
+
tampaknya: satu kelas yang sudah ada sejak lama tetapi kehilangan pengaruhnya, satu jalan keluar
|
|
122
|
+
teks yang cuma dipasang di satu jalur, satu grid yang terpasang sejak 1.32.0 tanpa pernah bisa jadi
|
|
123
|
+
tujuan, dan satu varian sumbu yang papan tombolnya kurang dua literal.
|
|
124
|
+
|
|
125
|
+
Yang terakhir membuka cacat yang digambarkan catatan itu tanpa pelapornya tahu ia sedang
|
|
126
|
+
menggambarkan kit. Ia menulis bahwa `<input type="month">` menamai bulan menurut bahasa peramban,
|
|
127
|
+
bukan `<html lang>`. Kalimat itu berlaku kata per kata untuk `FdyDatepicker` Vue dan React: dokumen
|
|
128
|
+
sudah menjanjikan `<html lang>` sejak lama, cuma enhancer vanilla yang betul melakukannya, dan
|
|
129
|
+
kedua adapter menyerahkan `props.locale` yang `undefined` langsung ke `Intl`.
|
|
130
|
+
|
|
131
|
+
### Fixed
|
|
132
|
+
- **Popover memposisikan panelnya dari tata letak yang ia batalkan tiga baris kemudian** (`#079`),
|
|
133
|
+
ada sejak 1.4.1 dan kena keenam enhancer yang memakai `attach()`: autocomplete, cascade, combo,
|
|
134
|
+
datepicker, menu dan timepicker. Keenam panel itu `position:absolute` dengan
|
|
135
|
+
`top:calc(100% + var(--space-1))` di stylesheet komponennya, dan aturan author mengalahkan gaya
|
|
136
|
+
UA popover, jadi panel yang baru di-`show()` sudah di top layer sambil masih memakai aturan itu.
|
|
137
|
+
Di top layer `100%` adalah tinggi viewport. Selama satu fungsi sinkron panelnya duduk satu
|
|
138
|
+
viewport di bawah halaman dan overflow dokumen tumbuh mengikutinya, terukur `scrollHeight` 1023
|
|
139
|
+
lawan viewport 900. Di tempat scrollbar memakan ruang tata letak, yakni setiap peramban Windows
|
|
140
|
+
dan Linux, dokumen mendapat scrollbar untuk sesaat, `clientWidth` menyusut selebar scrollbar itu,
|
|
141
|
+
halaman relayout, dan pemicunya bergeser bersamanya. `place()` membaca rect pemicu sebagai hal
|
|
142
|
+
pertama yang ia lakukan, jadi yang ia ukur adalah tata letak sementara itu. Pengukurannya
|
|
143
|
+
sekarang terjadi sesudah panel keluar dari flow.
|
|
144
|
+
|
|
145
|
+
macOS memakai scrollbar overlay yang tidak memakan ruang, jadi mesin tempat kit ini ditulis
|
|
146
|
+
secara struktural tidak bisa melihat cacat ini, dan 1.4.1 sampai 3.7.0 mengirimnya tanpa ada yang
|
|
147
|
+
tahu. Yang melihatnya runner Linux di CI, lewat uji perataan `#074` yang kebetulan mengukur
|
|
148
|
+
koordinat di sisi kanan layar. Reproduksinya sekarang ada di suite dan berjalan di macOS juga,
|
|
149
|
+
dengan menata `::-webkit-scrollbar` ke lebar tetap, plus assertion yang menolak lulus kalau
|
|
150
|
+
enginenya ternyata tidak memberi scrollbar ruang tata letak.
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
- **`align` di popover, dibacakan dari `.fdy-menu--end`** (`#074`). Kelasnya sudah ada sejak lama
|
|
154
|
+
dan stylesheet selalu menghormatinya lewat `left:auto;right:0`. Jalur popover menggantinya dengan
|
|
155
|
+
koordinat `fixed` inline, jadi kelas itu kehilangan pengaruh persis pada saat menu masuk top
|
|
156
|
+
layer: menu akun di talang kanan melewati avatarnya dan berhenti 4px dari tepi layar. Nol kelas
|
|
157
|
+
baru. `freeday-menu.js` yang membaca kelasnya, sebab popover dipakai enam enhancer dan tidak
|
|
158
|
+
boleh tahu nama kelas satu pun. Jepitan viewport sekarang dua sisi: lantai `GAP` yang lama duduk
|
|
159
|
+
di dalam cabang jepitan kanan dan tak pernah berjalan untuk panel rata-kanan, jadi rata-kanan di
|
|
160
|
+
layar sempit cuma akan menukar luber kanan dengan luber kiri.
|
|
161
|
+
- **Enam prop teks di `FdyTableFooter`** (`#075`), di ketiga adapter. `data-fdy-text-<key>` sudah
|
|
162
|
+
lama membuka jalur mentah dan jalur bertipe tidak punya padanannya, jadi app yang tidak berbahasa
|
|
163
|
+
Inggris hanya bisa menulis ulang seluruh pita kakinya. Satu app mengirim 81 baris markup sendiri
|
|
164
|
+
untuk mengganti lima kata. Namanya mengikuti kunci `TEXT` enhancer di mana kuncinya ada (`info`,
|
|
165
|
+
`prev`, `next`), supaya satu teks tidak punya dua nama di dua jalur; tiga sisanya tanpa kembaran
|
|
166
|
+
sebab di jalur mentah markup itu milik app sendiri. Bawaan tetap Inggris di setiap jalur.
|
|
167
|
+
`FdyTable` masih merender pita kakinya sendiri tanpa meneruskan keenamnya; jalan keluarnya
|
|
168
|
+
`pager={false}` lalu render `<FdyTableFooter>` sendiri, dan COMPONENTS.md menuliskannya.
|
|
169
|
+
- **`mode="month"` di `FdyDatepicker`** (`#076`), di ketiga adapter dan jalur mentah. Grid bulannya
|
|
170
|
+
sudah dipasang 1.32.0, hanya tak pernah bisa jadi tujuan. Menjawab `YYYY-MM`, menerima `min` dan
|
|
171
|
+
`max` dalam bentuk yang sama, dan berhenti di grid itu alih-alih turun ke hari. Nama acara dan
|
|
172
|
+
`detail.date` tidak berubah.
|
|
173
|
+
- **`.fdy-tabs--vertical`, dan sumbu papan tombol dari `aria-orientation`** (`#077`). Kelasnya
|
|
174
|
+
memutuskan tampilan, atributnya memutuskan panah, dan satu penanda cukup: enhancer mengisi
|
|
175
|
+
atribut dari kelas kalau markup belum menulisnya, sementara atribut yang sudah ditulis selalu
|
|
176
|
+
menang. Panah kiri dan kanan tidak dipetakan di tegak, mengikuti APG satu sumbu per tablist, dan
|
|
177
|
+
tombol di luar sumbu jatuh tanpa `preventDefault` sehingga tetap milik halaman. Lebar kolom lewat
|
|
178
|
+
`--fdy-tabs-list-w`, bawaan `auto`. Tabs tidak punya wrapper bertipe di stack mana pun, jadi tidak
|
|
179
|
+
ada prop `orientation` yang bisa ditambahkan dan tidak ada wrapper baru yang dibuat demi punya
|
|
180
|
+
tempat menaruhnya.
|
|
181
|
+
|
|
182
|
+
### Changed
|
|
183
|
+
- **`FdyDatepicker` Vue dan React membaca `<html lang>` kalau `locale` tidak dikirim** (`#078`).
|
|
184
|
+
Ini satu-satunya perubahan perilaku di rilis ini, dan ia kena mode hari juga, bukan cuma mode
|
|
185
|
+
bulan. Sebelumnya `undefined` sampai ke `Intl` apa adanya, yang berarti bahasa peramban: halaman
|
|
186
|
+
Indonesia yang dibuka di peramban Inggris menulis nama bulan Inggris, dan halaman Inggris di
|
|
187
|
+
peramban Indonesia menulis nama bulan Indonesia. Kontrak yang tertulis selalu `<html lang>`, dan
|
|
188
|
+
aturan "Inggris di semua jalur" tidak bisa ditegakkan oleh nilai yang datang dari preferensi
|
|
189
|
+
pembaca. App yang memang menginginkan bahasa peramban mengirim `locale` sendiri. Halaman tanpa
|
|
190
|
+
`lang` jatuh ke `en`, dan `lang=""` ikut ke sana sebab `Intl` melempar `RangeError` atas string
|
|
191
|
+
kosong.
|
|
192
|
+
|
|
193
|
+
### Catatan dukungan peramban
|
|
194
|
+
Floor-nya tetap Chrome 105, Safari 15.4 dan Firefox 121. Tab tegak sengaja tidak memakai `:has()`
|
|
195
|
+
walau itu akan memendekkan CSS-nya, sebab `:has()` baru ada di Safari 15.4 sementara lantai yang
|
|
196
|
+
pernah dilaporkan dari perangkat nyata adalah iOS 15.0.
|
|
197
|
+
|
|
198
|
+
### Yang perlu dibuang app saat upgrade
|
|
199
|
+
- Tambalan `translate` apa pun yang menggeser menu rata-kanan ke dalam talangnya. Menu itu sekarang
|
|
200
|
+
diposisikan benar, jadi tambalannya menggeser sejauh satu talang ke arah yang salah.
|
|
201
|
+
- Markup pita kaki tabel yang ditulis tangan cuma karena teksnya tak bisa diganti, berikut CSS
|
|
202
|
+
lokal yang menemaninya.
|
|
203
|
+
|
|
204
|
+
### Belum masuk
|
|
205
|
+
Butir keempat catatan itu, bawaan `.fdy-badge` yang berwarna primer sehingga status netral terbaca
|
|
206
|
+
seperti aksi, menunggu 4.0: mengubah bawaannya mengubah tampilan setiap lencana yang sudah terkirim.
|
|
207
|
+
Pilihan yang tidak mematahkan apa pun, `fdy-badge--neutral` sebagai opt-in, masih terbuka lebih awal.
|
|
208
|
+
|
|
12
209
|
## [3.7.0] - 2026-10-23
|
|
13
210
|
|
|
14
211
|
**Kit ini tidak bisa dipakai di Safari 15, dan yang menemukannya adalah sebuah HP.** Pemilik sebuah
|
package/COMPONENTS.md
CHANGED
|
@@ -34,9 +34,9 @@ live docs.
|
|
|
34
34
|
invalid state does not belong to the control itself.
|
|
35
35
|
6. **Interactive components need their enhancer script.** Static ones (button, card, badge, table,
|
|
36
36
|
alert, breadcrumb, timeline, accordion, tree-without-cascade…) are CSS-only.
|
|
37
|
-
7. **On Vue, React or Blazor,
|
|
38
|
-
`FdyDatepicker` · `
|
|
39
|
-
`FdyTable` · `FdyModal` · `FdyDrawer` · `FdyAppShell`, from `@cahyo-dimas/freeday/vue`, `/react`, or the
|
|
37
|
+
7. **On Vue, React or Blazor, twelve components have a typed wrapper. Use it.** `FdyCombo` ·
|
|
38
|
+
`FdyDatepicker` · `FdyMonthpicker` · `FdyDateRange` · `FdyAutocomplete` · `FdyCascade` · `FdyCfl` ·
|
|
39
|
+
`FdyChart` · `FdyTable` · `FdyModal` · `FdyDrawer` · `FdyAppShell`, from `@cahyo-dimas/freeday/vue`, `/react`, or the
|
|
40
40
|
`Freeday.Blazor` RCL. Each is flagged at its own section below. Hand-writing their raw markup in
|
|
41
41
|
those stacks *looks* right at first, because the enhancer initialises once and the first render is
|
|
42
42
|
correct. Then
|
|
@@ -510,8 +510,10 @@ WAI-ARIA APG menu-button. Keyboard ↑/↓/Home/End/Esc handled by the enhancer.
|
|
|
510
510
|
|
|
511
511
|
- Wrapper: `.fdy-menu-wrap` (or `.fdy-btn-split` for a split button, with
|
|
512
512
|
`.fdy-btn-split__toggle` as the second button) + `data-fdy-menu`
|
|
513
|
-
- List: `.fdy-menu` (`--end`
|
|
514
|
-
|
|
513
|
+
- List: `.fdy-menu` (`--end` puts the panel's **right edge on the trigger's right edge**, which is
|
|
514
|
+
what a control parked in a page gutter needs; on a screen too narrow for that the panel slides
|
|
515
|
+
back inside the viewport instead of hanging off the left edge), items `.fdy-menu__item`
|
|
516
|
+
(`--danger`), separator `<hr class="fdy-menu__sep">`
|
|
515
517
|
- A11y: trigger gets `aria-haspopup="menu"` + `aria-expanded="false"`; `<ul role="menu" hidden>`;
|
|
516
518
|
each `<li role="none">` wraps a `<button role="menuitem">`.
|
|
517
519
|
|
|
@@ -526,6 +528,15 @@ WAI-ARIA APG menu-button. Keyboard ↑/↓/Home/End/Esc handled by the enhancer.
|
|
|
526
528
|
</div>
|
|
527
529
|
```
|
|
528
530
|
|
|
531
|
+
`.fdy-menu--end` is honoured on both of the paths an open menu can take. The stylesheet handles the
|
|
532
|
+
one where the panel stays in the flow, and the enhancer handles the other: it lifts the panel into
|
|
533
|
+
the top layer so it escapes any ancestor overflow clip, and positions it there with coordinates of
|
|
534
|
+
its own, so the modifier has to be read by `freeday-popover.js` too. An app reported what happens
|
|
535
|
+
when it is not. An account menu beside its avatar in a 24px gutter opened 20px further right than
|
|
536
|
+
the avatar and stopped 4px off the screen edge, and the app's own `translate` patch for it was keyed
|
|
537
|
+
to a constant inside the kit. For a control the kit does not ship, the same option is
|
|
538
|
+
`FreedayPopover.attach(panel, trigger, { align: 'end' })`.
|
|
539
|
+
|
|
529
540
|
---
|
|
530
541
|
|
|
531
542
|
# Forms
|
|
@@ -942,9 +953,10 @@ a new class.
|
|
|
942
953
|
|
|
943
954
|
| Prop | Type | What it does |
|
|
944
955
|
|---|---|---|
|
|
945
|
-
| `modelValue` · `value` | `string \| null` | ISO `YYYY-MM-DD`, or `''`/`null` for empty. |
|
|
956
|
+
| `modelValue` · `value` | `string \| null` | ISO `YYYY-MM-DD`, or `YYYY-MM` under `mode="month"`. `''`/`null` for empty. |
|
|
946
957
|
| `onChange` | `(value: string) => void` | React. Vue emits `update:modelValue` and `change`. Clearing sends `''`. |
|
|
947
|
-
| `
|
|
958
|
+
| `mode?` | `'day' \| 'month'` | **Deprecated, use `<FdyMonthpicker>`.** `day` is the calendar as it always was. `month` opens on the month grid, stops there, and answers with `YYYY-MM`. Blazor takes the enum `FdyDatepickerMode`. Still works, unchanged; removed in 4.0. See below for why, and for the raw path, which keeps `data-mode="month"`. |
|
|
959
|
+
| `min?` · `max?` | `string` | ISO bounds; days outside them cannot be picked. Month mode also accepts `YYYY-MM`, and a bound inside a month leaves that month pickable. |
|
|
948
960
|
| `locale?` | `string` | BCP-47 tag deciding month and weekday names. Defaults to the page's `<html lang>`, then `en`. |
|
|
949
961
|
| `placeholder?` | `string` | |
|
|
950
962
|
| `id?` · `ariaLabelledby?` · `describedby?` | `string` | Trigger id; the element that labels it; the help/error text it points at. |
|
|
@@ -969,6 +981,33 @@ Blazor carries the same surface bar four: `locale` (the enhancer formats through
|
|
|
969
981
|
page's `<html lang>`), `clearable` and `clearLabel` (this path builds no clear button), and
|
|
970
982
|
`ariaLabelledby` (the trigger it builds is named by `Label`).
|
|
971
983
|
|
|
984
|
+
### Props — `<FdyMonthpicker>`
|
|
985
|
+
|
|
986
|
+
> Vue (`v-model`) · React (`value`/`onChange`) · Blazor (`@bind-Value`). It renders the same trigger
|
|
987
|
+
> and panel as the date picker, over the same classes, and shares its month and year grids.
|
|
988
|
+
|
|
989
|
+
| Prop | Type | What it does |
|
|
990
|
+
|---|---|---|
|
|
991
|
+
| `modelValue` · `value` | `string \| null` | The chosen month, ISO `YYYY-MM`. `''`/`null` for empty. One format, always: nothing on this component can change it. |
|
|
992
|
+
| `onChange` | `(value: string) => void` | React. Vue emits `update:modelValue` and `change`. Clearing sends `''`. |
|
|
993
|
+
| `min?` · `max?` | `string` | ISO `YYYY-MM` bounds (a full `YYYY-MM-DD` is read too, as its own month). A bound that falls inside a month leaves that month pickable. |
|
|
994
|
+
| `locale?` | `string` | BCP-47 tag deciding the month names. Defaults to the page's `<html lang>`, then `en`. |
|
|
995
|
+
| `placeholder?` | `string` | Shown while nothing is picked. Default `Select month`. |
|
|
996
|
+
| `id?` · `ariaLabelledby?` · `describedby?` | `string` | Trigger id; the element that labels it; the help/error text it points at. |
|
|
997
|
+
| `disabled?` · `readonly?` · `invalid?` | `boolean` | `readonly` keeps focus and tab order and shows its month, but it cannot be opened, cleared or changed. |
|
|
998
|
+
| `clearable?` | `boolean` | Show a × in the trigger once a month is set, so an optional month can be unset. Sends `''`. Off by default. |
|
|
999
|
+
| `clearLabel?` | `string` | Accessible name for that ×. Default `Clear month`. |
|
|
1000
|
+
| `prevYearLabel?` · `nextYearLabel?` | `string` | The arrows beside the month grid, which step a year. Defaults `Previous year` / `Next year`. |
|
|
1001
|
+
| `chooseYearLabel?` | `string` | The title button that drills into the year grid. Default `Choose year`. |
|
|
1002
|
+
| `prevYearsLabel?` · `nextYearsLabel?` | `string` | Page arrows, shown in the year grid. Defaults `Previous years` / `Next years`. |
|
|
1003
|
+
|
|
1004
|
+
There is no `prevMonthLabel` or `nextMonthLabel` here. The arrows sit beside a month grid, so they
|
|
1005
|
+
step a year, and the day grid they would have belonged to does not exist on this component. Blazor
|
|
1006
|
+
carries the same surface bar three: `locale` (the enhancer formats through `Intl` from the page's
|
|
1007
|
+
`<html lang>`), `clearable` and `clearLabel` (that path builds no clear button) and
|
|
1008
|
+
`ariaLabelledby` (the trigger it builds is named by `Label`). It adds `BackToMonthsLabel`, the year
|
|
1009
|
+
grid's own title.
|
|
1010
|
+
|
|
972
1011
|
### Props — `<FdyDateRange>`
|
|
973
1012
|
|
|
974
1013
|
| Prop | Type | What it does |
|
|
@@ -1013,7 +1052,39 @@ months, `Home`/`End` January/December, `PageUp`/`PageDown` a year, `Enter`/`Spac
|
|
|
1013
1052
|
year grid likewise: arrows ±1/±3 years, `Home`/`End` the ends of the page, `PageUp`/`PageDown` a
|
|
1014
1053
|
page of twelve.
|
|
1015
1054
|
|
|
1016
|
-
|
|
1055
|
+
**Picking a month, not a day.** `<FdyMonthpicker>` answers with `YYYY-MM`. The panel opens on the
|
|
1056
|
+
12-cell month grid, a click or `Enter` commits from it, and there is no day grid to fall into. The
|
|
1057
|
+
title still steps up to the year grid and back down, because a year is navigation and never an
|
|
1058
|
+
answer. On the raw path the same thing is `data-mode="month"` on a `data-fdy-datepicker` div.
|
|
1059
|
+
|
|
1060
|
+
This exists because `<input type="month">` names the month in the **browser's** language, which an
|
|
1061
|
+
Indonesian page read on an English phone cannot override. The month cells, the cell's accessible
|
|
1062
|
+
name and the value in the trigger all go through `Intl` with the page's `<html lang>`, the same
|
|
1063
|
+
route the day grid's month names have always taken, so `lang="id"` reads `Oktober 2026` with nothing
|
|
1064
|
+
configured. Keyboard in the month grid: arrows move one and three months, `Home`/`End` jump to
|
|
1065
|
+
January and December, `PageUp`/`PageDown` step a year, and `Enter`/`Space` commits. The cell keeps
|
|
1066
|
+
its name when it becomes selected and carries the state in `aria-selected`, as the day cells do.
|
|
1067
|
+
|
|
1068
|
+
**`mode="month"` on `<FdyDatepicker>` is deprecated, and still works.** It shipped in 3.8.0 and
|
|
1069
|
+
behaves exactly as it did, so nothing has to move today; it goes in 4.0. The type was the defect.
|
|
1070
|
+
That prop left `modelValue` / `value`, `min` and `max` each carrying `YYYY-MM-DD` or `YYYY-MM` while
|
|
1071
|
+
a *different* prop decided which, and a union discriminated by a sibling prop is a shape neither
|
|
1072
|
+
`defineProps<{...}>` nor an exported `FdyDatepickerProps` can hold. A separate component states it
|
|
1073
|
+
in one line per prop. A consumer's editor says so as well: `mode` is marked `@deprecated` in both
|
|
1074
|
+
adapters, and `Mode` together with `FdyDatepickerMode.Month` carries `[Obsolete]` in Blazor, each
|
|
1075
|
+
naming `FdyMonthpicker`.
|
|
1076
|
+
|
|
1077
|
+
**The raw path keeps `data-mode="month"`, and it is not deprecated.** `data-mode` is a string
|
|
1078
|
+
attribute with no type to be wrong about: a reader of the markup sees `data-mode` and `data-value`
|
|
1079
|
+
side by side and can tell what the value holds. The typed signatures were what could not say it, so
|
|
1080
|
+
the retirement is theirs. Shipping a second attribute would leave two spellings of one feature on
|
|
1081
|
+
the one path that never had the problem.
|
|
1082
|
+
|
|
1083
|
+
Nothing changes without either of them. A picker with no `mode` and no `data-mode` is the day picker
|
|
1084
|
+
it has always been, down to the `YYYY-MM-DD` it reports.
|
|
1085
|
+
|
|
1086
|
+
- Attributes: `data-value="YYYY-MM-DD"`, `data-mode="month"`, `data-placeholder`, `data-label`,
|
|
1087
|
+
`data-fdy-no-icon`
|
|
1017
1088
|
|
|
1018
1089
|
```html
|
|
1019
1090
|
<div class="fdy-field">
|
|
@@ -1021,6 +1092,12 @@ page of twelve.
|
|
|
1021
1092
|
<div data-fdy-datepicker data-value="2026-07-21" data-label="Invoice date"></div>
|
|
1022
1093
|
</div>
|
|
1023
1094
|
|
|
1095
|
+
<div class="fdy-field">
|
|
1096
|
+
<span class="fdy-label">Reporting month</span>
|
|
1097
|
+
<!-- Typed stacks: <FdyMonthpicker>, see its props above. -->
|
|
1098
|
+
<div data-fdy-datepicker data-mode="month" data-value="2026-10"></div>
|
|
1099
|
+
</div>
|
|
1100
|
+
|
|
1024
1101
|
<div class="fdy-daterange" data-fdy-daterange role="group" aria-label="Reporting period">
|
|
1025
1102
|
<div data-fdy-datepicker data-role="from" data-placeholder="From" data-label="Start date"></div>
|
|
1026
1103
|
<span class="fdy-daterange__sep" aria-hidden="true">–</span>
|
|
@@ -1199,7 +1276,16 @@ over the connector line, so omitting `__btn` cannot make the connector draw thro
|
|
|
1199
1276
|
## Table — `.fdy-table`
|
|
1200
1277
|
Semantic static table. Wrap in `.fdy-table-wrap` (bordered surface) or `.fdy-table-scroll`
|
|
1201
1278
|
(horizontal scroll). Always a `<caption>` (use `.fdy-visually-hidden` if it shouldn't show) and
|
|
1202
|
-
`<th scope="col">`. Numeric cells get `.fdy-table__num` (right-aligned, tabular
|
|
1279
|
+
`<th scope="col">`. Numeric body cells get `.fdy-table__num` (right-aligned, tabular figures, data
|
|
1280
|
+
font).
|
|
1281
|
+
|
|
1282
|
+
**`.fdy-table__num` aligns a `<td>`, not a `<th>`.** The class is specificity `(0,1,0)` and
|
|
1283
|
+
`.fdy-table th` is `(0,2,0)`, so on a header cell the data font and the tabular figures arrive but
|
|
1284
|
+
`text-align:left` from the header rule wins: the label stays left while the numbers under it go
|
|
1285
|
+
right. Put the class on the header anyway for the font, then add `style="text-align:right"` to move
|
|
1286
|
+
the label with them. That inline value is exactly what the typed `FdyTable` writes for a column with
|
|
1287
|
+
`align: 'right'`, and it is the only thing no ancestor rule can outrank. The class is deliberately
|
|
1288
|
+
left at its own weight: raising it would shift every numeric heading already shipped.
|
|
1203
1289
|
|
|
1204
1290
|
**`--striped`** zebras the even body rows. The tint is deliberately *half* a surface step, not the
|
|
1205
1291
|
full one hover uses, so hover still reads on a striped row instead of matching it — and stripe,
|
|
@@ -1220,7 +1306,7 @@ for 4.0.
|
|
|
1220
1306
|
<div class="fdy-table-wrap">
|
|
1221
1307
|
<table class="fdy-table">
|
|
1222
1308
|
<caption>Recent invoices</caption>
|
|
1223
|
-
<thead><tr><th scope="col">Invoice</th><th scope="col" class="fdy-table__num">Amount</th></tr></thead>
|
|
1309
|
+
<thead><tr><th scope="col">Invoice</th><th scope="col" class="fdy-table__num" style="text-align:right">Amount</th></tr></thead>
|
|
1224
1310
|
<tbody>
|
|
1225
1311
|
<tr><td>INV-1042</td><td class="fdy-table__num">1,240,000</td></tr>
|
|
1226
1312
|
</tbody>
|
|
@@ -1252,7 +1338,7 @@ sum, so measure them once after render (and on resize) and set the variable.
|
|
|
1252
1338
|
<caption class="fdy-visually-hidden">Exchange rates, August 2026</caption>
|
|
1253
1339
|
<thead><tr>
|
|
1254
1340
|
<th scope="col" class="fdy-table__freeze fdy-table__freeze--edge">Date</th>
|
|
1255
|
-
<th scope="col" class="fdy-table__num">USD</th>
|
|
1341
|
+
<th scope="col" class="fdy-table__num" style="text-align:right">USD</th>
|
|
1256
1342
|
</tr></thead>
|
|
1257
1343
|
<tbody>
|
|
1258
1344
|
<tr>
|
|
@@ -1281,7 +1367,7 @@ the two ever disagree.
|
|
|
1281
1367
|
| `label` | `string` | Header label. |
|
|
1282
1368
|
| `labelHidden` | `boolean` | Render the label for assistive tech only; the header cell looks empty. |
|
|
1283
1369
|
| `sortable` | `boolean` | Show a sort toggle in the header. |
|
|
1284
|
-
| `filter` | `'text' \| 'enum' \| 'number' \| 'date'` | Offer a column filter of this type (the funnel popover). |
|
|
1370
|
+
| `filter` | `'text' \| 'enum' \| 'number' \| 'date'` | Offer a column filter of this type (the funnel popover). The panel is several times wider than the funnel, so it hangs from the funnel's RIGHT edge and the rightmost column keeps the page's gutter; near the left edge of a narrow screen it slides right instead of off it. |
|
|
1285
1371
|
| `align` | `'left' \| 'right' \| 'center'` | Cell alignment. Default `left`. |
|
|
1286
1372
|
| `mono` | `boolean` | Render cells in the monospace data font (`.fdy-mono`). |
|
|
1287
1373
|
| `sortType` | `'text' \| 'number' \| 'date'` | Override the comparator; defaults to one derived from `filter`, else `text`. |
|
|
@@ -1322,7 +1408,7 @@ provide the state prop for a concern and you own that concern instead. React nam
|
|
|
1322
1408
|
| `pageIndex?` · `onPageIndexChange?` | `number` · `(index) => void` | Client mode: own the 0-based index while the table keeps filtering, sorting and paginating — this is what lets one external pager drive both a table and a card list. |
|
|
1323
1409
|
| `pageSizes?` · `onPageSizeChange?` | `readonly number[]` · `(size) => void` | Offer a rows-per-page control. Server mode reports the pick through the page event (read its `size`); client mode applies it itself and reports it too, so it works with nothing wired. |
|
|
1324
1410
|
| `pager?` | `boolean` | Default true. Turn it off to withhold the table's own footer when the screen renders one. |
|
|
1325
|
-
| `loading?` | `boolean` | Show the in-table loading
|
|
1411
|
+
| `loading?` · `loadingText?` | `boolean` · `string` | Show the in-table loading row, and the word in it. Default `Loading…`, the same name and the same default as Blazor's `LoadingText`, so a screen in another language translates the loading row the way it already translates the empty one instead of finding that only one of the two neighbouring states has a prop. |
|
|
1326
1412
|
| `emptyText?` · `empty?` | `string` · `ReactNode \| (ctx) => ReactNode` | The empty state as text, or as markup (React `empty`, Vue's `empty` slot). As a React function or through the Vue slot's props it receives `{ filtered, clearFilters }`: whether an active column filter is what emptied the table, and how to clear every filter (through `onFiltersChange` / `update:filters` when they are controlled). "No rows match" wants a Clear button, "nothing here yet" a Create one. |
|
|
1327
1413
|
| `ariaLabel?` | `string` | Names the table for assistive tech. |
|
|
1328
1414
|
| `rowActivatable?` · `onRowActivate?` | `boolean` · `(row) => void` | Rows become focusable and activate on click, Enter or Space. |
|
|
@@ -1335,14 +1421,16 @@ provide the state prop for a concern and you own that concern instead. React nam
|
|
|
1335
1421
|
| `bulkActions?` | `ReactNode` | React. Content in the bulk bar beside the count; Vue uses the `bulk-actions` slot (scoped: `keys`, `clear`), Blazor `BulkContent`. |
|
|
1336
1422
|
| `selectedText?` · `clearSelectionText?` | `string` | Bulk-bar copy. Defaults `{n} selected` (with `{n}` substituted) and `Clear`. |
|
|
1337
1423
|
| `selectAllLabel?` · `selectRowLabel?` · `bulkLabel?` | `string` | Accessible names for the header checkbox, each row checkbox, and the bulk-bar region. Defaults `Select all rows on this page`, `Select row`, `Bulk actions`. |
|
|
1424
|
+
| `infoText?` · `pageSizeLabel?` | `string` | The footer this table draws itself: its range line (`{from}`, `{to}`, `{total}` substituted, every occurrence) and the visible word beside its rows-per-page control. Same names, same substitution and the same English defaults as `<FdyTableFooter>` below, `Showing {from}–{to} of {total}` and `Rows`, so a screen that moves between the table's footer and its own moves a prop instead of rewriting it. |
|
|
1425
|
+
| `paginationLabel?` · `prevLabel?` · `nextLabel?` · `pageLabel?` | `string` | The accessible names inside that footer: the pager landmark, both arrows, and a numbered page button (`{n}` substituted). Defaults `Pagination`, `Previous page`, `Next page`, `Go to page {n}`. |
|
|
1338
1426
|
| `renderCell?` | `(column, row, value) => ReactNode` | React. Custom cell rendering; Vue uses the `cell` slot. |
|
|
1339
1427
|
| `toolbar?` | `ReactNode` | React. Content above the table; Vue uses the `toolbar` slot. |
|
|
1340
1428
|
| `onProcess?` | `(result: { rows, total }) => void` | React. The processed page after filter/sort/paginate, in **both** modes. Vue emits `process`, Blazor calls it `Process`. Render the same set elsewhere — a card list, a summary, a CSV export — without re-deriving the pipeline. |
|
|
1341
1429
|
|
|
1342
|
-
Blazor matches this surface (`
|
|
1343
|
-
`
|
|
1344
|
-
|
|
1345
|
-
`
|
|
1430
|
+
Blazor matches this surface (`EmptyContent` in place of `empty`, `Toolbar` and `RowDetail` as
|
|
1431
|
+
`RenderFragment`s) and is the one adapter that also exposes `FiltersChanged`. Its filter-aware
|
|
1432
|
+
empty state is `EmptyTemplate`, a `RenderFragment<FdyTableEmptyContext>` (`Filtered`,
|
|
1433
|
+
`ClearFilters`) that wins over `EmptyContent`.
|
|
1346
1434
|
|
|
1347
1435
|
**Select-all ticks the current page, not the whole result set.** A header checkbox that silently
|
|
1348
1436
|
selects rows the reader cannot see is how a bulk delete goes wrong, so it acts on the rows on
|
|
@@ -1408,6 +1496,21 @@ pagination. Needs `freeday-table.js`. Wrap the whole thing in `.fdy-datatable` +
|
|
|
1408
1496
|
labels, the timepicker's, the cascade's two defaults, the table's `Min`/`Maks` filter
|
|
1409
1497
|
placeholders and the chart's `Seri 1` all sat outside it.
|
|
1410
1498
|
|
|
1499
|
+
**Where a filter panel opens, on both paths.** `.fdy-filter` is lifted into the top layer and
|
|
1500
|
+
anchored to its funnel by `freeday-popover.js` on the raw path and by `usePopover` on the typed one,
|
|
1501
|
+
so the raw path needs that file loaded beside `freeday-table.js` (`dist/freeday.js` has both). Four
|
|
1502
|
+
things follow, and the raw path moved to meet the typed one in 3.9.0 rather than the other way
|
|
1503
|
+
round. The panel's RIGHT edge lines up with the funnel's, because a 24px icon in a column header is
|
|
1504
|
+
no guide to where a 14rem form should hang, and the rightmost column is where that used to push the
|
|
1505
|
+
panel off the page; it is clamped to stay on screen either way, so the leftmost column slides right
|
|
1506
|
+
instead of hanging off. It sits 4px below the funnel, the kit's one gap, where the raw path used to
|
|
1507
|
+
keep 4px below and 8px from the viewport edge. It flips above the funnel when there is no room
|
|
1508
|
+
below, which the raw path never did. And it keeps the `min-width:14rem` the stylesheet asks for,
|
|
1509
|
+
which the typed path used to lose to an inline floor taken from the funnel's own 24px. The panel is
|
|
1510
|
+
a child of its own `<th>` on both paths: the top layer is what escapes a card's `overflow:hidden`
|
|
1511
|
+
now, and a panel parked on `<body>` is outside any open modal `<dialog>`, which makes it inert, so a
|
|
1512
|
+
filter in a table inside a modal painted a form that refused every click.
|
|
1513
|
+
|
|
1411
1514
|
**Testing note:** a column's filter button and the dialog it opens deliberately share one
|
|
1412
1515
|
accessible name (`Filter <column>`), since a dialog named after its trigger is the normal pattern. In a
|
|
1413
1516
|
Playwright/Testing-Library suite that means `getByLabel('Filter Name')` resolves to two elements;
|
|
@@ -1440,6 +1543,32 @@ render it once, outside both, with `pager={false}` on the table.
|
|
|
1440
1543
|
| `page` | `FdyPageState` = `{ index, size, total }` | The page being shown. `size` drives both the range text and the rows-per-page control's value. |
|
|
1441
1544
|
| `onPageChange?` | `(page: FdyPageState) => void` | React. A page click or a size change. Vue emits `update:page`; Blazor `PageChanged`. |
|
|
1442
1545
|
| `pageSizes?` | `readonly number[]` | Offer a rows-per-page control. Omit it and the footer is range + pager only. Picking a size reports the new `size` together with the index that still holds the row the reader was looking at. |
|
|
1546
|
+
| `infoText?` | `string` | The range line, with `{from}`, `{to}` and `{total}` substituted. Default `Showing {from}–{to} of {total}`, the enhancer's `info` string. |
|
|
1547
|
+
| `pageSizeLabel?` | `string` | The word beside the rows-per-page control, which also names that control for a screen reader. Default `Rows`. |
|
|
1548
|
+
| `paginationLabel?` | `string` | Accessible name of the pager landmark. Default `Pagination`. |
|
|
1549
|
+
| `prevLabel?` | `string` | Accessible name of the previous-page arrow. Default `Previous page`. |
|
|
1550
|
+
| `nextLabel?` | `string` | Accessible name of the next-page arrow. Default `Next page`. |
|
|
1551
|
+
| `pageLabel?` | `string` | Accessible name of a numbered page button, with `{n}` substituted. Default `Go to page {n}`. |
|
|
1552
|
+
|
|
1553
|
+
**Saying it in another language.** The raw path has had `data-fdy-text-<key>` since 1.39.0 and this
|
|
1554
|
+
one had nothing, so a Vue, React or Blazor app that does not speak English could only rewrite the
|
|
1555
|
+
band by hand. One did: eighty one lines of its own markup, to change five words. The six string
|
|
1556
|
+
props above are the hatch that was missing. Where the enhancer already keys a string the prop
|
|
1557
|
+
carries that key's name, `infoText` for `info`, `prevLabel` for `prev`, `nextLabel` for `next`,
|
|
1558
|
+
and the same `{from}` `{to}` `{total}` `{n}` substitution, every occurrence replaced, so an app on
|
|
1559
|
+
both paths writes one wording once. The other three have no enhancer twin because on the raw path
|
|
1560
|
+
that markup is the app's own: the word beside the rows-per-page control, the pager landmark's name,
|
|
1561
|
+
and the name of a numbered page button. It is `pageSizeLabel` and not `rows`, since the enhancer's
|
|
1562
|
+
`rows` key is the table's `{n} rows` count, a different string.
|
|
1563
|
+
|
|
1564
|
+
Omit a prop and you get the English above, so nothing already shipped moves. `FdyTable` takes all
|
|
1565
|
+
six under the same names and hands them to the footer it draws itself, so a table keeps its own
|
|
1566
|
+
footer and still speaks the app's language. For one version it did not, and the only way out was to
|
|
1567
|
+
switch the footer off: a hatch that opened only if you stopped using the table.
|
|
1568
|
+
|
|
1569
|
+
`pager={false}` plus your own `<FdyTableFooter>` is still the route for a **responsive** list, where
|
|
1570
|
+
one band has to serve a table at `lg` and a card list below it, and that is now the only reason to
|
|
1571
|
+
reach for it. Wording alone is not one.
|
|
1443
1572
|
|
|
1444
1573
|
**Who draws the pager.** The table renders its own footer (range + pager) whenever there is more
|
|
1445
1574
|
than one page. Two ways to take it over:
|
|
@@ -1832,10 +1961,15 @@ record. A crumb for a menu group that has no page of its own is plain text in th
|
|
|
1832
1961
|
to nowhere.
|
|
1833
1962
|
|
|
1834
1963
|
## Tabs — `.fdy-tabs`
|
|
1835
|
-
APG tabs:
|
|
1964
|
+
APG tabs: arrows, Home/End, roving tabindex. Needs `freeday-tabs.js`.
|
|
1836
1965
|
`.fdy-tabs__tab` marks its active state from **`aria-selected="true"` or `aria-current="page"`**,
|
|
1837
1966
|
so the same look serves routed sub-navigation built from plain links. See the note under Nav.
|
|
1838
1967
|
|
|
1968
|
+
**Which arrows move the selection** comes from the tablist's `aria-orientation`: Left and Right by
|
|
1969
|
+
default, Up and Down when it says `vertical`. One axis per tablist, the way APG maps it. The other
|
|
1970
|
+
pair stays unmapped, so Left and Right over a column of tabs still reach whatever else on the page
|
|
1971
|
+
wants them. Home and End answer either way.
|
|
1972
|
+
|
|
1839
1973
|
> **The strip's line is an inset `box-shadow`, not a `border-bottom`** (`#052`; through 3.1.0 it
|
|
1840
1974
|
> was a border, so an app upgrading past that release changes the override with it). To
|
|
1841
1975
|
> remove it — a strip inside a header that draws its own rule — override `box-shadow:none`;
|
|
@@ -1857,6 +1991,56 @@ so the same look serves routed sub-navigation built from plain links. See the no
|
|
|
1857
1991
|
</div>
|
|
1858
1992
|
```
|
|
1859
1993
|
|
|
1994
|
+
### Vertical — `.fdy-tabs--vertical`
|
|
1995
|
+
|
|
1996
|
+
The same tabs turned down the page: the list on the leading side, the panel beside it. For a
|
|
1997
|
+
settings screen whose sections are a list, not a strip.
|
|
1998
|
+
|
|
1999
|
+
```html
|
|
2000
|
+
<div class="fdy-tabs fdy-tabs--vertical" data-fdy-tabs style="--fdy-tabs-list-w:16rem">
|
|
2001
|
+
<div class="fdy-tabs__list" role="tablist" aria-orientation="vertical" aria-label="Settings">
|
|
2002
|
+
<button class="fdy-tabs__tab" role="tab" id="s-1" aria-controls="sp-1" aria-selected="true">Identity</button>
|
|
2003
|
+
<button class="fdy-tabs__tab" role="tab" id="s-2" aria-controls="sp-2" aria-selected="false" tabindex="-1">Posts</button>
|
|
2004
|
+
</div>
|
|
2005
|
+
<div class="fdy-tabs__panel" role="tabpanel" id="sp-1" aria-labelledby="s-1" tabindex="0">…</div>
|
|
2006
|
+
<div class="fdy-tabs__panel" role="tabpanel" id="sp-2" aria-labelledby="s-2" tabindex="0" hidden>…</div>
|
|
2007
|
+
</div>
|
|
2008
|
+
```
|
|
2009
|
+
|
|
2010
|
+
- **Two markers, and they are not the same marker.** `.fdy-tabs--vertical` decides the look,
|
|
2011
|
+
because the two-column box belongs to `.fdy-tabs` and a stylesheet cannot reach a parent from a
|
|
2012
|
+
child. `aria-orientation="vertical"` on the tablist decides the keyboard, because that is what an
|
|
2013
|
+
assistive technology announces and APG asks for it on a vertical tablist regardless. Write
|
|
2014
|
+
**either** one: `freeday-tabs.js` fills in the attribute when it finds the class without it, so
|
|
2015
|
+
the ARIA can never drift from the layout. An `aria-orientation` you wrote always wins, including
|
|
2016
|
+
when you write `horizontal` on a widget that carries the class.
|
|
2017
|
+
- **Selection is a soft fill.** A bar down the leading edge would be the wrong translation:
|
|
2018
|
+
sideways the marker is an underline beneath the tab it belongs to, and turned ninety degrees that
|
|
2019
|
+
line lands between two tabs and points at the wrong one. So the vertical variant spends
|
|
2020
|
+
`--color-primary-soft` behind `--color-primary-strong`, the same pair
|
|
2021
|
+
`.fdy-nav__item[aria-current="page"]` and the picker options already use for “this is where you
|
|
2022
|
+
are” in a vertical list. The strip's own line (`box-shadow`) is dropped with the underline.
|
|
2023
|
+
- **Column width:** `--fdy-tabs-list-w` on `.fdy-tabs` (default `auto`, so the column hugs the
|
|
2024
|
+
longest label). Set the token rather than a `width` on `.fdy-tabs__list`: the list is a flex item
|
|
2025
|
+
at `flex:none`, so a width arriving from outside has no effect and no error to show for it.
|
|
2026
|
+
- **It does not collapse on a narrow screen**, and the breakpoint is yours because the kit cannot
|
|
2027
|
+
know whether these tabs span the viewport or sit in a 20rem drawer. Going back to a strip takes
|
|
2028
|
+
three declarations, not one, since the variant turns both boxes:
|
|
2029
|
+
|
|
2030
|
+
```css
|
|
2031
|
+
@media (max-width: 63.9375rem) {
|
|
2032
|
+
.settings-tabs.fdy-tabs--vertical { display: block; }
|
|
2033
|
+
.settings-tabs .fdy-tabs__list { flex-direction: row; width: auto; overflow-x: auto; }
|
|
2034
|
+
.settings-tabs .fdy-tabs__panel { padding: var(--space-4) 0 0; }
|
|
2035
|
+
}
|
|
2036
|
+
```
|
|
2037
|
+
|
|
2038
|
+
The keyboard stays on Up and Down across that switch, because `aria-orientation` is an attribute
|
|
2039
|
+
and a media query cannot reach it. A list of sections is still a list of sections at 320px, which
|
|
2040
|
+
is why the axis is read from the attribute and never from the painted layout.
|
|
2041
|
+
- Measured in `browser/tabs-vertical.mjs`, which also holds the line that a strip with no
|
|
2042
|
+
`aria-orientation` behaves and paints exactly as it did before the variant existed.
|
|
2043
|
+
|
|
1860
2044
|
---
|
|
1861
2045
|
|
|
1862
2046
|
# Overlays
|
|
@@ -2044,6 +2228,18 @@ the rest, so two states a workflow distinguishes never share a look. Map each st
|
|
|
2044
2228
|
tone, never to a hash of the string: a status is a closed set, and a hash reassigns colours the
|
|
2045
2229
|
moment the set grows.
|
|
2046
2230
|
|
|
2231
|
+
**Neutral status takes `--outline`.** A badge with no modifier is the page's accent: primary ink
|
|
2232
|
+
on a primary tint, the same pair the primary button wears, so a status that means nothing in
|
|
2233
|
+
particular ("Not scheduled", "Holiday", "Read 0 of 3") ends up looking like something to click.
|
|
2234
|
+
`--outline` is the neutral one: no fill, a `--color-border-strong` edge, and `--color-text-muted`
|
|
2235
|
+
ink at 7.46:1 on the light surface, 7.28:1 on the dark one.
|
|
2236
|
+
|
|
2237
|
+
There is no filled neutral on purpose. The only neutral fill the token set offers is one surface
|
|
2238
|
+
step, `--color-surface-2`, and a status pill sits either on `--color-surface` or on the app ground,
|
|
2239
|
+
which is `--color-surface-2` itself, so the pill's body would land between 1.00:1 and 1.10:1
|
|
2240
|
+
against what is behind it: a badge whose shape you cannot see. The semantic fills live at 1.03:1 to
|
|
2241
|
+
1.44:1 and read clearly anyway because they carry hue, and a neutral has no hue to carry.
|
|
2242
|
+
|
|
2047
2243
|
**Notification badge is the overlay variant**, not a separate component: wrap anything in
|
|
2048
2244
|
`.fdy-badge-wrap` and add `.fdy-badge-ov` (`--primary` `--danger` `--accent` `--info` `--success`
|
|
2049
2245
|
`--warning`, `--dot` for a bare dot, `--top-left` / `--bottom-right` / `--bottom-left` to move it).
|
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/v3.9.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`.
|
|
@@ -44,9 +44,9 @@ enhancer:
|
|
|
44
44
|
| **Blazor (net8.0)** | RCL `Freeday.Blazor` | `@bind-Value` |
|
|
45
45
|
| HTML statis · Svelte · template server-rendered | *(tanpa wrapper)* markup mentah + enhancer, di bawah | event `fdy-*` |
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
`FdyCfl` · `FdyChart` · `FdyTable` · `FdyModal` · `FdyDrawer` ·
|
|
49
|
-
untuk
|
|
47
|
+
Kedua belasnya: `FdyCombo` · `FdyDatepicker` · `FdyMonthpicker` · `FdyDateRange` ·
|
|
48
|
+
`FdyAutocomplete` · `FdyCascade` · `FdyCfl` · `FdyChart` · `FdyTable` · `FdyModal` · `FdyDrawer` ·
|
|
49
|
+
`FdyAppShell`. Menulis markup mentah untuk kedua belas ini di dalam Vue/React/Blazor *kelihatan* baik-baik saja pada mulanya, karena render
|
|
50
50
|
pertamanya memang benar. Setelah itu ia rusak diam-diam: DOM yang di-render framework belakangan tak pernah ter-hydrate, dan state widget
|
|
51
51
|
berakhir di DOM alih-alih di state framework-mu. Selebihnya (button, card, badge, alert, markup
|
|
52
52
|
tabel, layout, dan komponen interaktif yang tak punya wrapper) sama persis di semua stack. Untuk
|
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/v3.9.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`.
|
|
@@ -44,8 +44,9 @@ enhancer:
|
|
|
44
44
|
| **Blazor (net8.0)** | `Freeday.Blazor` RCL | `@bind-Value` |
|
|
45
45
|
| Static HTML · Svelte · server-rendered templates | *(no wrapper)* raw markup + enhancers, below | `fdy-*` events |
|
|
46
46
|
|
|
47
|
-
The
|
|
48
|
-
`FdyCfl` · `FdyChart` · `FdyTable` · `FdyModal` · `FdyDrawer` · `FdyAppShell`.
|
|
47
|
+
The twelve: `FdyCombo` · `FdyDatepicker` · `FdyMonthpicker` · `FdyDateRange` · `FdyAutocomplete` ·
|
|
48
|
+
`FdyCascade` · `FdyCfl` · `FdyChart` · `FdyTable` · `FdyModal` · `FdyDrawer` · `FdyAppShell`.
|
|
49
|
+
Hand-writing the raw
|
|
49
50
|
markup for these inside Vue/React/Blazor *looks* fine at first, because the first render is correct.
|
|
50
51
|
Then it breaks quietly:
|
|
51
52
|
DOM the framework renders later is never hydrated, and the widget's state ends up in the DOM instead
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
@* Seed-based wrapper over freeday-datepicker.js: the enhancer builds the input-styled
|
|
5
5
|
trigger + calendar popover from this empty div and emits fdy-datepicker-change. The
|
|
6
6
|
enhancer owns that DOM, so we render the seed once (ShouldRender => false) and read the
|
|
7
|
-
picked value back through @bind-Value. Value is an ISO date string ("yyyy-MM-dd") or ""
|
|
7
|
+
picked value back through @bind-Value. Value is an ISO date string ("yyyy-MM-dd") or "",
|
|
8
|
+
and "yyyy-MM" when Mode is FdyDatepickerMode.Month. *@
|
|
8
9
|
<div @ref="Root" data-fdy-datepicker
|
|
10
|
+
data-mode="@ModeAttribute"
|
|
9
11
|
data-value="@Value"
|
|
10
12
|
data-label="@Label"
|
|
11
13
|
data-placeholder="@Placeholder"
|