@cahyo-dimas/freeday 3.4.0 → 3.6.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 +127 -0
- package/COMPONENTS.md +278 -12
- package/README.id.md +9 -5
- package/README.md +9 -5
- package/adapters/blazor/FdyDateRange.razor.cs +10 -0
- package/adapters/blazor/FdyTable.razor +6 -2
- package/adapters/blazor/FdyTable.razor.cs +21 -0
- package/adapters/blazor/TableTypes.cs +4 -0
- package/adapters/react/components/FdyDateRange.tsx +8 -2
- package/adapters/react/components/FdyTable.tsx +24 -3
- package/adapters/react/index.d.ts +1 -1
- package/adapters/vue/components/FdyDateRange.vue +11 -5
- package/adapters/vue/components/FdyTable.vue +18 -2
- package/dist/freeday-confirm.js +101 -0
- package/dist/freeday-copy.js +58 -0
- package/dist/freeday-datepicker.js +17 -2
- package/dist/freeday-form.js +30 -1
- package/dist/freeday-search.js +77 -0
- package/dist/freeday.bundle.css +98 -8
- package/dist/freeday.css +98 -8
- package/dist/freeday.d.ts +17 -2
- package/dist/freeday.js +286 -3
- package/dist/freeday.media.css +27 -0
- package/docs/agent-onboarding.md +10 -0
- package/docs/getting-started.md +1 -1
- package/package.json +6 -2
- package/src/components/avatar.css +2 -0
- package/src/components/breakpoints.css +7 -1
- package/src/components/card.css +7 -0
- package/src/components/composition.css +36 -0
- package/src/components/description-list.css +11 -1
- package/src/components/filterbar.css +8 -1
- package/src/components/selection.css +6 -0
- package/src/components/states.css +7 -0
- package/src/components/table.css +14 -5
- package/tokens/breakpoints.d.ts +4 -0
- package/tokens/breakpoints.mjs +22 -8
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/* Freeday, list search enhancer (optional, zero-dependency). Note #061.
|
|
2
|
+
* Turns an .fdy-input-group into a search box that searches while the reader pauses, at once on
|
|
3
|
+
* Enter, and has a clear button. All three paths end in ONE bubbling event on the group,
|
|
4
|
+
* "fdy-search" {term}, so an app listens once instead of wiring keyup, a timer and a click.
|
|
5
|
+
*
|
|
6
|
+
* Markup (the group, not a new block; the clear button is yours, so is its aria-label):
|
|
7
|
+
* <div class="fdy-input-group" data-fdy-search [data-fdy-search-delay="400"]>
|
|
8
|
+
* <span class="fdy-input-group__addon fdy-input-group__addon--icon">…svg…</span>
|
|
9
|
+
* <input class="fdy-input" type="text" aria-label="Search orders">
|
|
10
|
+
* <button type="button" class="fdy-input-group__btn" data-fdy-search-clear aria-label="Clear search">×</button>
|
|
11
|
+
* </div>
|
|
12
|
+
*
|
|
13
|
+
* type="text", not "search": WebKit's own × is unthemed and fires no Enter, which is what made
|
|
14
|
+
* clearing a field leave the old rows on screen until somebody pressed Enter on an empty box.
|
|
15
|
+
* The clear button is hidden while the box is empty. Clearing writes through the native value
|
|
16
|
+
* setter and fires a native `input`, so v-model, a React onChange and Blazor @bind all follow.
|
|
17
|
+
* `term` is trimmed; "" means "show everything again". Exposes window.FreedaySearch.
|
|
18
|
+
*/
|
|
19
|
+
(function () {
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
var DEFAULT_DELAY = 400;
|
|
23
|
+
var nativeValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
|
|
24
|
+
|
|
25
|
+
function initSearch(root) {
|
|
26
|
+
if (root.dataset.fdySearchReady === '1') return;
|
|
27
|
+
var input = root.querySelector('input');
|
|
28
|
+
if (!input) return;
|
|
29
|
+
root.dataset.fdySearchReady = '1';
|
|
30
|
+
var clearBtn = root.querySelector('[data-fdy-search-clear]');
|
|
31
|
+
var delayAttr = parseInt(root.getAttribute('data-fdy-search-delay'), 10);
|
|
32
|
+
var delay = isNaN(delayAttr) ? DEFAULT_DELAY : delayAttr;
|
|
33
|
+
var timer = null;
|
|
34
|
+
|
|
35
|
+
function cancel() { if (timer !== null) { clearTimeout(timer); timer = null; } }
|
|
36
|
+
function emit() {
|
|
37
|
+
cancel();
|
|
38
|
+
root.dispatchEvent(new CustomEvent('fdy-search', { bubbles: true, detail: { term: input.value.trim() } }));
|
|
39
|
+
}
|
|
40
|
+
function syncClear() { if (clearBtn) clearBtn.hidden = input.value === ''; }
|
|
41
|
+
|
|
42
|
+
input.addEventListener('input', function () {
|
|
43
|
+
syncClear();
|
|
44
|
+
cancel();
|
|
45
|
+
timer = setTimeout(emit, delay);
|
|
46
|
+
});
|
|
47
|
+
input.addEventListener('keydown', function (e) {
|
|
48
|
+
if (e.key !== 'Enter') return;
|
|
49
|
+
e.preventDefault(); // inside a <form>, Enter would submit it as well
|
|
50
|
+
emit();
|
|
51
|
+
});
|
|
52
|
+
if (clearBtn) {
|
|
53
|
+
clearBtn.addEventListener('click', function () {
|
|
54
|
+
nativeValue.call(input, '');
|
|
55
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
56
|
+
emit(); // at once, and it cancels the pause the input event just started
|
|
57
|
+
input.focus();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
syncClear();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function initAll(context) {
|
|
64
|
+
var root = context || document;
|
|
65
|
+
/* root included: querySelectorAll never matches its own root, and a framework ref often sits ON the widget. */
|
|
66
|
+
if (root.matches && root.matches('[data-fdy-search]')) initSearch(root);
|
|
67
|
+
Array.prototype.forEach.call(root.querySelectorAll('[data-fdy-search]'), initSearch);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (document.readyState === 'loading') {
|
|
71
|
+
document.addEventListener('DOMContentLoaded', function () { initAll(); });
|
|
72
|
+
} else {
|
|
73
|
+
initAll();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
window.FreedaySearch = { init: initSearch, initAll: initAll };
|
|
77
|
+
})();
|
package/dist/freeday.bundle.css
CHANGED
|
@@ -1151,6 +1151,8 @@ a { color: var(--color-primary); }
|
|
|
1151
1151
|
.fdy-avatar--xs{width:1.5rem;height:1.5rem;font-size:var(--text-xs);}
|
|
1152
1152
|
.fdy-avatar--sm{width:2rem;height:2rem;font-size:var(--text-xs);}
|
|
1153
1153
|
.fdy-avatar--lg{width:3.5rem;height:3.5rem;font-size:var(--text-lg);}
|
|
1154
|
+
/* Square: a mark that is not a person (a company logo, a card's icon chip, #070). */
|
|
1155
|
+
.fdy-avatar--square{border-radius:var(--radius-md);}
|
|
1154
1156
|
.fdy-avatar-group{display:inline-flex;}
|
|
1155
1157
|
.fdy-avatar-group .fdy-avatar{border:2px solid var(--color-surface);margin-left:calc(var(--space-2)*-1);}
|
|
1156
1158
|
.fdy-avatar-group .fdy-avatar:first-child{margin-left:0;}
|
|
@@ -1290,8 +1292,14 @@ a { color: var(--color-primary); }
|
|
|
1290
1292
|
|
|
1291
1293
|
/* Freeday. Responsive display utilities (pairs with freeday-breakpoint.js).
|
|
1292
1294
|
* Breakpoint scale (min-width, px): sm 600 · md 960 · lg 1280 · xl 1920.
|
|
1295
|
+
* Two further widths exist and are NOT ramp steps: `nav` (app-shell.css, off-canvas drawer vs
|
|
1296
|
+
* static sidebar) and `filterbar` (filterbar.css, the width its fields stack at). Both are named
|
|
1297
|
+
* in tokens/breakpoints.mjs; a rule that has to fire with one of them aligns to it, not to the
|
|
1298
|
+
* nearest ramp step, which is a band of broken widths in both directions.
|
|
1293
1299
|
* NOTE: media-query breakpoints must be literal. CSS can't read custom properties
|
|
1294
|
-
* inside @media, so these px are structural, not stray magic numbers.
|
|
1300
|
+
* inside @media, so these px are structural, not stray magic numbers. An app that runs PostCSS
|
|
1301
|
+
* need not retype them: `@cahyo-dimas/freeday/media` declares every width above as a
|
|
1302
|
+
* @custom-media, so it can write `@media (--fdy-below-sm)` and never drift (#058 §2). */
|
|
1295
1303
|
.fdy-hidden{display:none !important;}
|
|
1296
1304
|
|
|
1297
1305
|
/* hide-below-X, hidden when the viewport is narrower than X (content shows at X and up) */
|
|
@@ -1481,6 +1489,13 @@ a { color: var(--color-primary); }
|
|
|
1481
1489
|
absolutely positioned dismiss button back into the flow. Do not merge these two rules. */
|
|
1482
1490
|
:where(:is(.fdy-card,.fdy-list__row):has(.fdy-btn--stretch) :is(a[href],button,input,select,textarea,[tabindex]):not(.fdy-btn--stretch)){position:relative;}
|
|
1483
1491
|
.fdy-card__body{padding:var(--space-5);}
|
|
1492
|
+
/* Header band (#070): an identity mark, the title, and the card's way out pinned right. Beside the
|
|
1493
|
+
title the action sits in one place on every card; under the content it moved each time the
|
|
1494
|
+
content grew. Outside __body's padding, so its rule spans the card edge to edge. The mark is an
|
|
1495
|
+
.fdy-avatar (--sm --square --tone-N), so its tints are the ones the contrast gate already covers. */
|
|
1496
|
+
.fdy-card__head{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
|
|
1497
|
+
.fdy-card__head .fdy-card__title{flex:1 1 auto;min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
1498
|
+
.fdy-card__action{flex:none;display:inline-flex;align-items:center;gap:var(--space-2);}
|
|
1484
1499
|
.fdy-card__title{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 0 var(--space-1);}
|
|
1485
1500
|
.fdy-card__desc{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;line-height:var(--leading-normal);}
|
|
1486
1501
|
.fdy-card__footer{padding:var(--space-3) var(--space-5);border-top:var(--bw) solid var(--color-border-muted);background:linear-gradient(180deg,var(--color-surface),var(--color-surface-2));display:flex;gap:var(--space-2);justify-content:flex-end;}
|
|
@@ -1863,6 +1878,42 @@ a { color: var(--color-primary); }
|
|
|
1863
1878
|
.fdy-text-success{color:var(--color-success-strong);}
|
|
1864
1879
|
.fdy-text-warning{color:var(--color-warning-strong);}
|
|
1865
1880
|
.fdy-text-danger{color:var(--color-danger-strong);}
|
|
1881
|
+
/* Document columns (#065): a form or record page with an optional identity rail on the left
|
|
1882
|
+
* (__lead) and a summary rail on the right (__aside). The widths follow which rails are PRESENT,
|
|
1883
|
+
* so the layout is decided once here and not again by every screen:
|
|
1884
|
+
* __main alone one column
|
|
1885
|
+
* __main + __aside 8 / 4 (from md)
|
|
1886
|
+
* __lead + __main 3 / 9 (from md)
|
|
1887
|
+
* all three 3 / 6 / 3 (from lg; between md and lg both rails share the left)
|
|
1888
|
+
* Stacked, the summary rail comes FIRST: last, it sits under every section, and its button with it.
|
|
1889
|
+
* From md each rail is sticky and scrolls on its own, so the total and the status stay answerable
|
|
1890
|
+
* while the main column scrolls with the page, and a rail taller than the screen never hangs its
|
|
1891
|
+
* end off it. Screen only: a printed page is not held to a window. */
|
|
1892
|
+
.fdy-page__columns{--_fdy-rail-top:var(--fdy-page-sticky-top,var(--space-4));display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-6);align-items:start;}
|
|
1893
|
+
/* Under the shell the sticky topbar is in the way; --fdy-page-sticky-top overrides either default. */
|
|
1894
|
+
.fdy-app .fdy-page__columns{--_fdy-rail-top:var(--fdy-page-sticky-top,calc(var(--space-16) + var(--space-4)));}
|
|
1895
|
+
.fdy-page__columns > *{min-width:0;}
|
|
1896
|
+
@media (max-width:959.98px){.fdy-page__aside{order:-1;}}
|
|
1897
|
+
@media (min-width:960px){
|
|
1898
|
+
.fdy-page__columns:has(> .fdy-page__aside){grid-template-columns:minmax(0,2fr) minmax(0,1fr);}
|
|
1899
|
+
.fdy-page__columns:has(> .fdy-page__lead){grid-template-columns:minmax(0,1fr) minmax(0,3fr);}
|
|
1900
|
+
/* Both rails, two columns: they stack on the left while __main spans both rows on the right. */
|
|
1901
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > .fdy-page__main{grid-row:span 2;}
|
|
1902
|
+
}
|
|
1903
|
+
@media (min-width:1280px){
|
|
1904
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside){grid-template-columns:minmax(0,1fr) minmax(0,2fr) minmax(0,1fr);}
|
|
1905
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > .fdy-page__main{grid-row:auto;}
|
|
1906
|
+
}
|
|
1907
|
+
/* The padding, taken back by the margin, is room for a focus ring at a card's edge: a scrolling box
|
|
1908
|
+
clips whatever draws outside it. */
|
|
1909
|
+
@media screen and (min-width:960px){
|
|
1910
|
+
.fdy-page__lead,.fdy-page__aside{position:sticky;top:var(--_fdy-rail-top);max-height:calc(100dvh - var(--_fdy-rail-top) - var(--space-4));overflow-y:auto;overscroll-behavior:contain;padding:var(--space-1);margin:calc(var(--space-1) * -1);}
|
|
1911
|
+
}
|
|
1912
|
+
/* Two sticky rails in ONE column would slide over each other: between md and lg, with both rails
|
|
1913
|
+
present, they scroll with the page instead. */
|
|
1914
|
+
@media screen and (min-width:960px) and (max-width:1279.98px){
|
|
1915
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > :is(.fdy-page__lead,.fdy-page__aside){position:static;max-height:none;overflow:visible;}
|
|
1916
|
+
}
|
|
1866
1917
|
|
|
1867
1918
|
/* Freeday. Date picker (input-styled trigger + calendar popover).
|
|
1868
1919
|
* Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
|
|
@@ -1958,9 +2009,19 @@ a { color: var(--color-primary); }
|
|
|
1958
2009
|
/* Freeday. Description list (key-value detail view) */
|
|
1959
2010
|
.fdy-dl{display:grid;grid-template-columns:auto 1fr;gap:var(--space-2) var(--space-5);margin:0;}
|
|
1960
2011
|
.fdy-dl dt{color:var(--color-text-muted);font-size:var(--text-sm);}
|
|
1961
|
-
|
|
2012
|
+
/* overflow-wrap: an email, an order code or a 13-digit phone number is one unbreakable token, and
|
|
2013
|
+
without it the value prints over whatever sits beside it (#066). */
|
|
2014
|
+
.fdy-dl dd{margin:0;min-width:0;color:var(--color-text);font-size:var(--text-sm);font-weight:var(--weight-medium);overflow-wrap:anywhere;}
|
|
1962
2015
|
.fdy-dl--rows{grid-template-columns:1fr;gap:var(--space-1);}
|
|
1963
2016
|
.fdy-dl--rows dt{margin-top:var(--space-2);}
|
|
2017
|
+
/* Facts grid (#066): each pair a <div> (allowed inside <dl>), caption above value, as many columns
|
|
2018
|
+
as the list's OWN width holds, never more than three. Sized by the element rather than the
|
|
2019
|
+
window, so a grid in a narrow side rail stays one column with no modifier: a window breakpoint
|
|
2020
|
+
gave a rail two columns of about 110px. min(100%, …) keeps a single column from overflowing a
|
|
2021
|
+
rail narrower than the column floor. */
|
|
2022
|
+
.fdy-dl--grid{grid-template-columns:repeat(auto-fill,minmax(min(100%,max(var(--fdy-dl-col-min,14rem),calc((100% - 2 * var(--space-6)) / 3))),1fr));gap:var(--space-5) var(--space-6);}
|
|
2023
|
+
.fdy-dl--grid > div{min-width:0;}
|
|
2024
|
+
.fdy-dl--grid dd{margin-top:var(--space-1);}
|
|
1964
2025
|
|
|
1965
2026
|
/* Freeday. Divider */
|
|
1966
2027
|
.fdy-divider{border:0;border-top:var(--bw) solid var(--color-border);margin:var(--space-4) 0;}
|
|
@@ -2077,7 +2138,14 @@ a { color: var(--color-primary); }
|
|
|
2077
2138
|
/* Opt-in: sit the actions inline right after the last field instead of at the far edge, so when the
|
|
2078
2139
|
* bar wraps they read as attached to the filters rather than shoved to the right of a lonely row. */
|
|
2079
2140
|
.fdy-filterbar--actions-inline .fdy-filterbar__actions{margin-left:0;}
|
|
2080
|
-
/* Stack on narrow screens so nothing overflows the row.
|
|
2141
|
+
/* Stack on narrow screens so nothing overflows the row.
|
|
2142
|
+
* 640 is deliberately not a ramp step (sm is 600): it is a real phone-landscape width, and moving
|
|
2143
|
+
* it now would reflow every filter bar in every consuming app. So it is NAMED instead — `filterbar`
|
|
2144
|
+
* in tokens/breakpoints.mjs, `--fdy-filterbar-stacked` in the media sheet — because an app rule
|
|
2145
|
+
* that must fire with the stack (releasing a --w-grow field's max-width, say) has to match this
|
|
2146
|
+
* exact condition, and aligning it to sm from the docs alone leaves 600–640px half-stacked: the
|
|
2147
|
+
* fields are already full width while the app still caps the growing one. Raised from
|
|
2148
|
+
* IDU_AI_DOC_SAPB1_CLIENT and IDU_AI_DOC_SAAS (#058 §1). */
|
|
2081
2149
|
@media (max-width:640px){
|
|
2082
2150
|
.fdy-filterbar>.fdy-field,.fdy-filterbar>.fdy-field--w-sm,.fdy-filterbar>.fdy-field--w-lg,.fdy-filterbar>.fdy-field--w-xl,.fdy-filterbar>.fdy-field--w-2xl,.fdy-filterbar>.fdy-field--w-grow{width:100%;flex:1 1 100%;}
|
|
2083
2151
|
.fdy-filterbar__actions{width:100%;margin-left:0;}
|
|
@@ -2345,6 +2413,12 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
2345
2413
|
.fdy-switch input::before{content:"";position:absolute;top:2px;left:2px;width:calc(var(--control-switch-h) - 4px);height:calc(var(--control-switch-h) - 4px);border-radius:50%;background:var(--color-switch-thumb);box-shadow:var(--shadow-1);transition:transform var(--dur-base) var(--ease-spring);}
|
|
2346
2414
|
.fdy-switch input:checked{background:var(--color-primary);}
|
|
2347
2415
|
.fdy-switch input:checked::before{transform:translateX(calc(var(--control-switch-w) - var(--control-switch-h)));}
|
|
2416
|
+
/* State hint (#068): one line saying what the CURRENT state means, never "on means X, off means Y",
|
|
2417
|
+
which leaves the reader to work out which half applies. The control's aria-describedby points at
|
|
2418
|
+
the .fdy-help WRAPPING both lines: a hidden child of a referenced node is skipped, so a screen
|
|
2419
|
+
reader hears only the line on screen. Point it at the two children and both are read. */
|
|
2420
|
+
.fdy-field:has(:is(.fdy-switch,.fdy-check) input:checked) [data-fdy-when="off"],
|
|
2421
|
+
.fdy-field:not(:has(:is(.fdy-switch,.fdy-check) input:checked)) [data-fdy-when="on"]{display:none;}
|
|
2348
2422
|
|
|
2349
2423
|
/* Standalone checkbox (e.g. table row/select-all), supports :indeterminate */
|
|
2350
2424
|
.fdy-checkbox{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);border-radius:var(--radius-sm);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
@@ -2396,6 +2470,13 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
2396
2470
|
.fdy-state__text{font-size:var(--text-sm);color:var(--color-text-muted);max-width:24rem;line-height:var(--leading-normal);}
|
|
2397
2471
|
.fdy-state__actions{margin-top:var(--space-2);display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:center;}
|
|
2398
2472
|
.fdy-state--danger .fdy-state__icon{color:var(--color-danger-strong);}
|
|
2473
|
+
/* Compact (#069): the same state inside a small card, one line of icon and text instead of a
|
|
2474
|
+
page-sized block. Danger matters most here: a widget that renders zero for a failed request
|
|
2475
|
+
reads as "nothing to do", the opposite of what happened. */
|
|
2476
|
+
.fdy-state--compact{flex-direction:row;align-items:flex-start;text-align:left;gap:var(--space-2);padding:var(--space-2) 0;}
|
|
2477
|
+
.fdy-state--compact .fdy-state__icon{width:1rem;height:1rem;flex:none;}
|
|
2478
|
+
.fdy-state--compact .fdy-state__text{font-size:var(--text-xs);max-width:none;}
|
|
2479
|
+
.fdy-state--compact .fdy-state__actions{margin:0 0 0 auto;}
|
|
2399
2480
|
|
|
2400
2481
|
/* Freeday. Stepper / Wizard (progress header + optional panels).
|
|
2401
2482
|
* Enhanced by freeday-stepper.js for prev/next navigation. */
|
|
@@ -2452,8 +2533,17 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
2452
2533
|
itself, for the same reason: hover paints the full step, so the two must not be the same colour.
|
|
2453
2534
|
Exposed as a Tier-3 token through the var() FALLBACK, never as a declaration — a custom property
|
|
2454
2535
|
set here would outrank a host's own :root override. */
|
|
2455
|
-
|
|
2456
|
-
|
|
2536
|
+
/* The resting colour is also published as --_fdy-row-rest, so a row that must not answer the
|
|
2537
|
+
pointer can hover back to exactly what it was (see --hover-actionable). */
|
|
2538
|
+
.fdy-table--striped tbody tr:nth-child(even){--_fdy-row-rest:var(--fdy-table-stripe,color-mix(in srgb,var(--color-surface-2) 55%,var(--color-surface)));background:var(--_fdy-row-rest);}
|
|
2539
|
+
.fdy-table tbody tr:hover{background:var(--_fdy-row-hover,var(--color-surface-2));}
|
|
2540
|
+
/* Opt-in (#064): only a row that DOES something answers the pointer. A tint on a row with no click
|
|
2541
|
+
handler promises a target that is not there: people click the row, nothing happens, and the
|
|
2542
|
+
button they had to aim at was elsewhere in it. Striping, not hover, is what keeps the eye on a
|
|
2543
|
+
line. The inert row's hover resolves to its own resting colour, so a stripe survives it; a frozen
|
|
2544
|
+
cell falls back to its opaque surface, because it scrolls over its neighbours. */
|
|
2545
|
+
.fdy-table--hover-actionable tbody tr:not(.fdy-table__row--activatable){--_fdy-row-hover:var(--_fdy-row-rest,transparent);}
|
|
2546
|
+
.fdy-table--hover-actionable tbody tr:not(.fdy-table__row--activatable) td.fdy-table__freeze{--_fdy-row-hover:var(--_fdy-row-rest,var(--color-surface));}
|
|
2457
2547
|
/* Monospace data, ids, codes, IPs, timestamps. Alignment-neutral so it works in any cell
|
|
2458
2548
|
or inline (`<span class="fdy-mono">`), not just numeric columns. */
|
|
2459
2549
|
.fdy-mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
|
|
@@ -2462,8 +2552,8 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
2462
2552
|
.fdy-table__num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--font-mono);}
|
|
2463
2553
|
/* Loading / empty state cell (FdyTable spans it across all columns). */
|
|
2464
2554
|
.fdy-table__state{text-align:center;color:var(--color-text-muted);padding:var(--space-6) var(--space-4);}
|
|
2465
|
-
/* Activatable row (FdyTable rowActivatable): pointer + keyboard focus ring. The hover tint
|
|
2466
|
-
|
|
2555
|
+
/* Activatable row (FdyTable rowActivatable): pointer + keyboard focus ring. The hover tint comes from
|
|
2556
|
+
the base `.fdy-table tbody tr:hover` rule, which --hover-actionable keeps for these rows only. */
|
|
2467
2557
|
.fdy-table__row--activatable{cursor:pointer;}
|
|
2468
2558
|
.fdy-table__row--activatable:focus-visible{outline:2px solid var(--color-primary);outline-offset:-2px;}
|
|
2469
2559
|
/* Expandable detail row (FdyTable row-detail slot): a muted panel under its row, no hover tint. */
|
|
@@ -2523,7 +2613,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
2523
2613
|
(0,4,3) and beat the hover rule below. At (0,3,3) it ties, and the later rule wins, so a hovered
|
|
2524
2614
|
row still bands unbroken across the freeze line. */
|
|
2525
2615
|
.fdy-table--striped tbody tr:nth-child(even) td.fdy-table__freeze{background:var(--fdy-table-stripe,color-mix(in srgb,var(--color-surface-2) 55%,var(--color-surface)));}
|
|
2526
|
-
.fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--color-surface-2);}
|
|
2616
|
+
.fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--_fdy-row-hover,var(--color-surface-2));}
|
|
2527
2617
|
/* A frozen header cell is stuck on both axes wherever the head is frozen too, so
|
|
2528
2618
|
it has to outrank both. */
|
|
2529
2619
|
.fdy-table--sticky thead .fdy-table__freeze{z-index:3;}
|
package/dist/freeday.css
CHANGED
|
@@ -273,6 +273,8 @@ a { color: var(--color-primary); }
|
|
|
273
273
|
.fdy-avatar--xs{width:1.5rem;height:1.5rem;font-size:var(--text-xs);}
|
|
274
274
|
.fdy-avatar--sm{width:2rem;height:2rem;font-size:var(--text-xs);}
|
|
275
275
|
.fdy-avatar--lg{width:3.5rem;height:3.5rem;font-size:var(--text-lg);}
|
|
276
|
+
/* Square: a mark that is not a person (a company logo, a card's icon chip, #070). */
|
|
277
|
+
.fdy-avatar--square{border-radius:var(--radius-md);}
|
|
276
278
|
.fdy-avatar-group{display:inline-flex;}
|
|
277
279
|
.fdy-avatar-group .fdy-avatar{border:2px solid var(--color-surface);margin-left:calc(var(--space-2)*-1);}
|
|
278
280
|
.fdy-avatar-group .fdy-avatar:first-child{margin-left:0;}
|
|
@@ -412,8 +414,14 @@ a { color: var(--color-primary); }
|
|
|
412
414
|
|
|
413
415
|
/* Freeday. Responsive display utilities (pairs with freeday-breakpoint.js).
|
|
414
416
|
* Breakpoint scale (min-width, px): sm 600 · md 960 · lg 1280 · xl 1920.
|
|
417
|
+
* Two further widths exist and are NOT ramp steps: `nav` (app-shell.css, off-canvas drawer vs
|
|
418
|
+
* static sidebar) and `filterbar` (filterbar.css, the width its fields stack at). Both are named
|
|
419
|
+
* in tokens/breakpoints.mjs; a rule that has to fire with one of them aligns to it, not to the
|
|
420
|
+
* nearest ramp step, which is a band of broken widths in both directions.
|
|
415
421
|
* NOTE: media-query breakpoints must be literal. CSS can't read custom properties
|
|
416
|
-
* inside @media, so these px are structural, not stray magic numbers.
|
|
422
|
+
* inside @media, so these px are structural, not stray magic numbers. An app that runs PostCSS
|
|
423
|
+
* need not retype them: `@cahyo-dimas/freeday/media` declares every width above as a
|
|
424
|
+
* @custom-media, so it can write `@media (--fdy-below-sm)` and never drift (#058 §2). */
|
|
417
425
|
.fdy-hidden{display:none !important;}
|
|
418
426
|
|
|
419
427
|
/* hide-below-X, hidden when the viewport is narrower than X (content shows at X and up) */
|
|
@@ -603,6 +611,13 @@ a { color: var(--color-primary); }
|
|
|
603
611
|
absolutely positioned dismiss button back into the flow. Do not merge these two rules. */
|
|
604
612
|
:where(:is(.fdy-card,.fdy-list__row):has(.fdy-btn--stretch) :is(a[href],button,input,select,textarea,[tabindex]):not(.fdy-btn--stretch)){position:relative;}
|
|
605
613
|
.fdy-card__body{padding:var(--space-5);}
|
|
614
|
+
/* Header band (#070): an identity mark, the title, and the card's way out pinned right. Beside the
|
|
615
|
+
title the action sits in one place on every card; under the content it moved each time the
|
|
616
|
+
content grew. Outside __body's padding, so its rule spans the card edge to edge. The mark is an
|
|
617
|
+
.fdy-avatar (--sm --square --tone-N), so its tints are the ones the contrast gate already covers. */
|
|
618
|
+
.fdy-card__head{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
|
|
619
|
+
.fdy-card__head .fdy-card__title{flex:1 1 auto;min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
620
|
+
.fdy-card__action{flex:none;display:inline-flex;align-items:center;gap:var(--space-2);}
|
|
606
621
|
.fdy-card__title{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 0 var(--space-1);}
|
|
607
622
|
.fdy-card__desc{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;line-height:var(--leading-normal);}
|
|
608
623
|
.fdy-card__footer{padding:var(--space-3) var(--space-5);border-top:var(--bw) solid var(--color-border-muted);background:linear-gradient(180deg,var(--color-surface),var(--color-surface-2));display:flex;gap:var(--space-2);justify-content:flex-end;}
|
|
@@ -985,6 +1000,42 @@ a { color: var(--color-primary); }
|
|
|
985
1000
|
.fdy-text-success{color:var(--color-success-strong);}
|
|
986
1001
|
.fdy-text-warning{color:var(--color-warning-strong);}
|
|
987
1002
|
.fdy-text-danger{color:var(--color-danger-strong);}
|
|
1003
|
+
/* Document columns (#065): a form or record page with an optional identity rail on the left
|
|
1004
|
+
* (__lead) and a summary rail on the right (__aside). The widths follow which rails are PRESENT,
|
|
1005
|
+
* so the layout is decided once here and not again by every screen:
|
|
1006
|
+
* __main alone one column
|
|
1007
|
+
* __main + __aside 8 / 4 (from md)
|
|
1008
|
+
* __lead + __main 3 / 9 (from md)
|
|
1009
|
+
* all three 3 / 6 / 3 (from lg; between md and lg both rails share the left)
|
|
1010
|
+
* Stacked, the summary rail comes FIRST: last, it sits under every section, and its button with it.
|
|
1011
|
+
* From md each rail is sticky and scrolls on its own, so the total and the status stay answerable
|
|
1012
|
+
* while the main column scrolls with the page, and a rail taller than the screen never hangs its
|
|
1013
|
+
* end off it. Screen only: a printed page is not held to a window. */
|
|
1014
|
+
.fdy-page__columns{--_fdy-rail-top:var(--fdy-page-sticky-top,var(--space-4));display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-6);align-items:start;}
|
|
1015
|
+
/* Under the shell the sticky topbar is in the way; --fdy-page-sticky-top overrides either default. */
|
|
1016
|
+
.fdy-app .fdy-page__columns{--_fdy-rail-top:var(--fdy-page-sticky-top,calc(var(--space-16) + var(--space-4)));}
|
|
1017
|
+
.fdy-page__columns > *{min-width:0;}
|
|
1018
|
+
@media (max-width:959.98px){.fdy-page__aside{order:-1;}}
|
|
1019
|
+
@media (min-width:960px){
|
|
1020
|
+
.fdy-page__columns:has(> .fdy-page__aside){grid-template-columns:minmax(0,2fr) minmax(0,1fr);}
|
|
1021
|
+
.fdy-page__columns:has(> .fdy-page__lead){grid-template-columns:minmax(0,1fr) minmax(0,3fr);}
|
|
1022
|
+
/* Both rails, two columns: they stack on the left while __main spans both rows on the right. */
|
|
1023
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > .fdy-page__main{grid-row:span 2;}
|
|
1024
|
+
}
|
|
1025
|
+
@media (min-width:1280px){
|
|
1026
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside){grid-template-columns:minmax(0,1fr) minmax(0,2fr) minmax(0,1fr);}
|
|
1027
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > .fdy-page__main{grid-row:auto;}
|
|
1028
|
+
}
|
|
1029
|
+
/* The padding, taken back by the margin, is room for a focus ring at a card's edge: a scrolling box
|
|
1030
|
+
clips whatever draws outside it. */
|
|
1031
|
+
@media screen and (min-width:960px){
|
|
1032
|
+
.fdy-page__lead,.fdy-page__aside{position:sticky;top:var(--_fdy-rail-top);max-height:calc(100dvh - var(--_fdy-rail-top) - var(--space-4));overflow-y:auto;overscroll-behavior:contain;padding:var(--space-1);margin:calc(var(--space-1) * -1);}
|
|
1033
|
+
}
|
|
1034
|
+
/* Two sticky rails in ONE column would slide over each other: between md and lg, with both rails
|
|
1035
|
+
present, they scroll with the page instead. */
|
|
1036
|
+
@media screen and (min-width:960px) and (max-width:1279.98px){
|
|
1037
|
+
.fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > :is(.fdy-page__lead,.fdy-page__aside){position:static;max-height:none;overflow:visible;}
|
|
1038
|
+
}
|
|
988
1039
|
|
|
989
1040
|
/* Freeday. Date picker (input-styled trigger + calendar popover).
|
|
990
1041
|
* Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
|
|
@@ -1080,9 +1131,19 @@ a { color: var(--color-primary); }
|
|
|
1080
1131
|
/* Freeday. Description list (key-value detail view) */
|
|
1081
1132
|
.fdy-dl{display:grid;grid-template-columns:auto 1fr;gap:var(--space-2) var(--space-5);margin:0;}
|
|
1082
1133
|
.fdy-dl dt{color:var(--color-text-muted);font-size:var(--text-sm);}
|
|
1083
|
-
|
|
1134
|
+
/* overflow-wrap: an email, an order code or a 13-digit phone number is one unbreakable token, and
|
|
1135
|
+
without it the value prints over whatever sits beside it (#066). */
|
|
1136
|
+
.fdy-dl dd{margin:0;min-width:0;color:var(--color-text);font-size:var(--text-sm);font-weight:var(--weight-medium);overflow-wrap:anywhere;}
|
|
1084
1137
|
.fdy-dl--rows{grid-template-columns:1fr;gap:var(--space-1);}
|
|
1085
1138
|
.fdy-dl--rows dt{margin-top:var(--space-2);}
|
|
1139
|
+
/* Facts grid (#066): each pair a <div> (allowed inside <dl>), caption above value, as many columns
|
|
1140
|
+
as the list's OWN width holds, never more than three. Sized by the element rather than the
|
|
1141
|
+
window, so a grid in a narrow side rail stays one column with no modifier: a window breakpoint
|
|
1142
|
+
gave a rail two columns of about 110px. min(100%, …) keeps a single column from overflowing a
|
|
1143
|
+
rail narrower than the column floor. */
|
|
1144
|
+
.fdy-dl--grid{grid-template-columns:repeat(auto-fill,minmax(min(100%,max(var(--fdy-dl-col-min,14rem),calc((100% - 2 * var(--space-6)) / 3))),1fr));gap:var(--space-5) var(--space-6);}
|
|
1145
|
+
.fdy-dl--grid > div{min-width:0;}
|
|
1146
|
+
.fdy-dl--grid dd{margin-top:var(--space-1);}
|
|
1086
1147
|
|
|
1087
1148
|
/* Freeday. Divider */
|
|
1088
1149
|
.fdy-divider{border:0;border-top:var(--bw) solid var(--color-border);margin:var(--space-4) 0;}
|
|
@@ -1199,7 +1260,14 @@ a { color: var(--color-primary); }
|
|
|
1199
1260
|
/* Opt-in: sit the actions inline right after the last field instead of at the far edge, so when the
|
|
1200
1261
|
* bar wraps they read as attached to the filters rather than shoved to the right of a lonely row. */
|
|
1201
1262
|
.fdy-filterbar--actions-inline .fdy-filterbar__actions{margin-left:0;}
|
|
1202
|
-
/* Stack on narrow screens so nothing overflows the row.
|
|
1263
|
+
/* Stack on narrow screens so nothing overflows the row.
|
|
1264
|
+
* 640 is deliberately not a ramp step (sm is 600): it is a real phone-landscape width, and moving
|
|
1265
|
+
* it now would reflow every filter bar in every consuming app. So it is NAMED instead — `filterbar`
|
|
1266
|
+
* in tokens/breakpoints.mjs, `--fdy-filterbar-stacked` in the media sheet — because an app rule
|
|
1267
|
+
* that must fire with the stack (releasing a --w-grow field's max-width, say) has to match this
|
|
1268
|
+
* exact condition, and aligning it to sm from the docs alone leaves 600–640px half-stacked: the
|
|
1269
|
+
* fields are already full width while the app still caps the growing one. Raised from
|
|
1270
|
+
* IDU_AI_DOC_SAPB1_CLIENT and IDU_AI_DOC_SAAS (#058 §1). */
|
|
1203
1271
|
@media (max-width:640px){
|
|
1204
1272
|
.fdy-filterbar>.fdy-field,.fdy-filterbar>.fdy-field--w-sm,.fdy-filterbar>.fdy-field--w-lg,.fdy-filterbar>.fdy-field--w-xl,.fdy-filterbar>.fdy-field--w-2xl,.fdy-filterbar>.fdy-field--w-grow{width:100%;flex:1 1 100%;}
|
|
1205
1273
|
.fdy-filterbar__actions{width:100%;margin-left:0;}
|
|
@@ -1467,6 +1535,12 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1467
1535
|
.fdy-switch input::before{content:"";position:absolute;top:2px;left:2px;width:calc(var(--control-switch-h) - 4px);height:calc(var(--control-switch-h) - 4px);border-radius:50%;background:var(--color-switch-thumb);box-shadow:var(--shadow-1);transition:transform var(--dur-base) var(--ease-spring);}
|
|
1468
1536
|
.fdy-switch input:checked{background:var(--color-primary);}
|
|
1469
1537
|
.fdy-switch input:checked::before{transform:translateX(calc(var(--control-switch-w) - var(--control-switch-h)));}
|
|
1538
|
+
/* State hint (#068): one line saying what the CURRENT state means, never "on means X, off means Y",
|
|
1539
|
+
which leaves the reader to work out which half applies. The control's aria-describedby points at
|
|
1540
|
+
the .fdy-help WRAPPING both lines: a hidden child of a referenced node is skipped, so a screen
|
|
1541
|
+
reader hears only the line on screen. Point it at the two children and both are read. */
|
|
1542
|
+
.fdy-field:has(:is(.fdy-switch,.fdy-check) input:checked) [data-fdy-when="off"],
|
|
1543
|
+
.fdy-field:not(:has(:is(.fdy-switch,.fdy-check) input:checked)) [data-fdy-when="on"]{display:none;}
|
|
1470
1544
|
|
|
1471
1545
|
/* Standalone checkbox (e.g. table row/select-all), supports :indeterminate */
|
|
1472
1546
|
.fdy-checkbox{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);border-radius:var(--radius-sm);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
@@ -1518,6 +1592,13 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1518
1592
|
.fdy-state__text{font-size:var(--text-sm);color:var(--color-text-muted);max-width:24rem;line-height:var(--leading-normal);}
|
|
1519
1593
|
.fdy-state__actions{margin-top:var(--space-2);display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:center;}
|
|
1520
1594
|
.fdy-state--danger .fdy-state__icon{color:var(--color-danger-strong);}
|
|
1595
|
+
/* Compact (#069): the same state inside a small card, one line of icon and text instead of a
|
|
1596
|
+
page-sized block. Danger matters most here: a widget that renders zero for a failed request
|
|
1597
|
+
reads as "nothing to do", the opposite of what happened. */
|
|
1598
|
+
.fdy-state--compact{flex-direction:row;align-items:flex-start;text-align:left;gap:var(--space-2);padding:var(--space-2) 0;}
|
|
1599
|
+
.fdy-state--compact .fdy-state__icon{width:1rem;height:1rem;flex:none;}
|
|
1600
|
+
.fdy-state--compact .fdy-state__text{font-size:var(--text-xs);max-width:none;}
|
|
1601
|
+
.fdy-state--compact .fdy-state__actions{margin:0 0 0 auto;}
|
|
1521
1602
|
|
|
1522
1603
|
/* Freeday. Stepper / Wizard (progress header + optional panels).
|
|
1523
1604
|
* Enhanced by freeday-stepper.js for prev/next navigation. */
|
|
@@ -1574,8 +1655,17 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1574
1655
|
itself, for the same reason: hover paints the full step, so the two must not be the same colour.
|
|
1575
1656
|
Exposed as a Tier-3 token through the var() FALLBACK, never as a declaration — a custom property
|
|
1576
1657
|
set here would outrank a host's own :root override. */
|
|
1577
|
-
|
|
1578
|
-
|
|
1658
|
+
/* The resting colour is also published as --_fdy-row-rest, so a row that must not answer the
|
|
1659
|
+
pointer can hover back to exactly what it was (see --hover-actionable). */
|
|
1660
|
+
.fdy-table--striped tbody tr:nth-child(even){--_fdy-row-rest:var(--fdy-table-stripe,color-mix(in srgb,var(--color-surface-2) 55%,var(--color-surface)));background:var(--_fdy-row-rest);}
|
|
1661
|
+
.fdy-table tbody tr:hover{background:var(--_fdy-row-hover,var(--color-surface-2));}
|
|
1662
|
+
/* Opt-in (#064): only a row that DOES something answers the pointer. A tint on a row with no click
|
|
1663
|
+
handler promises a target that is not there: people click the row, nothing happens, and the
|
|
1664
|
+
button they had to aim at was elsewhere in it. Striping, not hover, is what keeps the eye on a
|
|
1665
|
+
line. The inert row's hover resolves to its own resting colour, so a stripe survives it; a frozen
|
|
1666
|
+
cell falls back to its opaque surface, because it scrolls over its neighbours. */
|
|
1667
|
+
.fdy-table--hover-actionable tbody tr:not(.fdy-table__row--activatable){--_fdy-row-hover:var(--_fdy-row-rest,transparent);}
|
|
1668
|
+
.fdy-table--hover-actionable tbody tr:not(.fdy-table__row--activatable) td.fdy-table__freeze{--_fdy-row-hover:var(--_fdy-row-rest,var(--color-surface));}
|
|
1579
1669
|
/* Monospace data, ids, codes, IPs, timestamps. Alignment-neutral so it works in any cell
|
|
1580
1670
|
or inline (`<span class="fdy-mono">`), not just numeric columns. */
|
|
1581
1671
|
.fdy-mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
|
|
@@ -1584,8 +1674,8 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1584
1674
|
.fdy-table__num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--font-mono);}
|
|
1585
1675
|
/* Loading / empty state cell (FdyTable spans it across all columns). */
|
|
1586
1676
|
.fdy-table__state{text-align:center;color:var(--color-text-muted);padding:var(--space-6) var(--space-4);}
|
|
1587
|
-
/* Activatable row (FdyTable rowActivatable): pointer + keyboard focus ring. The hover tint
|
|
1588
|
-
|
|
1677
|
+
/* Activatable row (FdyTable rowActivatable): pointer + keyboard focus ring. The hover tint comes from
|
|
1678
|
+
the base `.fdy-table tbody tr:hover` rule, which --hover-actionable keeps for these rows only. */
|
|
1589
1679
|
.fdy-table__row--activatable{cursor:pointer;}
|
|
1590
1680
|
.fdy-table__row--activatable:focus-visible{outline:2px solid var(--color-primary);outline-offset:-2px;}
|
|
1591
1681
|
/* Expandable detail row (FdyTable row-detail slot): a muted panel under its row, no hover tint. */
|
|
@@ -1645,7 +1735,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1645
1735
|
(0,4,3) and beat the hover rule below. At (0,3,3) it ties, and the later rule wins, so a hovered
|
|
1646
1736
|
row still bands unbroken across the freeze line. */
|
|
1647
1737
|
.fdy-table--striped tbody tr:nth-child(even) td.fdy-table__freeze{background:var(--fdy-table-stripe,color-mix(in srgb,var(--color-surface-2) 55%,var(--color-surface)));}
|
|
1648
|
-
.fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--color-surface-2);}
|
|
1738
|
+
.fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--_fdy-row-hover,var(--color-surface-2));}
|
|
1649
1739
|
/* A frozen header cell is stuck on both axes wherever the head is frozen too, so
|
|
1650
1740
|
it has to outrank both. */
|
|
1651
1741
|
.fdy-table--sticky thead .fdy-table__freeze{z-index:3;}
|
package/dist/freeday.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Type surface for the root side-effect import `@cahyo-dimas/freeday`, which loads every
|
|
2
|
-
// enhancer and registers the `window.Freeday` global. Authored from src/freeday-toast.js
|
|
3
|
-
// Keep it in sync with
|
|
2
|
+
// enhancer and registers the `window.Freeday` global. Authored from src/freeday-toast.js and
|
|
3
|
+
// src/freeday-confirm.js. Keep it in sync with those files. (The per-component window.Freeday<Name> enhancer namespaces are
|
|
4
4
|
// intentionally not typed here; no consumer reconstructs those.)
|
|
5
5
|
|
|
6
6
|
export interface FreedayToastOptions {
|
|
@@ -15,11 +15,26 @@ export interface FreedayToastOptions {
|
|
|
15
15
|
key?: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
export interface FreedayConfirmOptions {
|
|
19
|
+
title?: string;
|
|
20
|
+
message?: string;
|
|
21
|
+
/** Findings shown as a list under the message. */
|
|
22
|
+
details?: string[];
|
|
23
|
+
/** Accept label. Default 'Confirm'; name the action instead ('Delete order'). */
|
|
24
|
+
acceptText?: string;
|
|
25
|
+
/** Reject label. Default 'Cancel'. */
|
|
26
|
+
rejectText?: string;
|
|
27
|
+
/** The accept button's variant. Default 'primary'. */
|
|
28
|
+
tone?: 'primary' | 'danger';
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
export interface FreedayGlobal {
|
|
19
32
|
/** Show a transient toast in a live region (created on first use). Returns the toast element. */
|
|
20
33
|
toast(opts?: FreedayToastOptions): HTMLElement;
|
|
21
34
|
/** Dismiss a toast early: pass the element returned by toast(), or a key string. */
|
|
22
35
|
dismiss(target: HTMLElement | string): void;
|
|
36
|
+
/** Ask a yes/no question in a modal. true on accept; false on reject, Escape or a backdrop click. */
|
|
37
|
+
confirm(opts?: FreedayConfirmOptions): Promise<boolean>;
|
|
23
38
|
}
|
|
24
39
|
|
|
25
40
|
declare global {
|