@elasticias/ui 1.0.42 → 1.0.44
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/fesm2022/elasticias-ui.mjs +303 -46
- package/fesm2022/elasticias-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/_patterns.scss +182 -45
- package/types/elasticias-ui.d.ts +98 -12
package/package.json
CHANGED
package/styles/_patterns.scss
CHANGED
|
@@ -765,12 +765,24 @@
|
|
|
765
765
|
/* ──────────── RESULTS BAR + TABLE WRAPPER ──────────────────────── */
|
|
766
766
|
|
|
767
767
|
.tbl-wrap {
|
|
768
|
+
min-width: 0;
|
|
768
769
|
background: var(--paper-alt);
|
|
769
770
|
border: 1px solid var(--rule);
|
|
770
771
|
border-radius: var(--r-2xl);
|
|
771
772
|
overflow: hidden;
|
|
772
773
|
}
|
|
773
774
|
|
|
775
|
+
/* The table's own horizontal scroller, inside the clipping shell. A table
|
|
776
|
+
wider than its column (a tablet, or a desktop with the side panel open)
|
|
777
|
+
pans here rather than being cut off by .tbl-wrap or widening the page.
|
|
778
|
+
overscroll-behavior keeps a sideways drag that hits the end from
|
|
779
|
+
panning the page behind it. */
|
|
780
|
+
.tbl-scroll {
|
|
781
|
+
min-width: 0;
|
|
782
|
+
overflow-x: auto;
|
|
783
|
+
overscroll-behavior-x: contain;
|
|
784
|
+
}
|
|
785
|
+
|
|
774
786
|
.tbl-head {
|
|
775
787
|
display: flex;
|
|
776
788
|
justify-content: space-between;
|
|
@@ -971,59 +983,85 @@
|
|
|
971
983
|
}
|
|
972
984
|
|
|
973
985
|
/* ──────────── APP-BAR ICON ACTIONS ──────────────────────────────
|
|
974
|
-
The theme switch
|
|
975
|
-
ef-app-top
|
|
976
|
-
|
|
986
|
+
The theme switch, sign-out and any app action (`.ef-app-top__action`)
|
|
987
|
+
at the trailing edge of ef-app-top, and the theme switch in the login
|
|
988
|
+
header. Plain icons in the treatment of the phone's menu button
|
|
989
|
+
(`.ef-app-shell__menu`): no fill, no border, a 40px box, an 18px glyph.
|
|
990
|
+
|
|
991
|
+
They used to borrow the row-actions trigger's bordered pill, and the
|
|
992
|
+
touch tier grew that painted pill to 48px, so a tablet's top bar was a
|
|
993
|
+
row of big circles. The touch target now grows invisibly instead (see
|
|
994
|
+
the `pointer: coarse` block below): a thumb needs 48px, an eye does not.
|
|
995
|
+
The slot gap is 8px, so two 48px targets meet edge to edge and never
|
|
996
|
+
overlap.
|
|
997
|
+
|
|
998
|
+
Each carries an aria-label and a pTooltip (hover and focus), not a
|
|
999
|
+
`title`: a title never shows on touch and doubles PrimeNG's tooltip on
|
|
1000
|
+
a desktop. */
|
|
977
1001
|
|
|
978
1002
|
.ef-theme-toggle,
|
|
979
1003
|
.ef-app-top__logout,
|
|
980
1004
|
.ef-app-top__action {
|
|
981
1005
|
appearance: none;
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1006
|
+
position: relative;
|
|
1007
|
+
flex: 0 0 auto;
|
|
1008
|
+
background: transparent;
|
|
1009
|
+
border: 0;
|
|
1010
|
+
padding: 0;
|
|
1011
|
+
width: var(--hit-base);
|
|
1012
|
+
height: var(--hit-base);
|
|
1013
|
+
border-radius: var(--r-md);
|
|
987
1014
|
display: inline-grid;
|
|
988
1015
|
place-items: center;
|
|
989
1016
|
color: var(--text-mute);
|
|
990
1017
|
cursor: pointer;
|
|
991
1018
|
transition:
|
|
992
1019
|
background var(--t-fast) var(--ease-out),
|
|
993
|
-
border-color var(--t-fast) var(--ease-out),
|
|
994
1020
|
color var(--t-fast) var(--ease-out);
|
|
995
1021
|
}
|
|
996
1022
|
|
|
997
|
-
|
|
998
|
-
.
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1023
|
+
/* Only where hover is real: iOS keeps :hover on the last element tapped,
|
|
1024
|
+
so the fill would stay on that icon until the next tap elsewhere. */
|
|
1025
|
+
@media (hover: hover) {
|
|
1026
|
+
.ef-theme-toggle:hover,
|
|
1027
|
+
.ef-app-top__logout:hover,
|
|
1028
|
+
.ef-app-top__action:hover {
|
|
1029
|
+
background: color-mix(in srgb, var(--text) 8%, transparent);
|
|
1030
|
+
color: var(--text);
|
|
1031
|
+
}
|
|
1002
1032
|
}
|
|
1003
1033
|
|
|
1004
1034
|
.ef-theme-toggle:focus-visible,
|
|
1005
1035
|
.ef-app-top__logout:focus-visible,
|
|
1006
1036
|
.ef-app-top__action:focus-visible {
|
|
1007
|
-
outline:
|
|
1008
|
-
|
|
1009
|
-
box-shadow: 0 0 0 4px var(--tenant-100);
|
|
1037
|
+
outline: 2px solid var(--tenant-400);
|
|
1038
|
+
outline-offset: 2px;
|
|
1010
1039
|
color: var(--text);
|
|
1011
1040
|
}
|
|
1012
1041
|
|
|
1013
1042
|
.ef-theme-toggle i,
|
|
1014
1043
|
.ef-app-top__logout i,
|
|
1015
1044
|
.ef-app-top__action i {
|
|
1016
|
-
font-size:
|
|
1045
|
+
font-size: 18px;
|
|
1017
1046
|
line-height: 1;
|
|
1018
1047
|
}
|
|
1019
1048
|
|
|
1020
1049
|
/* ──────────── PAGER ─────────────────────────────────────────────── */
|
|
1021
1050
|
|
|
1051
|
+
/* Both halves wrap and can shrink. A row of controls has an intrinsic
|
|
1052
|
+
min-content width, so a pager that cannot wrap is as wide as its
|
|
1053
|
+
lines-per-page block plus every page button, and the card clips the
|
|
1054
|
+
rest: on a 390px phone, or in a narrow card on a tablet with the side
|
|
1055
|
+
panel open, "next" and the last page were cut off. When the two halves
|
|
1056
|
+
no longer fit on one line the page buttons take their own line, still
|
|
1057
|
+
at the end. */
|
|
1022
1058
|
.pager {
|
|
1023
1059
|
display: flex;
|
|
1060
|
+
flex-wrap: wrap;
|
|
1024
1061
|
justify-content: space-between;
|
|
1025
1062
|
align-items: center;
|
|
1026
1063
|
gap: var(--s-3);
|
|
1064
|
+
min-width: 0;
|
|
1027
1065
|
padding: var(--s-3) var(--s-5);
|
|
1028
1066
|
border-top: 1px solid var(--rule);
|
|
1029
1067
|
background: var(--paper-alt);
|
|
@@ -1031,8 +1069,21 @@
|
|
|
1031
1069
|
|
|
1032
1070
|
.pager .pages {
|
|
1033
1071
|
display: flex;
|
|
1072
|
+
flex-wrap: wrap;
|
|
1034
1073
|
align-items: center;
|
|
1035
1074
|
gap: 4px;
|
|
1075
|
+
min-width: 0;
|
|
1076
|
+
margin-inline-start: auto;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/* The phone pager's "5 sur 20", between previous and next. */
|
|
1080
|
+
.pager .pg-status {
|
|
1081
|
+
padding: 0 8px;
|
|
1082
|
+
font-size: 12.5px;
|
|
1083
|
+
font-weight: 600;
|
|
1084
|
+
color: var(--text);
|
|
1085
|
+
font-variant-numeric: tabular-nums;
|
|
1086
|
+
white-space: nowrap;
|
|
1036
1087
|
}
|
|
1037
1088
|
|
|
1038
1089
|
.pager .pg {
|
|
@@ -1061,7 +1112,9 @@
|
|
|
1061
1112
|
|
|
1062
1113
|
.pager .size {
|
|
1063
1114
|
display: flex;
|
|
1115
|
+
flex-wrap: wrap;
|
|
1064
1116
|
align-items: center;
|
|
1117
|
+
min-width: 0;
|
|
1065
1118
|
gap: 8px;
|
|
1066
1119
|
font-size: 12.5px;
|
|
1067
1120
|
color: var(--text-mute);
|
|
@@ -1554,6 +1607,18 @@
|
|
|
1554
1607
|
inset-inline-start: 0;
|
|
1555
1608
|
}
|
|
1556
1609
|
|
|
1610
|
+
/* Placed by ef-row-actions from the trigger's rect (inline top / right /
|
|
1611
|
+
bottom / left), so no scrolling or clipping ancestor can cut it off. */
|
|
1612
|
+
.row-actions__menu.is-floating {
|
|
1613
|
+
position: fixed;
|
|
1614
|
+
/* max-height is set inline from the room on the menu's side; border-box
|
|
1615
|
+
so the padding and border count inside it and a capped menu stays on
|
|
1616
|
+
screen. */
|
|
1617
|
+
box-sizing: border-box;
|
|
1618
|
+
overflow-y: auto;
|
|
1619
|
+
overscroll-behavior: contain;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1557
1622
|
/* Not enough room below the trigger — flip above it, and reverse the
|
|
1558
1623
|
entry slide so the menu still moves away from its trigger. */
|
|
1559
1624
|
.row-actions__menu.up {
|
|
@@ -1795,9 +1860,12 @@
|
|
|
1795
1860
|
min-width: 0;
|
|
1796
1861
|
}
|
|
1797
1862
|
|
|
1798
|
-
/*
|
|
1799
|
-
|
|
1800
|
-
.has-open-menu
|
|
1863
|
+
/* While the density or columns menu (in .tbl-head) is open, the
|
|
1864
|
+
table-wrap lets it escape its rounded clip; the data-card binds
|
|
1865
|
+
.has-open-menu for those two only. Row menus never need it: they are
|
|
1866
|
+
fixed to the viewport, because .tbl-scroll must keep its own overflow
|
|
1867
|
+
(lifting it would reset the reader's scroll position and spill the
|
|
1868
|
+
table across the page). */
|
|
1801
1869
|
.tbl-wrap.has-open-menu {
|
|
1802
1870
|
overflow: visible;
|
|
1803
1871
|
}
|
|
@@ -4541,40 +4609,77 @@ input.ef-input[type='number'] {
|
|
|
4541
4609
|
min-inline-size: var(--hit-touch);
|
|
4542
4610
|
}
|
|
4543
4611
|
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4612
|
+
/* The row ⋯ trigger and the clear ✕ draw at their desktop size (34x28
|
|
4613
|
+
pill, 22px circle) and reach the 48px tier through an invisible
|
|
4614
|
+
overhang, like the top-bar icons. Growing the painted box instead
|
|
4615
|
+
turned every row's ⋯ into a 48px bordered circle.
|
|
4616
|
+
|
|
4617
|
+
The margin reserves the same 48x48 the old rule laid out, so rows,
|
|
4618
|
+
cards and inputs keep their height and spacing, and the overhang
|
|
4619
|
+
never leaves that box: two triggers in adjacent compact rows cannot
|
|
4620
|
+
overlap, because each target is the footprint its own row already
|
|
4621
|
+
made room for. The trigger's overhang adds its 1px border, since an
|
|
4622
|
+
absolute box is measured from the padding edge. */
|
|
4547
4623
|
.row-actions__trigger,
|
|
4548
|
-
.ef-clear
|
|
4549
|
-
|
|
4550
|
-
inline-size: var(--hit-touch);
|
|
4551
|
-
block-size: var(--hit-touch);
|
|
4624
|
+
.ef-clear {
|
|
4625
|
+
position: relative;
|
|
4552
4626
|
}
|
|
4553
4627
|
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
40px square and the TOUCH TARGET reaches the tier through a hit
|
|
4558
|
-
slop, which is the point of the tier: a thumb needs 48px, an eye
|
|
4559
|
-
does not. */
|
|
4560
|
-
.detail-toolbar .right .btn,
|
|
4561
|
-
.search-toolbar .right .btn {
|
|
4562
|
-
position: relative;
|
|
4563
|
-
inline-size: var(--hit-base);
|
|
4564
|
-
block-size: var(--hit-base);
|
|
4565
|
-
min-inline-size: 0;
|
|
4566
|
-
min-block-size: 0;
|
|
4567
|
-
padding: 0;
|
|
4628
|
+
.row-actions__trigger {
|
|
4629
|
+
margin-block: calc((var(--hit-touch) - 28px) / 2);
|
|
4630
|
+
margin-inline: calc((var(--hit-touch) - 34px) / 2);
|
|
4568
4631
|
}
|
|
4569
4632
|
|
|
4570
|
-
.
|
|
4571
|
-
|
|
4633
|
+
.ef-clear {
|
|
4634
|
+
margin: calc((var(--hit-touch) - 22px) / 2);
|
|
4635
|
+
}
|
|
4636
|
+
|
|
4637
|
+
.row-actions__trigger::after,
|
|
4638
|
+
.ef-clear::after {
|
|
4639
|
+
content: '';
|
|
4640
|
+
position: absolute;
|
|
4641
|
+
}
|
|
4642
|
+
|
|
4643
|
+
.row-actions__trigger::after {
|
|
4644
|
+
inset-block: calc((var(--hit-touch) - 28px) / -2 - 1px);
|
|
4645
|
+
inset-inline: calc((var(--hit-touch) - 34px) / -2 - 1px);
|
|
4646
|
+
}
|
|
4647
|
+
|
|
4648
|
+
.ef-clear::after {
|
|
4649
|
+
inset: calc((var(--hit-touch) - 22px) / -2);
|
|
4650
|
+
}
|
|
4651
|
+
|
|
4652
|
+
/* Written as `.pager .pg` to match the base rule's specificity: a bare
|
|
4653
|
+
`.pg` lost to its `height: 32px`, so page buttons drew 48px wide but
|
|
4654
|
+
32px tall on every touch screen. A floor rather than a fixed width,
|
|
4655
|
+
so a four-digit page number is not squeezed. */
|
|
4656
|
+
.pager .pg {
|
|
4657
|
+
min-inline-size: var(--hit-touch);
|
|
4658
|
+
block-size: var(--hit-touch);
|
|
4659
|
+
}
|
|
4660
|
+
|
|
4661
|
+
/* The top-bar icons keep their 40px painted box and reach the 48px
|
|
4662
|
+
tier through an invisible overhang, 4px each side. The 8px slot gap
|
|
4663
|
+
means neighbouring overhangs meet and never overlap, so a tap always
|
|
4664
|
+
lands on the control nearest it. */
|
|
4665
|
+
.ef-app-top__action::after,
|
|
4666
|
+
.ef-theme-toggle::after,
|
|
4667
|
+
.ef-app-top__logout::after {
|
|
4572
4668
|
content: '';
|
|
4573
4669
|
position: absolute;
|
|
4574
|
-
/* (48 - 40) / 2 outward on every side */
|
|
4575
4670
|
inset: calc((var(--hit-touch) - var(--hit-base)) / -2);
|
|
4576
4671
|
}
|
|
4577
4672
|
|
|
4673
|
+
/* Where the lib's controls meet whatever the app projects into the
|
|
4674
|
+
actions slot, the gap is 12px rather than 8. An app control can carry
|
|
4675
|
+
a wider overhang than ours: the phone avatar paints 32px inside a
|
|
4676
|
+
48px target, 8px each side. 8 + 4 needs 12px between the painted
|
|
4677
|
+
boxes, or the theme switch takes the avatar's trailing edge. */
|
|
4678
|
+
ef-app-top .ef-app-top__slot--actions > * + ef-theme-toggle,
|
|
4679
|
+
ef-app-top .ef-app-top__slot--actions > :not(ef-theme-toggle) + .ef-app-top__logout {
|
|
4680
|
+
margin-inline-start: 4px;
|
|
4681
|
+
}
|
|
4682
|
+
|
|
4578
4683
|
/* The switch draws at its theme size (Aura: 40x24) and its click
|
|
4579
4684
|
target is PrimeNG's invisible input, sized to exactly that box. In a
|
|
4580
4685
|
list row a tap that lands just outside it did nothing, because the
|
|
@@ -4599,6 +4704,38 @@ input.ef-input[type='number'] {
|
|
|
4599
4704
|
}
|
|
4600
4705
|
|
|
4601
4706
|
|
|
4707
|
+
/* Toolbar buttons collapse to icon-only below 767px, so the blanket
|
|
4708
|
+
min-block-size above stretched a 40px-wide button to 48px tall and
|
|
4709
|
+
produced a lozenge four-across in the header. Here the VISUAL stays
|
|
4710
|
+
40px square and the TOUCH TARGET reaches the tier through a hit
|
|
4711
|
+
slop, which is the point of the tier: a thumb needs 48px, an eye
|
|
4712
|
+
does not.
|
|
4713
|
+
|
|
4714
|
+
Scoped to the same 767px as the rule that hides the label. Keyed on
|
|
4715
|
+
pointer type alone it also caught every tablet, which kept its label
|
|
4716
|
+
and got a 40px box: "Nouvelle commande" spilled out of the button and
|
|
4717
|
+
the detail actions piled into each other. Above 767px a touch screen
|
|
4718
|
+
keeps the labelled button, 48px tall from the generic rule above. */
|
|
4719
|
+
@media (pointer: coarse) and (max-width: 767px) {
|
|
4720
|
+
.detail-toolbar .right .btn,
|
|
4721
|
+
.search-toolbar .right .btn {
|
|
4722
|
+
position: relative;
|
|
4723
|
+
inline-size: var(--hit-base);
|
|
4724
|
+
block-size: var(--hit-base);
|
|
4725
|
+
min-inline-size: 0;
|
|
4726
|
+
min-block-size: 0;
|
|
4727
|
+
padding: 0;
|
|
4728
|
+
}
|
|
4729
|
+
|
|
4730
|
+
.detail-toolbar .right .btn::after,
|
|
4731
|
+
.search-toolbar .right .btn::after {
|
|
4732
|
+
content: '';
|
|
4733
|
+
position: absolute;
|
|
4734
|
+
/* (48 - 40) / 2 outward on every side */
|
|
4735
|
+
inset: calc((var(--hit-touch) - var(--hit-base)) / -2);
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
|
|
4602
4739
|
/* ── MOBILE BOTTOM BAR ──────────────────────────────────────────────
|
|
4603
4740
|
The phone shell renders a 64px tab bar fixed to the bottom edge.
|
|
4604
4741
|
Anything else pinned down there has to clear it, so the height is
|
package/types/elasticias-ui.d.ts
CHANGED
|
@@ -980,6 +980,9 @@ declare class EfConfirmDialogComponent {
|
|
|
980
980
|
* The icon shows the theme you would move to, not the one you are in, which
|
|
981
981
|
* is what makes a single-button toggle readable: a moon means "go dark".
|
|
982
982
|
*
|
|
983
|
+
* Named by `aria-label` and a pTooltip that opens on hover and on
|
|
984
|
+
* keyboard focus (not `title`, which never shows on touch).
|
|
985
|
+
*
|
|
983
986
|
* ```html
|
|
984
987
|
* <ef-theme-toggle />
|
|
985
988
|
* ```
|
|
@@ -990,13 +993,18 @@ declare class EfThemeToggleComponent {
|
|
|
990
993
|
readonly darkIcon: _angular_core.InputSignal<string>;
|
|
991
994
|
/** Icon shown while dark is active (i.e. "switch to light"). */
|
|
992
995
|
readonly lightIcon: _angular_core.InputSignal<string>;
|
|
993
|
-
/**
|
|
996
|
+
/**
|
|
997
|
+
* Where the tooltip opens. Below suits a top bar (the app bar, the
|
|
998
|
+
* login header); a toggle placed low on a page can open it above.
|
|
999
|
+
*/
|
|
1000
|
+
readonly tooltipPosition: _angular_core.InputSignal<"top" | "left" | "bottom" | "right">;
|
|
1001
|
+
/** Translation key for the accessible name (also the tooltip text). */
|
|
994
1002
|
readonly ariaLabelKey: _angular_core.InputSignal<string>;
|
|
995
1003
|
readonly isDark: _angular_core.Signal<boolean>;
|
|
996
1004
|
protected readonly labelKey: _angular_core.Signal<string>;
|
|
997
1005
|
toggle(): void;
|
|
998
1006
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfThemeToggleComponent, never>;
|
|
999
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfThemeToggleComponent, "ef-theme-toggle", never, { "darkIcon": { "alias": "darkIcon"; "required": false; "isSignal": true; }; "lightIcon": { "alias": "lightIcon"; "required": false; "isSignal": true; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1007
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfThemeToggleComponent, "ef-theme-toggle", never, { "darkIcon": { "alias": "darkIcon"; "required": false; "isSignal": true; }; "lightIcon": { "alias": "lightIcon"; "required": false; "isSignal": true; }; "tooltipPosition": { "alias": "tooltipPosition"; "required": false; "isSignal": true; }; "ariaLabelKey": { "alias": "ariaLabelKey"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1000
1008
|
}
|
|
1001
1009
|
|
|
1002
1010
|
/**
|
|
@@ -1579,7 +1587,8 @@ declare class EfToolbarComponent {
|
|
|
1579
1587
|
*
|
|
1580
1588
|
* **Desktop / tablet:** rail (64px) + side (240px) + (top + main). The
|
|
1581
1589
|
* side folds away from a toggle at the foot of the rail (or mod+B); the
|
|
1582
|
-
* rail never does.
|
|
1590
|
+
* rail never does. A tablet starts with it folded unless the viewer has
|
|
1591
|
+
* a stored choice (`EF_SIDE_COLLAPSED_KEY`).
|
|
1583
1592
|
* **Mobile:** delegates to `<ef-app-shell-mobile>` — top bar + scrollable
|
|
1584
1593
|
* body + 5-tab bottom bar + FAB slot.
|
|
1585
1594
|
*
|
|
@@ -1601,7 +1610,20 @@ declare class EfAppShellComponent {
|
|
|
1601
1610
|
readonly isMobile: _angular_core.Signal<boolean>;
|
|
1602
1611
|
readonly showSide: _angular_core.Signal<boolean>;
|
|
1603
1612
|
private readonly prefs;
|
|
1604
|
-
|
|
1613
|
+
/** The viewer's explicit choice ('1' folded, '0' open), or null when they never made one. */
|
|
1614
|
+
private readonly storedSide;
|
|
1615
|
+
/**
|
|
1616
|
+
* Folded or open. An explicit choice always wins. Without one, a
|
|
1617
|
+
* tablet starts folded: at 768-1279px the 240px panel left a
|
|
1618
|
+
* phone-width column for a desktop table, so the right-hand columns
|
|
1619
|
+
* were cut off. A desktop keeps it open and a phone never renders it.
|
|
1620
|
+
*
|
|
1621
|
+
* The default is derived, never written: storing it would turn it
|
|
1622
|
+
* into a choice the viewer did not make, pin it when they rotate or
|
|
1623
|
+
* resize across the breakpoint, and with a server-backed store send
|
|
1624
|
+
* a request on every page load.
|
|
1625
|
+
*/
|
|
1626
|
+
readonly sideCollapsed: _angular_core.Signal<boolean>;
|
|
1605
1627
|
/** Shown in the toggle's tooltip: the button is far from the content, the shortcut is not. */
|
|
1606
1628
|
readonly shortcutHint: string;
|
|
1607
1629
|
toggleSide(): void;
|
|
@@ -1727,6 +1749,21 @@ declare class EfAppTopComponent {
|
|
|
1727
1749
|
/** Present only when the app provides `EF_SESSION`. */
|
|
1728
1750
|
protected readonly session: _elasticias_core.EfSession;
|
|
1729
1751
|
protected signOut(): void;
|
|
1752
|
+
/** A touch or pen press at least this long is a hold, not a tap. */
|
|
1753
|
+
static readonly HOLD_MS = 500;
|
|
1754
|
+
/** True on a screen whose primary pointer cannot hover (a phone, a tablet). */
|
|
1755
|
+
protected readonly touchOnly: _angular_core.WritableSignal<boolean>;
|
|
1756
|
+
private pressStart;
|
|
1757
|
+
constructor();
|
|
1758
|
+
protected onSignOutPointerDown(event: PointerEvent): void;
|
|
1759
|
+
/**
|
|
1760
|
+
* A cancelled press (the browser took the gesture) must not judge a
|
|
1761
|
+
* later click. Not on pointerleave: on touch it fires after pointerup
|
|
1762
|
+
* and before the synthesised click, so clearing there would erase
|
|
1763
|
+
* every long press just before the guard reads it.
|
|
1764
|
+
*/
|
|
1765
|
+
protected forgetSignOutPress(): void;
|
|
1766
|
+
protected onSignOutClick(event: MouseEvent): void;
|
|
1730
1767
|
private readonly active;
|
|
1731
1768
|
readonly module: _angular_core.Signal<_elasticias_core.EfModule>;
|
|
1732
1769
|
readonly navItem: _angular_core.Signal<_elasticias_core.EfNavItem>;
|
|
@@ -4826,11 +4863,6 @@ declare class EfDataCardComponent<TRow = any> implements AfterContentInit {
|
|
|
4826
4863
|
private storageKey;
|
|
4827
4864
|
private loadPreferences;
|
|
4828
4865
|
private savePreferences;
|
|
4829
|
-
/** Tracks how many `ef-row-actions` are currently open inside this
|
|
4830
|
-
* card. When > 0, `.tbl-wrap` lifts its `overflow: hidden` so the
|
|
4831
|
-
* popup can escape its rounded edges. */
|
|
4832
|
-
readonly openMenuCount: _angular_core.WritableSignal<number>;
|
|
4833
|
-
onRowActionsToggle(event: Event): void;
|
|
4834
4866
|
/**
|
|
4835
4867
|
* Children that own their own click. Opening the row on top of them would
|
|
4836
4868
|
* run two things from one gesture.
|
|
@@ -5003,6 +5035,9 @@ declare class EfOverflowTooltipDirective {
|
|
|
5003
5035
|
* - Click the trigger to toggle. Trigger gets `.open` (ink-active) while the menu is open.
|
|
5004
5036
|
* - Only one row menu is open at a time — opening one closes the last.
|
|
5005
5037
|
* - The menu flips above the trigger when there isn't room below it.
|
|
5038
|
+
* - The menu is fixed to the viewport, placed from the trigger's rect, so a
|
|
5039
|
+
* table's horizontal scroller cannot clip it. Scrolling any ancestor or
|
|
5040
|
+
* resizing the window closes it, rather than leaving it behind its row.
|
|
5006
5041
|
* - Click outside or press `Escape` to close.
|
|
5007
5042
|
* - Clicking an item runs its `command` then closes the menu.
|
|
5008
5043
|
* - dblclick on the trigger or menu does NOT propagate, so the data-card's
|
|
@@ -5077,14 +5112,54 @@ declare class EfRowActionsComponent {
|
|
|
5077
5112
|
* below wins, which is where it has always been.
|
|
5078
5113
|
*/
|
|
5079
5114
|
private shouldDropUp;
|
|
5115
|
+
/** Gap between the trigger and the menu. Matches the CSS `calc(100% + 8px)`. */
|
|
5116
|
+
private static readonly MENU_GAP;
|
|
5117
|
+
/**
|
|
5118
|
+
* Where the fixed menu sits, as physical offsets from the viewport
|
|
5119
|
+
* edges. The four values are always set, so they override the CSS
|
|
5120
|
+
* `top` / `bottom` / `inset-inline-*` an absolute menu uses.
|
|
5121
|
+
*/
|
|
5122
|
+
readonly menuPosition: _angular_core.WritableSignal<{
|
|
5123
|
+
top: string;
|
|
5124
|
+
right: string;
|
|
5125
|
+
bottom: string;
|
|
5126
|
+
left: string;
|
|
5127
|
+
maxHeight: string;
|
|
5128
|
+
}>;
|
|
5129
|
+
/**
|
|
5130
|
+
* Fixed rather than absolute: inside ef-data-card the table sits in a
|
|
5131
|
+
* horizontal scroller, and `overflow-x: auto` makes the block axis clip
|
|
5132
|
+
* too, so an absolute menu was cut off at the scroller's edge. Lifting
|
|
5133
|
+
* the scroller's overflow while a menu is open would reset its
|
|
5134
|
+
* scrollLeft and spill the table across the page, exactly when the
|
|
5135
|
+
* reader has scrolled across to reach the actions column.
|
|
5136
|
+
*/
|
|
5137
|
+
private place;
|
|
5138
|
+
/**
|
|
5139
|
+
* An ancestor with a transform, filter or containment becomes the fixed
|
|
5140
|
+
* menu's containing block (a dialog mid-animation, for one), and the
|
|
5141
|
+
* viewport offsets land in the wrong place. Measure where the menu
|
|
5142
|
+
* actually is against where the trigger says it should be, and shift
|
|
5143
|
+
* by the difference. Also nudges it back inside the viewport.
|
|
5144
|
+
*/
|
|
5145
|
+
private correctOffset;
|
|
5146
|
+
/** The nearest `dir` wins (the app sets it on <html>); computed style covers a page without one. */
|
|
5147
|
+
private isRtl;
|
|
5148
|
+
private triggerRect;
|
|
5149
|
+
/** The fixed containing block: the viewport without its scrollbars. */
|
|
5150
|
+
private static viewportWidth;
|
|
5151
|
+
private static viewportHeight;
|
|
5152
|
+
private displacementListener;
|
|
5153
|
+
private listenForDisplacement;
|
|
5154
|
+
private stopListeningForDisplacement;
|
|
5080
5155
|
/** Rough height before the menu exists: item padding + line box, with
|
|
5081
5156
|
* dividers and the menu's own padding and border. */
|
|
5082
5157
|
private estimateMenuHeight;
|
|
5083
5158
|
/** Re-decide against the rendered height — item labels wrap, and the
|
|
5084
5159
|
* estimate can't know that. */
|
|
5085
5160
|
private correctPlacementAfterRender;
|
|
5086
|
-
/** Bubbling DOM event so ancestors
|
|
5087
|
-
*
|
|
5161
|
+
/** Bubbling DOM event so ancestors can react to a row menu opening or
|
|
5162
|
+
* closing. ef-data-card no longer needs it (the menu is fixed). */
|
|
5088
5163
|
private dispatchToggle;
|
|
5089
5164
|
/** Outside click — closes the menu. */
|
|
5090
5165
|
onDocumentClick(event: MouseEvent): void;
|
|
@@ -5119,6 +5194,13 @@ interface PageBtn {
|
|
|
5119
5194
|
*
|
|
5120
5195
|
* Renders a windowed page list (first / current ±1 / last with
|
|
5121
5196
|
* ellipsis) for total > 7 pages. Below 7 pages all buttons show.
|
|
5197
|
+
*
|
|
5198
|
+
* On a phone (`EfViewportService.isMobile`) the window becomes
|
|
5199
|
+
* first · previous · "5 of 20" · next · last. The window is up to nine
|
|
5200
|
+
* 48px touch targets (about 464px) and a 390px phone has about 316px
|
|
5201
|
+
* inside a card, so it ran off the edge. The compact set still reaches
|
|
5202
|
+
* every page the window offered: its numbered neighbours are one step
|
|
5203
|
+
* away, and the first and the last page are one tap.
|
|
5122
5204
|
*/
|
|
5123
5205
|
declare class EfPagerComponent {
|
|
5124
5206
|
readonly pageNumber: _angular_core.InputSignal<number>;
|
|
@@ -5130,10 +5212,14 @@ declare class EfPagerComponent {
|
|
|
5130
5212
|
readonly ofLabelKey: _angular_core.InputSignal<string>;
|
|
5131
5213
|
readonly prevLabelKey: _angular_core.InputSignal<string>;
|
|
5132
5214
|
readonly nextLabelKey: _angular_core.InputSignal<string>;
|
|
5215
|
+
readonly firstLabelKey: _angular_core.InputSignal<string>;
|
|
5216
|
+
readonly lastLabelKey: _angular_core.InputSignal<string>;
|
|
5133
5217
|
/** Hide the lines-per-page selector + range when only paging is wanted. */
|
|
5134
5218
|
readonly hideSize: _angular_core.InputSignal<boolean>;
|
|
5135
5219
|
readonly pageChange: _angular_core.OutputEmitterRef<number>;
|
|
5136
5220
|
readonly pageSizeChange: _angular_core.OutputEmitterRef<number>;
|
|
5221
|
+
/** Phone layout: first · prev · "n of N" · next · last. */
|
|
5222
|
+
readonly compact: _angular_core.Signal<boolean>;
|
|
5137
5223
|
/** Falls back to the first configured option if the value ever goes empty. */
|
|
5138
5224
|
private get defaultSize();
|
|
5139
5225
|
/** `pageSizeOptions` shaped for `ef-select`, which reads a label and a
|
|
@@ -5153,7 +5239,7 @@ declare class EfPagerComponent {
|
|
|
5153
5239
|
onSizeChange(value: number | string | null): void;
|
|
5154
5240
|
trackBtn(index: number, btn: PageBtn): string;
|
|
5155
5241
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EfPagerComponent, never>;
|
|
5156
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPagerComponent, "ef-pager", never, { "pageNumber": { "alias": "pageNumber"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "totalCount": { "alias": "totalCount"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "linesPerPageLabelKey": { "alias": "linesPerPageLabelKey"; "required": false; "isSignal": true; }; "ofLabelKey": { "alias": "ofLabelKey"; "required": false; "isSignal": true; }; "prevLabelKey": { "alias": "prevLabelKey"; "required": false; "isSignal": true; }; "nextLabelKey": { "alias": "nextLabelKey"; "required": false; "isSignal": true; }; "hideSize": { "alias": "hideSize"; "required": false; "isSignal": true; }; }, { "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; }, never, never, true, never>;
|
|
5242
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EfPagerComponent, "ef-pager", never, { "pageNumber": { "alias": "pageNumber"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "totalCount": { "alias": "totalCount"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "linesPerPageLabelKey": { "alias": "linesPerPageLabelKey"; "required": false; "isSignal": true; }; "ofLabelKey": { "alias": "ofLabelKey"; "required": false; "isSignal": true; }; "prevLabelKey": { "alias": "prevLabelKey"; "required": false; "isSignal": true; }; "nextLabelKey": { "alias": "nextLabelKey"; "required": false; "isSignal": true; }; "firstLabelKey": { "alias": "firstLabelKey"; "required": false; "isSignal": true; }; "lastLabelKey": { "alias": "lastLabelKey"; "required": false; "isSignal": true; }; "hideSize": { "alias": "hideSize"; "required": false; "isSignal": true; }; }, { "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; }, never, never, true, never>;
|
|
5157
5243
|
}
|
|
5158
5244
|
|
|
5159
5245
|
/**
|