ngwr 9.1.0 → 10.0.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/README.md +14 -0
- package/badge/styles/_index.scss +16 -6
- package/badge/styles/_tag.scss +10 -1
- package/button/styles/_index.scss +48 -1
- package/dialog/styles/_index.scss +65 -0
- package/drawer/styles/_index.scss +61 -1
- package/fesm2022/ngwr-action-sheet.mjs +1 -1
- package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +2 -2
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +6 -5
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +6 -5
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +7 -7
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +14 -9
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +10 -2
- package/fesm2022/ngwr-density.mjs.map +1 -1
- package/fesm2022/ngwr-dialog.mjs +10 -1
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +2 -2
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +37 -16
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +4 -3
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +16 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +17 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +74 -1
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +74 -35
- package/fesm2022/ngwr-icon.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +2 -2
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +2 -2
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-mention.mjs +166 -10
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +173 -3
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +17 -4
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm.mjs +4 -3
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +10 -3
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +12 -5
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +1 -1
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +13 -3
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +4 -3
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +25 -3
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/i18n/en.json +165 -0
- package/i18n/ru.json +165 -0
- package/icon/styles/_index.scss +10 -1
- package/input/styles/_index.scss +16 -0
- package/input-number/styles/_index.scss +8 -1
- package/keyboard/styles/_index.scss +10 -3
- package/llms-full.txt +3 -3
- package/mention/styles/_index.scss +23 -0
- package/package.json +3 -1
- package/pagination/styles/_index.scss +12 -0
- package/popover/styles/_index.scss +129 -45
- package/schematics/ng-add/index.js +18 -4
- package/schematics/package.json +3 -0
- package/schematics/use/symbol-map.json +2 -0
- package/select/styles/_index.scss +13 -0
- package/splitter/styles/_index.scss +7 -1
- package/table/styles/_index.scss +28 -10
- package/theme/styles/_box-model.scss +29 -0
- package/theme/styles/_colors.scss +74 -15
- package/theme/styles/_dark.scss +52 -6
- package/theme/styles/_index.scss +1 -0
- package/types/ngwr-cascader.d.ts +1 -0
- package/types/ngwr-color-picker.d.ts +1 -0
- package/types/ngwr-context-menu.d.ts +1 -0
- package/types/ngwr-date-picker.d.ts +5 -3
- package/types/ngwr-density.d.ts +8 -1
- package/types/ngwr-dialog.d.ts +9 -0
- package/types/ngwr-drawer.d.ts +20 -4
- package/types/ngwr-dropdown.d.ts +1 -0
- package/types/ngwr-i18n.d.ts +65 -2
- package/types/ngwr-icon.d.ts +11 -19
- package/types/ngwr-mention.d.ts +45 -1
- package/types/ngwr-overlay.d.ts +80 -2
- package/types/ngwr-pagination.d.ts +14 -1
- package/types/ngwr-popconfirm.d.ts +1 -0
- package/types/ngwr-popover.d.ts +1 -0
- package/types/ngwr-select.d.ts +6 -0
- package/types/ngwr-theme.d.ts +8 -1
- package/types/ngwr-tree.d.ts +1 -0
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-waves.d.ts +10 -0
- package/waves/styles/_index.scss +21 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngwr",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Signals-first Angular UI library: 100+ standalone, zoneless-ready components with native Signal Forms, SSR-safe rendering and CSS-variable theming.",
|
|
6
6
|
"author": "Roman Khegay <rk@garuna.dev> (https://garuna.dev)",
|
|
@@ -68,6 +68,8 @@
|
|
|
68
68
|
"migrations": "./schematics/migrations.json"
|
|
69
69
|
},
|
|
70
70
|
"exports": {
|
|
71
|
+
"./schematics/*": "./schematics/*",
|
|
72
|
+
"./i18n/*.json": "./i18n/*.json",
|
|
71
73
|
".": {
|
|
72
74
|
"sass": "./styles.scss",
|
|
73
75
|
"types": "./types/ngwr.d.ts",
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
.wr-pagination {
|
|
4
4
|
display: block;
|
|
5
|
+
// Breathing room of its own. A pager almost always sits right under a table or
|
|
6
|
+
// list, and with a bare `display: block` and no padding its cells touched the
|
|
7
|
+
// content above and below. Padding rather than margin so it stays inside the
|
|
8
|
+
// component's own box and can't collapse into the consumer's spacing.
|
|
9
|
+
padding-block: 0.5rem;
|
|
5
10
|
|
|
6
11
|
&--disabled {
|
|
7
12
|
opacity: 0.6;
|
|
@@ -80,6 +85,13 @@
|
|
|
80
85
|
|
|
81
86
|
&__size {
|
|
82
87
|
min-width: 0;
|
|
88
|
+
|
|
89
|
+
// The 10rem floor on `.wr-select__trigger` is sized for a standalone field.
|
|
90
|
+
// Here the select only ever holds "20 / page", so the pager was reserving
|
|
91
|
+
// 160px for a ~75px label — the single widest thing in the control.
|
|
92
|
+
.wr-select__trigger {
|
|
93
|
+
min-width: 0;
|
|
94
|
+
}
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
// Size variants — set the cell size; each button gets the matching [size].
|
|
@@ -1,8 +1,85 @@
|
|
|
1
|
+
@use 'sass:list';
|
|
2
|
+
|
|
1
3
|
@use '../../theme/styles' as theme;
|
|
2
4
|
@use '../../overlay/styles'; // shared .wr-overlay-sheet / .wr-overlay-backdrop for responsive mode
|
|
3
5
|
|
|
6
|
+
// --- Arrow -----------------------------------------------------------------
|
|
7
|
+
//
|
|
8
|
+
// A small square rotated 45°, half tucked under the panel so only the tip shows.
|
|
9
|
+
// Tooltip and popover share the geometry; the popover additionally carries the
|
|
10
|
+
// panel's border on the two outward faces so the outline reads as continuous.
|
|
11
|
+
//
|
|
12
|
+
// Placement is driven by the resolved position modifier the directive puts on the
|
|
13
|
+
// overlay host (`--top` means the panel sits ABOVE the trigger, so the arrow hangs
|
|
14
|
+
// off the panel's BOTTOM edge). All 12 positions are covered — the hand-written
|
|
15
|
+
// tooltip rules this replaces only handled 8, so `top-start` / `top-end` /
|
|
16
|
+
// `bottom-start` / `bottom-end` tooltips have been shipping with no arrow at all.
|
|
17
|
+
$_arrow: 6px;
|
|
18
|
+
$_arrow-half: 3px;
|
|
19
|
+
$_arrow-inset: 0.625rem; // distance from the panel corner for the -start / -end variants
|
|
20
|
+
|
|
21
|
+
// edge = which side of the panel the arrow hangs off; align = where along that side.
|
|
22
|
+
$_arrow-placement: (
|
|
23
|
+
top: (bottom, center),
|
|
24
|
+
top-start: (bottom, start),
|
|
25
|
+
top-end: (bottom, end),
|
|
26
|
+
bottom: (top, center),
|
|
27
|
+
bottom-start: (top, start),
|
|
28
|
+
bottom-end: (top, end),
|
|
29
|
+
left: (right, center),
|
|
30
|
+
left-start: (right, start),
|
|
31
|
+
left-end: (right, end),
|
|
32
|
+
right: (left, center),
|
|
33
|
+
right-start: (left, start),
|
|
34
|
+
right-end: (left, end),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/// Positions the arrow against `$edge`, offset outwards by `$pull`.
|
|
38
|
+
@mixin _arrow-at($edge, $align, $pull) {
|
|
39
|
+
#{$edge}: -$pull;
|
|
40
|
+
|
|
41
|
+
@if $edge == bottom or $edge == top {
|
|
42
|
+
@if $align == center {
|
|
43
|
+
left: 50%;
|
|
44
|
+
margin-left: -$_arrow-half;
|
|
45
|
+
} @else if $align == start {
|
|
46
|
+
left: $_arrow-inset;
|
|
47
|
+
} @else {
|
|
48
|
+
right: $_arrow-inset;
|
|
49
|
+
}
|
|
50
|
+
} @else {
|
|
51
|
+
@if $align == center {
|
|
52
|
+
top: 50%;
|
|
53
|
+
margin-top: -$_arrow-half;
|
|
54
|
+
} @else if $align == start {
|
|
55
|
+
top: $_arrow-inset;
|
|
56
|
+
} @else {
|
|
57
|
+
bottom: $_arrow-inset;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// The two faces of the rotated square that stay visible outside the panel.
|
|
63
|
+
@mixin _arrow-border($edge) {
|
|
64
|
+
@if $edge == bottom {
|
|
65
|
+
border-right: 1px solid var(--wr-color-outline);
|
|
66
|
+
border-bottom: 1px solid var(--wr-color-outline);
|
|
67
|
+
} @else if $edge == top {
|
|
68
|
+
border-left: 1px solid var(--wr-color-outline);
|
|
69
|
+
border-top: 1px solid var(--wr-color-outline);
|
|
70
|
+
} @else if $edge == right {
|
|
71
|
+
border-top: 1px solid var(--wr-color-outline);
|
|
72
|
+
border-right: 1px solid var(--wr-color-outline);
|
|
73
|
+
} @else {
|
|
74
|
+
border-bottom: 1px solid var(--wr-color-outline);
|
|
75
|
+
border-left: 1px solid var(--wr-color-outline);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
4
79
|
.wr-popover-overlay {
|
|
5
80
|
> * {
|
|
81
|
+
// Containing block for the arrow below.
|
|
82
|
+
position: relative;
|
|
6
83
|
display: block;
|
|
7
84
|
min-width: 8rem;
|
|
8
85
|
max-width: 22rem;
|
|
@@ -14,6 +91,36 @@
|
|
|
14
91
|
color: var(--wr-color-on-surface);
|
|
15
92
|
animation: wr-overlay-in var(--wr-overlay-duration) var(--wr-overlay-ease);
|
|
16
93
|
transform-origin: top left;
|
|
94
|
+
|
|
95
|
+
&::after {
|
|
96
|
+
content: '';
|
|
97
|
+
position: absolute;
|
|
98
|
+
width: $_arrow;
|
|
99
|
+
height: $_arrow;
|
|
100
|
+
background: var(--wr-color-surface);
|
|
101
|
+
transform: rotate(45deg);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Responsive bottom-sheet mode has no anchor to point at, so no arrow.
|
|
106
|
+
&.wr-overlay-sheet > *::after {
|
|
107
|
+
content: none;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@each $position, $spec in $_arrow-placement {
|
|
112
|
+
$edge: list.nth($spec, 1);
|
|
113
|
+
$align: list.nth($spec, 2);
|
|
114
|
+
|
|
115
|
+
// Pulled one extra pixel so the square's own border lines up flush with the
|
|
116
|
+
// panel's, instead of leaving the panel border showing across the arrow's base.
|
|
117
|
+
.wr-popover-overlay--#{$position} > *::after {
|
|
118
|
+
@include _arrow-at($edge, $align, $_arrow-half + 1px);
|
|
119
|
+
@include _arrow-border($edge);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.wr-tooltip-overlay--#{$position} .wr-tooltip::after {
|
|
123
|
+
@include _arrow-at($edge, $align, $_arrow-half);
|
|
17
124
|
}
|
|
18
125
|
}
|
|
19
126
|
|
|
@@ -28,12 +135,25 @@
|
|
|
28
135
|
}
|
|
29
136
|
|
|
30
137
|
.wr-tooltip {
|
|
138
|
+
// Follows the theme instead of inverting against it: on a dark canvas the
|
|
139
|
+
// tooltip is dark, on a light canvas light. It used to invert (a light chip on
|
|
140
|
+
// dark), which is a common convention but reads as a foreign element here.
|
|
141
|
+
//
|
|
142
|
+
// Both halves are tokens, so an app that wants the inverted look back needs one
|
|
143
|
+
// rule, not an override of every tooltip rule:
|
|
144
|
+
// .wr-tooltip { --wr-tooltip-bg: var(--wr-color-on-surface);
|
|
145
|
+
// --wr-tooltip-color: var(--wr-color-dark-contrast); }
|
|
146
|
+
--wr-tooltip-bg: var(--wr-color-surface);
|
|
147
|
+
--wr-tooltip-color: var(--wr-color-on-surface);
|
|
148
|
+
--wr-tooltip-border: var(--wr-color-outline);
|
|
149
|
+
|
|
31
150
|
position: relative;
|
|
32
151
|
display: inline-block;
|
|
33
152
|
max-width: 18rem;
|
|
34
153
|
padding: 0.3125rem 0.5rem;
|
|
35
|
-
background: var(--wr-
|
|
36
|
-
color: var(--wr-color
|
|
154
|
+
background: var(--wr-tooltip-bg);
|
|
155
|
+
color: var(--wr-tooltip-color);
|
|
156
|
+
border: 1px solid var(--wr-tooltip-border);
|
|
37
157
|
border-radius: var(--wr-border-radius-sm);
|
|
38
158
|
font-family: var(--wr-font-family-base);
|
|
39
159
|
font-size: var(--wr-text-xs);
|
|
@@ -42,56 +162,20 @@
|
|
|
42
162
|
animation: wr-overlay-in var(--wr-overlay-duration) var(--wr-overlay-ease);
|
|
43
163
|
transform-origin: top left;
|
|
44
164
|
|
|
45
|
-
// Arrow
|
|
165
|
+
// Arrow — geometry from the shared placement loop at the top of this file.
|
|
46
166
|
&::after {
|
|
47
167
|
content: '';
|
|
48
168
|
position: absolute;
|
|
49
|
-
width:
|
|
50
|
-
height:
|
|
51
|
-
background: var(--wr-
|
|
169
|
+
width: $_arrow;
|
|
170
|
+
height: $_arrow;
|
|
171
|
+
background: var(--wr-tooltip-bg);
|
|
52
172
|
transform: rotate(45deg);
|
|
53
173
|
}
|
|
54
174
|
}
|
|
55
175
|
|
|
56
|
-
// Arrow placement is
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
.wr-tooltip-overlay--top .wr-tooltip::after {
|
|
60
|
-
left: 50%;
|
|
61
|
-
bottom: -3px;
|
|
62
|
-
margin-left: -3px;
|
|
63
|
-
}
|
|
64
|
-
.wr-tooltip-overlay--bottom .wr-tooltip::after {
|
|
65
|
-
left: 50%;
|
|
66
|
-
top: -3px;
|
|
67
|
-
margin-left: -3px;
|
|
68
|
-
}
|
|
69
|
-
.wr-tooltip-overlay--left .wr-tooltip::after {
|
|
70
|
-
top: 50%;
|
|
71
|
-
right: -3px;
|
|
72
|
-
margin-top: -3px;
|
|
73
|
-
}
|
|
74
|
-
.wr-tooltip-overlay--left-start .wr-tooltip::after {
|
|
75
|
-
top: 0.625rem;
|
|
76
|
-
right: -3px;
|
|
77
|
-
}
|
|
78
|
-
.wr-tooltip-overlay--left-end .wr-tooltip::after {
|
|
79
|
-
bottom: 0.625rem;
|
|
80
|
-
right: -3px;
|
|
81
|
-
}
|
|
82
|
-
.wr-tooltip-overlay--right .wr-tooltip::after {
|
|
83
|
-
top: 50%;
|
|
84
|
-
left: -3px;
|
|
85
|
-
margin-top: -3px;
|
|
86
|
-
}
|
|
87
|
-
.wr-tooltip-overlay--right-start .wr-tooltip::after {
|
|
88
|
-
top: 0.625rem;
|
|
89
|
-
left: -3px;
|
|
90
|
-
}
|
|
91
|
-
.wr-tooltip-overlay--right-end .wr-tooltip::after {
|
|
92
|
-
bottom: 0.625rem;
|
|
93
|
-
left: -3px;
|
|
94
|
-
}
|
|
176
|
+
// Arrow placement for both modes is generated from `$_arrow-placement` at the top
|
|
177
|
+
// of this file — the per-position rules that used to live here are emitted there,
|
|
178
|
+
// byte-identical for the 8 positions they covered plus the 4 they missed.
|
|
95
179
|
|
|
96
180
|
@media (prefers-reduced-motion: reduce) {
|
|
97
181
|
.wr-tooltip {
|
|
@@ -10,8 +10,17 @@ const schematics_1 = require("@angular-devkit/schematics");
|
|
|
10
10
|
const tasks_1 = require("@angular-devkit/schematics/tasks");
|
|
11
11
|
const dependencies_1 = require("@schematics/angular/utility/dependencies");
|
|
12
12
|
const workspace_1 = require("@schematics/angular/utility/workspace");
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Base peers, regardless of prompt answers.
|
|
15
|
+
*
|
|
16
|
+
* `lucide` is here because the bootstrap snippet this schematic prints registers
|
|
17
|
+
* icons through the lucide adapter — leaving it out made the very first snippet
|
|
18
|
+
* a consumer pastes fail to resolve.
|
|
19
|
+
*/
|
|
20
|
+
const BASE_PEERS = [
|
|
21
|
+
['@angular/cdk', '>=22.0.0'],
|
|
22
|
+
['lucide', '>=1.0.0'],
|
|
23
|
+
];
|
|
15
24
|
/** Extra peers contributed by the `dateAdapter` choice. */
|
|
16
25
|
const DATE_ADAPTER_PEERS = {
|
|
17
26
|
none: [],
|
|
@@ -121,11 +130,16 @@ function printNextSteps(options, context) {
|
|
|
121
130
|
const adapter = options.dateAdapter ?? 'none';
|
|
122
131
|
const density = options.density ?? 'none';
|
|
123
132
|
const theme = options.theme ?? 'none';
|
|
133
|
+
// `ngwr/icon` exports only WrIcon / provideWrIcons / svgIcon — there are no
|
|
134
|
+
// bare icon exports, so the old `{ plus, trash }` import did not compile.
|
|
135
|
+
// Icons come from an adapter (lucide / feather) or from svgIcon().
|
|
124
136
|
const imports = [
|
|
125
137
|
"import { provideWrOverlay } from 'ngwr/overlay';",
|
|
126
|
-
"import { provideWrIcons
|
|
138
|
+
"import { provideWrIcons } from 'ngwr/icon';",
|
|
139
|
+
"import { lucideIcons } from 'ngwr/icon/adapters/lucide';",
|
|
140
|
+
"import { Plus, Trash2 } from 'lucide';",
|
|
127
141
|
];
|
|
128
|
-
const providers = ['provideWrOverlay()', 'provideWrIcons(
|
|
142
|
+
const providers = ['provideWrOverlay()', 'provideWrIcons(lucideIcons({ plus: Plus, trash: Trash2 }))'];
|
|
129
143
|
const adapterCall = DATE_ADAPTER_PROVIDER[adapter];
|
|
130
144
|
if (adapterCall) {
|
|
131
145
|
imports.push("import { provideWrDateAdapter } from 'ngwr/date-adapter';");
|
|
@@ -158,6 +158,7 @@
|
|
|
158
158
|
"WrI18nParams": "ngwr/i18n",
|
|
159
159
|
"WrI18nMissingHandler": "ngwr/i18n",
|
|
160
160
|
"WrI18nLoader": "ngwr/i18n",
|
|
161
|
+
"WrI18nBaseCatalogs": "ngwr/i18n",
|
|
161
162
|
"WrI18nStaticLoader": "ngwr/i18n",
|
|
162
163
|
"WrI18nStaticCatalogs": "ngwr/i18n",
|
|
163
164
|
"WrI18nStaticScopedCatalogs": "ngwr/i18n",
|
|
@@ -216,6 +217,7 @@
|
|
|
216
217
|
"WrMetaTwitter": "ngwr/meta",
|
|
217
218
|
"WrMeterGroup": "ngwr/meter-group",
|
|
218
219
|
"WrMeterSegment": "ngwr/meter-group",
|
|
220
|
+
"WrOutsideClick": "ngwr/overlay",
|
|
219
221
|
"WrOverlayContainer": "ngwr/overlay",
|
|
220
222
|
"WrPageHeader": "ngwr/page-header",
|
|
221
223
|
"WrPagination": "ngwr/pagination",
|
|
@@ -270,6 +270,10 @@
|
|
|
270
270
|
--wr-option-padding-y: 0.4375rem;
|
|
271
271
|
--wr-option-padding-x: 0.625rem;
|
|
272
272
|
--wr-option-gap: 0.1875rem;
|
|
273
|
+
// Space between an option's own bits — an icon or checkbox and its label.
|
|
274
|
+
// `--wr-option-gap` above is the panel's row rhythm, a different axis; without
|
|
275
|
+
// this one, a projected `<wr-icon>` sat flush against the text.
|
|
276
|
+
--wr-option-inline-gap: 0.5rem;
|
|
273
277
|
|
|
274
278
|
display: flex;
|
|
275
279
|
flex-direction: column;
|
|
@@ -292,6 +296,7 @@
|
|
|
292
296
|
--wr-option-padding-y: 0.3125rem;
|
|
293
297
|
--wr-option-padding-x: 0.5rem;
|
|
294
298
|
--wr-option-gap: 0.125rem;
|
|
299
|
+
--wr-option-inline-gap: 0.375rem;
|
|
295
300
|
}
|
|
296
301
|
|
|
297
302
|
&--lg {
|
|
@@ -300,6 +305,7 @@
|
|
|
300
305
|
--wr-option-padding-y: 0.5rem;
|
|
301
306
|
--wr-option-padding-x: 0.75rem;
|
|
302
307
|
--wr-option-gap: 0.25rem;
|
|
308
|
+
--wr-option-inline-gap: 0.625rem;
|
|
303
309
|
}
|
|
304
310
|
|
|
305
311
|
&__loading,
|
|
@@ -314,6 +320,7 @@
|
|
|
314
320
|
.wr-option {
|
|
315
321
|
display: flex;
|
|
316
322
|
align-items: center;
|
|
323
|
+
gap: var(--wr-option-inline-gap, 0.5rem);
|
|
317
324
|
padding: var(--wr-option-padding-y) var(--wr-option-padding-x);
|
|
318
325
|
border-radius: var(--wr-border-radius-sm);
|
|
319
326
|
color: var(--wr-color-on-surface);
|
|
@@ -399,6 +406,12 @@
|
|
|
399
406
|
text-transform: uppercase;
|
|
400
407
|
}
|
|
401
408
|
|
|
409
|
+
// A group needs a visible seam above it, or its label reads as one more dim
|
|
410
|
+
// option in the list. This only fired between two groups, so the common shape
|
|
411
|
+
// — loose options and then a group — got no divider at all. Match a preceding
|
|
412
|
+
// option too; deliberately not `* + &`, which would also draw a line under the
|
|
413
|
+
// search field, where the field's own border already does the job.
|
|
414
|
+
.wr-option + &,
|
|
402
415
|
+ .wr-option-group {
|
|
403
416
|
border-top: 1px solid var(--wr-color-outline);
|
|
404
417
|
margin-top: 0.25rem;
|
|
@@ -19,7 +19,13 @@
|
|
|
19
19
|
|
|
20
20
|
&__pane {
|
|
21
21
|
flex-grow: 0;
|
|
22
|
-
|
|
22
|
+
// Shrinkable on purpose. The two panes are sized in percentages that add up
|
|
23
|
+
// to 100, and the 4px divider sits between them — with `flex-shrink: 0` the
|
|
24
|
+
// row came to 100% + 4px and both panes hung over the container's edge.
|
|
25
|
+
// Shrinking splits that 4px between them and keeps the ratio the drag set.
|
|
26
|
+
// Safe to shrink: `min-width: 0` plus `overflow: auto` means content never
|
|
27
|
+
// props a pane open.
|
|
28
|
+
flex-shrink: 1;
|
|
23
29
|
min-width: 0;
|
|
24
30
|
min-height: 0;
|
|
25
31
|
overflow: auto;
|
package/table/styles/_index.scss
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
@use '../../theme/styles' as theme;
|
|
2
2
|
|
|
3
3
|
.wr-table {
|
|
4
|
+
--wr-table-bg: var(--wr-color-surface);
|
|
4
5
|
--wr-table-border: var(--wr-color-outline);
|
|
5
6
|
--wr-table-head-bg: rgba(var(--wr-color-outline-rgb), 0.25);
|
|
6
7
|
--wr-table-row-hover: rgba(var(--wr-color-outline-rgb), 0.15);
|
|
8
|
+
// Head casing. The library renders column titles as the author wrote them —
|
|
9
|
+
// forcing `uppercase` mutates their content, and a header written "ID" or
|
|
10
|
+
// "created at" is the consumer's decision to make, not ours. Both halves stay
|
|
11
|
+
// tokens, so opting back into the old treatment is one rule:
|
|
12
|
+
// .wr-table { --wr-table-head-transform: uppercase;
|
|
13
|
+
// --wr-table-head-letter-spacing: 0.04em; }
|
|
14
|
+
// The tracking moves with the casing on purpose — 0.04em is there to open up
|
|
15
|
+
// capitals and looks wrong on sentence case.
|
|
16
|
+
--wr-table-head-transform: none;
|
|
17
|
+
--wr-table-head-letter-spacing: var(--wr-tracking-normal);
|
|
7
18
|
|
|
8
19
|
display: block;
|
|
9
20
|
position: relative;
|
|
21
|
+
// The table paints its own surface. Sticky pinned cells already fill with
|
|
22
|
+
// --wr-color-surface, so leaving the table transparent let a page background
|
|
23
|
+
// show through the normal rows while the pinned ones stayed opaque.
|
|
24
|
+
background: var(--wr-table-bg);
|
|
10
25
|
// Own stacking context so the loading overlay reliably outranks the sticky
|
|
11
26
|
// pinned cells regardless of the surrounding page z-index.
|
|
12
27
|
isolation: isolate;
|
|
@@ -50,8 +65,8 @@
|
|
|
50
65
|
color: var(--wr-color-on-surface-muted);
|
|
51
66
|
font-weight: 600;
|
|
52
67
|
font-size: var(--wr-text-xs);
|
|
53
|
-
letter-spacing:
|
|
54
|
-
text-transform:
|
|
68
|
+
letter-spacing: var(--wr-table-head-letter-spacing);
|
|
69
|
+
text-transform: var(--wr-table-head-transform);
|
|
55
70
|
white-space: nowrap;
|
|
56
71
|
}
|
|
57
72
|
|
|
@@ -203,7 +218,7 @@
|
|
|
203
218
|
// Pinned footer cells need an opaque base (over the head tint) to stay sticky.
|
|
204
219
|
&__foot-cell#{&}__td--pin-left,
|
|
205
220
|
&__foot-cell#{&}__td--pin-right {
|
|
206
|
-
background-color: var(--wr-
|
|
221
|
+
background-color: var(--wr-table-bg);
|
|
207
222
|
background-image: linear-gradient(var(--wr-table-head-bg), var(--wr-table-head-bg));
|
|
208
223
|
}
|
|
209
224
|
|
|
@@ -221,14 +236,14 @@
|
|
|
221
236
|
&__td--pin-left,
|
|
222
237
|
&__td--pin-right {
|
|
223
238
|
z-index: 1;
|
|
224
|
-
background-color: var(--wr-
|
|
239
|
+
background-color: var(--wr-table-bg);
|
|
225
240
|
}
|
|
226
241
|
|
|
227
242
|
// Header cells lay the head tint over the opaque base so the row stays seamless.
|
|
228
243
|
&__th--pin-left,
|
|
229
244
|
&__th--pin-right {
|
|
230
245
|
z-index: 2;
|
|
231
|
-
background-color: var(--wr-
|
|
246
|
+
background-color: var(--wr-table-bg);
|
|
232
247
|
background-image: linear-gradient(var(--wr-table-head-bg), var(--wr-table-head-bg));
|
|
233
248
|
}
|
|
234
249
|
|
|
@@ -392,7 +407,7 @@
|
|
|
392
407
|
&__group-foot-row > &__foot-cell {
|
|
393
408
|
border-top: 0;
|
|
394
409
|
border-bottom: 1px solid var(--wr-table-border);
|
|
395
|
-
background-color: var(--wr-
|
|
410
|
+
background-color: var(--wr-table-bg);
|
|
396
411
|
background-image: linear-gradient(var(--wr-table-group-bg), var(--wr-table-group-bg));
|
|
397
412
|
}
|
|
398
413
|
|
|
@@ -424,7 +439,7 @@
|
|
|
424
439
|
}
|
|
425
440
|
|
|
426
441
|
thead th:not(.wr-table__th--pin-left):not(.wr-table__th--pin-right) {
|
|
427
|
-
background-color: var(--wr-
|
|
442
|
+
background-color: var(--wr-table-bg);
|
|
428
443
|
background-image: linear-gradient(var(--wr-table-head-bg), var(--wr-table-head-bg));
|
|
429
444
|
}
|
|
430
445
|
|
|
@@ -456,7 +471,7 @@
|
|
|
456
471
|
}
|
|
457
472
|
|
|
458
473
|
tfoot .wr-table__foot-cell:not(.wr-table__td--pin-left):not(.wr-table__td--pin-right) {
|
|
459
|
-
background-color: var(--wr-
|
|
474
|
+
background-color: var(--wr-table-bg);
|
|
460
475
|
background-image: linear-gradient(var(--wr-table-head-bg), var(--wr-table-head-bg));
|
|
461
476
|
}
|
|
462
477
|
|
|
@@ -518,8 +533,11 @@
|
|
|
518
533
|
color: var(--wr-color-on-surface-muted);
|
|
519
534
|
font-size: var(--wr-text-xs);
|
|
520
535
|
font-weight: 600;
|
|
521
|
-
|
|
522
|
-
|
|
536
|
+
// Route through the same tokens as `&__th` instead of re-hard-coding the
|
|
537
|
+
// treatment — card mode used to ignore `--wr-table-head-transform` entirely,
|
|
538
|
+
// so a consumer who opted out still got capitals on mobile.
|
|
539
|
+
letter-spacing: var(--wr-table-head-letter-spacing);
|
|
540
|
+
text-transform: var(--wr-table-head-transform);
|
|
523
541
|
text-align: left;
|
|
524
542
|
white-space: nowrap;
|
|
525
543
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Box model baseline for ngwr's own elements.
|
|
2
|
+
//
|
|
3
|
+
// `ngwr/reset` (which does set `box-sizing: border-box` globally) is opt-in by
|
|
4
|
+
// design, and the umbrella `@use 'ngwr'` deliberately leaves it out — so a
|
|
5
|
+
// consumer following the documented "import once, everything works" path lands
|
|
6
|
+
// in a `content-box` world. Components can't assume otherwise, and several of
|
|
7
|
+
// them did: any `.wr-*` element that pairs `width: 100%` with padding or a
|
|
8
|
+
// border overflowed its container by exactly padding + border. Measured in a
|
|
9
|
+
// stock Angular app on `@use 'ngwr'`:
|
|
10
|
+
//
|
|
11
|
+
// .wr-btn--block → 30px over (14+14 padding, 1+1 border)
|
|
12
|
+
// .wr-input (sm / md / lg) → 18 / 26 / 34px over
|
|
13
|
+
// .wr-textarea__native → 16 / 24 / 32px over
|
|
14
|
+
// .wr-input-group → 2px over (border only)
|
|
15
|
+
//
|
|
16
|
+
// Which is where the stray horizontal scrollbars came from.
|
|
17
|
+
//
|
|
18
|
+
// Scoped to the `wr-` namespace so it can never change the box model of
|
|
19
|
+
// consumer markup — that stays their call, via `ngwr/reset` or their own reset.
|
|
20
|
+
//
|
|
21
|
+
// `:where()` contributes zero specificity on purpose: this is a baseline, not an
|
|
22
|
+
// override. `.wr-icon` and `.wr-spinner` declare `content-box` deliberately (the
|
|
23
|
+
// declared size IS the glyph box) and keep winning without needing `!important`
|
|
24
|
+
// or a particular load order.
|
|
25
|
+
:where([class^='wr-'], [class*=' wr-']),
|
|
26
|
+
:where([class^='wr-'], [class*=' wr-'])::before,
|
|
27
|
+
:where([class^='wr-'], [class*=' wr-'])::after {
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
}
|
|
@@ -21,11 +21,10 @@ $base-colors: (
|
|
|
21
21
|
dark: #0f172a,
|
|
22
22
|
) !default;
|
|
23
23
|
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// pure blue as 50%-lightness even though it reads as dark).
|
|
24
|
+
// Deprecated: the old YIQ brightness cut-off. No longer read — `_contrast()`
|
|
25
|
+
// compares real WCAG contrast ratios, which needs no threshold. Kept declared
|
|
26
|
+
// so `@use 'ngwr/theme' with ($contrast-threshold: …)` in an existing app still
|
|
27
|
+
// compiles instead of erroring on an unknown variable.
|
|
29
28
|
$contrast-threshold: 150 !default;
|
|
30
29
|
|
|
31
30
|
// Dark text used on light backgrounds.
|
|
@@ -40,20 +39,69 @@ $contrast-light: #ffffff !default;
|
|
|
40
39
|
@return color.channel($c, 'red'), color.channel($c, 'green'), color.channel($c, 'blue');
|
|
41
40
|
}
|
|
42
41
|
|
|
42
|
+
/** One channel, linearised per WCAG 2.x. */
|
|
43
|
+
@function _linear($channel) {
|
|
44
|
+
$s: math.div($channel, 255);
|
|
45
|
+
@if $s <= 0.03928 {
|
|
46
|
+
@return math.div($s, 12.92);
|
|
47
|
+
}
|
|
48
|
+
@return math.pow(math.div($s + 0.055, 1.055), 2.4);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** WCAG relative luminance of a colour. */
|
|
52
|
+
@function _luminance($c) {
|
|
53
|
+
@return 0.2126 * _linear(color.channel($c, 'red')) + 0.7152 * _linear(color.channel($c, 'green')) + 0.0722 *
|
|
54
|
+
_linear(color.channel($c, 'blue'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** WCAG contrast ratio between two colours. */
|
|
58
|
+
@function _ratio($a, $b) {
|
|
59
|
+
$la: _luminance($a);
|
|
60
|
+
$lb: _luminance($b);
|
|
61
|
+
$hi: math.max($la, $lb);
|
|
62
|
+
$lo: math.min($la, $lb);
|
|
63
|
+
@return math.div($hi + 0.05, $lo + 0.05);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Foreground that actually reads best on `$c` — whichever of the two candidates
|
|
68
|
+
* wins on WCAG contrast ratio.
|
|
69
|
+
*
|
|
70
|
+
* Was a YIQ brightness threshold, which is a brightness *estimate*, not the
|
|
71
|
+
* metric accessibility is judged by, and it mispicked on every mid-saturation
|
|
72
|
+
* colour: five of the nine shipped intents got white text below AA
|
|
73
|
+
* (success 3.33:1, medium 3.10:1, danger and info 3.68:1, secondary 3.99:1)
|
|
74
|
+
* where dark text would have cleared it (5.43 / 5.82 / 4.90 / 4.91 / 4.52).
|
|
75
|
+
* Comparing the real ratios needs no threshold to tune.
|
|
76
|
+
*/
|
|
43
77
|
@function _contrast($c) {
|
|
44
|
-
$
|
|
45
|
-
$g: color.channel($c, 'green');
|
|
46
|
-
$b: color.channel($c, 'blue');
|
|
47
|
-
$yiq: math.div($r * 299 + $g * 587 + $b * 114, 1000);
|
|
48
|
-
@if $yiq >= $contrast-threshold {
|
|
78
|
+
@if _ratio($c, $contrast-dark) >= _ratio($c, $contrast-light) {
|
|
49
79
|
@return $contrast-dark;
|
|
50
80
|
}
|
|
51
81
|
@return $contrast-light;
|
|
52
82
|
}
|
|
53
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Public alias of {@link _contrast}. Any theme partial that re-tunes an intent
|
|
86
|
+
* has to re-derive its `-contrast` token too, otherwise the token stays baked
|
|
87
|
+
* from the light palette — which is how the dark theme ended up putting white
|
|
88
|
+
* text on its lightened primary at 3.36:1.
|
|
89
|
+
*/
|
|
90
|
+
@function contrast($c) {
|
|
91
|
+
@return _contrast($c);
|
|
92
|
+
}
|
|
93
|
+
|
|
54
94
|
// CSS variables
|
|
55
95
|
|
|
56
96
|
:root {
|
|
97
|
+
// Declared here so the light token set also claims the light UA scheme.
|
|
98
|
+
// Without it the browser fell back to the OS preference, and an app running
|
|
99
|
+
// ngwr's LIGHT theme on a dark-mode machine got dark scrollbars and dark
|
|
100
|
+
// native controls (<textarea>, date/colour inputs) against light surfaces.
|
|
101
|
+
// `_dark.scss` still flips it to `dark` under [data-theme='dark'], which wins
|
|
102
|
+
// on specificity.
|
|
103
|
+
color-scheme: light;
|
|
104
|
+
|
|
57
105
|
--wr-color-white: #ffffff;
|
|
58
106
|
--wr-color-white-rgb: 255, 255, 255;
|
|
59
107
|
--wr-color-black: #000000;
|
|
@@ -80,10 +128,21 @@ $contrast-light: #ffffff !default;
|
|
|
80
128
|
// regardless of theme.
|
|
81
129
|
--wr-color-overlay: rgba(var(--wr-color-backdrop-rgb), 0.45);
|
|
82
130
|
|
|
83
|
-
//
|
|
131
|
+
// Muted TEXT, deliberately its own token rather than an alias of the `medium`
|
|
132
|
+
// intent. The two roles pull in opposite directions and cannot share a value:
|
|
133
|
+
// as text it needs >= 4.5:1 against the lightest surface (#ebeff4), while as a
|
|
134
|
+
// button fill it carries dark text and needs >= 4.5:1 the other way. The best
|
|
135
|
+
// candidate for either fails the other — the window closes around #6b7a8a
|
|
136
|
+
// (3.81 as text, 4.11 as fill). Aliasing them left secondary text at 2.69:1.
|
|
137
|
+
--wr-color-muted-text: #5f6c7d;
|
|
138
|
+
--wr-color-muted-text-rgb: 95, 108, 125;
|
|
139
|
+
|
|
140
|
+
// De-emphasized text — the muted ink dimmed to two canonical strengths so
|
|
84
141
|
// labels / captions / placeholders read consistently. One place each.
|
|
85
|
-
|
|
86
|
-
|
|
142
|
+
// Only the full-opacity token above is AA-safe for body text; these two are
|
|
143
|
+
// decorative by design (0.85 reaches ~3.5:1, 0.6 ~2.3:1 on a light surface).
|
|
144
|
+
--wr-color-text-muted: rgba(var(--wr-color-muted-text-rgb), 0.85);
|
|
145
|
+
--wr-color-text-faint: rgba(var(--wr-color-muted-text-rgb), 0.6);
|
|
87
146
|
|
|
88
147
|
// Neutral gray ramp — a FIXED primitive scale (slate). Each step is a stable
|
|
89
148
|
// job (50 = subtlest surface tint … 950 = strongest ink) and, unlike the
|
|
@@ -121,8 +180,8 @@ $contrast-light: #ffffff !default;
|
|
|
121
180
|
--wr-color-surface-rgb: var(--wr-color-white-rgb);
|
|
122
181
|
--wr-color-on-surface: var(--wr-color-dark);
|
|
123
182
|
--wr-color-on-surface-rgb: var(--wr-color-dark-rgb);
|
|
124
|
-
--wr-color-on-surface-muted: var(--wr-color-
|
|
125
|
-
--wr-color-on-surface-muted-rgb: var(--wr-color-
|
|
183
|
+
--wr-color-on-surface-muted: var(--wr-color-muted-text);
|
|
184
|
+
--wr-color-on-surface-muted-rgb: var(--wr-color-muted-text-rgb);
|
|
126
185
|
|
|
127
186
|
// Solid hairline / divider colour. Distinct from `--wr-color-border` above,
|
|
128
187
|
// which is deliberately translucent (`light` at 50%) so it reads on any
|