@cahyo-dimas/freeday 1.17.0 → 1.20.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +202 -0
  2. package/COMPONENTS.md +748 -0
  3. package/README.id.md +20 -2
  4. package/README.md +32 -8
  5. package/USAGE.md +127 -0
  6. package/adapters/blazor/FdyTable.razor.cs +66 -7
  7. package/adapters/blazor/TableTypes.cs +6 -0
  8. package/adapters/react/components/FdyTable.tsx +43 -8
  9. package/adapters/vue/components/FdyDrawer.vue +5 -2
  10. package/adapters/vue/components/FdyModal.vue +5 -2
  11. package/adapters/vue/components/FdyTable.vue +42 -6
  12. package/dist/freeday.bundle.css +120 -9
  13. package/dist/freeday.css +103 -8
  14. package/dist/freeday.tokens.css +17 -1
  15. package/docs/agent-onboarding.md +151 -0
  16. package/docs/getting-started.md +454 -0
  17. package/docs/integrations.md +321 -0
  18. package/docs/reference-screen.html +461 -0
  19. package/package.json +11 -3
  20. package/src/components/avatar.css +8 -8
  21. package/src/components/card.css +3 -0
  22. package/src/components/chip.css +12 -0
  23. package/src/components/composition.css +49 -0
  24. package/src/components/list.css +29 -0
  25. package/tokens/breakpoints.d.ts +3 -0
  26. package/tokens/breakpoints.mjs +8 -1
  27. package/tokens/tokens.json +14 -2
  28. package/src/components/.gitkeep +0 -0
  29. package/src/freeday-autocomplete.js +0 -135
  30. package/src/freeday-breakpoint.js +0 -51
  31. package/src/freeday-carousel.js +0 -111
  32. package/src/freeday-cascade.js +0 -256
  33. package/src/freeday-cfl.js +0 -213
  34. package/src/freeday-chart.js +0 -429
  35. package/src/freeday-chip.js +0 -83
  36. package/src/freeday-datepicker.js +0 -321
  37. package/src/freeday-datetime.js +0 -83
  38. package/src/freeday-drawer.js +0 -43
  39. package/src/freeday-form.js +0 -181
  40. package/src/freeday-mask.js +0 -114
  41. package/src/freeday-menu.js +0 -93
  42. package/src/freeday-popover.js +0 -69
  43. package/src/freeday-rating.js +0 -50
  44. package/src/freeday-select.js +0 -218
  45. package/src/freeday-slider.js +0 -34
  46. package/src/freeday-stepper.js +0 -90
  47. package/src/freeday-table.js +0 -475
  48. package/src/freeday-tabs.js +0 -68
  49. package/src/freeday-timepicker.js +0 -180
  50. package/src/freeday-toast.js +0 -105
  51. package/src/freeday-tree.js +0 -94
  52. package/src/freeday-upload.js +0 -206
@@ -0,0 +1,461 @@
1
+ <!doctype html>
2
+ <html lang="en" data-theme="light" data-density="comfortable">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Freeday — reference screen</title>
7
+
8
+ <!-- The kit in one file: tokens + components (this is what @cahyo-dimas/freeday/css resolves to).
9
+ Note the file name: dist/freeday.css is components ONLY — link that alone and every
10
+ var(--color-*) / var(--space-*) silently falls back, which looks like a broken stylesheet.
11
+ Paths are relative, so this works both in the repo and in node_modules/@cahyo-dimas/freeday/docs/. -->
12
+ <link rel="stylesheet" href="../dist/freeday.bundle.css">
13
+
14
+ <!-- Fonts are NOT shipped by the package. Load them yourself (Fontsource, self-hosted, or a
15
+ <link> to a provider) or override --font-display / --font-body / --font-mono. Without them
16
+ the kit renders in the system fallback, which reads as an unfinished design. -->
17
+ </head>
18
+ <body>
19
+
20
+ <!-- ══════════════════════════════════════════════════════════════════════════════════════════
21
+ REFERENCE SCREEN — one business screen, assembled the intended way.
22
+
23
+ Structure, outermost to innermost:
24
+ .fdy-app the shell (topbar · sidebar · main)
25
+ └ .fdy-page the screen body — vertical section rhythm
26
+ ├ .fdy-page__header eyebrow + page title + desc | ONE primary action
27
+ ├ .fdy-page-section KPI strip (.fdy-stats — deliberately not cards)
28
+ ├ .fdy-page-section chart
29
+ └ .fdy-page-section the data table
30
+
31
+ Every class here exists in COMPONENTS.md. Every value comes from a token — search this file
32
+ for "#" or "px" and you will find none outside the demo-only sizing on the chart wrappers.
33
+ Doctrine behind the choices: USAGE.md.
34
+ ══════════════════════════════════════════════════════════════════════════════════════════ -->
35
+
36
+ <div class="fdy-app" id="app">
37
+ <a class="fdy-skip" href="#main">Skip to main content</a>
38
+
39
+ <!-- Sidebar comes FIRST: .fdy-app is a flex row of [sidebar | content]. The brand lives in the
40
+ sidebar (it is sized to match the topbar height), not in the topbar. -->
41
+ <aside class="fdy-app__sidebar" id="sidebar" aria-label="Main navigation">
42
+ <a class="fdy-app__brand" href="#app">
43
+ <span class="fdy-app__brand-mark">
44
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M4 12h10M4 17h6"/></svg>
45
+ </span>
46
+ <span class="fdy-app__brand-text">
47
+ <span class="fdy-app__brand-title">Acme Books</span>
48
+ <span class="fdy-app__brand-subtitle">Finance</span>
49
+ </span>
50
+ </a>
51
+ <nav class="fdy-nav">
52
+ <a class="fdy-nav__item" href="#app">
53
+ <span class="fdy-nav__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg></span>
54
+ <span class="fdy-nav__label">Dashboard</span>
55
+ </a>
56
+ <a class="fdy-nav__item" href="#app" aria-current="page">
57
+ <span class="fdy-nav__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M9 13h6M9 17h6"/></svg></span>
58
+ <span class="fdy-nav__label">Invoices</span>
59
+ <span class="fdy-nav__badge">12</span>
60
+ </a>
61
+ <a class="fdy-nav__item" href="#app">
62
+ <span class="fdy-nav__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/></svg></span>
63
+ <span class="fdy-nav__label">Customers</span>
64
+ </a>
65
+ <details class="fdy-nav__group" open>
66
+ <summary class="fdy-nav__grouplabel">Reports</summary>
67
+ <a class="fdy-nav__item" href="#app"><span class="fdy-nav__label">Aging</span></a>
68
+ <a class="fdy-nav__item" href="#app"><span class="fdy-nav__label">Collections</span></a>
69
+ </details>
70
+ </nav>
71
+ </aside>
72
+
73
+ <!-- __content is the column wrapper holding the topbar + main. It exists so the sticky topbar
74
+ has a tall containing block to travel in — a bare flex cell cannot do that. -->
75
+ <div class="fdy-app__content">
76
+
77
+ <header class="fdy-app__topbar">
78
+ <button class="fdy-app__navtoggle" type="button" aria-label="Toggle navigation" aria-expanded="true" aria-controls="sidebar">
79
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
80
+ </button>
81
+
82
+ <!-- .fdy-app__title carries an auto-spacer (margin-right:auto): everything after it sits at
83
+ the right edge. -->
84
+ <h1 class="fdy-app__title fdy-hide-below-md">Receivables</h1>
85
+
86
+ <div class="fdy-input-group fdy-hide-below-md" style="max-width:14rem">
87
+ <span class="fdy-input-group__addon fdy-input-group__addon--icon">
88
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.35-4.35"/></svg>
89
+ </span>
90
+ <input class="fdy-input" type="search" placeholder="Search…" aria-label="Search the app">
91
+ </div>
92
+
93
+ <span class="fdy-badge-wrap">
94
+ <button class="fdy-btn fdy-btn--ghost fdy-btn--icon" type="button" aria-label="Notifications — 3 new">
95
+ <span class="fdy-btn__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg></span>
96
+ </button>
97
+ <span class="fdy-badge-ov fdy-badge-ov--danger" aria-hidden="true">3</span>
98
+ </span>
99
+
100
+ <div class="fdy-menu-wrap" data-fdy-menu>
101
+ <button class="fdy-btn fdy-btn--ghost" type="button" aria-haspopup="menu" aria-expanded="false" aria-label="User menu">
102
+ <span class="fdy-avatar fdy-avatar--sm fdy-avatar--tone-3">AK</span>
103
+ </button>
104
+ <ul class="fdy-menu fdy-menu--end" role="menu" hidden>
105
+ <li role="none"><button class="fdy-menu__item" role="menuitem" type="button">Profile</button></li>
106
+ <li role="none"><button class="fdy-menu__item" role="menuitem" type="button" id="toggle-theme">Toggle theme</button></li>
107
+ <li role="none"><button class="fdy-menu__item" role="menuitem" type="button" id="toggle-density">Toggle density</button></li>
108
+ <li role="none"><hr class="fdy-menu__sep"></li>
109
+ <li role="none"><button class="fdy-menu__item fdy-menu__item--danger" role="menuitem" type="button">Sign out</button></li>
110
+ </ul>
111
+ </div>
112
+ </header>
113
+
114
+ <!-- __main is the padded content area (--space-8). The screen goes straight inside it. -->
115
+ <main class="fdy-app__main" id="main" tabindex="-1">
116
+
117
+ <!-- ═══ THE SCREEN ═══ -->
118
+ <div class="fdy-page">
119
+
120
+ <!-- One page header. One primary action — everything else is ghost. -->
121
+ <header class="fdy-page__header">
122
+ <div class="fdy-page__heading">
123
+ <p class="fdy-eyebrow">Sales</p>
124
+ <h2 class="fdy-title-page">Receivables</h2>
125
+ <p class="fdy-page__desc">Every open invoice, with the amount still outstanding per customer.</p>
126
+ </div>
127
+ <div class="fdy-page__actions">
128
+ <button class="fdy-btn fdy-btn--ghost" type="button" id="btn-export">Export</button>
129
+ <button class="fdy-btn" type="button" id="btn-new">New invoice</button>
130
+ </div>
131
+ </header>
132
+
133
+ <!-- KPI strip: .fdy-stat tiles, NOT cards — a metric row must not read as a card grid. -->
134
+ <section class="fdy-page-section" aria-labelledby="kpi-h">
135
+ <h3 class="fdy-title-section fdy-visually-hidden" id="kpi-h">Key figures</h3>
136
+ <div class="fdy-stats fdy-stats--boxed">
137
+ <div class="fdy-stat">
138
+ <span class="fdy-stat__label">Outstanding</span>
139
+ <span class="fdy-stat__value">8.42<small> M</small></span>
140
+ <span class="fdy-stat__meta">across 34 invoices</span>
141
+ </div>
142
+ <div class="fdy-stat">
143
+ <span class="fdy-stat__label">Overdue</span>
144
+ <span class="fdy-stat__value">1.19<small> M</small></span>
145
+ <span class="fdy-stat__meta"><span class="fdy-badge fdy-badge--warning">7 invoices</span></span>
146
+ </div>
147
+ <div class="fdy-stat">
148
+ <span class="fdy-stat__label">Collected (MTD)</span>
149
+ <span class="fdy-stat__value">5.60<small> M</small></span>
150
+ <span class="fdy-stat__meta">target 6.00 M</span>
151
+ </div>
152
+ <div class="fdy-stat">
153
+ <span class="fdy-stat__label">Avg. days to pay</span>
154
+ <span class="fdy-stat__value">27</span>
155
+ <span class="fdy-stat__meta">terms: 30 days</span>
156
+ </div>
157
+ </div>
158
+ </section>
159
+
160
+ <!-- A section with a chart. Flat section, one card holding the chart. -->
161
+ <section class="fdy-page-section" aria-labelledby="trend-h">
162
+ <div class="fdy-page-section__head">
163
+ <h3 class="fdy-title-section" id="trend-h">Collection trend</h3>
164
+ <p class="fdy-text-caption">Last six months</p>
165
+ </div>
166
+ <div class="fdy-card">
167
+ <div class="fdy-card__body">
168
+ <div data-fdy-chart="line"
169
+ data-labels="Feb,Mar,Apr,May,Jun,Jul"
170
+ data-series='[{"label":"Invoiced","values":[62,68,75,71,83,88]},{"label":"Collected","values":[54,60,66,69,74,81]}]'
171
+ data-fdy-format="number"
172
+ role="img"
173
+ aria-label="Invoiced versus collected, in millions: invoiced rose from 62 to 88, collected from 54 to 81">
174
+ <table>
175
+ <caption>Invoiced vs collected (millions)</caption>
176
+ <tr><th scope="col">Month</th><th scope="col">Invoiced</th><th scope="col">Collected</th></tr>
177
+ <tr><td>Jul</td><td>88</td><td>81</td></tr>
178
+ </table>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </section>
183
+
184
+ <!-- The data table, with its filter bar composed onto the toolbar baseline.
185
+ KNOWN GAP: the vanilla freeday-table.js writes its footer/bulk strings in Indonesian
186
+ ("Menampilkan 1–5 dari 7", "N dipilih") and offers no override, so they stay Indonesian
187
+ on this English page. The Vue/React/Blazor <FdyTable> components default to English.
188
+ If you need English in a static page, render those two nodes yourself from the
189
+ fdy-table-change event instead of using data-fdy-table-info / -bulk-count. -->
190
+ <section class="fdy-page-section" aria-labelledby="list-h">
191
+ <div class="fdy-page-section__head">
192
+ <h3 class="fdy-title-section" id="list-h">Open invoices</h3>
193
+ </div>
194
+
195
+ <!-- md and up: the table. Below md it is hidden and the flat list below takes over. -->
196
+ <div class="fdy-datatable fdy-hide-below-md" data-fdy-table data-page-size="5">
197
+ <div class="fdy-table-toolbar fdy-filterbar">
198
+ <div class="fdy-field fdy-field--w-grow">
199
+ <span class="fdy-label" id="f-q">Search</span>
200
+ <div class="fdy-input-group">
201
+ <span class="fdy-input-group__addon fdy-input-group__addon--icon">
202
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.35-4.35"/></svg>
203
+ </span>
204
+ <input class="fdy-input" type="search" placeholder="Invoice or customer…" data-fdy-table-search aria-labelledby="f-q">
205
+ </div>
206
+ </div>
207
+ <div class="fdy-field fdy-field--w-sm">
208
+ <span class="fdy-label" id="f-st">Status</span>
209
+ <div class="fdy-combo" data-fdy-combo data-value="all">
210
+ <button type="button" class="fdy-combo__button" role="combobox" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="f-st f-st-v">
211
+ <span class="fdy-combo__value" id="f-st-v">All</span>
212
+ </button>
213
+ <ul class="fdy-combo__listbox" role="listbox" aria-labelledby="f-st" hidden>
214
+ <li class="fdy-combo__option" role="option" data-value="all" aria-selected="true"><span class="fdy-combo__check">✓</span>All</li>
215
+ <li class="fdy-combo__option" role="option" data-value="open" aria-selected="false"><span class="fdy-combo__check"></span>Open</li>
216
+ <li class="fdy-combo__option" role="option" data-value="overdue" aria-selected="false"><span class="fdy-combo__check"></span>Overdue</li>
217
+ </ul>
218
+ </div>
219
+ </div>
220
+ <div class="fdy-field fdy-field--w-2xl">
221
+ <span class="fdy-label">Issued between</span>
222
+ <div class="fdy-daterange" data-fdy-daterange role="group" aria-label="Issued between">
223
+ <div data-fdy-datepicker data-role="from" data-placeholder="From" data-label="Issued from"></div>
224
+ <span class="fdy-daterange__sep" aria-hidden="true">–</span>
225
+ <div data-fdy-datepicker data-role="to" data-placeholder="To" data-label="Issued to"></div>
226
+ </div>
227
+ </div>
228
+ <div class="fdy-filterbar__actions">
229
+ <button class="fdy-btn fdy-btn--ghost" type="button">Reset</button>
230
+ </div>
231
+ </div>
232
+
233
+ <div class="fdy-table-bulkbar" data-fdy-table-bulk hidden role="region" aria-label="Bulk actions">
234
+ <span class="fdy-table-bulkbar__count" data-fdy-table-bulk-count aria-live="polite">0 selected</span>
235
+ <span class="fdy-table-bulkbar__spacer"></span>
236
+ <div class="fdy-table-bulkbar__actions">
237
+ <button class="fdy-btn fdy-btn--ghost fdy-btn--sm" type="button">Send reminder</button>
238
+ <button class="fdy-btn fdy-btn--ghost fdy-btn--sm" type="button" data-fdy-table-bulk-clear>Clear</button>
239
+ </div>
240
+ </div>
241
+
242
+ <div class="fdy-table-scroll">
243
+ <table class="fdy-table">
244
+ <caption class="fdy-visually-hidden">Open invoices, sortable and filterable</caption>
245
+ <thead>
246
+ <tr>
247
+ <th class="fdy-table__selcol" scope="col"><input type="checkbox" class="fdy-checkbox" data-fdy-select-all aria-label="Select all rows"></th>
248
+ <th scope="col"><button type="button" class="fdy-table__sortbtn" data-fdy-sort>Invoice</button></th>
249
+ <th scope="col" data-fdy-filter="text"><button type="button" class="fdy-table__sortbtn" data-fdy-sort>Customer</button></th>
250
+ <th scope="col" data-fdy-filter="enum"><button type="button" class="fdy-table__sortbtn" data-fdy-sort>Status</button></th>
251
+ <th scope="col">Due</th>
252
+ <th scope="col" class="fdy-table__num" data-fdy-filter="number"><button type="button" class="fdy-table__sortbtn" data-fdy-sort="number">Amount</button></th>
253
+ </tr>
254
+ </thead>
255
+ <tbody>
256
+ <tr>
257
+ <td class="fdy-table__selcol"><input type="checkbox" class="fdy-checkbox" data-fdy-row-select aria-label="Select INV-2041"></td>
258
+ <td class="fdy-mono">INV-2041</td><td>Acme Ltd</td>
259
+ <td><span class="fdy-badge fdy-badge--success">Paid</span></td>
260
+ <td>30 Jul 2026</td><td class="fdy-table__num" data-sort-value="1240000">1,240,000</td>
261
+ </tr>
262
+ <tr>
263
+ <td class="fdy-table__selcol"><input type="checkbox" class="fdy-checkbox" data-fdy-row-select aria-label="Select INV-2042"></td>
264
+ <td class="fdy-mono">INV-2042</td><td>Northwind Trading</td>
265
+ <td><span class="fdy-badge fdy-badge--warning">Overdue</span></td>
266
+ <td>12 Jul 2026</td><td class="fdy-table__num" data-sort-value="290000">290,000</td>
267
+ </tr>
268
+ <tr>
269
+ <td class="fdy-table__selcol"><input type="checkbox" class="fdy-checkbox" data-fdy-row-select aria-label="Select INV-2043"></td>
270
+ <td class="fdy-mono">INV-2043</td><td>Globex Supply</td>
271
+ <td><span class="fdy-badge fdy-badge--info">Draft</span></td>
272
+ <td>—</td><td class="fdy-table__num" data-sort-value="0">0</td>
273
+ </tr>
274
+ <tr>
275
+ <td class="fdy-table__selcol"><input type="checkbox" class="fdy-checkbox" data-fdy-row-select aria-label="Select INV-2044"></td>
276
+ <td class="fdy-mono">INV-2044</td><td>Initech Partners</td>
277
+ <td><span class="fdy-badge fdy-badge--success">Paid</span></td>
278
+ <td>02 Aug 2026</td><td class="fdy-table__num" data-sort-value="4750000">4,750,000</td>
279
+ </tr>
280
+ <tr>
281
+ <td class="fdy-table__selcol"><input type="checkbox" class="fdy-checkbox" data-fdy-row-select aria-label="Select INV-2045"></td>
282
+ <td class="fdy-mono">INV-2045</td><td>Umbra Logistics</td>
283
+ <td><span class="fdy-badge fdy-badge--warning">Overdue</span></td>
284
+ <td>08 Jul 2026</td><td class="fdy-table__num" data-sort-value="820000">820,000</td>
285
+ </tr>
286
+ <tr>
287
+ <td class="fdy-table__selcol"><input type="checkbox" class="fdy-checkbox" data-fdy-row-select aria-label="Select INV-2046"></td>
288
+ <td class="fdy-mono">INV-2046</td><td>Stark Materials</td>
289
+ <td><span class="fdy-badge fdy-badge--danger">Cancelled</span></td>
290
+ <td>—</td><td class="fdy-table__num" data-sort-value="150000">150,000</td>
291
+ </tr>
292
+ <tr>
293
+ <td class="fdy-table__selcol"><input type="checkbox" class="fdy-checkbox" data-fdy-row-select aria-label="Select INV-2047"></td>
294
+ <td class="fdy-mono">INV-2047</td><td>Acme Ltd</td>
295
+ <td><span class="fdy-badge fdy-badge--success">Paid</span></td>
296
+ <td>21 Aug 2026</td><td class="fdy-table__num" data-sort-value="3120000">3,120,000</td>
297
+ </tr>
298
+ </tbody>
299
+ </table>
300
+ </div>
301
+
302
+ <div class="fdy-table-footer">
303
+ <span class="fdy-table-footer__info" data-fdy-table-info></span>
304
+ <nav class="fdy-pagination" aria-label="Table pages" data-fdy-table-pagination></nav>
305
+ </div>
306
+ </div>
307
+
308
+ <!-- Below md: the SAME rows as a flat list. .fdy-list is the flat container the doctrine
309
+ asks for — a stack of .fdy-card would put a 34px lift under every row.
310
+ In a framework app you would not duplicate the markup: hide <FdyTable> below md, feed
311
+ this list from its `process` event, and drive both from a controlled `pageIndex` with
312
+ ONE pager rendered outside the table. See COMPONENTS.md §Data table. -->
313
+ <ul class="fdy-list fdy-hide-above-md">
314
+ <li>
315
+ <button type="button" class="fdy-list__row fdy-list__row--button">
316
+ <span class="fdy-list__main">
317
+ <span class="fdy-list__title">Acme Ltd</span>
318
+ <span class="fdy-list__meta"><span class="fdy-mono">INV-2041</span> · due 30 Jul <span class="fdy-badge fdy-badge--success">Paid</span></span>
319
+ </span>
320
+ <span class="fdy-list__aside fdy-mono">1,240,000</span>
321
+ </button>
322
+ </li>
323
+ <li>
324
+ <button type="button" class="fdy-list__row fdy-list__row--button">
325
+ <span class="fdy-list__main">
326
+ <span class="fdy-list__title">Northwind Trading</span>
327
+ <span class="fdy-list__meta"><span class="fdy-mono">INV-2042</span> · due 12 Jul <span class="fdy-badge fdy-badge--warning">Overdue</span></span>
328
+ </span>
329
+ <span class="fdy-list__aside fdy-mono">290,000</span>
330
+ </button>
331
+ </li>
332
+ <li>
333
+ <button type="button" class="fdy-list__row fdy-list__row--button">
334
+ <span class="fdy-list__main">
335
+ <span class="fdy-list__title">Globex Supply</span>
336
+ <span class="fdy-list__meta"><span class="fdy-mono">INV-2043</span> · draft <span class="fdy-badge fdy-badge--info">Draft</span></span>
337
+ </span>
338
+ <span class="fdy-list__aside fdy-mono">0</span>
339
+ </button>
340
+ </li>
341
+ </ul>
342
+ </section>
343
+
344
+ <!-- A detail region: key-value pairs + a timeline. Flat section, one card each. -->
345
+ <section class="fdy-page-section" aria-labelledby="detail-h">
346
+ <div class="fdy-page-section__head">
347
+ <h3 class="fdy-title-section" id="detail-h">INV-2042 — detail</h3>
348
+ <button class="fdy-btn fdy-btn--text" type="button">Open full record</button>
349
+ </div>
350
+ <div class="fdy-card">
351
+ <div class="fdy-card__body">
352
+ <h4 class="fdy-title-card">Northwind Trading</h4>
353
+ <dl class="fdy-dl">
354
+ <dt>Invoice</dt><dd class="fdy-mono">INV-2042</dd>
355
+ <dt>Status</dt><dd><span class="fdy-badge fdy-badge--warning">Overdue</span></dd>
356
+ <dt>Issued</dt><dd>12 Jun 2026</dd>
357
+ <dt>Due</dt><dd>12 Jul 2026</dd>
358
+ <dt>Amount</dt><dd>290,000</dd>
359
+ </dl>
360
+ </div>
361
+ </div>
362
+ </section>
363
+
364
+ </div>
365
+ <!-- ═══ /THE SCREEN ═══ -->
366
+
367
+ </main>
368
+
369
+ </div><!-- /.fdy-app__content -->
370
+
371
+ <div class="fdy-app__backdrop"></div>
372
+ </div>
373
+
374
+ <!-- Native <dialog>: focus trap, Esc and backdrop are the browser's job, not yours. -->
375
+ <dialog class="fdy-modal" id="new-invoice" aria-labelledby="new-invoice-t">
376
+ <form method="dialog" data-fdy-validate>
377
+ <div class="fdy-modal__header">
378
+ <h2 class="fdy-modal__title" id="new-invoice-t">New invoice</h2>
379
+ <button class="fdy-modal__close" type="button" aria-label="Close" data-close>&times;</button>
380
+ </div>
381
+ <div class="fdy-modal__body">
382
+ <div class="fdy-form-grid">
383
+ <label class="fdy-field">
384
+ <span class="fdy-label">Customer</span>
385
+ <input class="fdy-input" name="customer" required data-fdy-msg-required="Customer is required.">
386
+ </label>
387
+ <label class="fdy-field">
388
+ <span class="fdy-label">Invoice no.</span>
389
+ <input class="fdy-input fdy-mono" name="no" value="INV-2048" readonly>
390
+ </label>
391
+ <div class="fdy-field">
392
+ <span class="fdy-label">Issue date</span>
393
+ <div data-fdy-datepicker data-value="2026-08-12" data-label="Issue date"></div>
394
+ </div>
395
+ <label class="fdy-field">
396
+ <span class="fdy-label">Amount</span>
397
+ <div class="fdy-input-group">
398
+ <span class="fdy-input-group__addon">Rp</span>
399
+ <input class="fdy-input" name="amount" inputmode="numeric" required data-fdy-msg-required="Amount is required.">
400
+ </div>
401
+ </label>
402
+ <label class="fdy-field fdy-field--full fdy-switch">
403
+ <input type="checkbox" checked> Email the invoice to the customer
404
+ </label>
405
+ </div>
406
+ </div>
407
+ <div class="fdy-modal__footer">
408
+ <button class="fdy-btn fdy-btn--ghost" type="button" data-close>Cancel</button>
409
+ <button class="fdy-btn" type="submit">Save invoice</button>
410
+ </div>
411
+ </form>
412
+ </dialog>
413
+
414
+ <!-- Enhancers: only the ones this screen actually uses. Zero dependencies, load order free. -->
415
+ <script src="../dist/freeday-popover.js"></script>
416
+ <script src="../dist/freeday-select.js"></script>
417
+ <script src="../dist/freeday-datepicker.js"></script>
418
+ <script src="../dist/freeday-menu.js"></script>
419
+ <script src="../dist/freeday-table.js"></script>
420
+ <script src="../dist/freeday-chart.js"></script>
421
+ <script src="../dist/freeday-form.js"></script>
422
+ <script src="../dist/freeday-toast.js"></script>
423
+
424
+ <script>
425
+ // The shell's only wiring: the nav toggle. Two state classes, no library.
426
+ // Above 720px the sidebar collapses to zero width (--nav-collapsed); at or below it slides in as
427
+ // an off-canvas drawer (--nav-open). 720px is where app-shell.css switches — keep them in step.
428
+ const app = document.getElementById('app');
429
+ const toggle = app.querySelector('.fdy-app__navtoggle');
430
+ const wide = () => window.matchMedia('(min-width: 721px)').matches;
431
+ toggle.addEventListener('click', () => {
432
+ const cls = wide() ? 'fdy-app--nav-collapsed' : 'fdy-app--nav-open';
433
+ const open = app.classList.toggle(cls);
434
+ toggle.setAttribute('aria-expanded', String(wide() ? !open : open));
435
+ });
436
+ app.querySelector('.fdy-app__backdrop').addEventListener('click', () => {
437
+ app.classList.remove('fdy-app--nav-open');
438
+ toggle.setAttribute('aria-expanded', 'false');
439
+ });
440
+
441
+ // Theme + density live on <html>, set once — never per component.
442
+ const root = document.documentElement;
443
+ document.getElementById('toggle-theme').addEventListener('click', () => {
444
+ root.dataset.theme = root.dataset.theme === 'dark' ? 'light' : 'dark';
445
+ });
446
+ document.getElementById('toggle-density').addEventListener('click', () => {
447
+ root.dataset.density = root.dataset.density === 'compact' ? 'comfortable' : 'compact';
448
+ });
449
+
450
+ document.getElementById('btn-new').addEventListener('click', () => {
451
+ document.getElementById('new-invoice').showModal();
452
+ });
453
+ document.querySelectorAll('#new-invoice [data-close]').forEach(b => {
454
+ b.addEventListener('click', () => document.getElementById('new-invoice').close());
455
+ });
456
+ document.getElementById('btn-export').addEventListener('click', () => {
457
+ Freeday.toast({ variant: 'info', title: 'Export queued', message: 'The file will download shortly.', key: 'export' });
458
+ });
459
+ </script>
460
+ </body>
461
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cahyo-dimas/freeday",
3
- "version": "1.17.0",
3
+ "version": "1.20.0",
4
4
  "description": "Freeday — token-driven, framework-agnostic UI KIT (design source-of-truth).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,14 +21,21 @@
21
21
  },
22
22
  "files": [
23
23
  "dist",
24
- "src",
24
+ "src/base.css",
25
+ "src/components",
25
26
  "adapters",
26
27
  "tokens/tokens.json",
27
28
  "tokens/breakpoints.mjs",
28
29
  "tokens/breakpoints.d.ts",
29
30
  "README.md",
30
31
  "README.id.md",
31
- "CHANGELOG.md"
32
+ "USAGE.md",
33
+ "COMPONENTS.md",
34
+ "CHANGELOG.md",
35
+ "docs/getting-started.md",
36
+ "docs/integrations.md",
37
+ "docs/agent-onboarding.md",
38
+ "docs/reference-screen.html"
32
39
  ],
33
40
  "exports": {
34
41
  ".": { "types": "./dist/freeday.d.ts", "default": "./dist/freeday.js" },
@@ -36,6 +43,7 @@
36
43
  "./css/components": { "types": "./dist/asset.d.ts", "default": "./dist/freeday.css" },
37
44
  "./tokens": { "types": "./dist/asset.d.ts", "default": "./dist/freeday.tokens.css" },
38
45
  "./breakpoints": { "types": "./tokens/breakpoints.d.ts", "default": "./tokens/breakpoints.mjs" },
46
+ "./table-model": { "types": "./adapters/core/table-model.d.ts", "default": "./adapters/core/table-model.js" },
39
47
  "./enhancers/*": { "types": "./dist/asset.d.ts", "default": "./dist/freeday-*.js" },
40
48
  "./vue": {
41
49
  "types": "./adapters/vue/index.d.ts",
@@ -11,11 +11,11 @@
11
11
  * test/contrast.test.mjs). Use to disambiguate same-initial avatars (e.g. many "PT …" names):
12
12
  * hash a stable 1..8 index off the FULL name in your app, then apply .fdy-avatar--tone-<n>. */
13
13
  .fdy-avatar--tone-1,.fdy-avatar--tone-2,.fdy-avatar--tone-3,.fdy-avatar--tone-4,.fdy-avatar--tone-5,.fdy-avatar--tone-6,.fdy-avatar--tone-7,.fdy-avatar--tone-8{background:color-mix(in srgb,var(--_fdy-avatar-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-avatar-tone) 50%,var(--color-text));}
14
- .fdy-avatar--tone-1{--_fdy-avatar-tone:var(--chart-1);}
15
- .fdy-avatar--tone-2{--_fdy-avatar-tone:var(--chart-2);}
16
- .fdy-avatar--tone-3{--_fdy-avatar-tone:var(--chart-3);}
17
- .fdy-avatar--tone-4{--_fdy-avatar-tone:var(--chart-4);}
18
- .fdy-avatar--tone-5{--_fdy-avatar-tone:var(--chart-5);}
19
- .fdy-avatar--tone-6{--_fdy-avatar-tone:var(--chart-6);}
20
- .fdy-avatar--tone-7{--_fdy-avatar-tone:var(--chart-7);}
21
- .fdy-avatar--tone-8{--_fdy-avatar-tone:var(--chart-8);}
14
+ .fdy-avatar--tone-1{--_fdy-avatar-tone:var(--tone-1);}
15
+ .fdy-avatar--tone-2{--_fdy-avatar-tone:var(--tone-2);}
16
+ .fdy-avatar--tone-3{--_fdy-avatar-tone:var(--tone-3);}
17
+ .fdy-avatar--tone-4{--_fdy-avatar-tone:var(--tone-4);}
18
+ .fdy-avatar--tone-5{--_fdy-avatar-tone:var(--tone-5);}
19
+ .fdy-avatar--tone-6{--_fdy-avatar-tone:var(--tone-6);}
20
+ .fdy-avatar--tone-7{--_fdy-avatar-tone:var(--tone-7);}
21
+ .fdy-avatar--tone-8{--_fdy-avatar-tone:var(--tone-8);}
@@ -3,6 +3,9 @@
3
3
  .fdy-card--elevated{box-shadow:var(--shadow-lift-hover);}
4
4
  .fdy-card--interactive{cursor:pointer;}
5
5
  .fdy-card--interactive:hover{box-shadow:var(--shadow-lift-hover);transform:translateY(-3px);}
6
+ /* The global reduced-motion reset kills the ANIMATION; drop the -3px shift too so the card
7
+ * doesn't jump. The shadow still communicates the affordance. */
8
+ @media (prefers-reduced-motion:reduce){.fdy-card--interactive:hover{transform:none;}}
6
9
  /* When the whole card IS the control — a real <button> so the row is keyboard-reachable and
7
10
  * announced — reset the UA button box. Note: it does NOT reset background/border, because
8
11
  * .fdy-card already sets its own surface + border (resetting them here would strip the card). */
@@ -1,6 +1,18 @@
1
1
  /* Freeday — Chip (tag / removable filter) */
2
2
  .fdy-chip{display:inline-flex;align-items:center;gap:var(--space-2);height:1.75rem;padding:0 var(--space-3);border-radius:var(--radius-full);font-size:var(--text-xs);font-weight:var(--weight-medium);color:var(--color-text);background:var(--color-surface-3);border:var(--bw) solid var(--color-border);}
3
3
  .fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:color-mix(in srgb,var(--color-primary) 22%,transparent);}
4
+ /* Categorical tone chips — non-semantic category tags / legend swatches, from the general
5
+ * --tone-1..8 palette. Same validated AA formula as .fdy-avatar--tone-* (text >=4.5:1 both themes,
6
+ * gated by test/contrast.test.mjs). */
7
+ .fdy-chip--tone-1,.fdy-chip--tone-2,.fdy-chip--tone-3,.fdy-chip--tone-4,.fdy-chip--tone-5,.fdy-chip--tone-6,.fdy-chip--tone-7,.fdy-chip--tone-8{background:color-mix(in srgb,var(--_fdy-chip-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-chip-tone) 50%,var(--color-text));border-color:color-mix(in srgb,var(--_fdy-chip-tone) 30%,var(--color-border));}
8
+ .fdy-chip--tone-1{--_fdy-chip-tone:var(--tone-1);}
9
+ .fdy-chip--tone-2{--_fdy-chip-tone:var(--tone-2);}
10
+ .fdy-chip--tone-3{--_fdy-chip-tone:var(--tone-3);}
11
+ .fdy-chip--tone-4{--_fdy-chip-tone:var(--tone-4);}
12
+ .fdy-chip--tone-5{--_fdy-chip-tone:var(--tone-5);}
13
+ .fdy-chip--tone-6{--_fdy-chip-tone:var(--tone-6);}
14
+ .fdy-chip--tone-7{--_fdy-chip-tone:var(--tone-7);}
15
+ .fdy-chip--tone-8{--_fdy-chip-tone:var(--tone-8);}
4
16
  .fdy-chip__remove{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-right:calc(var(--space-1)*-1);border:0;padding:0;border-radius:var(--radius-full);background:transparent;color:currentColor;opacity:.6;cursor:pointer;font-size:var(--text-sm);line-height:1;}
5
17
  .fdy-chip__remove:hover{opacity:1;background:color-mix(in srgb,currentColor 16%,transparent);}
6
18
  .fdy-chip__remove:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
@@ -0,0 +1,49 @@
1
+ /* Freeday — Page composition & type roles.
2
+ *
3
+ * The kit stops at the component; these primitives encode *how a page is assembled* so screens
4
+ * built by different people cohere. They carry the usage doctrine (see USAGE.md) in markup: a
5
+ * named type role gets used, a doc gets skimmed. Everything here is token-driven layout — no new
6
+ * colours, no new shadows. Pair with .fdy-app (the shell) and the components inside.
7
+ */
8
+
9
+ /* ---- Type roles (three title levels + supporting text) -------------------------------------
10
+ * One role per level of hierarchy. Reach for these instead of re-using .fdy-card__title for a
11
+ * page title (the mistake that collapses three levels into one grey mush). */
12
+ .fdy-eyebrow{font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--color-text-subtle);}
13
+ .fdy-title-page{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tighter);line-height:var(--leading-tight);color:var(--color-text);margin:0;text-wrap:balance;}
14
+ .fdy-title-section{font-family:var(--font-display);font-size:var(--text-xl);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-tight);line-height:var(--leading-snug);color:var(--color-text);margin:0;text-wrap:balance;}
15
+ .fdy-title-card{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-tight);color:var(--color-text);margin:0;}
16
+ .fdy-text-muted{color:var(--color-text-muted);}
17
+ .fdy-text-subtle{color:var(--color-text-subtle);}
18
+ .fdy-text-caption{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;}
19
+
20
+ /* ---- Page: the vertical stack of sections, with the section rhythm between them ------------- */
21
+ .fdy-page{display:flex;flex-direction:column;gap:var(--space-8);}
22
+
23
+ /* ---- Page header: eyebrow + title + description on the left, primary action(s) on the right -- */
24
+ .fdy-page__header{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--space-4);flex-wrap:wrap;}
25
+ .fdy-page__heading{display:flex;flex-direction:column;gap:var(--space-2);min-width:0;}
26
+ .fdy-page__desc{font-size:var(--text-base);color:var(--color-text-muted);margin:0;max-width:62ch;line-height:var(--leading-normal);}
27
+ .fdy-page__actions{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;}
28
+
29
+ /* ---- Page section: a heading (+ optional toolbar) and its body, with the intra-group rhythm --- */
30
+ .fdy-page-section{display:flex;flex-direction:column;gap:var(--space-4);}
31
+ .fdy-page-section__head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-3);flex-wrap:wrap;}
32
+
33
+ /* ---- Toolbar: a horizontal control row (search + actions), wraps cleanly ---------------------- */
34
+ .fdy-toolbar{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;}
35
+ .fdy-toolbar__spacer{flex:1;}
36
+ .fdy-toolbar__search{max-width:18rem;flex:1 1 12rem;}
37
+
38
+ /* ---- Stat tile: a KPI metric that is deliberately NOT a card, so a KPI strip doesn't become an
39
+ * identical-white-card grid. Flat by default; wrap the .fdy-stats grid in one card if you want a
40
+ * single boxed strip, or use .fdy-stats--boxed for shared surface + dividers. */
41
+ .fdy-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:var(--space-5);}
42
+ .fdy-stat{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;}
43
+ .fdy-stat__label{font-size:var(--text-xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--color-text-subtle);}
44
+ .fdy-stat__value{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tighter);line-height:1;color:var(--color-text);font-variant-numeric:tabular-nums;}
45
+ .fdy-stat__value small{font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
46
+ .fdy-stat__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);}
47
+ .fdy-stats--boxed{background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);gap:0;}
48
+ .fdy-stats--boxed .fdy-stat{padding:var(--space-5);border-right:var(--bw) solid var(--color-border-muted);}
49
+ .fdy-stats--boxed .fdy-stat:last-child{border-right:0;}