@cahyo-dimas/freeday 3.7.0 → 3.8.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 +93 -0
- package/COMPONENTS.md +119 -6
- package/README.id.md +1 -1
- package/README.md +1 -1
- package/adapters/blazor/FdyDatepicker.razor +3 -1
- package/adapters/blazor/FdyDatepicker.razor.cs +22 -1
- package/adapters/blazor/FdyTableFooter.razor +6 -6
- package/adapters/blazor/FdyTableFooter.razor.cs +36 -0
- package/adapters/react/components/FdyDatepicker.tsx +122 -42
- package/adapters/react/components/FdyTableFooter.tsx +37 -6
- package/adapters/react/index.d.ts +1 -1
- package/adapters/vue/components/FdyDatepicker.vue +119 -34
- package/adapters/vue/components/FdyTableFooter.vue +39 -6
- package/dist/freeday-datepicker.js +68 -14
- package/dist/freeday-menu.js +15 -1
- package/dist/freeday-popover.js +33 -11
- package/dist/freeday-tabs.js +21 -2
- package/dist/freeday.bundle.css +31 -0
- package/dist/freeday.css +31 -0
- package/dist/freeday.js +137 -28
- package/docs/getting-started.md +1 -1
- package/package.json +2 -2
- package/src/components/tabs.css +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,99 @@ 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.8.0] - 2026-10-23
|
|
13
|
+
|
|
14
|
+
**Empat penambahan dari catatan yang sama, dan tiga di antaranya ternyata sudah separuh ada di
|
|
15
|
+
kit.** App ronda yang menemukan Safari 15 di 3.7.0 menulis delapan butir; dua di antaranya cacat,
|
|
16
|
+
dan keduanya sudah terbit. Empat dari yang tersisa penambahan, dan keempatnya lebih murah daripada
|
|
17
|
+
tampaknya: satu kelas yang sudah ada sejak lama tetapi kehilangan pengaruhnya, satu jalan keluar
|
|
18
|
+
teks yang cuma dipasang di satu jalur, satu grid yang terpasang sejak 1.32.0 tanpa pernah bisa jadi
|
|
19
|
+
tujuan, dan satu varian sumbu yang papan tombolnya kurang dua literal.
|
|
20
|
+
|
|
21
|
+
Yang terakhir membuka cacat yang digambarkan catatan itu tanpa pelapornya tahu ia sedang
|
|
22
|
+
menggambarkan kit. Ia menulis bahwa `<input type="month">` menamai bulan menurut bahasa peramban,
|
|
23
|
+
bukan `<html lang>`. Kalimat itu berlaku kata per kata untuk `FdyDatepicker` Vue dan React: dokumen
|
|
24
|
+
sudah menjanjikan `<html lang>` sejak lama, cuma enhancer vanilla yang betul melakukannya, dan
|
|
25
|
+
kedua adapter menyerahkan `props.locale` yang `undefined` langsung ke `Intl`.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **Popover memposisikan panelnya dari tata letak yang ia batalkan tiga baris kemudian** (`#079`),
|
|
29
|
+
ada sejak 1.4.1 dan kena keenam enhancer yang memakai `attach()`: autocomplete, cascade, combo,
|
|
30
|
+
datepicker, menu dan timepicker. Keenam panel itu `position:absolute` dengan
|
|
31
|
+
`top:calc(100% + var(--space-1))` di stylesheet komponennya, dan aturan author mengalahkan gaya
|
|
32
|
+
UA popover, jadi panel yang baru di-`show()` sudah di top layer sambil masih memakai aturan itu.
|
|
33
|
+
Di top layer `100%` adalah tinggi viewport. Selama satu fungsi sinkron panelnya duduk satu
|
|
34
|
+
viewport di bawah halaman dan overflow dokumen tumbuh mengikutinya, terukur `scrollHeight` 1023
|
|
35
|
+
lawan viewport 900. Di tempat scrollbar memakan ruang tata letak, yakni setiap peramban Windows
|
|
36
|
+
dan Linux, dokumen mendapat scrollbar untuk sesaat, `clientWidth` menyusut selebar scrollbar itu,
|
|
37
|
+
halaman relayout, dan pemicunya bergeser bersamanya. `place()` membaca rect pemicu sebagai hal
|
|
38
|
+
pertama yang ia lakukan, jadi yang ia ukur adalah tata letak sementara itu. Pengukurannya
|
|
39
|
+
sekarang terjadi sesudah panel keluar dari flow.
|
|
40
|
+
|
|
41
|
+
macOS memakai scrollbar overlay yang tidak memakan ruang, jadi mesin tempat kit ini ditulis
|
|
42
|
+
secara struktural tidak bisa melihat cacat ini, dan 1.4.1 sampai 3.7.0 mengirimnya tanpa ada yang
|
|
43
|
+
tahu. Yang melihatnya runner Linux di CI, lewat uji perataan `#074` yang kebetulan mengukur
|
|
44
|
+
koordinat di sisi kanan layar. Reproduksinya sekarang ada di suite dan berjalan di macOS juga,
|
|
45
|
+
dengan menata `::-webkit-scrollbar` ke lebar tetap, plus assertion yang menolak lulus kalau
|
|
46
|
+
enginenya ternyata tidak memberi scrollbar ruang tata letak.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- **`align` di popover, dibacakan dari `.fdy-menu--end`** (`#074`). Kelasnya sudah ada sejak lama
|
|
50
|
+
dan stylesheet selalu menghormatinya lewat `left:auto;right:0`. Jalur popover menggantinya dengan
|
|
51
|
+
koordinat `fixed` inline, jadi kelas itu kehilangan pengaruh persis pada saat menu masuk top
|
|
52
|
+
layer: menu akun di talang kanan melewati avatarnya dan berhenti 4px dari tepi layar. Nol kelas
|
|
53
|
+
baru. `freeday-menu.js` yang membaca kelasnya, sebab popover dipakai enam enhancer dan tidak
|
|
54
|
+
boleh tahu nama kelas satu pun. Jepitan viewport sekarang dua sisi: lantai `GAP` yang lama duduk
|
|
55
|
+
di dalam cabang jepitan kanan dan tak pernah berjalan untuk panel rata-kanan, jadi rata-kanan di
|
|
56
|
+
layar sempit cuma akan menukar luber kanan dengan luber kiri.
|
|
57
|
+
- **Enam prop teks di `FdyTableFooter`** (`#075`), di ketiga adapter. `data-fdy-text-<key>` sudah
|
|
58
|
+
lama membuka jalur mentah dan jalur bertipe tidak punya padanannya, jadi app yang tidak berbahasa
|
|
59
|
+
Inggris hanya bisa menulis ulang seluruh pita kakinya. Satu app mengirim 81 baris markup sendiri
|
|
60
|
+
untuk mengganti lima kata. Namanya mengikuti kunci `TEXT` enhancer di mana kuncinya ada (`info`,
|
|
61
|
+
`prev`, `next`), supaya satu teks tidak punya dua nama di dua jalur; tiga sisanya tanpa kembaran
|
|
62
|
+
sebab di jalur mentah markup itu milik app sendiri. Bawaan tetap Inggris di setiap jalur.
|
|
63
|
+
`FdyTable` masih merender pita kakinya sendiri tanpa meneruskan keenamnya; jalan keluarnya
|
|
64
|
+
`pager={false}` lalu render `<FdyTableFooter>` sendiri, dan COMPONENTS.md menuliskannya.
|
|
65
|
+
- **`mode="month"` di `FdyDatepicker`** (`#076`), di ketiga adapter dan jalur mentah. Grid bulannya
|
|
66
|
+
sudah dipasang 1.32.0, hanya tak pernah bisa jadi tujuan. Menjawab `YYYY-MM`, menerima `min` dan
|
|
67
|
+
`max` dalam bentuk yang sama, dan berhenti di grid itu alih-alih turun ke hari. Nama acara dan
|
|
68
|
+
`detail.date` tidak berubah.
|
|
69
|
+
- **`.fdy-tabs--vertical`, dan sumbu papan tombol dari `aria-orientation`** (`#077`). Kelasnya
|
|
70
|
+
memutuskan tampilan, atributnya memutuskan panah, dan satu penanda cukup: enhancer mengisi
|
|
71
|
+
atribut dari kelas kalau markup belum menulisnya, sementara atribut yang sudah ditulis selalu
|
|
72
|
+
menang. Panah kiri dan kanan tidak dipetakan di tegak, mengikuti APG satu sumbu per tablist, dan
|
|
73
|
+
tombol di luar sumbu jatuh tanpa `preventDefault` sehingga tetap milik halaman. Lebar kolom lewat
|
|
74
|
+
`--fdy-tabs-list-w`, bawaan `auto`. Tabs tidak punya wrapper bertipe di stack mana pun, jadi tidak
|
|
75
|
+
ada prop `orientation` yang bisa ditambahkan dan tidak ada wrapper baru yang dibuat demi punya
|
|
76
|
+
tempat menaruhnya.
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
- **`FdyDatepicker` Vue dan React membaca `<html lang>` kalau `locale` tidak dikirim** (`#078`).
|
|
80
|
+
Ini satu-satunya perubahan perilaku di rilis ini, dan ia kena mode hari juga, bukan cuma mode
|
|
81
|
+
bulan. Sebelumnya `undefined` sampai ke `Intl` apa adanya, yang berarti bahasa peramban: halaman
|
|
82
|
+
Indonesia yang dibuka di peramban Inggris menulis nama bulan Inggris, dan halaman Inggris di
|
|
83
|
+
peramban Indonesia menulis nama bulan Indonesia. Kontrak yang tertulis selalu `<html lang>`, dan
|
|
84
|
+
aturan "Inggris di semua jalur" tidak bisa ditegakkan oleh nilai yang datang dari preferensi
|
|
85
|
+
pembaca. App yang memang menginginkan bahasa peramban mengirim `locale` sendiri. Halaman tanpa
|
|
86
|
+
`lang` jatuh ke `en`, dan `lang=""` ikut ke sana sebab `Intl` melempar `RangeError` atas string
|
|
87
|
+
kosong.
|
|
88
|
+
|
|
89
|
+
### Catatan dukungan peramban
|
|
90
|
+
Floor-nya tetap Chrome 105, Safari 15.4 dan Firefox 121. Tab tegak sengaja tidak memakai `:has()`
|
|
91
|
+
walau itu akan memendekkan CSS-nya, sebab `:has()` baru ada di Safari 15.4 sementara lantai yang
|
|
92
|
+
pernah dilaporkan dari perangkat nyata adalah iOS 15.0.
|
|
93
|
+
|
|
94
|
+
### Yang perlu dibuang app saat upgrade
|
|
95
|
+
- Tambalan `translate` apa pun yang menggeser menu rata-kanan ke dalam talangnya. Menu itu sekarang
|
|
96
|
+
diposisikan benar, jadi tambalannya menggeser sejauh satu talang ke arah yang salah.
|
|
97
|
+
- Markup pita kaki tabel yang ditulis tangan cuma karena teksnya tak bisa diganti, berikut CSS
|
|
98
|
+
lokal yang menemaninya.
|
|
99
|
+
|
|
100
|
+
### Belum masuk
|
|
101
|
+
Butir keempat catatan itu, bawaan `.fdy-badge` yang berwarna primer sehingga status netral terbaca
|
|
102
|
+
seperti aksi, menunggu 4.0: mengubah bawaannya mengubah tampilan setiap lencana yang sudah terkirim.
|
|
103
|
+
Pilihan yang tidak mematahkan apa pun, `fdy-badge--neutral` sebagai opt-in, masih terbuka lebih awal.
|
|
104
|
+
|
|
12
105
|
## [3.7.0] - 2026-10-23
|
|
13
106
|
|
|
14
107
|
**Kit ini tidak bisa dipakai di Safari 15, dan yang menemukannya adalah sebuah HP.** Pemilik sebuah
|
package/COMPONENTS.md
CHANGED
|
@@ -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'` | `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`; the raw path takes `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. |
|
|
@@ -1013,7 +1025,26 @@ months, `Home`/`End` January/December, `PageUp`/`PageDown` a year, `Enter`/`Spac
|
|
|
1013
1025
|
year grid likewise: arrows ±1/±3 years, `Home`/`End` the ends of the page, `PageUp`/`PageDown` a
|
|
1014
1026
|
page of twelve.
|
|
1015
1027
|
|
|
1016
|
-
|
|
1028
|
+
**Picking a month, not a day.** `mode="month"` (`data-mode="month"` on the raw path,
|
|
1029
|
+
`Mode="FdyDatepickerMode.Month"` in Blazor) makes the month grid the destination instead of a
|
|
1030
|
+
waypoint: the panel opens on it, a click or `Enter` commits from it, the day grid is never built,
|
|
1031
|
+
and the value is `YYYY-MM`. The title still steps up to the year grid and back, because that is
|
|
1032
|
+
navigation and not an answer.
|
|
1033
|
+
|
|
1034
|
+
This exists because `<input type="month">` names the month in the **browser's** language, which an
|
|
1035
|
+
Indonesian page read on an English phone cannot override. The month cells, the cell's accessible
|
|
1036
|
+
name and the value in the trigger all go through `Intl` with the page's `<html lang>`, the same
|
|
1037
|
+
route the day grid's month names have always taken, so `lang="id"` reads `Oktober 2026` with nothing
|
|
1038
|
+
configured. Keyboard in the month grid is unchanged: arrows move one and three months, `Home`/`End`
|
|
1039
|
+
jump to January and December, `PageUp`/`PageDown` step a year, and `Enter`/`Space` now answers
|
|
1040
|
+
rather than drilling. The cell keeps its name when it becomes selected and carries the state in
|
|
1041
|
+
`aria-selected`, as the day cells do.
|
|
1042
|
+
|
|
1043
|
+
Nothing changes without the prop. A picker with no `mode` is the day picker it has always been,
|
|
1044
|
+
down to the `YYYY-MM-DD` it reports.
|
|
1045
|
+
|
|
1046
|
+
- Attributes: `data-value="YYYY-MM-DD"`, `data-mode="month"`, `data-placeholder`, `data-label`,
|
|
1047
|
+
`data-fdy-no-icon`
|
|
1017
1048
|
|
|
1018
1049
|
```html
|
|
1019
1050
|
<div class="fdy-field">
|
|
@@ -1021,6 +1052,11 @@ page of twelve.
|
|
|
1021
1052
|
<div data-fdy-datepicker data-value="2026-07-21" data-label="Invoice date"></div>
|
|
1022
1053
|
</div>
|
|
1023
1054
|
|
|
1055
|
+
<div class="fdy-field">
|
|
1056
|
+
<span class="fdy-label">Reporting month</span>
|
|
1057
|
+
<div data-fdy-datepicker data-mode="month" data-value="2026-10"></div>
|
|
1058
|
+
</div>
|
|
1059
|
+
|
|
1024
1060
|
<div class="fdy-daterange" data-fdy-daterange role="group" aria-label="Reporting period">
|
|
1025
1061
|
<div data-fdy-datepicker data-role="from" data-placeholder="From" data-label="Start date"></div>
|
|
1026
1062
|
<span class="fdy-daterange__sep" aria-hidden="true">–</span>
|
|
@@ -1440,6 +1476,28 @@ render it once, outside both, with `pager={false}` on the table.
|
|
|
1440
1476
|
| `page` | `FdyPageState` = `{ index, size, total }` | The page being shown. `size` drives both the range text and the rows-per-page control's value. |
|
|
1441
1477
|
| `onPageChange?` | `(page: FdyPageState) => void` | React. A page click or a size change. Vue emits `update:page`; Blazor `PageChanged`. |
|
|
1442
1478
|
| `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. |
|
|
1479
|
+
| `infoText?` | `string` | The range line, with `{from}`, `{to}` and `{total}` substituted. Default `Showing {from}–{to} of {total}`, the enhancer's `info` string. |
|
|
1480
|
+
| `pageSizeLabel?` | `string` | The word beside the rows-per-page control, which also names that control for a screen reader. Default `Rows`. |
|
|
1481
|
+
| `paginationLabel?` | `string` | Accessible name of the pager landmark. Default `Pagination`. |
|
|
1482
|
+
| `prevLabel?` | `string` | Accessible name of the previous-page arrow. Default `Previous page`. |
|
|
1483
|
+
| `nextLabel?` | `string` | Accessible name of the next-page arrow. Default `Next page`. |
|
|
1484
|
+
| `pageLabel?` | `string` | Accessible name of a numbered page button, with `{n}` substituted. Default `Go to page {n}`. |
|
|
1485
|
+
|
|
1486
|
+
**Saying it in another language.** The raw path has had `data-fdy-text-<key>` since 1.39.0 and this
|
|
1487
|
+
one had nothing, so a Vue, React or Blazor app that does not speak English could only rewrite the
|
|
1488
|
+
band by hand. One did: eighty one lines of its own markup, to change five words. The six string
|
|
1489
|
+
props above are the hatch that was missing. Where the enhancer already keys a string the prop
|
|
1490
|
+
carries that key's name, `infoText` for `info`, `prevLabel` for `prev`, `nextLabel` for `next`,
|
|
1491
|
+
and the same `{from}` `{to}` `{total}` `{n}` substitution, every occurrence replaced, so an app on
|
|
1492
|
+
both paths writes one wording once. The other three have no enhancer twin because on the raw path
|
|
1493
|
+
that markup is the app's own: the word beside the rows-per-page control, the pager landmark's name,
|
|
1494
|
+
and the name of a numbered page button. It is `pageSizeLabel` and not `rows`, since the enhancer's
|
|
1495
|
+
`rows` key is the table's `{n} rows` count, a different string.
|
|
1496
|
+
|
|
1497
|
+
Omit a prop and you get the English above, so nothing already shipped moves. `FdyTable` draws its
|
|
1498
|
+
own footer and does not pass these through, so a table that owns its footer still reads English; a
|
|
1499
|
+
screen that needs its own wording takes the footer over with `pager={false}` and renders
|
|
1500
|
+
`<FdyTableFooter>` beside it, which is the same route a responsive list already takes.
|
|
1443
1501
|
|
|
1444
1502
|
**Who draws the pager.** The table renders its own footer (range + pager) whenever there is more
|
|
1445
1503
|
than one page. Two ways to take it over:
|
|
@@ -1832,10 +1890,15 @@ record. A crumb for a menu group that has no page of its own is plain text in th
|
|
|
1832
1890
|
to nowhere.
|
|
1833
1891
|
|
|
1834
1892
|
## Tabs — `.fdy-tabs`
|
|
1835
|
-
APG tabs:
|
|
1893
|
+
APG tabs: arrows, Home/End, roving tabindex. Needs `freeday-tabs.js`.
|
|
1836
1894
|
`.fdy-tabs__tab` marks its active state from **`aria-selected="true"` or `aria-current="page"`**,
|
|
1837
1895
|
so the same look serves routed sub-navigation built from plain links. See the note under Nav.
|
|
1838
1896
|
|
|
1897
|
+
**Which arrows move the selection** comes from the tablist's `aria-orientation`: Left and Right by
|
|
1898
|
+
default, Up and Down when it says `vertical`. One axis per tablist, the way APG maps it. The other
|
|
1899
|
+
pair stays unmapped, so Left and Right over a column of tabs still reach whatever else on the page
|
|
1900
|
+
wants them. Home and End answer either way.
|
|
1901
|
+
|
|
1839
1902
|
> **The strip's line is an inset `box-shadow`, not a `border-bottom`** (`#052`; through 3.1.0 it
|
|
1840
1903
|
> was a border, so an app upgrading past that release changes the override with it). To
|
|
1841
1904
|
> remove it — a strip inside a header that draws its own rule — override `box-shadow:none`;
|
|
@@ -1857,6 +1920,56 @@ so the same look serves routed sub-navigation built from plain links. See the no
|
|
|
1857
1920
|
</div>
|
|
1858
1921
|
```
|
|
1859
1922
|
|
|
1923
|
+
### Vertical — `.fdy-tabs--vertical`
|
|
1924
|
+
|
|
1925
|
+
The same tabs turned down the page: the list on the leading side, the panel beside it. For a
|
|
1926
|
+
settings screen whose sections are a list, not a strip.
|
|
1927
|
+
|
|
1928
|
+
```html
|
|
1929
|
+
<div class="fdy-tabs fdy-tabs--vertical" data-fdy-tabs style="--fdy-tabs-list-w:16rem">
|
|
1930
|
+
<div class="fdy-tabs__list" role="tablist" aria-orientation="vertical" aria-label="Settings">
|
|
1931
|
+
<button class="fdy-tabs__tab" role="tab" id="s-1" aria-controls="sp-1" aria-selected="true">Identity</button>
|
|
1932
|
+
<button class="fdy-tabs__tab" role="tab" id="s-2" aria-controls="sp-2" aria-selected="false" tabindex="-1">Posts</button>
|
|
1933
|
+
</div>
|
|
1934
|
+
<div class="fdy-tabs__panel" role="tabpanel" id="sp-1" aria-labelledby="s-1" tabindex="0">…</div>
|
|
1935
|
+
<div class="fdy-tabs__panel" role="tabpanel" id="sp-2" aria-labelledby="s-2" tabindex="0" hidden>…</div>
|
|
1936
|
+
</div>
|
|
1937
|
+
```
|
|
1938
|
+
|
|
1939
|
+
- **Two markers, and they are not the same marker.** `.fdy-tabs--vertical` decides the look,
|
|
1940
|
+
because the two-column box belongs to `.fdy-tabs` and a stylesheet cannot reach a parent from a
|
|
1941
|
+
child. `aria-orientation="vertical"` on the tablist decides the keyboard, because that is what an
|
|
1942
|
+
assistive technology announces and APG asks for it on a vertical tablist regardless. Write
|
|
1943
|
+
**either** one: `freeday-tabs.js` fills in the attribute when it finds the class without it, so
|
|
1944
|
+
the ARIA can never drift from the layout. An `aria-orientation` you wrote always wins, including
|
|
1945
|
+
when you write `horizontal` on a widget that carries the class.
|
|
1946
|
+
- **Selection is a soft fill.** A bar down the leading edge would be the wrong translation:
|
|
1947
|
+
sideways the marker is an underline beneath the tab it belongs to, and turned ninety degrees that
|
|
1948
|
+
line lands between two tabs and points at the wrong one. So the vertical variant spends
|
|
1949
|
+
`--color-primary-soft` behind `--color-primary-strong`, the same pair
|
|
1950
|
+
`.fdy-nav__item[aria-current="page"]` and the picker options already use for “this is where you
|
|
1951
|
+
are” in a vertical list. The strip's own line (`box-shadow`) is dropped with the underline.
|
|
1952
|
+
- **Column width:** `--fdy-tabs-list-w` on `.fdy-tabs` (default `auto`, so the column hugs the
|
|
1953
|
+
longest label). Set the token rather than a `width` on `.fdy-tabs__list`: the list is a flex item
|
|
1954
|
+
at `flex:none`, so a width arriving from outside has no effect and no error to show for it.
|
|
1955
|
+
- **It does not collapse on a narrow screen**, and the breakpoint is yours because the kit cannot
|
|
1956
|
+
know whether these tabs span the viewport or sit in a 20rem drawer. Going back to a strip takes
|
|
1957
|
+
three declarations, not one, since the variant turns both boxes:
|
|
1958
|
+
|
|
1959
|
+
```css
|
|
1960
|
+
@media (max-width: 63.9375rem) {
|
|
1961
|
+
.settings-tabs.fdy-tabs--vertical { display: block; }
|
|
1962
|
+
.settings-tabs .fdy-tabs__list { flex-direction: row; width: auto; overflow-x: auto; }
|
|
1963
|
+
.settings-tabs .fdy-tabs__panel { padding: var(--space-4) 0 0; }
|
|
1964
|
+
}
|
|
1965
|
+
```
|
|
1966
|
+
|
|
1967
|
+
The keyboard stays on Up and Down across that switch, because `aria-orientation` is an attribute
|
|
1968
|
+
and a media query cannot reach it. A list of sections is still a list of sections at 320px, which
|
|
1969
|
+
is why the axis is read from the attribute and never from the painted layout.
|
|
1970
|
+
- Measured in `browser/tabs-vertical.mjs`, which also holds the line that a strip with no
|
|
1971
|
+
`aria-orientation` behaves and paints exactly as it did before the variant existed.
|
|
1972
|
+
|
|
1860
1973
|
---
|
|
1861
1974
|
|
|
1862
1975
|
# Overlays
|
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.8.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/v3.8.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`.
|
|
@@ -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"
|
|
@@ -5,11 +5,22 @@ namespace Freeday.Blazor;
|
|
|
5
5
|
|
|
6
6
|
public partial class FdyDatepicker
|
|
7
7
|
{
|
|
8
|
-
/// <summary>Selected date as an ISO string (<c>yyyy-MM-dd</c>), or
|
|
8
|
+
/// <summary>Selected date as an ISO string (<c>yyyy-MM-dd</c>), or <c>yyyy-MM</c> when
|
|
9
|
+
/// <see cref="Mode"/> is <see cref="FdyDatepickerMode.Month"/>, or "" when unset.
|
|
9
10
|
/// Two-way bindable via <c>@bind-Value</c>.</summary>
|
|
10
11
|
[Parameter] public string? Value { get; set; }
|
|
11
12
|
[Parameter] public EventCallback<string?> ValueChanged { get; set; }
|
|
12
13
|
|
|
14
|
+
/// <summary>What the picker answers with. <see cref="FdyDatepickerMode.Day"/> (default) is the
|
|
15
|
+
/// calendar as it always was. <see cref="FdyDatepickerMode.Month"/> opens on the month grid,
|
|
16
|
+
/// stops there, and reports <c>yyyy-MM</c>.</summary>
|
|
17
|
+
[Parameter] public FdyDatepickerMode Mode { get; set; } = FdyDatepickerMode.Day;
|
|
18
|
+
|
|
19
|
+
/* null rather than "day" for the default: an attribute the enhancer does not need is an
|
|
20
|
+
attribute no reader of the seed has to interpret, and the enhancer's own check is
|
|
21
|
+
`=== 'month'`, so the day path is the absence of this. */
|
|
22
|
+
private string? ModeAttribute => Mode == FdyDatepickerMode.Month ? "month" : null;
|
|
23
|
+
|
|
13
24
|
/// <summary>Accessible label for the trigger.</summary>
|
|
14
25
|
[Parameter] public string? Label { get; set; }
|
|
15
26
|
[Parameter] public string? Placeholder { get; set; }
|
|
@@ -83,3 +94,13 @@ public partial class FdyDatepicker
|
|
|
83
94
|
/// <summary>The detail shape of the enhancer's <c>fdy-datepicker-change</c> event.</summary>
|
|
84
95
|
public sealed record ChangeDetail(string? Value, string? Date);
|
|
85
96
|
}
|
|
97
|
+
|
|
98
|
+
/// <summary>What <see cref="FdyDatepicker"/> answers with.</summary>
|
|
99
|
+
public enum FdyDatepickerMode
|
|
100
|
+
{
|
|
101
|
+
/// <summary>A single day, reported as <c>yyyy-MM-dd</c>.</summary>
|
|
102
|
+
Day,
|
|
103
|
+
|
|
104
|
+
/// <summary>A whole month, reported as <c>yyyy-MM</c>.</summary>
|
|
105
|
+
Month,
|
|
106
|
+
}
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
@if (Visible)
|
|
10
10
|
{
|
|
11
11
|
<div class="fdy-table-footer">
|
|
12
|
-
<span class="fdy-table-footer__info"
|
|
12
|
+
<span class="fdy-table-footer__info">@InfoLine</span>
|
|
13
13
|
|
|
14
14
|
@if (Sizes.Count > 0)
|
|
15
15
|
{
|
|
16
16
|
<div class="fdy-table-footer__size">
|
|
17
|
-
<span id="@SizeLabelId"
|
|
17
|
+
<span id="@SizeLabelId">@(PageSizeLabel ?? "Rows")</span>
|
|
18
18
|
@* The kit's own listbox, not a native <select>: an OS menu is unthemeable, and on
|
|
19
19
|
macOS it drops a dark panel into a light page. FdyCombo's own header says so. *@
|
|
20
20
|
<FdyCombo TValue="string" Value="@Page.Size.ToString()" Options="SizeOptions"
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
|
|
25
25
|
@if (HasPager)
|
|
26
26
|
{
|
|
27
|
-
<nav class="fdy-pagination" aria-label="Pagination">
|
|
27
|
+
<nav class="fdy-pagination" aria-label="@(PaginationLabel ?? "Pagination")">
|
|
28
28
|
<ul class="fdy-pagination__list">
|
|
29
29
|
<li>
|
|
30
|
-
<button type="button" class="fdy-pagination__link" aria-label="Previous page"
|
|
30
|
+
<button type="button" class="fdy-pagination__link" aria-label="@(PrevLabel ?? "Previous page")"
|
|
31
31
|
disabled="@(CurrentPage1 == 1)" @onclick="() => GoTo(CurrentPage1 - 1)">‹</button>
|
|
32
32
|
</li>
|
|
33
33
|
@foreach (int? p in Pages)
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
else
|
|
45
45
|
{
|
|
46
46
|
int page = p.Value;
|
|
47
|
-
<button type="button" class="fdy-pagination__link" aria-label="@(
|
|
47
|
+
<button type="button" class="fdy-pagination__link" aria-label="@PageAriaLabel(page)"
|
|
48
48
|
@onclick="() => GoTo(page)">@(page)</button>
|
|
49
49
|
}
|
|
50
50
|
</li>
|
|
51
51
|
}
|
|
52
52
|
<li>
|
|
53
|
-
<button type="button" class="fdy-pagination__link" aria-label="Next page"
|
|
53
|
+
<button type="button" class="fdy-pagination__link" aria-label="@(NextLabel ?? "Next page")"
|
|
54
54
|
disabled="@(CurrentPage1 == TotalPages)" @onclick="() => GoTo(CurrentPage1 + 1)">›</button>
|
|
55
55
|
</li>
|
|
56
56
|
</ul>
|
|
@@ -21,6 +21,35 @@ public partial class FdyTableFooter
|
|
|
21
21
|
/// </summary>
|
|
22
22
|
[Parameter] public IReadOnlyList<int>? PageSizes { get; set; }
|
|
23
23
|
|
|
24
|
+
// The strings, six of them, one per thing this band says. The raw path has had a hatch since
|
|
25
|
+
// 1.39.0 (data-fdy-text-<key> read through textOf()) and this path had none, so an app that
|
|
26
|
+
// does not speak English could only rewrite the whole band by hand. A Blazor app mixes both
|
|
27
|
+
// paths by construction, since FreedayBlazor.initAll runs the enhancers, so these names follow
|
|
28
|
+
// the enhancer's TEXT keys where a key exists. Defaults stay English on every path, which
|
|
29
|
+
// COMPONENTS.md promises and the suite enforces.
|
|
30
|
+
|
|
31
|
+
/// <summary>
|
|
32
|
+
/// The range line. <c>{from}</c>, <c>{to}</c> and <c>{total}</c> are substituted, every
|
|
33
|
+
/// occurrence, as the enhancer's <c>textOf()</c> does. Default <c>Showing {from}–{to} of
|
|
34
|
+
/// {total}</c>, the enhancer's <c>info</c> key.
|
|
35
|
+
/// </summary>
|
|
36
|
+
[Parameter] public string? InfoText { get; set; }
|
|
37
|
+
|
|
38
|
+
/// <summary>The visible word beside the rows-per-page control, which also names it. Default <c>Rows</c>.</summary>
|
|
39
|
+
[Parameter] public string? PageSizeLabel { get; set; }
|
|
40
|
+
|
|
41
|
+
/// <summary>Accessible name of the pager landmark. Default <c>Pagination</c>.</summary>
|
|
42
|
+
[Parameter] public string? PaginationLabel { get; set; }
|
|
43
|
+
|
|
44
|
+
/// <summary>Accessible name of the previous-page arrow. Default <c>Previous page</c>.</summary>
|
|
45
|
+
[Parameter] public string? PrevLabel { get; set; }
|
|
46
|
+
|
|
47
|
+
/// <summary>Accessible name of the next-page arrow. Default <c>Next page</c>.</summary>
|
|
48
|
+
[Parameter] public string? NextLabel { get; set; }
|
|
49
|
+
|
|
50
|
+
/// <summary>Accessible name of a numbered page button. <c>{n}</c> substituted. Default <c>Go to page {n}</c>.</summary>
|
|
51
|
+
[Parameter] public string? PageLabel { get; set; }
|
|
52
|
+
|
|
24
53
|
private IReadOnlyList<int> Sizes => PageSizes ?? Array.Empty<int>();
|
|
25
54
|
|
|
26
55
|
// Just the number: the control sits beside a label reading "Rows", so "20 per page" states the
|
|
@@ -40,6 +69,13 @@ public partial class FdyTableFooter
|
|
|
40
69
|
|
|
41
70
|
private bool HasPager => Page.Size > 0 && TotalPages > 1;
|
|
42
71
|
|
|
72
|
+
private string InfoLine => (InfoText ?? "Showing {from}–{to} of {total}")
|
|
73
|
+
.Replace("{from}", RangeFrom.ToString())
|
|
74
|
+
.Replace("{to}", RangeTo.ToString())
|
|
75
|
+
.Replace("{total}", Page.Total.ToString());
|
|
76
|
+
|
|
77
|
+
private string PageAriaLabel(int page1) => (PageLabel ?? "Go to page {n}").Replace("{n}", page1.ToString());
|
|
78
|
+
|
|
43
79
|
// One page and no size control means there is nothing here to say, the table has always
|
|
44
80
|
// withheld the whole band in that case, and this is where that decision now lives.
|
|
45
81
|
private bool Visible => HasPager || Sizes.Count > 0;
|