@surflane/ui 0.11.0 → 0.12.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/dist/app-shell.d.ts +10 -0
- package/dist/app-shell.js +7 -1
- package/dist/bar-list.d.ts +31 -0
- package/dist/bar-list.js +25 -0
- package/dist/bottom-navigation.d.ts +6 -2
- package/dist/bottom-navigation.js +2 -2
- package/dist/chart.d.ts +56 -3
- package/dist/chart.js +266 -48
- package/dist/css/bar-list.css +9 -0
- package/dist/css/chart.css +2 -0
- package/dist/css/dashboard.css +20 -0
- package/dist/css/rules/alert-dialog.css +4 -0
- package/dist/css/rules/app-shell.css +10 -0
- package/dist/css/rules/bar-list.css +101 -0
- package/dist/css/rules/bottom-navigation.css +16 -0
- package/dist/css/rules/card.css +5 -0
- package/dist/css/rules/chart.css +74 -3
- package/dist/css/rules/dashboard.css +66 -0
- package/dist/css/rules/dialog.css +11 -0
- package/dist/css/rules/filter-bar.css +8 -1
- package/dist/css/rules/panel.css +9 -1
- package/dist/css/rules/select.css +25 -1
- package/dist/css/rules/sizing.css +10 -0
- package/dist/css/rules/stat.css +57 -0
- package/dist/css/rules/time-range-picker.css +14 -0
- package/dist/css/styles.css +3 -0
- package/dist/css/time-range-picker.css +14 -0
- package/dist/dashboard.d.ts +46 -0
- package/dist/dashboard.js +35 -0
- package/dist/icon.d.ts +10 -0
- package/dist/icon.js +11 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +5 -2
- package/dist/select.d.ts +33 -6
- package/dist/select.js +94 -21
- package/dist/stat.d.ts +20 -0
- package/dist/stat.js +15 -1
- package/dist/time-range-picker.d.ts +42 -0
- package/dist/time-range-picker.js +69 -0
- package/package.json +18 -3
|
@@ -117,6 +117,22 @@
|
|
|
117
117
|
font-variant-numeric: tabular-nums;
|
|
118
118
|
text-align: center;
|
|
119
119
|
}
|
|
120
|
+
/* Status fills, each 4.5:1 against the canvas-colored count. */
|
|
121
|
+
.surf-bottom-nav-badge[data-tone='caution'] {
|
|
122
|
+
background: var(--surf-status-caution-fg);
|
|
123
|
+
}
|
|
124
|
+
.surf-bottom-nav-badge[data-tone='success'] {
|
|
125
|
+
background: var(--surf-status-success-fg);
|
|
126
|
+
}
|
|
127
|
+
.surf-bottom-nav-badge[data-tone='info'] {
|
|
128
|
+
background: var(--surf-status-info-fg);
|
|
129
|
+
}
|
|
130
|
+
.surf-bottom-nav-badge[data-tone='progress'] {
|
|
131
|
+
background: var(--surf-status-progress-fg);
|
|
132
|
+
}
|
|
133
|
+
.surf-bottom-nav-badge[data-tone='neutral'] {
|
|
134
|
+
background: var(--surf-status-neutral-fg);
|
|
135
|
+
}
|
|
120
136
|
.surf-bottom-nav-badge[data-dot] {
|
|
121
137
|
inset-block-start: 0;
|
|
122
138
|
min-inline-size: 0;
|
package/dist/css/rules/card.css
CHANGED
|
@@ -7,4 +7,9 @@
|
|
|
7
7
|
padding: var(--surf-surface-padding, var(--surf-ui-card-padding));
|
|
8
8
|
gap: var(--surf-ui-surface-padding);
|
|
9
9
|
}
|
|
10
|
+
:where([data-ui-size], [data-ui-size] *).surf-card-title {
|
|
11
|
+
font-size: var(--surf-ui-title-size);
|
|
12
|
+
line-height: var(--surf-ui-title-leading);
|
|
13
|
+
letter-spacing: var(--surf-ui-title-tracking);
|
|
14
|
+
}
|
|
10
15
|
}
|
package/dist/css/rules/chart.css
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
display: inline-flex;
|
|
55
55
|
align-items: center;
|
|
56
56
|
gap: var(--surf-space-2);
|
|
57
|
-
min-height:
|
|
57
|
+
min-height: var(--surf-control-height);
|
|
58
58
|
padding: var(--surf-space-1) var(--surf-space-2);
|
|
59
59
|
border: 0;
|
|
60
60
|
border-radius: var(--surf-radius-md);
|
|
@@ -66,9 +66,39 @@
|
|
|
66
66
|
cursor: pointer;
|
|
67
67
|
text-align: start;
|
|
68
68
|
}
|
|
69
|
-
.surf-chart-legend-button svg
|
|
69
|
+
.surf-chart-legend-button svg,
|
|
70
|
+
.surf-chart-legend-reference svg {
|
|
70
71
|
flex: none;
|
|
71
72
|
}
|
|
73
|
+
/* A threshold or band: named with its value, not a toggle. */
|
|
74
|
+
.surf-chart-legend-reference {
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: var(--surf-space-2);
|
|
78
|
+
min-height: var(--surf-control-height);
|
|
79
|
+
padding: var(--surf-space-1) var(--surf-space-2);
|
|
80
|
+
color: var(--surf-ink-body);
|
|
81
|
+
font: var(--surf-weight-regular)
|
|
82
|
+
var(--surf-type-body-size)/var(--surf-type-body-leading)
|
|
83
|
+
var(--surf-font-body);
|
|
84
|
+
}
|
|
85
|
+
.surf-chart-legend-more {
|
|
86
|
+
font-variant-numeric: tabular-nums;
|
|
87
|
+
}
|
|
88
|
+
/* The series past legendLimit, one per row in a popover. */
|
|
89
|
+
.surf-chart-legend-list {
|
|
90
|
+
display: grid;
|
|
91
|
+
gap: var(--surf-space-1);
|
|
92
|
+
margin: 0;
|
|
93
|
+
padding: 0;
|
|
94
|
+
list-style: none;
|
|
95
|
+
}
|
|
96
|
+
.surf-chart-legend-list .surf-chart-legend-button {
|
|
97
|
+
inline-size: 100%;
|
|
98
|
+
}
|
|
99
|
+
.surf-chart-key[data-position='bottom'] .surf-chart-legend {
|
|
100
|
+
margin-inline-start: 0;
|
|
101
|
+
}
|
|
72
102
|
.surf-chart-legend-button span {
|
|
73
103
|
overflow-wrap: anywhere;
|
|
74
104
|
}
|
|
@@ -166,6 +196,10 @@
|
|
|
166
196
|
inline-size: var(--surf-space-6);
|
|
167
197
|
block-size: var(--surf-space-4);
|
|
168
198
|
}
|
|
199
|
+
.surf-chart-tooltip-total {
|
|
200
|
+
padding-block-start: var(--surf-space-1);
|
|
201
|
+
border-block-start: 1px solid var(--surf-line);
|
|
202
|
+
}
|
|
169
203
|
.surf-chart-tooltip-value {
|
|
170
204
|
white-space: nowrap;
|
|
171
205
|
margin-inline-start: auto;
|
|
@@ -231,8 +265,39 @@
|
|
|
231
265
|
.surf-chart-table .surf-scroll-viewport {
|
|
232
266
|
max-block-size: 320px;
|
|
233
267
|
}
|
|
268
|
+
/* Compact, for charts in dashboard cards: the plot, its legend and the floating
|
|
269
|
+
values show; the caption, keyboard help, readout and x-axis name stay for
|
|
270
|
+
assistive technology. */
|
|
271
|
+
.surf-chart[data-density='compact'] {
|
|
272
|
+
gap: var(--surf-space-2);
|
|
273
|
+
}
|
|
274
|
+
.surf-chart[data-density='compact']
|
|
275
|
+
:is(
|
|
276
|
+
.surf-chart-caption,
|
|
277
|
+
.surf-chart-help,
|
|
278
|
+
.surf-chart-inspector,
|
|
279
|
+
.surf-chart-x-label
|
|
280
|
+
) {
|
|
281
|
+
position: absolute;
|
|
282
|
+
inline-size: 1px;
|
|
283
|
+
block-size: 1px;
|
|
284
|
+
min-height: 0;
|
|
285
|
+
margin: 0;
|
|
286
|
+
padding: 0;
|
|
287
|
+
overflow: hidden;
|
|
288
|
+
clip-path: inset(50%);
|
|
289
|
+
white-space: nowrap;
|
|
290
|
+
}
|
|
291
|
+
.surf-chart[data-density='compact']
|
|
292
|
+
:is(.surf-chart-legend-button, .surf-chart-legend-reference) {
|
|
293
|
+
min-height: var(--surf-space-6);
|
|
294
|
+
padding-block: 0;
|
|
295
|
+
font-size: var(--surf-type-compact-size);
|
|
296
|
+
line-height: var(--surf-type-compact-leading);
|
|
297
|
+
}
|
|
234
298
|
@media (max-width: 760px), (pointer: coarse) {
|
|
235
|
-
.surf-chart-legend-button
|
|
299
|
+
.surf-chart-legend-button,
|
|
300
|
+
.surf-chart[data-density='compact'] .surf-chart-legend-button {
|
|
236
301
|
min-height: var(--surf-control-target-size);
|
|
237
302
|
}
|
|
238
303
|
}
|
|
@@ -251,6 +316,12 @@
|
|
|
251
316
|
--surf-chart-category-4: CanvasText;
|
|
252
317
|
--surf-chart-category-5: CanvasText;
|
|
253
318
|
--surf-chart-category-6: CanvasText;
|
|
319
|
+
--surf-chart-category-7: CanvasText;
|
|
320
|
+
--surf-chart-category-8: CanvasText;
|
|
321
|
+
--surf-chart-category-9: CanvasText;
|
|
322
|
+
--surf-chart-category-10: CanvasText;
|
|
323
|
+
--surf-chart-category-11: CanvasText;
|
|
324
|
+
--surf-chart-category-12: CanvasText;
|
|
254
325
|
}
|
|
255
326
|
.surf-chart-plot .recharts-surface,
|
|
256
327
|
.surf-chart-mark {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* Dashboard, with the styles of what it renders. */
|
|
2
|
+
@layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
|
|
3
|
+
surflane.sizes;
|
|
4
|
+
|
|
5
|
+
@layer surflane.components {
|
|
6
|
+
/* As many columns as fit a panel's minimum width; one on a phone. */
|
|
7
|
+
.surf-dashboard {
|
|
8
|
+
--surf-dashboard-panel-min-width: 20rem;
|
|
9
|
+
display: grid;
|
|
10
|
+
grid-template-columns: repeat(
|
|
11
|
+
auto-fill,
|
|
12
|
+
minmax(min(100%, var(--surf-dashboard-panel-min-width)), 1fr)
|
|
13
|
+
);
|
|
14
|
+
gap: var(--surf-space-4);
|
|
15
|
+
min-inline-size: 0;
|
|
16
|
+
}
|
|
17
|
+
/* The header keeps its height; the content takes the rest of a stretched row. */
|
|
18
|
+
.surf-dashboard-panel {
|
|
19
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
20
|
+
align-content: start;
|
|
21
|
+
min-inline-size: 0;
|
|
22
|
+
}
|
|
23
|
+
.surf-dashboard-panel[data-span='2'] {
|
|
24
|
+
grid-column: span 2;
|
|
25
|
+
}
|
|
26
|
+
.surf-dashboard-panel[data-span='3'] {
|
|
27
|
+
grid-column: span 3;
|
|
28
|
+
}
|
|
29
|
+
.surf-dashboard-panel[data-span='full'] {
|
|
30
|
+
grid-column: 1 / -1;
|
|
31
|
+
}
|
|
32
|
+
/* As tall as an icon button, with or without one, so titles line up across a row. */
|
|
33
|
+
.surf-dashboard-panel-header {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: var(--surf-space-2);
|
|
37
|
+
min-inline-size: 0;
|
|
38
|
+
min-block-size: var(--surf-control-height);
|
|
39
|
+
}
|
|
40
|
+
.surf-dashboard-panel-header .surf-card-title {
|
|
41
|
+
min-inline-size: 0;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
text-overflow: ellipsis;
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
}
|
|
46
|
+
.surf-dashboard-panel-actions {
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
gap: var(--surf-space-1);
|
|
50
|
+
margin-inline-start: auto;
|
|
51
|
+
}
|
|
52
|
+
.surf-dashboard-panel-body {
|
|
53
|
+
min-inline-size: 0;
|
|
54
|
+
min-block-size: 0;
|
|
55
|
+
}
|
|
56
|
+
.surf-dashboard-panel-empty {
|
|
57
|
+
margin: 0;
|
|
58
|
+
color: var(--surf-ink-muted);
|
|
59
|
+
}
|
|
60
|
+
/* A phone shows one panel a row, whatever it spans. */
|
|
61
|
+
@media (max-width: 760px) {
|
|
62
|
+
.surf-dashboard-panel[data-span] {
|
|
63
|
+
grid-column: auto;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -158,9 +158,20 @@
|
|
|
158
158
|
) {
|
|
159
159
|
padding-inline: var(--surf-ui-card-padding);
|
|
160
160
|
}
|
|
161
|
+
/* Head and footer follow the size's card padding: 24/20px and 20px by default. */
|
|
161
162
|
:where([data-ui-size], [data-ui-size] *).surf-dialog-header {
|
|
163
|
+
padding-block: var(--surf-ui-card-padding)
|
|
164
|
+
calc(var(--surf-ui-card-padding) - var(--surf-space-1));
|
|
162
165
|
padding-inline-end: calc(
|
|
163
166
|
var(--surf-control-height) + var(--surf-space-4) * 2
|
|
164
167
|
);
|
|
165
168
|
}
|
|
169
|
+
:where([data-ui-size], [data-ui-size] *).surf-dialog-footer {
|
|
170
|
+
padding-block: calc(var(--surf-ui-card-padding) - var(--surf-space-1));
|
|
171
|
+
}
|
|
172
|
+
:where([data-ui-size], [data-ui-size] *).surf-dialog-header h2 {
|
|
173
|
+
font-size: var(--surf-ui-title-size);
|
|
174
|
+
line-height: var(--surf-ui-title-leading);
|
|
175
|
+
letter-spacing: var(--surf-ui-title-tracking);
|
|
176
|
+
}
|
|
166
177
|
}
|
|
@@ -15,16 +15,23 @@
|
|
|
15
15
|
gap: var(--surf-space-4);
|
|
16
16
|
min-inline-size: 0;
|
|
17
17
|
}
|
|
18
|
+
/* A filter keeps its width; only the search field takes the room left in the row. */
|
|
18
19
|
.surf-filter-bar-field {
|
|
19
20
|
display: grid;
|
|
20
21
|
gap: var(--surf-space-2);
|
|
21
|
-
flex:
|
|
22
|
+
flex: 0 1 12rem;
|
|
22
23
|
min-inline-size: 0;
|
|
23
24
|
max-inline-size: 100%;
|
|
24
25
|
}
|
|
25
26
|
.surf-filter-bar-field[data-width='search'] {
|
|
26
27
|
flex: 2 1 20rem;
|
|
27
28
|
}
|
|
29
|
+
/* On a phone each field fills its row. */
|
|
30
|
+
@media (max-width: 760px) {
|
|
31
|
+
.surf-filter-bar-field {
|
|
32
|
+
flex-grow: 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
28
35
|
.surf-filter-bar-field > :is(.surf-control, .surf-select-trigger) {
|
|
29
36
|
inline-size: 100%;
|
|
30
37
|
min-inline-size: 0;
|
package/dist/css/rules/panel.css
CHANGED
|
@@ -217,10 +217,18 @@
|
|
|
217
217
|
) {
|
|
218
218
|
padding-inline: var(--surf-ui-card-padding);
|
|
219
219
|
}
|
|
220
|
-
/* The title keeps clear of the close button: its 16px inset, its width and a 16px gap.
|
|
220
|
+
/* The title keeps clear of the close button: its 16px inset, its width and a 16px gap.
|
|
221
|
+
* Above and below, the head follows the size's card padding: 24/20px by default. */
|
|
221
222
|
:where([data-ui-size], [data-ui-size] *).surf-panel-header {
|
|
223
|
+
padding-block: var(--surf-ui-card-padding)
|
|
224
|
+
calc(var(--surf-ui-card-padding) - var(--surf-space-1));
|
|
222
225
|
padding-inline-end: calc(
|
|
223
226
|
var(--surf-control-height) + var(--surf-space-4) * 2
|
|
224
227
|
);
|
|
225
228
|
}
|
|
229
|
+
:where([data-ui-size], [data-ui-size] *).surf-panel-title {
|
|
230
|
+
font-size: var(--surf-ui-title-size);
|
|
231
|
+
line-height: var(--surf-ui-title-leading);
|
|
232
|
+
letter-spacing: var(--surf-ui-title-tracking);
|
|
233
|
+
}
|
|
226
234
|
}
|
|
@@ -11,11 +11,35 @@
|
|
|
11
11
|
text-align: start;
|
|
12
12
|
cursor: pointer;
|
|
13
13
|
}
|
|
14
|
-
.surf-select-trigger > span:first-child
|
|
14
|
+
.surf-select-trigger > span:first-child,
|
|
15
|
+
.surf-select-value {
|
|
15
16
|
overflow: hidden;
|
|
16
17
|
text-overflow: ellipsis;
|
|
17
18
|
white-space: nowrap;
|
|
18
19
|
}
|
|
20
|
+
/* Content width: the trigger fits its longest option and keeps that width. */
|
|
21
|
+
.surf-select[data-width='content'],
|
|
22
|
+
.surf-select[data-width='content'] .surf-select-trigger {
|
|
23
|
+
width: auto;
|
|
24
|
+
}
|
|
25
|
+
.surf-select-sizer {
|
|
26
|
+
display: grid;
|
|
27
|
+
min-width: 0;
|
|
28
|
+
}
|
|
29
|
+
.surf-select-sizer > * {
|
|
30
|
+
grid-area: 1 / 1;
|
|
31
|
+
overflow: hidden;
|
|
32
|
+
text-overflow: ellipsis;
|
|
33
|
+
white-space: nowrap;
|
|
34
|
+
}
|
|
35
|
+
.surf-select-sizer > [aria-hidden='true'] {
|
|
36
|
+
visibility: hidden;
|
|
37
|
+
}
|
|
38
|
+
.surf-select-separator {
|
|
39
|
+
block-size: 1px;
|
|
40
|
+
margin-block: var(--surf-space-1);
|
|
41
|
+
background: var(--surf-line);
|
|
42
|
+
}
|
|
19
43
|
.surf-select-icon {
|
|
20
44
|
display: flex;
|
|
21
45
|
align-items: center;
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
--surf-ui-slider-track: var(--surf-size-slider-track-small);
|
|
12
12
|
--surf-ui-file-padding: var(--surf-size-file-padding-small);
|
|
13
13
|
--surf-ui-content-gap: var(--surf-size-content-gap-small);
|
|
14
|
+
/* Surface titles (Card, Sheet, Dialog) step down one role at Small. */
|
|
15
|
+
--surf-ui-title-size: var(--surf-type-subsection-size);
|
|
16
|
+
--surf-ui-title-leading: var(--surf-type-subsection-leading);
|
|
17
|
+
--surf-ui-title-tracking: var(--surf-type-subsection-tracking);
|
|
14
18
|
|
|
15
19
|
--surf-ui-control-height: var(--surf-size-control-height-small);
|
|
16
20
|
--surf-ui-control-padding: var(--surf-size-control-padding-small);
|
|
@@ -51,6 +55,9 @@
|
|
|
51
55
|
--surf-ui-slider-track: var(--surf-size-slider-track-default);
|
|
52
56
|
--surf-ui-file-padding: var(--surf-size-file-padding-default);
|
|
53
57
|
--surf-ui-content-gap: var(--surf-size-content-gap-default);
|
|
58
|
+
--surf-ui-title-size: var(--surf-type-section-size);
|
|
59
|
+
--surf-ui-title-leading: var(--surf-type-section-leading);
|
|
60
|
+
--surf-ui-title-tracking: var(--surf-type-section-tracking);
|
|
54
61
|
|
|
55
62
|
--surf-ui-control-height: var(--surf-size-control-height-default);
|
|
56
63
|
--surf-ui-control-padding: var(--surf-size-control-padding-default);
|
|
@@ -91,6 +98,9 @@
|
|
|
91
98
|
--surf-ui-slider-track: var(--surf-size-slider-track-large);
|
|
92
99
|
--surf-ui-file-padding: var(--surf-size-file-padding-large);
|
|
93
100
|
--surf-ui-content-gap: var(--surf-size-content-gap-large);
|
|
101
|
+
--surf-ui-title-size: var(--surf-type-section-size);
|
|
102
|
+
--surf-ui-title-leading: var(--surf-type-section-leading);
|
|
103
|
+
--surf-ui-title-tracking: var(--surf-type-section-tracking);
|
|
94
104
|
|
|
95
105
|
--surf-ui-control-height: var(--surf-size-control-height-large);
|
|
96
106
|
--surf-ui-control-padding: var(--surf-size-control-padding-large);
|
package/dist/css/rules/stat.css
CHANGED
|
@@ -49,6 +49,35 @@
|
|
|
49
49
|
.surf-stat-change[data-tone='down'] {
|
|
50
50
|
color: var(--surf-trend-down-fg);
|
|
51
51
|
}
|
|
52
|
+
/* The value's state: the tone's icon and text, on the value's line. */
|
|
53
|
+
.surf-stat-status {
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: var(--surf-space-1);
|
|
57
|
+
color: var(--surf-status-neutral-fg);
|
|
58
|
+
font-size: var(--surf-type-label-size);
|
|
59
|
+
line-height: var(--surf-type-label-leading);
|
|
60
|
+
letter-spacing: var(--surf-type-label-tracking);
|
|
61
|
+
font-weight: var(--surf-weight-medium);
|
|
62
|
+
}
|
|
63
|
+
.surf-stat-status[data-tone='info'] {
|
|
64
|
+
color: var(--surf-status-info-fg);
|
|
65
|
+
}
|
|
66
|
+
.surf-stat-status[data-tone='success'] {
|
|
67
|
+
color: var(--surf-status-success-fg);
|
|
68
|
+
}
|
|
69
|
+
.surf-stat-status[data-tone='progress'] {
|
|
70
|
+
color: var(--surf-status-progress-fg);
|
|
71
|
+
}
|
|
72
|
+
/* Past a threshold the value takes the status color as well. */
|
|
73
|
+
.surf-stat-status[data-tone='caution'],
|
|
74
|
+
.surf-stat:has(> .surf-stat-status[data-tone='caution']) > .surf-stat-value {
|
|
75
|
+
color: var(--surf-status-caution-fg);
|
|
76
|
+
}
|
|
77
|
+
.surf-stat-status[data-tone='danger'],
|
|
78
|
+
.surf-stat:has(> .surf-stat-status[data-tone='danger']) > .surf-stat-value {
|
|
79
|
+
color: var(--surf-status-danger-fg);
|
|
80
|
+
}
|
|
52
81
|
.surf-stat-caption {
|
|
53
82
|
flex: 1 0 100%;
|
|
54
83
|
color: var(--surf-ink-muted);
|
|
@@ -56,4 +85,32 @@
|
|
|
56
85
|
line-height: var(--surf-type-caption-leading);
|
|
57
86
|
letter-spacing: var(--surf-type-caption-tracking);
|
|
58
87
|
}
|
|
88
|
+
/* A group: the shared label, then each value under its own name, side by side. */
|
|
89
|
+
.surf-stat-group {
|
|
90
|
+
display: grid;
|
|
91
|
+
gap: var(--surf-space-2);
|
|
92
|
+
min-width: 0;
|
|
93
|
+
}
|
|
94
|
+
.surf-stat-group-label {
|
|
95
|
+
color: var(--surf-ink-muted);
|
|
96
|
+
font-size: var(--surf-type-label-size);
|
|
97
|
+
line-height: var(--surf-type-label-leading);
|
|
98
|
+
letter-spacing: var(--surf-type-label-tracking);
|
|
99
|
+
}
|
|
100
|
+
/* Each value takes its own width and wraps to a new line when the row is full. */
|
|
101
|
+
.surf-stat-group-items {
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-wrap: wrap;
|
|
104
|
+
gap: var(--surf-space-3) var(--surf-space-6);
|
|
105
|
+
min-width: 0;
|
|
106
|
+
}
|
|
107
|
+
/* In a group each value stacks its lines, so it is only as wide as the widest. */
|
|
108
|
+
.surf-stat-group-items > .surf-stat {
|
|
109
|
+
display: grid;
|
|
110
|
+
justify-items: start;
|
|
111
|
+
align-content: start;
|
|
112
|
+
}
|
|
113
|
+
.surf-stat-group-items .surf-stat-label {
|
|
114
|
+
color: var(--surf-ink-body);
|
|
115
|
+
}
|
|
59
116
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* Time range picker, with the styles of what it renders. */
|
|
2
|
+
@layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
|
|
3
|
+
surflane.sizes;
|
|
4
|
+
|
|
5
|
+
@layer surflane.components {
|
|
6
|
+
/* The span, the interval and the button sit on one line in a header or toolbar. */
|
|
7
|
+
.surf-time-range {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
flex-wrap: wrap;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: var(--surf-space-2);
|
|
12
|
+
max-inline-size: 100%;
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/css/styles.css
CHANGED
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
@import './rules/app-shell.css';
|
|
63
63
|
@import './rules/tree.css';
|
|
64
64
|
@import './rules/date-picker.css';
|
|
65
|
+
@import './rules/time-range-picker.css';
|
|
65
66
|
@import './rules/chart.css';
|
|
66
67
|
@import '@surflane/css/foundations/layout.css';
|
|
67
68
|
@import './rules/layout.css';
|
|
@@ -73,8 +74,10 @@
|
|
|
73
74
|
@import './rules/list.css';
|
|
74
75
|
@import './rules/stat.css';
|
|
75
76
|
@import './rules/sparkline.css';
|
|
77
|
+
@import './rules/bar-list.css';
|
|
76
78
|
@import '@surflane/css/presentation/inline-message.css';
|
|
77
79
|
@import './rules/inline-message.css';
|
|
80
|
+
@import './rules/dashboard.css';
|
|
78
81
|
@import './rules/syntax-highlighter.css';
|
|
79
82
|
@import '@surflane/css/presentation/code.css';
|
|
80
83
|
@import './rules/code.css';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* Time range picker, with the styles of what it renders. */
|
|
2
|
+
@layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
|
|
3
|
+
surflane.sizes;
|
|
4
|
+
@import '@surflane/design-tokens/tokens.css';
|
|
5
|
+
@import '@surflane/css/presentation/base.css';
|
|
6
|
+
@import './rules/base.css';
|
|
7
|
+
@import './rules/field-control.css';
|
|
8
|
+
@import './rules/sizing.css';
|
|
9
|
+
@import './rules/popup.css';
|
|
10
|
+
@import './rules/item.css';
|
|
11
|
+
@import './rules/icon.css';
|
|
12
|
+
@import './rules/select.css';
|
|
13
|
+
@import './rules/button.css';
|
|
14
|
+
@import './rules/time-range-picker.css';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type DashboardRootProps = React.ComponentPropsWithoutRef<'div'>;
|
|
3
|
+
export type DashboardPanelLabels = {
|
|
4
|
+
/** The button that shows the description, given the title. */
|
|
5
|
+
about: (title: string) => string;
|
|
6
|
+
loading: string;
|
|
7
|
+
refreshing: string;
|
|
8
|
+
error: string;
|
|
9
|
+
retry: string;
|
|
10
|
+
empty: string;
|
|
11
|
+
};
|
|
12
|
+
export type DashboardPanelProps = Omit<React.ComponentPropsWithoutRef<'div'>, 'title'> & {
|
|
13
|
+
title: string;
|
|
14
|
+
/** What the panel shows and how it is measured, behind an info button by the title. */
|
|
15
|
+
description?: React.ReactNode;
|
|
16
|
+
/** The heading level of the title. */
|
|
17
|
+
level?: 2 | 3 | 4 | 5 | 6;
|
|
18
|
+
/** loading shows a placeholder until the first data arrives; error and empty
|
|
19
|
+
* replace the content with a message. */
|
|
20
|
+
state?: 'ready' | 'loading' | 'error' | 'empty';
|
|
21
|
+
/** New data is on its way: the content stays, a spinner shows by the title and the
|
|
22
|
+
* panel is busy for assistive technology. */
|
|
23
|
+
refreshing?: boolean;
|
|
24
|
+
/** Shows a retry button in the error message. */
|
|
25
|
+
onRetry?: () => void;
|
|
26
|
+
/** Why it failed or why it is empty, in place of the default text. */
|
|
27
|
+
message?: React.ReactNode;
|
|
28
|
+
/** Controls at the end of the header, such as a menu. */
|
|
29
|
+
actions?: React.ReactNode;
|
|
30
|
+
/** Columns the panel spans where they fit; a phone shows one. */
|
|
31
|
+
span?: 1 | 2 | 3 | 'full';
|
|
32
|
+
labels?: Partial<DashboardPanelLabels>;
|
|
33
|
+
};
|
|
34
|
+
export declare const Dashboard: {
|
|
35
|
+
Root: ({ className, ref, ...props }: DashboardRootProps & {
|
|
36
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
37
|
+
}) => React.JSX.Element;
|
|
38
|
+
Panel: ({ title, description, level, state, refreshing, onRetry, message, actions, span, labels, className, children, ref, ...props }: DashboardPanelProps & {
|
|
39
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
40
|
+
}) => React.JSX.Element;
|
|
41
|
+
};
|
|
42
|
+
export declare const DashboardRoot: ({ className, ref, ...props }: DashboardRootProps & {
|
|
43
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
44
|
+
}) => React.JSX.Element, DashboardPanel: ({ title, description, level, state, refreshing, onRetry, message, actions, span, labels, className, children, ref, ...props }: DashboardPanelProps & {
|
|
45
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
46
|
+
}) => React.JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Button } from './button.js';
|
|
5
|
+
import { Card } from './card.js';
|
|
6
|
+
import { Icon } from './icon.js';
|
|
7
|
+
import { InlineMessage } from './inline-message.js';
|
|
8
|
+
import { Popover } from './popover.js';
|
|
9
|
+
import { Skeleton } from './skeleton.js';
|
|
10
|
+
import { Spinner } from './spinner.js';
|
|
11
|
+
import { cx } from './class-names.js';
|
|
12
|
+
/** The panels of a dashboard on a responsive grid: as many columns as fit panels of
|
|
13
|
+
* --surf-dashboard-panel-min-width (20rem), one column on a phone. */
|
|
14
|
+
const Root = function DashboardRoot({ className, ref, ...props }) {
|
|
15
|
+
return (_jsx("div", { ...props, ref: ref, className: cx('surf-dashboard', className) }));
|
|
16
|
+
};
|
|
17
|
+
const panelLabels = {
|
|
18
|
+
about: (title) => `About ${title}`,
|
|
19
|
+
loading: 'Loading',
|
|
20
|
+
refreshing: 'Refreshing',
|
|
21
|
+
error: 'This panel could not load.',
|
|
22
|
+
retry: 'Retry',
|
|
23
|
+
empty: 'No data in this range.',
|
|
24
|
+
};
|
|
25
|
+
/** One panel of a dashboard: a card with its title, an optional description, the
|
|
26
|
+
* states of its data and the content. The content takes the room the row gives it. */
|
|
27
|
+
const Panel = function DashboardPanel({ title, description, level = 3, state = 'ready', refreshing = false, onRetry, message, actions, span, labels, className, children, ref, ...props }) {
|
|
28
|
+
const text = { ...panelLabels, ...labels };
|
|
29
|
+
const id = React.useId();
|
|
30
|
+
const busy = state === 'loading' || refreshing;
|
|
31
|
+
return (_jsxs(Card.Root, { role: 'group', "aria-labelledby": `${id}-title`, "aria-busy": busy || undefined, ...props, ref: ref, "data-span": span, "data-state": state, className: cx('surf-dashboard-panel', className), children: [_jsxs("div", { className: 'surf-dashboard-panel-header', children: [_jsx(Card.Title, { level: level, id: `${id}-title`, children: title }), description && (_jsxs(Popover.Root, { children: [_jsx(Popover.Trigger, { render: _jsx(Button, { variant: 'ghost', size: 'icon-small', "aria-label": text.about(title) }), children: _jsx(Icon, { name: 'info' }) }), _jsx(Popover.Content, { size: 'small', children: _jsx(Popover.Description, { children: description }) })] })), refreshing && state !== 'loading' && (_jsx(Spinner, { size: 'small', label: text.refreshing })), actions && (_jsx("div", { className: 'surf-dashboard-panel-actions', children: actions }))] }), _jsx("div", { className: 'surf-dashboard-panel-body', children: state === 'loading' ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { shape: 'block' }), _jsx("span", { className: 'surf-feedback-sr', children: text.loading })] })) : state === 'error' ? (_jsxs(InlineMessage.Root, { tone: 'danger', children: [_jsx(InlineMessage.Icon, { tone: 'danger' }), _jsxs(InlineMessage.Content, { children: [_jsx(InlineMessage.Description, { children: message ?? text.error }), onRetry && (_jsx(InlineMessage.Actions, { children: _jsx(Button, { variant: 'outline', size: 'small', onClick: onRetry, children: text.retry }) }))] })] })) : state === 'empty' ? (_jsx("p", { className: 'surf-dashboard-panel-empty', children: message ?? text.empty })) : (children) })] }));
|
|
32
|
+
};
|
|
33
|
+
export const Dashboard = { Root, Panel };
|
|
34
|
+
// Flat names for React Server Components, which cannot dot into client exports.
|
|
35
|
+
export const { Root: DashboardRoot, Panel: DashboardPanel } = Dashboard;
|
package/dist/icon.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ declare const shapes: {
|
|
|
23
23
|
'arrow-back': React.JSX.Element;
|
|
24
24
|
'arrow-up': React.JSX.Element;
|
|
25
25
|
'arrow-down': React.JSX.Element;
|
|
26
|
+
refresh: React.JSX.Element;
|
|
26
27
|
sort: React.JSX.Element;
|
|
27
28
|
'external-link': React.JSX.Element;
|
|
28
29
|
copy: React.JSX.Element;
|
|
@@ -31,6 +32,15 @@ declare const shapes: {
|
|
|
31
32
|
calendar: React.JSX.Element;
|
|
32
33
|
user: React.JSX.Element;
|
|
33
34
|
settings: React.JSX.Element;
|
|
35
|
+
server: React.JSX.Element;
|
|
36
|
+
database: React.JSX.Element;
|
|
37
|
+
disk: React.JSX.Element;
|
|
38
|
+
chip: React.JSX.Element;
|
|
39
|
+
memory: React.JSX.Element;
|
|
40
|
+
network: React.JSX.Element;
|
|
41
|
+
bolt: React.JSX.Element;
|
|
42
|
+
token: React.JSX.Element;
|
|
43
|
+
thermometer: React.JSX.Element;
|
|
34
44
|
};
|
|
35
45
|
export type IconName = keyof typeof shapes;
|
|
36
46
|
export declare const iconNames: readonly IconName[];
|
package/dist/icon.js
CHANGED
|
@@ -23,6 +23,7 @@ const shapes = {
|
|
|
23
23
|
'arrow-back': _jsx("path", { d: 'M20 12H4m7-7-7 7 7 7' }),
|
|
24
24
|
'arrow-up': _jsx("path", { d: 'M12 19V5m-6 6 6-6 6 6' }),
|
|
25
25
|
'arrow-down': _jsx("path", { d: 'M12 5v14m-6-6 6 6 6-6' }),
|
|
26
|
+
refresh: _jsx("path", { d: 'M20 12a8 8 0 1 1-2.34-5.66M20 4v4h-4' }),
|
|
26
27
|
sort: _jsx("path", { d: 'm8 9 4-4 4 4m-8 6 4 4 4-4' }),
|
|
27
28
|
'external-link': (_jsx(_Fragment, { children: _jsx("path", { d: 'M14 3h7v7m0-7L10 14M10 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-5' }) })),
|
|
28
29
|
copy: (_jsxs(_Fragment, { children: [_jsx("rect", { x: '8', y: '8', width: '12', height: '13', rx: '2' }), _jsx("path", { d: 'M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3' })] })),
|
|
@@ -31,6 +32,16 @@ const shapes = {
|
|
|
31
32
|
calendar: (_jsxs(_Fragment, { children: [_jsx("rect", { x: '3', y: '5', width: '18', height: '16', rx: '3' }), _jsx("path", { d: 'M7 3v4m10-4v4M3 11h18m-12 4h2' })] })),
|
|
32
33
|
user: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: '12', cy: '8', r: '4' }), _jsx("path", { d: 'M4 21v-2a8 8 0 0 1 16 0v2' })] })),
|
|
33
34
|
settings: (_jsx(_Fragment, { children: _jsx("path", { d: 'M4 6h16M4 12h16M4 18h16M9 3v6m6 0v6m-6 0v6' }) })),
|
|
35
|
+
// What a product runs on: machines, storage, links, power and model tokens.
|
|
36
|
+
server: (_jsxs(_Fragment, { children: [_jsx("rect", { x: '3', y: '4', width: '18', height: '7', rx: '2' }), _jsx("rect", { x: '3', y: '13', width: '18', height: '7', rx: '2' }), _jsx("path", { d: 'M7 7.5h3m-3 9h3' })] })),
|
|
37
|
+
database: (_jsxs(_Fragment, { children: [_jsx("ellipse", { cx: '12', cy: '6', rx: '8', ry: '3' }), _jsx("path", { d: 'M4 6v12c0 1.66 3.58 3 8 3s8-1.34 8-3V6M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3' })] })),
|
|
38
|
+
disk: (_jsxs(_Fragment, { children: [_jsx("path", { d: 'M3 14 5.6 5.4A2 2 0 0 1 7.5 4h9a2 2 0 0 1 1.9 1.4L21 14' }), _jsx("rect", { x: '3', y: '14', width: '18', height: '6', rx: '2' }), _jsx("path", { d: 'M7 17h2' })] })),
|
|
39
|
+
chip: (_jsxs(_Fragment, { children: [_jsx("rect", { x: '6', y: '6', width: '12', height: '12', rx: '2' }), _jsx("rect", { x: '9.5', y: '9.5', width: '5', height: '5', rx: '1' }), _jsx("path", { d: 'M9.5 2.5V6m5-3.5V6m-5 12v3.5m5-3.5v3.5M2.5 9.5H6m-3.5 5H6m12-5h3.5M18 14.5h3.5' })] })),
|
|
40
|
+
memory: (_jsxs(_Fragment, { children: [_jsx("rect", { x: '2', y: '7', width: '20', height: '10', rx: '2' }), _jsx("path", { d: 'M6 17v3m4-3v3m4-3v3m4-3v3M7 10.5v3m5-3v3m5-3v3' })] })),
|
|
41
|
+
network: (_jsxs(_Fragment, { children: [_jsx("rect", { x: '9', y: '3', width: '6', height: '5', rx: '1' }), _jsx("rect", { x: '3', y: '16', width: '6', height: '5', rx: '1' }), _jsx("rect", { x: '15', y: '16', width: '6', height: '5', rx: '1' }), _jsx("path", { d: 'M12 8v4M6 16v-4h12v4' })] })),
|
|
42
|
+
bolt: _jsx("path", { d: 'M13 2.5 4.5 13.5H12l-1 8 8.5-11H12l1-8Z' }),
|
|
43
|
+
token: (_jsxs(_Fragment, { children: [_jsx("path", { d: 'm12 3 7.8 4.5v9L12 21l-7.8-4.5v-9L12 3Z' }), _jsx("circle", { cx: '12', cy: '12', r: '3' })] })),
|
|
44
|
+
thermometer: (_jsxs(_Fragment, { children: [_jsx("path", { d: 'M14 14.5V5a2 2 0 0 0-4 0v9.5a4 4 0 1 0 4 0Z' }), _jsx("path", { d: 'M12 9v7.5' })] })),
|
|
34
45
|
};
|
|
35
46
|
export const iconNames = Object.keys(shapes);
|
|
36
47
|
// These point along the reading direction, so they mirror in right-to-left text.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { Button, type ButtonVariant, type ButtonSize, type ButtonProps, LinkButton, type LinkButtonProps, } from './button.js';
|
|
2
2
|
export { Toolbar, ToolbarButton, ToolbarGroup, ToolbarInput, ToolbarLink, ToolbarRoot, ToolbarSeparator, } from './toolbar.js';
|
|
3
3
|
export { Input } from './input.js';
|
|
4
|
-
export { Select, type SelectOption, type SelectGroup, type SelectProps, } from './select.js';
|
|
4
|
+
export { Select, type SelectOption, type SelectGroup, type SelectProps, type SelectMultipleProps, type SelectMultipleLabels, } from './select.js';
|
|
5
5
|
export { NativeSelect, NativeSelectOption, NativeSelectOptGroup, } from './native-select.js';
|
|
6
6
|
export { Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldRoot, FieldValidity, } from './field.js';
|
|
7
7
|
export { Fieldset, FieldsetLegend, FieldsetRoot } from './fieldset.js';
|
|
@@ -45,8 +45,11 @@ export { Table, TableContainer, TableRoot, TableCaption, TableHead, TableBody, T
|
|
|
45
45
|
export { CodeBlock, type CodeBlockProps, type CodeBlockLabels } from './code.js';
|
|
46
46
|
export { SyntaxHighlighter, syntaxLanguages, type SyntaxHighlighterProps, } from './syntax-highlighter.js';
|
|
47
47
|
export { List, ListRoot, ListItem, ListContent, ListTitle, ListDescription, ListActions, } from './list.js';
|
|
48
|
-
export { Stat, StatRoot, StatLabel, StatValue, StatChange, StatCaption, type StatChangeProps, type StatDirection, } from './stat.js';
|
|
48
|
+
export { Stat, StatRoot, StatLabel, StatValue, StatChange, StatCaption, StatStatus, StatGroup, type StatChangeProps, type StatStatusProps, type StatGroupProps, type StatDirection, } from './stat.js';
|
|
49
49
|
export { Sparkline, type SparklineProps, type SparklineTrend, } from './sparkline.js';
|
|
50
|
+
export { BarList, type BarListProps, type BarListItem } from './bar-list.js';
|
|
51
|
+
export { Dashboard, DashboardRoot, DashboardPanel, type DashboardRootProps, type DashboardPanelProps, type DashboardPanelLabels, } from './dashboard.js';
|
|
52
|
+
export { TimeRangePicker, timeRangeOptions, refreshIntervalOptions, type TimeRangePickerProps, type TimeRangePickerLabels, type TimeRangeOption, } from './time-range-picker.js';
|
|
50
53
|
export { ScrollArea } from './scroll-area.js';
|
|
51
54
|
export { Separator } from './separator.js';
|
|
52
55
|
export { Progress, ProgressIndicator, ProgressLabel, ProgressRoot, ProgressTrack, ProgressValue, } from './progress.js';
|
|
@@ -63,6 +66,6 @@ export { Icon, iconNames, type IconName, type IconProps } from './icon.js';
|
|
|
63
66
|
export { SizeProvider, type ComponentSize, type ComponentSizeAlias, } from './sizing.js';
|
|
64
67
|
export { DirectionProvider } from '@base-ui/react/direction-provider';
|
|
65
68
|
export { Slot } from './slot.js';
|
|
66
|
-
export { AppShell, AppShellRoot, AppShellHeader, AppShellSidebar, AppShellSidebarToggle, type AppShellRootProps, type AppShellSidebarToggleProps, type AppShellSidebarToggleLabels, AppShellBrand, type AppShellBrandProps, AppShellNav, type AppShellNavProps, AppShellContent, AppShellPage, AppShellFooter, AppShellLabel, AppShellLink, type AppShellLinkProps, } from './app-shell.js';
|
|
69
|
+
export { AppShell, AppShellRoot, AppShellHeader, AppShellSidebar, AppShellSidebarToggle, AppShellMenuTrigger, type AppShellRootProps, type AppShellSidebarToggleProps, type AppShellSidebarToggleLabels, type AppShellMenuTriggerProps, AppShellBrand, type AppShellBrandProps, AppShellNav, type AppShellNavProps, AppShellContent, AppShellPage, AppShellFooter, AppShellLabel, AppShellLink, type AppShellLinkProps, } from './app-shell.js';
|
|
67
70
|
export { PageHeader } from './page-header.js';
|
|
68
71
|
export { FilterBar, FilterBarRoot, FilterBarControls, FilterBarField, FilterBarActions, FilterBarSummary, } from './filter-bar.js';
|
package/dist/index.js
CHANGED
|
@@ -55,8 +55,11 @@ export { Table, TableContainer, TableRoot, TableCaption, TableHead, TableBody, T
|
|
|
55
55
|
export { CodeBlock } from './code.js';
|
|
56
56
|
export { SyntaxHighlighter, syntaxLanguages, } from './syntax-highlighter.js';
|
|
57
57
|
export { List, ListRoot, ListItem, ListContent, ListTitle, ListDescription, ListActions, } from './list.js';
|
|
58
|
-
export { Stat, StatRoot, StatLabel, StatValue, StatChange, StatCaption, } from './stat.js';
|
|
58
|
+
export { Stat, StatRoot, StatLabel, StatValue, StatChange, StatCaption, StatStatus, StatGroup, } from './stat.js';
|
|
59
59
|
export { Sparkline, } from './sparkline.js';
|
|
60
|
+
export { BarList } from './bar-list.js';
|
|
61
|
+
export { Dashboard, DashboardRoot, DashboardPanel, } from './dashboard.js';
|
|
62
|
+
export { TimeRangePicker, timeRangeOptions, refreshIntervalOptions, } from './time-range-picker.js';
|
|
60
63
|
// Structure
|
|
61
64
|
export { ScrollArea } from './scroll-area.js';
|
|
62
65
|
export { Separator } from './separator.js';
|
|
@@ -77,6 +80,6 @@ export { SizeProvider, } from './sizing.js';
|
|
|
77
80
|
export { DirectionProvider } from '@base-ui/react/direction-provider';
|
|
78
81
|
export { Slot } from './slot.js';
|
|
79
82
|
// Patterns
|
|
80
|
-
export { AppShell, AppShellRoot, AppShellHeader, AppShellSidebar, AppShellSidebarToggle, AppShellBrand, AppShellNav, AppShellContent, AppShellPage, AppShellFooter, AppShellLabel, AppShellLink, } from './app-shell.js';
|
|
83
|
+
export { AppShell, AppShellRoot, AppShellHeader, AppShellSidebar, AppShellSidebarToggle, AppShellMenuTrigger, AppShellBrand, AppShellNav, AppShellContent, AppShellPage, AppShellFooter, AppShellLabel, AppShellLink, } from './app-shell.js';
|
|
81
84
|
export { PageHeader } from './page-header.js';
|
|
82
85
|
export { FilterBar, FilterBarRoot, FilterBarControls, FilterBarField, FilterBarActions, FilterBarSummary, } from './filter-bar.js';
|