@cahyo-dimas/freeday 3.8.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 +104 -0
- package/COMPONENTS.md +112 -29
- package/README.id.md +4 -4
- package/README.md +4 -3
- package/adapters/blazor/FdyDatepicker.razor.cs +15 -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/core/month-grid.d.ts +116 -0
- package/adapters/core/month-grid.js +234 -0
- package/adapters/react/components/FdyDatepicker.tsx +78 -160
- 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/index.d.ts +5 -1
- package/adapters/react/index.js +1 -0
- package/adapters/react/usePopover.ts +54 -9
- package/adapters/vue/components/FdyDatepicker.vue +72 -159
- 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/index.d.ts +16 -0
- package/adapters/vue/index.js +1 -0
- package/adapters/vue/usePopover.ts +53 -10
- package/dist/freeday-table.js +64 -5
- package/dist/freeday.bundle.css +19 -2
- package/dist/freeday.css +19 -2
- package/dist/freeday.js +64 -5
- 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/dist/freeday-table.js
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Column filters: mark a header `<th data-fdy-filter="text|enum|number">` and a funnel
|
|
16
16
|
* button is injected; text filters by contains, enum by a value checklist, number by a
|
|
17
|
-
* min/max range. All active column filters AND the global search must pass (logical AND).
|
|
17
|
+
* min/max range. All active column filters AND the global search must pass (logical AND). The
|
|
18
|
+
* panel is anchored to its funnel by freeday-popover.js, as in every other enhancer with a
|
|
19
|
+
* dropdown, so load that file too (dist/freeday.js already has it); without it the panel still
|
|
20
|
+
* opens, at whatever position its stylesheet leaves it.
|
|
18
21
|
*
|
|
19
22
|
* Bulk actions: an optional `[data-fdy-table-bulk]` bar (hidden by default) with a
|
|
20
23
|
* `[data-fdy-table-bulk-count]` label and a `[data-fdy-table-bulk-clear]` button shows
|
|
@@ -244,6 +247,8 @@
|
|
|
244
247
|
function closePopover(returnFocus) {
|
|
245
248
|
if (!openPopover) return;
|
|
246
249
|
var btn = openPopover._trigger;
|
|
250
|
+
// Out of the top layer first, which also drops the reposition listeners attach() added.
|
|
251
|
+
if (openPopover._pop) openPopover._pop.hide();
|
|
247
252
|
openPopover.remove();
|
|
248
253
|
openPopover = null;
|
|
249
254
|
window.removeEventListener('scroll', closeOnScroll, true);
|
|
@@ -365,11 +370,65 @@
|
|
|
365
370
|
foot.appendChild(reset); foot.appendChild(done);
|
|
366
371
|
pop.appendChild(foot);
|
|
367
372
|
|
|
368
|
-
|
|
373
|
+
/* Positioned by freeday-popover.js, like every other enhancer with a dropdown. This used to
|
|
374
|
+
* be a fourth copy of that arithmetic, and the copies had drifted: it kept an 8px margin
|
|
375
|
+
* where the kit's gap is 4, measured against window.innerWidth (which counts a classic
|
|
376
|
+
* scrollbar the layout no longer has, so the clamp parked the panel under it), aligned the
|
|
377
|
+
* left edges only, and never reached the top layer. The typed FdyTableFilter asks for
|
|
378
|
+
* `align:'end'`, so a funnel in the rightmost column opened differently depending on which
|
|
379
|
+
* path rendered the table, and the two paths are supposed to be interchangeable.
|
|
380
|
+
* align 'end': the funnel is a 24px button in a column header and the panel is 14rem, so
|
|
381
|
+
* lining up their LEFT edges aims the panel at nothing anybody can see, and in the rightmost
|
|
382
|
+
* column it ran past the page. place() still clamps both sides, so the first column is safe.
|
|
383
|
+
* matchWidth false: `.fdy-filter` asks the stylesheet for min-width:14rem, and a floor
|
|
384
|
+
* taken from a 24px funnel is an inline style that outranks it.
|
|
385
|
+
* In the top layer the panel is a child of the <th>, where FdyTableFilter renders it, rather
|
|
386
|
+
* than of <body>. The top layer is what escapes an ancestor's overflow clip now, so the
|
|
387
|
+
* <body> trick has nothing left to do, and it cost something real: everything outside an open
|
|
388
|
+
* modal <dialog> is inert, so a filter panel on <body> painted a form that refused focus and
|
|
389
|
+
* every click for any table inside a modal. */
|
|
390
|
+
/* WHERE it comes to rest depends on whether the top layer is available, and the degraded path
|
|
391
|
+
* is not an edge case: `supported` is read once when freeday-popover.js loads, the Popover API
|
|
392
|
+
* landed in Chrome 114 / Safari 17 / Firefox 125, and the README promises Chrome 105 /
|
|
393
|
+
* Safari 15.4 / Firefox 121. Chrome 105..113, Safari 15.4 and 16, and Firefox 121..124 are all
|
|
394
|
+
* inside what the kit supports and all of them come through here.
|
|
395
|
+
*
|
|
396
|
+
* In the top layer the parent decides nothing about paint order, so the panel stays in the
|
|
397
|
+
* <th> and the modal fix above holds. Without it the <th> is a CAGE:
|
|
398
|
+
* `.fdy-table--sticky-head thead th` is `position:sticky; z-index:2`, and a positioned box
|
|
399
|
+
* with a z-index is a stacking context, so the panel's own 130 is sorted inside that 2 and
|
|
400
|
+
* every page overlay from 3 up paints over it. Measured on a phone: 224x49px of the panel,
|
|
401
|
+
* its Reset and Close buttons, under `.fdy-bottomnav`'s z-index 50, and the hit test at that
|
|
402
|
+
* point returned the bar. Raising the 130 cannot reach it, a number raised inside a stacking
|
|
403
|
+
* context never leaves that context; and the context has to exist, the 2 is what keeps a
|
|
404
|
+
* sticky header above the z-index:1 frozen body cells scrolling under it. So the panel is what
|
|
405
|
+
* moves.
|
|
406
|
+
*
|
|
407
|
+
* It moves to `root`, the element that carries `data-fdy-table`, and not to <body>. `root` is
|
|
408
|
+
* documented as `.fdy-datatable`: no position, no z-index, no transform, so no stacking
|
|
409
|
+
* context and no containing block for a fixed box, which is all the panel needs. And it is
|
|
410
|
+
* INSIDE whatever dialog the table is in, so the inert fault that sent the panel here in the
|
|
411
|
+
* first place cannot come back, with no `closest('dialog')` special case to get wrong.
|
|
412
|
+
*
|
|
413
|
+
* Re-parenting alone would trade one fault for another, so it comes with place(). A fixed box
|
|
414
|
+
* with auto offsets takes its STATIC position, and in a sticky cell inside a scrolled
|
|
415
|
+
* scrollport that spot is nowhere in particular: measured 99px below the funnel it belongs to,
|
|
416
|
+
* with the panel's foot 12px off the bottom of a 740px screen, and at the end of the datatable
|
|
417
|
+
* once it is parented to `root`. place() is the kit's one copy of that arithmetic and it needs
|
|
418
|
+
* nothing from the Popover API, it only writes inline coordinates. The panel closes on any
|
|
419
|
+
* scroll (closeOnScroll, below), so a fixed panel that does not follow the page is not a
|
|
420
|
+
* second fault.
|
|
421
|
+
*
|
|
422
|
+
* Where the helper is absent ENTIRELY nothing moves: there would be nothing to place the panel
|
|
423
|
+
* with, and the static position inside the <th> at least lands near its own funnel. */
|
|
424
|
+
var popover = window.FreedayPopover || null;
|
|
425
|
+
var degraded = popover !== null && popover.supported !== true;
|
|
426
|
+
var opts = { matchWidth: false, align: 'end' };
|
|
427
|
+
(degraded ? root : btn.parentNode).appendChild(pop);
|
|
369
428
|
pop._trigger = btn;
|
|
370
|
-
|
|
371
|
-
pop.
|
|
372
|
-
|
|
429
|
+
pop._pop = popover === null ? null : popover.attach(pop, btn, opts);
|
|
430
|
+
if (pop._pop) pop._pop.show();
|
|
431
|
+
if (degraded) popover.place(pop, btn, opts);
|
|
373
432
|
openPopover = pop;
|
|
374
433
|
window.addEventListener('scroll', closeOnScroll, true);
|
|
375
434
|
window.addEventListener('resize', closeOnScroll);
|
package/dist/freeday.bundle.css
CHANGED
|
@@ -2998,8 +2998,25 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
2998
2998
|
.fdy-table__filterbtn.is-active{color:var(--color-primary-strong);}
|
|
2999
2999
|
.fdy-table__filterbtn svg{width:.85rem;height:.85rem;}
|
|
3000
3000
|
|
|
3001
|
-
/* Filter popover
|
|
3002
|
-
|
|
3001
|
+
/* Filter popover. `position:fixed` because the panel is positioned by freeday-popover.js (and by
|
|
3002
|
+
usePopover on the typed path) with inline viewport coordinates, in the top layer, which is what
|
|
3003
|
+
escapes a card's or a scroll container's clip; the panel itself is a child of its own <th> on
|
|
3004
|
+
both paths, so an open modal <dialog> does not make it inert. It is the one kit panel that is
|
|
3005
|
+
fixed rather than absolute, and that is load-bearing: #079 cannot reach a panel which never
|
|
3006
|
+
resolves a `top:calc(100% + …)` against the viewport, which browser/filter-parity.mjs measures.
|
|
3007
|
+
`min-width` is the panel's own, so neither path may floor it to the width of a 24px funnel.
|
|
3008
|
+
|
|
3009
|
+
The typography block is a RESET of what the panel inherits from that <th>, and it has to name
|
|
3010
|
+
every inherited property the header rule sets, not the ones that looked worst. A header is
|
|
3011
|
+
uppercase, tracked, semibold, muted, 12px and `white-space:nowrap`; a numeric column adds mono
|
|
3012
|
+
and, on the typed path, an inline `text-align:right` that no ancestor rule could outrank anyway.
|
|
3013
|
+
None of that belongs on a panel of labels and inputs, and the four it reset from the start were
|
|
3014
|
+
the four that SHOWED: an 18rem cap hides a nowrap panel's damage, because the box stays 18rem
|
|
3015
|
+
wide while the text runs out of the list, which then scrolls sideways to reach a value nobody
|
|
3016
|
+
can read. `font-size` and `font-family` reach only the text that declares neither, the range
|
|
3017
|
+
separator and the title, which is exactly why they went unnoticed. Measured as geometry in
|
|
3018
|
+
browser/filter-wrap.mjs rather than asserted as declarations. */
|
|
3019
|
+
.fdy-filter{position:fixed;z-index:130;min-width:14rem;max-width:18rem;padding:var(--space-3);background:var(--color-surface);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);text-align:left;white-space:normal;font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-regular);text-transform:none;letter-spacing:normal;color:var(--color-text);}
|
|
3003
3020
|
.fdy-filter__title{font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:var(--tracking-wide);margin-bottom:var(--space-2);}
|
|
3004
3021
|
.fdy-filter__list{display:flex;flex-direction:column;gap:var(--space-1);max-height:12rem;overflow:auto;}
|
|
3005
3022
|
.fdy-filter__check{display:flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;}
|
package/dist/freeday.css
CHANGED
|
@@ -1821,8 +1821,25 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1821
1821
|
.fdy-table__filterbtn.is-active{color:var(--color-primary-strong);}
|
|
1822
1822
|
.fdy-table__filterbtn svg{width:.85rem;height:.85rem;}
|
|
1823
1823
|
|
|
1824
|
-
/* Filter popover
|
|
1825
|
-
|
|
1824
|
+
/* Filter popover. `position:fixed` because the panel is positioned by freeday-popover.js (and by
|
|
1825
|
+
usePopover on the typed path) with inline viewport coordinates, in the top layer, which is what
|
|
1826
|
+
escapes a card's or a scroll container's clip; the panel itself is a child of its own <th> on
|
|
1827
|
+
both paths, so an open modal <dialog> does not make it inert. It is the one kit panel that is
|
|
1828
|
+
fixed rather than absolute, and that is load-bearing: #079 cannot reach a panel which never
|
|
1829
|
+
resolves a `top:calc(100% + …)` against the viewport, which browser/filter-parity.mjs measures.
|
|
1830
|
+
`min-width` is the panel's own, so neither path may floor it to the width of a 24px funnel.
|
|
1831
|
+
|
|
1832
|
+
The typography block is a RESET of what the panel inherits from that <th>, and it has to name
|
|
1833
|
+
every inherited property the header rule sets, not the ones that looked worst. A header is
|
|
1834
|
+
uppercase, tracked, semibold, muted, 12px and `white-space:nowrap`; a numeric column adds mono
|
|
1835
|
+
and, on the typed path, an inline `text-align:right` that no ancestor rule could outrank anyway.
|
|
1836
|
+
None of that belongs on a panel of labels and inputs, and the four it reset from the start were
|
|
1837
|
+
the four that SHOWED: an 18rem cap hides a nowrap panel's damage, because the box stays 18rem
|
|
1838
|
+
wide while the text runs out of the list, which then scrolls sideways to reach a value nobody
|
|
1839
|
+
can read. `font-size` and `font-family` reach only the text that declares neither, the range
|
|
1840
|
+
separator and the title, which is exactly why they went unnoticed. Measured as geometry in
|
|
1841
|
+
browser/filter-wrap.mjs rather than asserted as declarations. */
|
|
1842
|
+
.fdy-filter{position:fixed;z-index:130;min-width:14rem;max-width:18rem;padding:var(--space-3);background:var(--color-surface);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);text-align:left;white-space:normal;font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-regular);text-transform:none;letter-spacing:normal;color:var(--color-text);}
|
|
1826
1843
|
.fdy-filter__title{font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:var(--tracking-wide);margin-bottom:var(--space-2);}
|
|
1827
1844
|
.fdy-filter__list{display:flex;flex-direction:column;gap:var(--space-1);max-height:12rem;overflow:auto;}
|
|
1828
1845
|
.fdy-filter__check{display:flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;}
|
package/dist/freeday.js
CHANGED
|
@@ -4208,7 +4208,10 @@
|
|
|
4208
4208
|
*
|
|
4209
4209
|
* Column filters: mark a header `<th data-fdy-filter="text|enum|number">` and a funnel
|
|
4210
4210
|
* button is injected; text filters by contains, enum by a value checklist, number by a
|
|
4211
|
-
* min/max range. All active column filters AND the global search must pass (logical AND).
|
|
4211
|
+
* min/max range. All active column filters AND the global search must pass (logical AND). The
|
|
4212
|
+
* panel is anchored to its funnel by freeday-popover.js, as in every other enhancer with a
|
|
4213
|
+
* dropdown, so load that file too (dist/freeday.js already has it); without it the panel still
|
|
4214
|
+
* opens, at whatever position its stylesheet leaves it.
|
|
4212
4215
|
*
|
|
4213
4216
|
* Bulk actions: an optional `[data-fdy-table-bulk]` bar (hidden by default) with a
|
|
4214
4217
|
* `[data-fdy-table-bulk-count]` label and a `[data-fdy-table-bulk-clear]` button shows
|
|
@@ -4438,6 +4441,8 @@
|
|
|
4438
4441
|
function closePopover(returnFocus) {
|
|
4439
4442
|
if (!openPopover) return;
|
|
4440
4443
|
var btn = openPopover._trigger;
|
|
4444
|
+
// Out of the top layer first, which also drops the reposition listeners attach() added.
|
|
4445
|
+
if (openPopover._pop) openPopover._pop.hide();
|
|
4441
4446
|
openPopover.remove();
|
|
4442
4447
|
openPopover = null;
|
|
4443
4448
|
window.removeEventListener('scroll', closeOnScroll, true);
|
|
@@ -4559,11 +4564,65 @@
|
|
|
4559
4564
|
foot.appendChild(reset); foot.appendChild(done);
|
|
4560
4565
|
pop.appendChild(foot);
|
|
4561
4566
|
|
|
4562
|
-
|
|
4567
|
+
/* Positioned by freeday-popover.js, like every other enhancer with a dropdown. This used to
|
|
4568
|
+
* be a fourth copy of that arithmetic, and the copies had drifted: it kept an 8px margin
|
|
4569
|
+
* where the kit's gap is 4, measured against window.innerWidth (which counts a classic
|
|
4570
|
+
* scrollbar the layout no longer has, so the clamp parked the panel under it), aligned the
|
|
4571
|
+
* left edges only, and never reached the top layer. The typed FdyTableFilter asks for
|
|
4572
|
+
* `align:'end'`, so a funnel in the rightmost column opened differently depending on which
|
|
4573
|
+
* path rendered the table, and the two paths are supposed to be interchangeable.
|
|
4574
|
+
* align 'end': the funnel is a 24px button in a column header and the panel is 14rem, so
|
|
4575
|
+
* lining up their LEFT edges aims the panel at nothing anybody can see, and in the rightmost
|
|
4576
|
+
* column it ran past the page. place() still clamps both sides, so the first column is safe.
|
|
4577
|
+
* matchWidth false: `.fdy-filter` asks the stylesheet for min-width:14rem, and a floor
|
|
4578
|
+
* taken from a 24px funnel is an inline style that outranks it.
|
|
4579
|
+
* In the top layer the panel is a child of the <th>, where FdyTableFilter renders it, rather
|
|
4580
|
+
* than of <body>. The top layer is what escapes an ancestor's overflow clip now, so the
|
|
4581
|
+
* <body> trick has nothing left to do, and it cost something real: everything outside an open
|
|
4582
|
+
* modal <dialog> is inert, so a filter panel on <body> painted a form that refused focus and
|
|
4583
|
+
* every click for any table inside a modal. */
|
|
4584
|
+
/* WHERE it comes to rest depends on whether the top layer is available, and the degraded path
|
|
4585
|
+
* is not an edge case: `supported` is read once when freeday-popover.js loads, the Popover API
|
|
4586
|
+
* landed in Chrome 114 / Safari 17 / Firefox 125, and the README promises Chrome 105 /
|
|
4587
|
+
* Safari 15.4 / Firefox 121. Chrome 105..113, Safari 15.4 and 16, and Firefox 121..124 are all
|
|
4588
|
+
* inside what the kit supports and all of them come through here.
|
|
4589
|
+
*
|
|
4590
|
+
* In the top layer the parent decides nothing about paint order, so the panel stays in the
|
|
4591
|
+
* <th> and the modal fix above holds. Without it the <th> is a CAGE:
|
|
4592
|
+
* `.fdy-table--sticky-head thead th` is `position:sticky; z-index:2`, and a positioned box
|
|
4593
|
+
* with a z-index is a stacking context, so the panel's own 130 is sorted inside that 2 and
|
|
4594
|
+
* every page overlay from 3 up paints over it. Measured on a phone: 224x49px of the panel,
|
|
4595
|
+
* its Reset and Close buttons, under `.fdy-bottomnav`'s z-index 50, and the hit test at that
|
|
4596
|
+
* point returned the bar. Raising the 130 cannot reach it, a number raised inside a stacking
|
|
4597
|
+
* context never leaves that context; and the context has to exist, the 2 is what keeps a
|
|
4598
|
+
* sticky header above the z-index:1 frozen body cells scrolling under it. So the panel is what
|
|
4599
|
+
* moves.
|
|
4600
|
+
*
|
|
4601
|
+
* It moves to `root`, the element that carries `data-fdy-table`, and not to <body>. `root` is
|
|
4602
|
+
* documented as `.fdy-datatable`: no position, no z-index, no transform, so no stacking
|
|
4603
|
+
* context and no containing block for a fixed box, which is all the panel needs. And it is
|
|
4604
|
+
* INSIDE whatever dialog the table is in, so the inert fault that sent the panel here in the
|
|
4605
|
+
* first place cannot come back, with no `closest('dialog')` special case to get wrong.
|
|
4606
|
+
*
|
|
4607
|
+
* Re-parenting alone would trade one fault for another, so it comes with place(). A fixed box
|
|
4608
|
+
* with auto offsets takes its STATIC position, and in a sticky cell inside a scrolled
|
|
4609
|
+
* scrollport that spot is nowhere in particular: measured 99px below the funnel it belongs to,
|
|
4610
|
+
* with the panel's foot 12px off the bottom of a 740px screen, and at the end of the datatable
|
|
4611
|
+
* once it is parented to `root`. place() is the kit's one copy of that arithmetic and it needs
|
|
4612
|
+
* nothing from the Popover API, it only writes inline coordinates. The panel closes on any
|
|
4613
|
+
* scroll (closeOnScroll, below), so a fixed panel that does not follow the page is not a
|
|
4614
|
+
* second fault.
|
|
4615
|
+
*
|
|
4616
|
+
* Where the helper is absent ENTIRELY nothing moves: there would be nothing to place the panel
|
|
4617
|
+
* with, and the static position inside the <th> at least lands near its own funnel. */
|
|
4618
|
+
var popover = window.FreedayPopover || null;
|
|
4619
|
+
var degraded = popover !== null && popover.supported !== true;
|
|
4620
|
+
var opts = { matchWidth: false, align: 'end' };
|
|
4621
|
+
(degraded ? root : btn.parentNode).appendChild(pop);
|
|
4563
4622
|
pop._trigger = btn;
|
|
4564
|
-
|
|
4565
|
-
pop.
|
|
4566
|
-
|
|
4623
|
+
pop._pop = popover === null ? null : popover.attach(pop, btn, opts);
|
|
4624
|
+
if (pop._pop) pop._pop.show();
|
|
4625
|
+
if (degraded) popover.place(pop, btn, opts);
|
|
4567
4626
|
openPopover = pop;
|
|
4568
4627
|
window.addEventListener('scroll', closeOnScroll, true);
|
|
4569
4628
|
window.addEventListener('resize', closeOnScroll);
|
package/docs/agent-onboarding.md
CHANGED
|
@@ -33,9 +33,9 @@ and the code looks correct and fails later.**
|
|
|
33
33
|
| Blazor (net8.0) | `@using Freeday.Blazor` (RCL) | `@bind-Value` |
|
|
34
34
|
| Static HTML, Svelte, server-rendered templates… | no wrapper: raw markup + the enhancer script | `fdy-*` DOM events |
|
|
35
35
|
|
|
36
|
-
The
|
|
37
|
-
FdyChart · FdyTable · FdyModal · FdyDrawer · FdyAppShell**. In
|
|
38
|
-
markup + enhancer for these
|
|
36
|
+
The twelve: **FdyCombo · FdyDatepicker · FdyMonthpicker · FdyDateRange · FdyAutocomplete ·
|
|
37
|
+
FdyCascade · FdyCfl · FdyChart · FdyTable · FdyModal · FdyDrawer · FdyAppShell**. In
|
|
38
|
+
Vue/React/Blazor, **never hand-write the raw markup + enhancer for these twelve.** The raw path *appears* to work: the enhancer auto-initialises
|
|
39
39
|
once on `DOMContentLoaded` and the first render is correct. Then it fails quietly: DOM your framework
|
|
40
40
|
renders later is never hydrated, and the widget's state lives in the DOM instead of in your
|
|
41
41
|
framework's state.
|
package/docs/getting-started.md
CHANGED
|
@@ -21,9 +21,9 @@ Freeday = **CSS** (semantic tokens + `fdy-*` classes) + **zero-dependency JS enh
|
|
|
21
21
|
the enhancer owns the widget's DOM. You **listen for `fdy-*` events** (all bubbling
|
|
22
22
|
`CustomEvent`s, data in `event.detail`) → store them in your framework state. Event/API contract
|
|
23
23
|
table: [`integrations.md` §Event & API contract](integrations.md).
|
|
24
|
-
**On Vue, React or Blazor this is not the path to take for
|
|
25
|
-
`FdyDatepicker`, `FdyDateRange`, `FdyAutocomplete`, `FdyCascade`, `FdyCfl`,
|
|
26
|
-
`FdyTable`, `FdyModal`, `FdyDrawer`, `FdyAppShell` ship typed wrappers that own the state properly (Vue and
|
|
24
|
+
**On Vue, React or Blazor this is not the path to take for twelve of the components.** `FdyCombo`,
|
|
25
|
+
`FdyDatepicker`, `FdyMonthpicker`, `FdyDateRange`, `FdyAutocomplete`, `FdyCascade`, `FdyCfl`,
|
|
26
|
+
`FdyChart`, `FdyTable`, `FdyModal`, `FdyDrawer`, `FdyAppShell` ship typed wrappers that own the state properly (Vue and
|
|
27
27
|
React re-implement the interaction natively; Blazor wraps the enhancer over interop). Use them;
|
|
28
28
|
the raw path is for the components without a wrapper, and for stacks without an adapter.
|
|
29
29
|
3. **Hydrate dynamic DOM.** Enhancers auto-init once on `DOMContentLoaded`. DOM an SPA renders
|
|
@@ -191,7 +191,7 @@ live docs also have a copy button per component.
|
|
|
191
191
|
```bash
|
|
192
192
|
npm i @cahyo-dimas/freeday
|
|
193
193
|
```
|
|
194
|
-
Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.
|
|
194
|
+
Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.9.0"` (public npm package). `dist/` is
|
|
195
195
|
committed and published → no build step; `npm ci` runs without auth.
|
|
196
196
|
|
|
197
197
|
### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
|
|
@@ -444,9 +444,9 @@ kit's CSS/enhancers), then bind:
|
|
|
444
444
|
<FdyChart Type="donut" Values="_byCity" Labels="_cityLabels" AriaLabel="Revenue by city" />
|
|
445
445
|
<FdyDrawer @bind-Open="_drawerOpen" Title="Detail" Side="right">…</FdyDrawer>
|
|
446
446
|
```
|
|
447
|
-
|
|
447
|
+
Twelve components at parity with the Vue/React adapters: **`FdyModal`** · **`FdyDrawer`** (`@bind-Open`,
|
|
448
448
|
`Title`, `Size`/`Side`, `Dismissible`) · **`FdyCombo<TValue>`** · **`FdyDatepicker`** ·
|
|
449
|
-
**`FdyAutocomplete`** · **`FdyCascade`** · **`FdyDateRange`** (`@bind-From`/`@bind-To`) ·
|
|
449
|
+
**`FdyMonthpicker`** (`@bind-Value` as `yyyy-MM`) · **`FdyAutocomplete`** · **`FdyCascade`** · **`FdyDateRange`** (`@bind-From`/`@bind-To`) ·
|
|
450
450
|
**`FdyCfl<TRow>`** (async `LoadPage`) · **`FdyChart`** · **`FdyTable<TRow>`** (client sort/filter/page,
|
|
451
451
|
or controlled `Sort`/`Filters`/`Page` for a server-paged table; `RowActivatable`, `RowDetail`) ·
|
|
452
452
|
**`FdyAppShell`** (`@bind-NavOpen`). Each
|
package/docs/integrations.md
CHANGED
|
@@ -153,8 +153,9 @@ function Panel(): JSX.Element {
|
|
|
153
153
|
}
|
|
154
154
|
```
|
|
155
155
|
> **Controlled alternative (parity with Vue's `v-model`):** `@cahyo-dimas/freeday/react` also exports
|
|
156
|
-
> the typed components `FdyCombo` / `FdyDatepicker` / `
|
|
157
|
-
> `FdyCascade` / `FdyCfl` / `FdyChart` / `FdyTable` / `FdyModal` / `FdyDrawer` /
|
|
156
|
+
> the typed components `FdyCombo` / `FdyDatepicker` / `FdyMonthpicker` / `FdyDateRange` /
|
|
157
|
+
> `FdyAutocomplete` / `FdyCascade` / `FdyCfl` / `FdyChart` / `FdyTable` / `FdyModal` / `FdyDrawer` /
|
|
158
|
+
> `FdyAppShell`, with plain
|
|
158
159
|
> `value`/`onChange` (props in, events out), no `data-fdy-*` + manual event listener:
|
|
159
160
|
> ```tsx
|
|
160
161
|
> import { FdyCombo } from '@cahyo-dimas/freeday/react';
|
|
@@ -199,8 +200,8 @@ document.addEventListener('fdy-form-invalid', (e) =>
|
|
|
199
200
|
|
|
200
201
|
> **Native components (parity with Vue's `v-model` / React's `value`/`onChange`):** the
|
|
201
202
|
> **`Freeday.Blazor`** RCL (net8.0, `adapters/blazor/`) ships the typed components `FdyCombo<TValue>` /
|
|
202
|
-
> `FdyDatepicker` / `
|
|
203
|
-
> `FdyTable<TRow>` / `FdyModal` / `FdyDrawer` / `FdyAppShell` with `@bind`, no `@ref` / manual `initAll` / `[JSInvokable]`:
|
|
203
|
+
> `FdyDatepicker` / `FdyMonthpicker` / `FdyDateRange` / `FdyAutocomplete` / `FdyCascade` /
|
|
204
|
+
> `FdyCfl<TRow>` / `FdyChart` / `FdyTable<TRow>` / `FdyModal` / `FdyDrawer` / `FdyAppShell` with `@bind`, no `@ref` / manual `initAll` / `[JSInvokable]`:
|
|
204
205
|
> ```razor
|
|
205
206
|
> @using Freeday.Blazor
|
|
206
207
|
> <FdyCombo TValue="string" @bind-Value="_status" Options="_statusOptions" AriaLabelledby="lbl-status" />
|
|
@@ -327,9 +328,26 @@ Use the `app-` prefix for its classes, and reuse the kit's plumbing rather than
|
|
|
327
328
|
```ts
|
|
328
329
|
import { usePopover } from '@cahyo-dimas/freeday/vue'; // or '/react'
|
|
329
330
|
usePopover(panelRef, triggerRef, open); // panel needs popover="manual"
|
|
331
|
+
usePopover(panelRef, triggerRef, open, 'end'); // right edges together, for a panel
|
|
332
|
+
// wider than a trigger in a gutter
|
|
333
|
+
usePopover(panelRef, triggerRef, open, 'end', false); // and let the panel keep its own width
|
|
330
334
|
```
|
|
331
335
|
|
|
332
336
|
This is what every kit dropdown uses to escape an ancestor's overflow clip, because `.fdy-card` is
|
|
333
337
|
`overflow:hidden`, so a panel positioned inside one is otherwise cut at the card's edge. It is
|
|
334
338
|
exported so a control you build behaves like one the kit ships, and improves when the kit's
|
|
335
339
|
positioning does.
|
|
340
|
+
|
|
341
|
+
The fourth argument is the alignment, `'start'` (the default, left edges together) or `'end'`
|
|
342
|
+
(right edges together). Reach for `'end'` when the panel is much wider than its trigger and the
|
|
343
|
+
trigger sits near the right of something, an icon in a page gutter or a funnel in a table
|
|
344
|
+
header. The panel is kept inside the viewport on both sides either way, so `'end'` beside the
|
|
345
|
+
left edge slides right rather than off the screen.
|
|
346
|
+
|
|
347
|
+
The fifth is `matchWidth`, and it defaults to `true`: the panel is floored to its trigger's width,
|
|
348
|
+
which is what makes a listbox look like part of the control it drops out of. Pass `false` when your
|
|
349
|
+
panel has a width of its own in CSS, because the floor is written as an inline style and an inline
|
|
350
|
+
style outranks your rule. The kit's own funnel panel is the case that needs it, a 14rem form hanging
|
|
351
|
+
off a 24px icon; `FdyTableFilter` passes `false` and the nine other typed dropdowns pass nothing.
|
|
352
|
+
Both arguments are plain values rather than an options object, so the React port can list them among
|
|
353
|
+
an effect's dependencies and you never have to memoise anything.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cahyo-dimas/freeday",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "Freeday: token-driven, framework-agnostic UI KIT (design source-of-truth).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"scripts": {
|
|
97
97
|
"build": "node tokens/build.mjs",
|
|
98
98
|
"test": "node --test",
|
|
99
|
-
"test:browser": "node --test --test-concurrency=3 browser/color-mix.mjs browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/field-width.mjs browser/cfl-multi.mjs browser/cfl-dialog-only.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/bottom-nav.mjs browser/picker-states.mjs browser/tabs-strip.mjs browser/tabs-vertical.mjs browser/menu-align.mjs browser/cfl-narrow.mjs browser/form-reveal.mjs browser/confirm-search-copy.mjs browser/daterange-settled.mjs browser/display-parts.mjs browser/table-empty.mjs browser/table-footer-text.mjs browser/month-mode.mjs browser/page-columns.mjs browser/table-hover.mjs browser/harness-contract.mjs",
|
|
99
|
+
"test:browser": "node --test --test-concurrency=3 browser/color-mix.mjs browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/field-width.mjs browser/cfl-multi.mjs browser/cfl-dialog-only.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/bottom-nav.mjs browser/picker-states.mjs browser/tabs-strip.mjs browser/tabs-vertical.mjs browser/menu-align.mjs browser/cfl-narrow.mjs browser/form-reveal.mjs browser/confirm-search-copy.mjs browser/daterange-settled.mjs browser/display-parts.mjs browser/table-empty.mjs browser/table-footer-text.mjs browser/month-mode.mjs browser/monthpicker.mjs browser/page-columns.mjs browser/table-hover.mjs browser/typed-popover.mjs browser/filter-parity.mjs browser/filter-wrap.mjs browser/filter-stack.mjs browser/harness-contract.mjs",
|
|
100
100
|
"prepack": "node tokens/build.mjs",
|
|
101
101
|
"version": "node tokens/build.mjs && git add dist",
|
|
102
102
|
"typecheck:react": "tsc -p adapters/react/tsconfig.json --noEmit",
|
package/src/components/table.css
CHANGED
|
@@ -140,8 +140,25 @@
|
|
|
140
140
|
.fdy-table__filterbtn.is-active{color:var(--color-primary-strong);}
|
|
141
141
|
.fdy-table__filterbtn svg{width:.85rem;height:.85rem;}
|
|
142
142
|
|
|
143
|
-
/* Filter popover
|
|
144
|
-
|
|
143
|
+
/* Filter popover. `position:fixed` because the panel is positioned by freeday-popover.js (and by
|
|
144
|
+
usePopover on the typed path) with inline viewport coordinates, in the top layer, which is what
|
|
145
|
+
escapes a card's or a scroll container's clip; the panel itself is a child of its own <th> on
|
|
146
|
+
both paths, so an open modal <dialog> does not make it inert. It is the one kit panel that is
|
|
147
|
+
fixed rather than absolute, and that is load-bearing: #079 cannot reach a panel which never
|
|
148
|
+
resolves a `top:calc(100% + …)` against the viewport, which browser/filter-parity.mjs measures.
|
|
149
|
+
`min-width` is the panel's own, so neither path may floor it to the width of a 24px funnel.
|
|
150
|
+
|
|
151
|
+
The typography block is a RESET of what the panel inherits from that <th>, and it has to name
|
|
152
|
+
every inherited property the header rule sets, not the ones that looked worst. A header is
|
|
153
|
+
uppercase, tracked, semibold, muted, 12px and `white-space:nowrap`; a numeric column adds mono
|
|
154
|
+
and, on the typed path, an inline `text-align:right` that no ancestor rule could outrank anyway.
|
|
155
|
+
None of that belongs on a panel of labels and inputs, and the four it reset from the start were
|
|
156
|
+
the four that SHOWED: an 18rem cap hides a nowrap panel's damage, because the box stays 18rem
|
|
157
|
+
wide while the text runs out of the list, which then scrolls sideways to reach a value nobody
|
|
158
|
+
can read. `font-size` and `font-family` reach only the text that declares neither, the range
|
|
159
|
+
separator and the title, which is exactly why they went unnoticed. Measured as geometry in
|
|
160
|
+
browser/filter-wrap.mjs rather than asserted as declarations. */
|
|
161
|
+
.fdy-filter{position:fixed;z-index:130;min-width:14rem;max-width:18rem;padding:var(--space-3);background:var(--color-surface);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);text-align:left;white-space:normal;font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-regular);text-transform:none;letter-spacing:normal;color:var(--color-text);}
|
|
145
162
|
.fdy-filter__title{font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:var(--tracking-wide);margin-bottom:var(--space-2);}
|
|
146
163
|
.fdy-filter__list{display:flex;flex-direction:column;gap:var(--space-1);max-height:12rem;overflow:auto;}
|
|
147
164
|
.fdy-filter__check{display:flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;}
|