@scalewing/react 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/README.md +69 -2
  3. package/dist/anchored-position.d.ts +28 -0
  4. package/dist/anchored-position.js +17 -0
  5. package/dist/calendar-keys.d.ts +19 -0
  6. package/dist/calendar-keys.js +39 -0
  7. package/dist/calendar-labels.d.ts +12 -0
  8. package/dist/calendar-labels.js +32 -0
  9. package/dist/calendar-month.d.ts +36 -0
  10. package/dist/calendar-month.js +111 -0
  11. package/dist/calendar-years.d.ts +14 -0
  12. package/dist/calendar-years.js +20 -0
  13. package/dist/components/Accordion.d.ts +6 -1
  14. package/dist/components/Accordion.js +12 -3
  15. package/dist/components/ActionBar.d.ts +30 -0
  16. package/dist/components/ActionBar.js +13 -0
  17. package/dist/components/Box.d.ts +7 -3
  18. package/dist/components/Box.js +2 -2
  19. package/dist/components/DateField.d.ts +15 -1
  20. package/dist/components/DateField.js +58 -18
  21. package/dist/components/Field.d.ts +5 -1
  22. package/dist/components/Field.js +16 -4
  23. package/dist/components/FieldAdornment.d.ts +20 -0
  24. package/dist/components/FieldAdornment.js +34 -0
  25. package/dist/components/Grid.d.ts +4 -1
  26. package/dist/components/Grid.js +2 -2
  27. package/dist/components/Progress.d.ts +3 -1
  28. package/dist/components/Progress.js +2 -2
  29. package/dist/components/date-field/CalendarDialog.d.ts +26 -0
  30. package/dist/components/date-field/CalendarDialog.js +93 -0
  31. package/dist/components/date-field/CalendarGrid.d.ts +23 -0
  32. package/dist/components/date-field/CalendarGrid.js +36 -0
  33. package/dist/components/date-field/CalendarHeader.d.ts +18 -0
  34. package/dist/components/date-field/CalendarHeader.js +33 -0
  35. package/dist/components/date-field/DateFieldGlyph.d.ts +9 -0
  36. package/dist/components/date-field/DateFieldGlyph.js +15 -0
  37. package/dist/components/date-field/use-anchored-popover.d.ts +8 -0
  38. package/dist/components/date-field/use-anchored-popover.js +61 -0
  39. package/dist/components/date-field/use-date-entry.d.ts +20 -0
  40. package/dist/components/date-field/use-date-entry.js +61 -0
  41. package/dist/components/date-field/use-lang-locale.d.ts +7 -0
  42. package/dist/components/date-field/use-lang-locale.js +17 -0
  43. package/dist/css/css-accordion.js +55 -8
  44. package/dist/css/css-action-bar.d.ts +4 -0
  45. package/dist/css/css-action-bar.js +99 -0
  46. package/dist/css/css-action-menu.js +2 -1
  47. package/dist/css/css-box-border.d.ts +5 -0
  48. package/dist/css/css-box-border.js +24 -0
  49. package/dist/css/css-chrome.js +2 -1
  50. package/dist/css/css-components.js +10 -6
  51. package/dist/css/css-data.js +2 -1
  52. package/dist/css/css-date-field.js +241 -11
  53. package/dist/css/css-denomination-grid.js +2 -1
  54. package/dist/css/css-document.d.ts +2 -0
  55. package/dist/css/css-document.js +2 -1
  56. package/dist/css/css-field.js +61 -1
  57. package/dist/css/css-select.js +2 -1
  58. package/dist/css/css-tooltip.js +2 -1
  59. package/dist/css/stacking.d.ts +10 -0
  60. package/dist/css/stacking.js +47 -0
  61. package/dist/css/stylesheet.js +4 -0
  62. package/dist/date-entry-validity.d.ts +10 -0
  63. package/dist/date-entry-validity.js +16 -0
  64. package/dist/date-entry.d.ts +32 -0
  65. package/dist/date-entry.js +106 -0
  66. package/dist/date-field-labels.d.ts +38 -0
  67. package/dist/date-field-labels.js +22 -0
  68. package/dist/date-field-locale.d.ts +8 -0
  69. package/dist/date-field-locale.js +27 -0
  70. package/dist/date-formatters.d.ts +13 -0
  71. package/dist/date-formatters.js +28 -0
  72. package/dist/date-only.d.ts +23 -0
  73. package/dist/date-only.js +63 -9
  74. package/dist/index.d.ts +5 -3
  75. package/dist/index.js +3 -2
  76. package/dist/styles.css +388 -21
  77. package/package.json +1 -1
@@ -1,8 +1,15 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { zIndex } from './stacking.js';
2
3
  const body = typographyVariants.body;
3
4
  const label = typographyVariants.label;
4
5
  const caption = typographyVariants.caption;
5
- export function cssDateFieldClasses() {
6
+ /* The calendar button, month steps, and days are one square control wide. */
7
+ const square = 'var(--sw-control-md-min-height)';
8
+ /* Wide enough for "MM/DD/YYYY" in the system sans. */
9
+ const entryWidth = '12ch';
10
+ /* Outranks the canvas rule for text inputs so the button keeps its room. */
11
+ const input = '.sw-date-field .sw-date-field-control > .sw-date-field-input';
12
+ function fieldRules() {
6
13
  return `.sw-date-field {
7
14
  display: flex;
8
15
  flex-direction: column;
@@ -20,7 +27,15 @@ export function cssDateFieldClasses() {
20
27
  line-height: ${label.lineHeight}px;
21
28
  }
22
29
 
23
- .sw-date-field-input {
30
+ .sw-date-field-control {
31
+ align-self: flex-start;
32
+ display: flex;
33
+ max-width: 100%;
34
+ min-width: 0;
35
+ position: relative;
36
+ }
37
+
38
+ ${input} {
24
39
  background: var(--sw-glass-fill);
25
40
  border: 1px solid var(--sw-color-border);
26
41
  border-radius: var(--sw-radius-sm);
@@ -28,26 +43,78 @@ export function cssDateFieldClasses() {
28
43
  color: var(--sw-color-text);
29
44
  font-family: var(--sw-font-sans);
30
45
  font-size: ${body.fontSize}px;
46
+ font-variant-numeric: tabular-nums;
47
+ margin: 0;
48
+ max-width: 100%;
31
49
  min-height: var(--sw-control-md-min-height);
32
50
  min-width: 0;
33
- padding-inline: var(--sw-control-md-padding-inline);
34
- width: 100%;
51
+ padding-inline: var(--sw-control-md-padding-inline) ${square};
52
+ width: calc(${entryWidth} + var(--sw-control-md-padding-inline) + ${square});
53
+ }
54
+
55
+ ${input}::placeholder {
56
+ color: var(--sw-color-muted);
57
+ opacity: 1;
58
+ }
59
+
60
+ ${input}:focus {
61
+ box-shadow: none;
62
+ outline: none;
35
63
  }
36
64
 
37
- .sw-date-field-input:focus-visible {
65
+ ${input}:focus-visible {
38
66
  outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
39
67
  outline-offset: var(--sw-focus-ring-offset);
40
68
  }
41
69
 
42
- .sw-date-field-input[aria-invalid='true'] {
70
+ ${input}[aria-invalid='true'] {
43
71
  border-color: var(--sw-color-danger);
44
72
  }
45
73
 
46
- .sw-date-field-input:disabled {
74
+ ${input}:disabled,
75
+ .sw-date-field-button:disabled {
47
76
  cursor: not-allowed;
48
77
  opacity: var(--sw-disabled-opacity);
49
78
  }
50
79
 
80
+ .sw-date-field-button {
81
+ align-items: center;
82
+ appearance: none;
83
+ background: transparent;
84
+ border: 0;
85
+ border-radius: var(--sw-radius-sm);
86
+ color: var(--sw-color-muted);
87
+ cursor: pointer;
88
+ display: flex;
89
+ inset-block: 0;
90
+ inset-inline-end: 0;
91
+ justify-content: center;
92
+ margin: 0;
93
+ padding: 0;
94
+ position: absolute;
95
+ width: ${square};
96
+ }
97
+
98
+ .sw-date-field-button:hover:not(:disabled),
99
+ .sw-date-field-button[aria-expanded='true'] {
100
+ color: var(--sw-color-text);
101
+ }
102
+
103
+ .sw-date-field-glyph {
104
+ fill: none;
105
+ flex: none;
106
+ height: var(--sw-space-5);
107
+ stroke: currentColor;
108
+ stroke-linecap: round;
109
+ stroke-linejoin: round;
110
+ stroke-width: 1.75;
111
+ width: var(--sw-space-5);
112
+ }
113
+
114
+ [dir='rtl'] .sw-date-field-glyph-directional {
115
+ transform: scaleX(-1);
116
+ }
117
+
51
118
  .sw-date-field-description,
52
119
  .sw-date-field-error {
53
120
  font-family: var(--sw-font-sans);
@@ -56,22 +123,185 @@ export function cssDateFieldClasses() {
56
123
  }
57
124
 
58
125
  .sw-date-field-description { color: var(--sw-color-muted); }
59
- .sw-date-field-error { color: var(--sw-color-danger); }
126
+ .sw-date-field-error { color: var(--sw-color-danger); }`;
127
+ }
128
+ function calendarRules() {
129
+ return `.sw-date-field-calendar {
130
+ background: var(--sw-glass-fill);
131
+ border: 1px solid var(--sw-glass-border);
132
+ border-radius: var(--sw-radius-md);
133
+ box-shadow: var(--sw-elevation-md), inset 0 1px 0 var(--sw-glass-specular);
134
+ backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
135
+ -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
136
+ box-sizing: border-box;
137
+ color: var(--sw-color-text);
138
+ display: flex;
139
+ flex-direction: column;
140
+ font-family: var(--sw-font-sans);
141
+ gap: var(--sw-space-2);
142
+ height: auto;
143
+ inset: auto;
144
+ margin: 0;
145
+ max-width: calc(100vw - var(--sw-space-2) - var(--sw-space-2));
146
+ overflow: visible;
147
+ padding: var(--sw-space-3);
148
+ position: fixed;
149
+ width: max-content;
150
+ ${zIndex('popup')}
151
+ }
152
+
153
+ .sw-date-field-header {
154
+ align-items: center;
155
+ display: flex;
156
+ gap: var(--sw-space-1);
157
+ justify-content: space-between;
158
+ }
159
+
160
+ .sw-date-field-jump {
161
+ display: flex;
162
+ gap: var(--sw-space-1);
163
+ min-width: 0;
164
+ }
165
+
166
+ .sw-date-field-step {
167
+ align-items: center;
168
+ appearance: none;
169
+ background: transparent;
170
+ border: 0;
171
+ border-radius: var(--sw-radius-pill);
172
+ color: var(--sw-color-text);
173
+ cursor: pointer;
174
+ display: flex;
175
+ flex: none;
176
+ height: ${square};
177
+ justify-content: center;
178
+ margin: 0;
179
+ padding: 0;
180
+ width: ${square};
181
+ }
182
+
183
+ .sw-date-field-step[aria-disabled='true'] {
184
+ cursor: not-allowed;
185
+ opacity: var(--sw-disabled-opacity);
186
+ }
187
+
188
+ .sw-date-field-grid {
189
+ border-collapse: separate;
190
+ border-spacing: 0;
191
+ table-layout: fixed;
192
+ }
193
+
194
+ .sw-date-field-grid th {
195
+ color: var(--sw-color-muted);
196
+ font-size: ${caption.fontSize}px;
197
+ font-weight: normal;
198
+ line-height: ${caption.lineHeight}px;
199
+ padding: 0 0 var(--sw-space-1);
200
+ text-align: center;
201
+ }
60
202
 
61
- @media (prefers-reduced-transparency: reduce) {
62
- .sw-date-field-input { background: var(--sw-color-surface); }
203
+ .sw-date-field-day {
204
+ border: 1px solid transparent;
205
+ border-radius: var(--sw-radius-pill);
206
+ box-sizing: border-box;
207
+ cursor: pointer;
208
+ font-size: ${label.fontSize}px;
209
+ font-variant-numeric: tabular-nums;
210
+ height: ${square};
211
+ padding: 0;
212
+ text-align: center;
213
+ vertical-align: middle;
214
+ width: ${square};
215
+ }
216
+
217
+ .sw-date-field-day-outside {
218
+ color: var(--sw-color-muted);
219
+ }
220
+
221
+ .sw-date-field-step:hover:not([aria-disabled='true']),
222
+ .sw-date-field-day:hover:not([aria-disabled='true']) {
223
+ background: color-mix(in srgb, var(--sw-color-muted) 16%, transparent);
224
+ }
225
+
226
+ .sw-date-field-day[aria-current='date'] {
227
+ border-color: var(--sw-color-accent);
228
+ color: var(--sw-color-accent);
229
+ font-weight: ${label.fontWeight};
230
+ }
231
+
232
+ .sw-date-field-day[aria-selected='true'],
233
+ .sw-date-field-day[aria-selected='true']:hover {
234
+ background: var(--sw-color-accent);
235
+ border-color: var(--sw-color-accent);
236
+ color: var(--sw-color-onAccent);
237
+ font-weight: ${label.fontWeight};
238
+ }
239
+
240
+ .sw-date-field-day[aria-disabled='true'] {
241
+ cursor: not-allowed;
242
+ opacity: var(--sw-disabled-opacity);
243
+ }
244
+
245
+ .sw-date-field-button:focus,
246
+ .sw-date-field-step:focus,
247
+ .sw-date-field-day:focus {
248
+ outline: none;
249
+ }
250
+
251
+ .sw-date-field-button:focus-visible,
252
+ .sw-date-field-step:focus-visible,
253
+ .sw-date-field-day:focus-visible {
254
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
255
+ outline-offset: var(--sw-focus-ring-offset);
256
+ }
257
+
258
+ .sw-date-field-footer {
259
+ display: flex;
260
+ gap: var(--sw-space-1);
261
+ justify-content: flex-end;
262
+ }`;
263
+ }
264
+ function adaptiveRules() {
265
+ return `@media (prefers-reduced-transparency: reduce) {
266
+ ${input} { background: var(--sw-color-surface); }
63
267
  }
64
268
 
65
269
  @media (forced-colors: active) {
66
- .sw-date-field-input[aria-invalid='true'] { border-color: CanvasText; }
270
+ ${input}[aria-invalid='true'] { border-color: CanvasText; }
271
+ .sw-date-field-calendar { border-color: CanvasText; }
272
+ .sw-date-field-day[aria-current='date'] { border-color: CanvasText; }
273
+ .sw-date-field-day[aria-selected='true'] {
274
+ background: Highlight;
275
+ color: HighlightText;
276
+ forced-color-adjust: none;
277
+ }
67
278
  }`;
68
279
  }
280
+ export function cssDateFieldClasses() {
281
+ return `${fieldRules()}
282
+
283
+ ${calendarRules()}
284
+
285
+ ${adaptiveRules()}`;
286
+ }
69
287
  export function dateFieldClassCatalog() {
70
288
  return [
71
289
  'sw-date-field',
72
290
  'sw-date-field-label',
291
+ 'sw-date-field-control',
73
292
  'sw-date-field-input',
293
+ 'sw-date-field-button',
294
+ 'sw-date-field-glyph',
295
+ 'sw-date-field-glyph-directional',
74
296
  'sw-date-field-description',
75
297
  'sw-date-field-error',
298
+ 'sw-date-field-calendar',
299
+ 'sw-date-field-header',
300
+ 'sw-date-field-jump',
301
+ 'sw-date-field-step',
302
+ 'sw-date-field-grid',
303
+ 'sw-date-field-day',
304
+ 'sw-date-field-day-outside',
305
+ 'sw-date-field-footer',
76
306
  ];
77
307
  }
@@ -2,6 +2,7 @@ import { typographyVariants } from '@scalewing/tokens';
2
2
  import { breakpointQuery } from './breakpoints.js';
3
3
  import { badgeTones } from './css-data.js';
4
4
  import { scrollRegionRules } from './css-scroll-region.js';
5
+ import { zIndex } from './stacking.js';
5
6
  const caption = typographyVariants.caption;
6
7
  const data = typographyVariants.data;
7
8
  const label = typographyVariants.label;
@@ -81,7 +82,7 @@ ${scrollRegionRules('.sw-denomination-scroll')}
81
82
  -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
82
83
  inset-inline-start: 0;
83
84
  position: sticky;
84
- z-index: 1;
85
+ ${zIndex('stickyCell')}
85
86
  }
86
87
 
87
88
  /*
@@ -1 +1,3 @@
1
+ /** The frame every native text control draws: fill, hairline border, radius. */
2
+ export declare const controlSurface = "background-color: var(--sw-glass-fill);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-radius-sm);\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n padding-inline: var(--sw-control-md-padding-inline);";
1
3
  export declare function cssDocumentCanvas(): string;
@@ -10,7 +10,8 @@ const typedInputs = [
10
10
  'input:not([type])',
11
11
  'textarea',
12
12
  ].join(', ');
13
- const controlSurface = `background-color: var(--sw-glass-fill);
13
+ /** The frame every native text control draws: fill, hairline border, radius. */
14
+ export const controlSurface = `background-color: var(--sw-glass-fill);
14
15
  border: 1px solid var(--sw-color-border);
15
16
  border-radius: var(--sw-radius-sm);
16
17
  color: inherit;
@@ -1,5 +1,58 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { controlSurface } from './css-document.js';
2
3
  const caption = typographyVariants.caption;
4
+ /*
5
+ * A prefix or suffix sits inside the control frame: the wrapper draws the
6
+ * canvas control surface and the focus ring, and the native input inside it
7
+ * drops its own frame so the text and the value read as one control.
8
+ */
9
+ function adornmentRules() {
10
+ return `.sw-field-adorned {
11
+ ${controlSurface}
12
+ align-items: center;
13
+ box-sizing: border-box;
14
+ cursor: text;
15
+ display: flex;
16
+ gap: var(--sw-space-1);
17
+ min-height: var(--sw-control-md-min-height);
18
+ }
19
+
20
+ [data-theme] .sw-field-adorned > input {
21
+ align-self: stretch;
22
+ background: transparent;
23
+ border: 0;
24
+ border-radius: 0;
25
+ flex: 1 1 auto;
26
+ min-height: 0;
27
+ min-width: 0;
28
+ padding-inline: 0;
29
+ }
30
+
31
+ [data-theme] .sw-field-adorned > input:focus-visible { outline: none; }
32
+
33
+ .sw-field-adorned:focus-within {
34
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
35
+ outline-offset: var(--sw-focus-ring-offset);
36
+ }
37
+
38
+ .sw-field-prefix,
39
+ .sw-field-suffix {
40
+ color: var(--sw-color-muted);
41
+ flex: none;
42
+ user-select: none;
43
+ white-space: nowrap;
44
+ }
45
+
46
+ .sw-field-xs .sw-field-adorned {
47
+ font-size: ${caption.fontSize}px;
48
+ letter-spacing: ${caption.letterSpacing}px;
49
+ line-height: ${caption.lineHeight}px;
50
+ min-height: var(--sw-control-xs-min-height);
51
+ padding-inline: var(--sw-control-xs-padding-inline);
52
+ }
53
+
54
+ .sw-field-invalid .sw-field-adorned { border-color: var(--sw-color-danger); }`;
55
+ }
3
56
  export function cssFieldClasses() {
4
57
  return `.sw-field-required { color: var(--sw-color-danger); }
5
58
 
@@ -17,8 +70,12 @@ export function cssFieldClasses() {
17
70
  border-color: var(--sw-color-danger);
18
71
  }
19
72
 
73
+ ${adornmentRules()}
74
+
20
75
  @media (forced-colors: active) {
21
- [data-theme] .sw-field-invalid :is(input, select, textarea) { border-color: Mark; }
76
+ [data-theme] .sw-field-invalid :is(input, select, textarea),
77
+ .sw-field-invalid .sw-field-adorned { border-color: Mark; }
78
+ .sw-field-prefix, .sw-field-suffix { color: CanvasText; }
22
79
  .sw-field-error, .sw-field-required { color: Mark; }
23
80
  }`;
24
81
  }
@@ -29,5 +86,8 @@ export function fieldClassCatalog() {
29
86
  'sw-field-description',
30
87
  'sw-field-error',
31
88
  'sw-field-invalid',
89
+ 'sw-field-adorned',
90
+ 'sw-field-prefix',
91
+ 'sw-field-suffix',
32
92
  ];
33
93
  }
@@ -1,4 +1,5 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { zIndex } from './stacking.js';
2
3
  const caption = typographyVariants.caption;
3
4
  const label = typographyVariants.label;
4
5
  const chevronImage = `background-image:
@@ -88,7 +89,7 @@ export function cssSelectClasses() {
88
89
  padding: var(--sw-space-1);
89
90
  position: absolute;
90
91
  width: max-content;
91
- z-index: 3;
92
+ ${zIndex('popup')}
92
93
  }
93
94
 
94
95
  .sw-select-option {
@@ -1,4 +1,5 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { zIndex } from './stacking.js';
2
3
  const caption = typographyVariants.caption;
3
4
  export function cssTooltipClasses() {
4
5
  return `.sw-tooltip-anchor {
@@ -21,7 +22,7 @@ export function cssTooltipClasses() {
21
22
  padding: var(--sw-space-2);
22
23
  position: absolute;
23
24
  width: max-content;
24
- z-index: 10;
25
+ ${zIndex('popup')}
25
26
  }
26
27
 
27
28
  .sw-tooltip[hidden] { display: none; }
@@ -0,0 +1,10 @@
1
+ export declare const stackingOrder: {
2
+ readonly stickyCell: 1;
3
+ readonly bottomChrome: 2;
4
+ readonly popupHost: 3;
5
+ readonly topChrome: 4;
6
+ readonly popup: 10;
7
+ };
8
+ export type StackingLayer = keyof typeof stackingOrder;
9
+ export declare function zIndex(layer: StackingLayer): string;
10
+ export declare function cssPopupHostRules(): string;
@@ -0,0 +1,47 @@
1
+ /*
2
+ * The one stacking order of the generated stylesheet. Every generated
3
+ * `z-index` reads a layer from here, lowest first:
4
+ *
5
+ * 1. `stickyCell`: a sticky table header cell or row label over the cells
6
+ * that scroll under it.
7
+ * 2. `bottomChrome`: the sticky ActionBar over the page flow it follows.
8
+ * 3. `popupHost`: a glass surface (glass Card, Accordion) while it holds an
9
+ * open popup. Backdrop blur makes that surface its own stacking context, so
10
+ * its popup can only paint as high as the surface does; lifting the surface
11
+ * over the ActionBar keeps an open list tappable where the bar would cover
12
+ * it, and over the next surface in the flow.
13
+ * 4. `topChrome`: the sticky AppHeader, over a lifted surface that scrolls up
14
+ * under it (the header is the page's way back, and a popup opens downward).
15
+ * 5. `popup`: a popup outside any lifted surface (Select list, ActionMenu
16
+ * list, Tooltip, DateField calendar) over everything in the page.
17
+ *
18
+ * Dialog and Toast sit on the browser's top layer, above all of these.
19
+ */
20
+ export const stackingOrder = {
21
+ stickyCell: 1,
22
+ bottomChrome: 2,
23
+ popupHost: 3,
24
+ topChrome: 4,
25
+ popup: 10,
26
+ };
27
+ export function zIndex(layer) {
28
+ return `z-index: ${stackingOrder[layer]};`;
29
+ }
30
+ const openPopups = [
31
+ '.sw-select-list',
32
+ '.sw-action-menu-list:not([hidden])',
33
+ '.sw-tooltip:not([hidden])',
34
+ // Rendered only while open; on the top layer where the popover API exists.
35
+ '.sw-date-field-calendar',
36
+ ].join(', ');
37
+ const popupHosts = ['.sw-card-glass', '.sw-accordion'];
38
+ export function cssPopupHostRules() {
39
+ const selectors = popupHosts
40
+ .map((host) => `${host}:has(${openPopups})`)
41
+ .join(',\n');
42
+ return `/* Backdrop blur makes a glass surface its own stacking context, so it lifts while it holds an open popup (see stacking.ts). */
43
+ ${selectors} {
44
+ position: relative;
45
+ ${zIndex('popupHost')}
46
+ }`;
47
+ }
@@ -1,4 +1,5 @@
1
1
  import { cardVariants, darkTheme, lightTheme, typographyVariants, } from '@scalewing/tokens';
2
+ import { actionBarClassCatalog } from './css-action-bar.js';
2
3
  import { actionMenuClassCatalog } from './css-action-menu.js';
3
4
  import { accordionClassCatalog } from './css-accordion.js';
4
5
  import { switchClassCatalog } from './css-switch.js';
@@ -17,6 +18,7 @@ import { gridClassCatalog } from './css-grid.js';
17
18
  import { gridSpanClassCatalog } from './css-grid-span.js';
18
19
  import { denominationGridClassCatalog } from './css-denomination-grid.js';
19
20
  import { fieldClassCatalog } from './css-field.js';
21
+ import { boxBorderClassCatalog } from './css-box-border.js';
20
22
  import { cssComponentClasses } from './css-components.js';
21
23
  import { buttonClassCatalog } from './css-button.js';
22
24
  import { chartClassCatalog } from './css-chart.js';
@@ -52,6 +54,7 @@ export function utilityClassCatalog() {
52
54
  ...accordionClassCatalog(),
53
55
  ...selectClassCatalog(),
54
56
  ...actionMenuClassCatalog(),
57
+ ...actionBarClassCatalog(),
55
58
  ...switchClassCatalog(),
56
59
  ...dateFieldClassCatalog(),
57
60
  ...checkboxClassCatalog(),
@@ -70,6 +73,7 @@ export function utilityClassCatalog() {
70
73
  ...splitClassCatalog(),
71
74
  ...toastClassCatalog(),
72
75
  ...responsiveClassCatalog(),
76
+ ...boxBorderClassCatalog(),
73
77
  ];
74
78
  }
75
79
  export { generatePaletteStylesheet, paletteStylesheetIds, } from './css-palettes.js';
@@ -0,0 +1,10 @@
1
+ import { type DateFieldLabels } from './date-field-labels.js';
2
+ /**
3
+ * The message a date entry reports through `setCustomValidity`, so a form
4
+ * will not submit while it shows something other than an allowed date, as
5
+ * a native date input's `badInput`, `rangeUnderflow` and `rangeOverflow`
6
+ * would block it. `parsed` is the date the text forms, '' when blank, or
7
+ * null when the text is not a date. Empty means valid; `required` covers a
8
+ * blank entry.
9
+ */
10
+ export declare function dateEntryValidity(parsed: string | null, labels: Pick<DateFieldLabels, 'invalidEntry' | 'outOfRange'>, min?: string, max?: string): string;
@@ -0,0 +1,16 @@
1
+ import { isOutsideDateRange } from './date-only.js';
2
+ /**
3
+ * The message a date entry reports through `setCustomValidity`, so a form
4
+ * will not submit while it shows something other than an allowed date, as
5
+ * a native date input's `badInput`, `rangeUnderflow` and `rangeOverflow`
6
+ * would block it. `parsed` is the date the text forms, '' when blank, or
7
+ * null when the text is not a date. Empty means valid; `required` covers a
8
+ * blank entry.
9
+ */
10
+ export function dateEntryValidity(parsed, labels, min, max) {
11
+ if (parsed === null)
12
+ return labels.invalidEntry;
13
+ if (isOutsideDateRange(parsed, min, max))
14
+ return labels.outOfRange;
15
+ return '';
16
+ }
@@ -0,0 +1,32 @@
1
+ export type DateEntryPart = 'day' | 'month' | 'year';
2
+ /** How a locale writes a numeric date: field order and the separator. */
3
+ export type DateEntryPattern = {
4
+ order: readonly [DateEntryPart, DateEntryPart, DateEntryPart];
5
+ separator: string;
6
+ };
7
+ /**
8
+ * Reads the numeric field order and separator from `Intl` for `locale`, for
9
+ * example month/day/year with "/" for en-US and day.month.year for de.
10
+ */
11
+ export declare function dateEntryPattern(locale: string): DateEntryPattern;
12
+ /** Zero-padded, Latin digits in the pattern's order; empty for no date. */
13
+ export declare function formatDateEntry(value: string, pattern: DateEntryPattern): string;
14
+ /** A hint such as "MM/DD/YYYY" from the consumer's per-part letters. */
15
+ export declare function dateEntryPlaceholder(pattern: DateEntryPattern, letters: Record<DateEntryPart, string>): string;
16
+ /**
17
+ * Whether typed text is finished, so a date it forms can be committed while
18
+ * the person is still typing: eight bare digits, or three fields whose last
19
+ * one is at full width (four year digits, two for a trailing day or month).
20
+ * A shorter last field may still grow, as "2024-03-1" on the way to
21
+ * "2024-03-10", so it waits for blur or Enter instead.
22
+ */
23
+ export declare function isCompleteDateEntry(text: string, pattern: DateEntryPattern): boolean;
24
+ /**
25
+ * Parses typed text in the pattern's order. Day and month take one or two
26
+ * digits, the year exactly four, and any non-digit run separates fields.
27
+ * Eight bare digits split by field width in the same order (MMDDYYYY for
28
+ * en-US). A four-digit first field reads as ISO year-month-day in every
29
+ * locale, since no locale starts a day or month with four digits. Returns
30
+ * the YYYY-MM-DD value, '' for blank text, or null when it is not a date.
31
+ */
32
+ export declare function parseDateEntry(text: string, pattern: DateEntryPattern): string | null;