@chayns-ui/layout 0.1.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/LICENSE +21 -0
- package/README.md +24 -0
- package/dist/app-layout.css +420 -0
- package/dist/components/app-layout/AppLayout.d.ts +3 -0
- package/dist/components/app-layout/AppLayout.d.ts.map +1 -0
- package/dist/components/app-layout/AppLayout.js +78 -0
- package/dist/components/app-layout/AppLayout.js.map +1 -0
- package/dist/components/app-layout/AppLayout.types.d.ts +103 -0
- package/dist/components/app-layout/AppLayout.types.d.ts.map +1 -0
- package/dist/components/app-layout/app-layout-icon/AppLayoutIcon.d.ts +6 -0
- package/dist/components/app-layout/app-layout-icon/AppLayoutIcon.d.ts.map +1 -0
- package/dist/components/app-layout/app-layout-icon/AppLayoutIcon.js +17 -0
- package/dist/components/app-layout/app-layout-icon/AppLayoutIcon.js.map +1 -0
- package/dist/components/app-layout/navigation-items/NavigationItems.d.ts +13 -0
- package/dist/components/app-layout/navigation-items/NavigationItems.d.ts.map +1 -0
- package/dist/components/app-layout/navigation-items/NavigationItems.js +54 -0
- package/dist/components/app-layout/navigation-items/NavigationItems.js.map +1 -0
- package/dist/components/tabs/Tabs.d.ts +3 -0
- package/dist/components/tabs/Tabs.d.ts.map +1 -0
- package/dist/components/tabs/Tabs.js +102 -0
- package/dist/components/tabs/Tabs.js.map +1 -0
- package/dist/components/tabs/Tabs.types.d.ts +69 -0
- package/dist/components/tabs/Tabs.types.d.ts.map +1 -0
- package/dist/components/tabs/tabs-icon/TabsIcon.d.ts +6 -0
- package/dist/components/tabs/tabs-icon/TabsIcon.d.ts.map +1 -0
- package/dist/components/tabs/tabs-icon/TabsIcon.js +11 -0
- package/dist/components/tabs/tabs-icon/TabsIcon.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tobit.Software
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @chayns-ui/layout
|
|
2
|
+
|
|
3
|
+
Accessible layout components for chayns UI.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @chayns-ui/layout @chayns-ui/core @chayns-ui/tokens react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Load the resolved token stylesheet, Core stylesheet required by the host
|
|
12
|
+
application, and the AppLayout stylesheet:
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import '@chayns-ui/tokens/baseline.css';
|
|
16
|
+
import '@chayns-ui/tokens/patch.css';
|
|
17
|
+
import '@chayns-ui/core/styles.css';
|
|
18
|
+
import '@chayns-ui/layout/app-layout.css';
|
|
19
|
+
|
|
20
|
+
import { AppLayout } from '@chayns-ui/layout';
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`AppLayout` renders the application shell with a 64px header, recursive
|
|
24
|
+
navigation, controlled or uncontrolled sidebar collapse, and consumer content.
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
@layer chayns.components {
|
|
2
|
+
.chayns-app-layout {
|
|
3
|
+
display: grid;
|
|
4
|
+
grid-template: var(--k64) minmax(0, 1fr) / calc(var(--k64) * 3) minmax(0, 1fr);
|
|
5
|
+
grid-template-areas:
|
|
6
|
+
'header header'
|
|
7
|
+
'sidebar content';
|
|
8
|
+
min-block-size: 100%;
|
|
9
|
+
background: var(--accent);
|
|
10
|
+
color: var(--on-accent);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.chayns-tabs {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
container-type: inline-size;
|
|
17
|
+
inline-size: 100%;
|
|
18
|
+
block-size: 100%;
|
|
19
|
+
min-inline-size: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.chayns-tabs__list {
|
|
23
|
+
display: flex;
|
|
24
|
+
gap: var(--sp-1);
|
|
25
|
+
align-items: stretch;
|
|
26
|
+
min-inline-size: 0;
|
|
27
|
+
overflow-x: auto;
|
|
28
|
+
background: transparent;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.chayns-tabs__bar {
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: stretch;
|
|
34
|
+
block-size: var(--ctrl-h);
|
|
35
|
+
min-inline-size: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.chayns-tabs__item {
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
flex: 0 0 auto;
|
|
41
|
+
align-items: stretch;
|
|
42
|
+
border-radius: var(--k12) var(--k12) 0 0;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.chayns-tabs__tab {
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
flex: 0 0 auto;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
gap: var(--sp-2);
|
|
52
|
+
block-size: var(--ctrl-h);
|
|
53
|
+
min-block-size: var(--ctrl-h);
|
|
54
|
+
min-inline-size: var(--ctrl-h);
|
|
55
|
+
padding-block: 0;
|
|
56
|
+
padding-inline: var(--sp-3) var(--sp-2);
|
|
57
|
+
border: 0;
|
|
58
|
+
border-radius: 0;
|
|
59
|
+
color: var(--on-accent);
|
|
60
|
+
font: inherit;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
background: transparent;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.chayns-tabs__item:has(.chayns-tabs__tab--active) {
|
|
66
|
+
background: var(--surface);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.chayns-tabs__item:not(:has(.chayns-tabs__tab--active)) {
|
|
70
|
+
background: color-mix(in srgb, var(--surface) 10%, transparent);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.chayns-tabs__remove,
|
|
74
|
+
.chayns-tabs__add {
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
inline-size: var(--ctrl-h);
|
|
79
|
+
block-size: var(--ctrl-h);
|
|
80
|
+
min-block-size: var(--ctrl-h);
|
|
81
|
+
padding: 0 var(--sp-2);
|
|
82
|
+
border: 0;
|
|
83
|
+
color: var(--on-accent);
|
|
84
|
+
font: inherit;
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
background: transparent;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.chayns-tabs__remove {
|
|
90
|
+
inline-size: var(--icon);
|
|
91
|
+
padding: 0;
|
|
92
|
+
border-radius: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.chayns-tabs__add {
|
|
96
|
+
flex: 0 0 auto;
|
|
97
|
+
border-radius: 0;
|
|
98
|
+
color: var(--on-accent);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.chayns-tabs__item:has(:focus-visible) {
|
|
102
|
+
outline: none;
|
|
103
|
+
box-shadow: inset 0 0 0 var(--focus-ring-size)
|
|
104
|
+
rgb(var(--focus-ring-rgb), var(--focus-ring-alpha-strong));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.chayns-tabs__tab:focus-visible,
|
|
108
|
+
.chayns-tabs__remove:focus-visible,
|
|
109
|
+
.chayns-tabs__add:focus-visible {
|
|
110
|
+
outline: none;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.chayns-tabs__icon {
|
|
114
|
+
display: inline-flex;
|
|
115
|
+
flex: none;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
inline-size: var(--icon);
|
|
119
|
+
block-size: var(--icon);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.chayns-tabs__remove .chayns-tabs__icon {
|
|
123
|
+
inline-size: var(--fs-meta);
|
|
124
|
+
block-size: var(--fs-meta);
|
|
125
|
+
font-size: var(--fs-meta);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.chayns-tabs__item:has(.chayns-tabs__tab--active) .chayns-tabs__tab,
|
|
129
|
+
.chayns-tabs__item:has(.chayns-tabs__tab--active) .chayns-tabs__remove {
|
|
130
|
+
color: var(--accent);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.chayns-tabs__panel {
|
|
134
|
+
flex: 1 1 auto;
|
|
135
|
+
min-inline-size: 0;
|
|
136
|
+
min-block-size: 0;
|
|
137
|
+
padding: var(--sp-4);
|
|
138
|
+
overflow: auto;
|
|
139
|
+
background: var(--surface);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@container (max-width: 20rem) {
|
|
143
|
+
.chayns-tabs__label {
|
|
144
|
+
position: absolute;
|
|
145
|
+
inline-size: 1px;
|
|
146
|
+
block-size: 1px;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
clip-path: inset(50%);
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.chayns-app-layout__header {
|
|
154
|
+
display: flex;
|
|
155
|
+
grid-area: header;
|
|
156
|
+
align-items: center;
|
|
157
|
+
min-inline-size: 0;
|
|
158
|
+
padding-inline: var(--sp-4);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.chayns-app-layout__logo {
|
|
162
|
+
display: block;
|
|
163
|
+
max-block-size: calc(var(--k32) + var(--k8));
|
|
164
|
+
max-inline-size: 100%;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.chayns-app-layout__sidebar {
|
|
168
|
+
display: flex;
|
|
169
|
+
grid-area: sidebar;
|
|
170
|
+
flex-direction: column;
|
|
171
|
+
min-inline-size: 0;
|
|
172
|
+
min-block-size: 0;
|
|
173
|
+
padding: var(--sp-2);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.chayns-app-layout__navigation {
|
|
177
|
+
min-block-size: 0;
|
|
178
|
+
overflow: auto;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.chayns-app-layout__list {
|
|
182
|
+
display: grid;
|
|
183
|
+
gap: var(--sp-1);
|
|
184
|
+
margin: 0;
|
|
185
|
+
padding: 0;
|
|
186
|
+
list-style: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.chayns-app-layout__children {
|
|
190
|
+
display: grid;
|
|
191
|
+
grid-template-rows: 0fr;
|
|
192
|
+
min-inline-size: 0;
|
|
193
|
+
padding-inline-start: var(--sp-4);
|
|
194
|
+
overflow: hidden;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.chayns-app-layout__children--open {
|
|
198
|
+
grid-template-rows: 1fr;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.chayns-app-layout__children > .chayns-app-layout__list {
|
|
202
|
+
min-block-size: 0;
|
|
203
|
+
overflow: hidden;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.chayns-app-layout__row {
|
|
207
|
+
display: flex;
|
|
208
|
+
align-items: stretch;
|
|
209
|
+
min-inline-size: 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.chayns-app-layout__item {
|
|
213
|
+
min-inline-size: 0;
|
|
214
|
+
border-radius: var(--k8);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.chayns-app-layout__item:hover,
|
|
218
|
+
.chayns-app-layout__item:focus-within,
|
|
219
|
+
.chayns-app-layout__item--active {
|
|
220
|
+
background: color-mix(in srgb, var(--on-accent) 12%, transparent);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.chayns-app-layout__action,
|
|
224
|
+
.chayns-app-layout__disclosure,
|
|
225
|
+
.chayns-app-layout__collapse {
|
|
226
|
+
border: 0;
|
|
227
|
+
margin: 0;
|
|
228
|
+
font: inherit;
|
|
229
|
+
color: inherit;
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
background: transparent;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.chayns-app-layout__action {
|
|
235
|
+
display: flex;
|
|
236
|
+
flex: 1;
|
|
237
|
+
align-items: center;
|
|
238
|
+
gap: var(--sp-2);
|
|
239
|
+
min-block-size: var(--ctrl-h);
|
|
240
|
+
min-inline-size: 0;
|
|
241
|
+
max-inline-size: 100%;
|
|
242
|
+
padding: var(--sp-2) var(--sp-3);
|
|
243
|
+
border-radius: var(--k8);
|
|
244
|
+
background: transparent;
|
|
245
|
+
text-align: start;
|
|
246
|
+
white-space: nowrap;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.chayns-app-layout__label {
|
|
250
|
+
min-inline-size: 0;
|
|
251
|
+
opacity: 1;
|
|
252
|
+
overflow: hidden;
|
|
253
|
+
text-overflow: ellipsis;
|
|
254
|
+
white-space: nowrap;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.chayns-app-layout--collapsed {
|
|
258
|
+
grid-template-columns: calc(var(--ctrl-h) + var(--sp-4)) minmax(0, 1fr);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.chayns-app-layout__disclosure {
|
|
262
|
+
display: inline-flex;
|
|
263
|
+
flex: none;
|
|
264
|
+
align-items: center;
|
|
265
|
+
justify-content: center;
|
|
266
|
+
min-inline-size: var(--ctrl-h);
|
|
267
|
+
border-radius: var(--k8);
|
|
268
|
+
background: transparent;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.chayns-app-layout__collapse {
|
|
272
|
+
display: inline-flex;
|
|
273
|
+
align-items: center;
|
|
274
|
+
justify-content: center;
|
|
275
|
+
inline-size: var(--ctrl-h);
|
|
276
|
+
block-size: var(--ctrl-h);
|
|
277
|
+
align-self: flex-end;
|
|
278
|
+
border-radius: 50%;
|
|
279
|
+
background: transparent;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.chayns-app-layout__footer {
|
|
283
|
+
display: flex;
|
|
284
|
+
justify-content: flex-end;
|
|
285
|
+
margin-block-start: auto;
|
|
286
|
+
padding-block-start: var(--sp-2);
|
|
287
|
+
border-block-start: 1px solid color-mix(in srgb, var(--on-accent) 35%, transparent);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.chayns-app-layout__collapse:hover {
|
|
291
|
+
background: color-mix(in srgb, var(--on-accent) 12%, transparent);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
:where(
|
|
295
|
+
.chayns-app-layout__action,
|
|
296
|
+
.chayns-app-layout__disclosure,
|
|
297
|
+
.chayns-app-layout__collapse
|
|
298
|
+
):focus-visible {
|
|
299
|
+
outline: none;
|
|
300
|
+
box-shadow: inset 0 0 0 var(--focus-ring-size) var(--on-accent);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.chayns-app-layout__icon {
|
|
304
|
+
display: inline-flex;
|
|
305
|
+
flex: none;
|
|
306
|
+
align-items: center;
|
|
307
|
+
justify-content: center;
|
|
308
|
+
inline-size: var(--icon);
|
|
309
|
+
block-size: var(--icon);
|
|
310
|
+
line-height: 1;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.chayns-app-layout__disclosure[aria-expanded='true'] .chayns-app-layout__icon {
|
|
314
|
+
transform: rotate(180deg);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.chayns-app-layout__weight {
|
|
318
|
+
display: inline-flex;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.chayns-app-layout__weight--active {
|
|
322
|
+
display: none;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
:where(
|
|
326
|
+
.chayns-app-layout__action:hover,
|
|
327
|
+
.chayns-app-layout__action:focus-visible,
|
|
328
|
+
.chayns-app-layout__disclosure:hover,
|
|
329
|
+
.chayns-app-layout__collapse:hover
|
|
330
|
+
)
|
|
331
|
+
.chayns-app-layout__weight:not(.chayns-app-layout__weight--active) {
|
|
332
|
+
display: none;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
:where(
|
|
336
|
+
.chayns-app-layout__action:hover,
|
|
337
|
+
.chayns-app-layout__action:focus-visible,
|
|
338
|
+
.chayns-app-layout__disclosure:hover,
|
|
339
|
+
.chayns-app-layout__collapse:hover
|
|
340
|
+
)
|
|
341
|
+
.chayns-app-layout__weight--active {
|
|
342
|
+
display: inline-flex;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.chayns-app-layout__content {
|
|
346
|
+
grid-area: content;
|
|
347
|
+
min-inline-size: 0;
|
|
348
|
+
min-block-size: 0;
|
|
349
|
+
overflow: auto;
|
|
350
|
+
border-start-start-radius: var(--k12);
|
|
351
|
+
background: var(--accent);
|
|
352
|
+
color: var(--on-accent);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.chayns-app-layout--collapsed .chayns-app-layout__sidebar {
|
|
356
|
+
align-items: stretch;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.chayns-app-layout--collapsed .chayns-app-layout__row {
|
|
360
|
+
position: relative;
|
|
361
|
+
inline-size: 100%;
|
|
362
|
+
max-inline-size: 100%;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.chayns-app-layout--collapsed .chayns-app-layout__action {
|
|
366
|
+
inline-size: 100%;
|
|
367
|
+
max-inline-size: 100%;
|
|
368
|
+
justify-content: flex-start;
|
|
369
|
+
padding-inline: var(--sp-3);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.chayns-app-layout--collapsed .chayns-app-layout__navigation {
|
|
373
|
+
overflow: hidden;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.chayns-app-layout--collapsed .chayns-app-layout__disclosure {
|
|
377
|
+
display: none;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.chayns-app-layout--collapsed .chayns-app-layout__children {
|
|
381
|
+
display: none;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.chayns-app-layout__label--hidden {
|
|
385
|
+
position: absolute;
|
|
386
|
+
inline-size: 1px;
|
|
387
|
+
block-size: 1px;
|
|
388
|
+
min-inline-size: auto;
|
|
389
|
+
opacity: 0;
|
|
390
|
+
overflow: hidden;
|
|
391
|
+
clip-path: inset(50%);
|
|
392
|
+
white-space: nowrap;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
396
|
+
.chayns-app-layout {
|
|
397
|
+
transition: grid-template-columns 160ms ease;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.chayns-app-layout__children {
|
|
401
|
+
transition: grid-template-rows 160ms ease;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.chayns-app-layout__disclosure .chayns-app-layout__icon {
|
|
405
|
+
transition: transform 160ms ease;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
@media (forced-colors: active) {
|
|
410
|
+
:where(
|
|
411
|
+
.chayns-app-layout__action,
|
|
412
|
+
.chayns-app-layout__disclosure,
|
|
413
|
+
.chayns-app-layout__collapse
|
|
414
|
+
):focus-visible {
|
|
415
|
+
outline: 2px solid ButtonText;
|
|
416
|
+
outline-offset: -2px;
|
|
417
|
+
box-shadow: none;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLayout.d.ts","sourceRoot":"","sources":["../../../src/components/app-layout/AppLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D,eAAO,MAAM,SAAS,wHAmFrB,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import AppLayoutIcon from "./app-layout-icon/AppLayoutIcon.js";
|
|
2
|
+
import NavigationItems from "./navigation-items/NavigationItems.js";
|
|
3
|
+
import { forwardRef, useId, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/app-layout/AppLayout.tsx
|
|
6
|
+
var AppLayout = forwardRef(({ activeItemId, children, className, collapseLabel, collapsed, defaultCollapsed = false, expandLabel, items, logo, navigationLabel, onClick, onCollapsedChange, ...rootProps }, ref) => {
|
|
7
|
+
const baseId = useId();
|
|
8
|
+
const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
|
|
9
|
+
const [expandedIds, setExpandedIds] = useState(() => /* @__PURE__ */ new Set());
|
|
10
|
+
const isCollapsed = collapsed ?? internalCollapsed;
|
|
11
|
+
const rootClassName = [
|
|
12
|
+
"chayns-app-layout",
|
|
13
|
+
isCollapsed ? "chayns-app-layout--collapsed" : void 0,
|
|
14
|
+
className
|
|
15
|
+
].filter(Boolean).join(" ");
|
|
16
|
+
const handleCollapsedChange = () => {
|
|
17
|
+
const nextCollapsed = !isCollapsed;
|
|
18
|
+
if (collapsed === void 0) setInternalCollapsed(nextCollapsed);
|
|
19
|
+
onCollapsedChange?.(nextCollapsed);
|
|
20
|
+
};
|
|
21
|
+
const handleToggle = (id) => {
|
|
22
|
+
setExpandedIds((current) => {
|
|
23
|
+
const next = new Set(current);
|
|
24
|
+
if (next.has(id)) next.delete(id);
|
|
25
|
+
else next.add(id);
|
|
26
|
+
return next;
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
30
|
+
...rootProps,
|
|
31
|
+
className: rootClassName,
|
|
32
|
+
ref,
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ jsx("header", {
|
|
35
|
+
className: "chayns-app-layout__header",
|
|
36
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
37
|
+
alt: "",
|
|
38
|
+
className: "chayns-app-layout__logo",
|
|
39
|
+
src: logo
|
|
40
|
+
})
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ jsxs("aside", {
|
|
43
|
+
className: "chayns-app-layout__sidebar",
|
|
44
|
+
children: [/* @__PURE__ */ jsx("nav", {
|
|
45
|
+
"aria-label": navigationLabel,
|
|
46
|
+
className: "chayns-app-layout__navigation",
|
|
47
|
+
children: /* @__PURE__ */ jsx(NavigationItems, {
|
|
48
|
+
activeItemId,
|
|
49
|
+
collapsed: isCollapsed,
|
|
50
|
+
expandedIds,
|
|
51
|
+
items,
|
|
52
|
+
onClick,
|
|
53
|
+
onToggle: handleToggle,
|
|
54
|
+
parentId: baseId
|
|
55
|
+
})
|
|
56
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
57
|
+
className: "chayns-app-layout__footer",
|
|
58
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
59
|
+
"aria-label": isCollapsed ? expandLabel : collapseLabel,
|
|
60
|
+
className: "chayns-app-layout__collapse",
|
|
61
|
+
onClick: handleCollapsedChange,
|
|
62
|
+
type: "button",
|
|
63
|
+
children: /* @__PURE__ */ jsx(AppLayoutIcon, { icon: "fa-sidebar" })
|
|
64
|
+
})
|
|
65
|
+
})]
|
|
66
|
+
}),
|
|
67
|
+
/* @__PURE__ */ jsx("main", {
|
|
68
|
+
className: "chayns-app-layout__content",
|
|
69
|
+
children
|
|
70
|
+
})
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
AppLayout.displayName = "AppLayout";
|
|
75
|
+
//#endregion
|
|
76
|
+
export { AppLayout };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=AppLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLayout.js","names":[],"sources":["../../../src/components/app-layout/AppLayout.tsx"],"sourcesContent":["import { forwardRef, useId, useState } from 'react';\n\nimport type { AppLayoutProps } from './AppLayout.types.js';\nimport AppLayoutIcon from './app-layout-icon/AppLayoutIcon.js';\nimport NavigationItems from './navigation-items/NavigationItems.js';\n\nexport const AppLayout = forwardRef<HTMLDivElement, AppLayoutProps>(\n (\n {\n activeItemId,\n children,\n className,\n collapseLabel,\n collapsed,\n defaultCollapsed = false,\n expandLabel,\n items,\n logo,\n navigationLabel,\n onClick,\n onCollapsedChange,\n ...rootProps\n },\n ref,\n ) => {\n const baseId = useId();\n const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);\n const [expandedIds, setExpandedIds] = useState<ReadonlySet<string>>(() => new Set());\n const isCollapsed = collapsed ?? internalCollapsed;\n const rootClassName = [\n 'chayns-app-layout',\n isCollapsed ? 'chayns-app-layout--collapsed' : undefined,\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const handleCollapsedChange = () => {\n const nextCollapsed = !isCollapsed;\n if (collapsed === undefined) {\n setInternalCollapsed(nextCollapsed);\n }\n onCollapsedChange?.(nextCollapsed);\n };\n\n const handleToggle = (id: string) => {\n setExpandedIds((current) => {\n const next = new Set(current);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n };\n\n return (\n <div {...rootProps} className={rootClassName} ref={ref}>\n <header className=\"chayns-app-layout__header\">\n <img alt=\"\" className=\"chayns-app-layout__logo\" src={logo} />\n </header>\n <aside className=\"chayns-app-layout__sidebar\">\n <nav aria-label={navigationLabel} className=\"chayns-app-layout__navigation\">\n <NavigationItems\n activeItemId={activeItemId}\n collapsed={isCollapsed}\n expandedIds={expandedIds}\n items={items}\n onClick={onClick}\n onToggle={handleToggle}\n parentId={baseId}\n />\n </nav>\n <div className=\"chayns-app-layout__footer\">\n <button\n aria-label={isCollapsed ? expandLabel : collapseLabel}\n className=\"chayns-app-layout__collapse\"\n onClick={handleCollapsedChange}\n type=\"button\"\n >\n <AppLayoutIcon icon=\"fa-sidebar\" />\n </button>\n </div>\n </aside>\n <main className=\"chayns-app-layout__content\">{children}</main>\n </div>\n );\n },\n);\n\nAppLayout.displayName = 'AppLayout';\n"],"mappings":";;;;;AAMA,IAAa,YAAY,YAErB,EACE,cACA,UACA,WACA,eACA,WACA,mBAAmB,OACnB,aACA,OACA,MACA,iBACA,SACA,mBACA,GAAG,aAEL,QACG;CACH,MAAM,SAAS,MAAM;CACrB,MAAM,CAAC,mBAAmB,wBAAwB,SAAS,gBAAgB;CAC3E,MAAM,CAAC,aAAa,kBAAkB,+BAAoC,IAAI,IAAI,CAAC;CACnF,MAAM,cAAc,aAAa;CACjC,MAAM,gBAAgB;EACpB;EACA,cAAc,iCAAiC,KAAA;EAC/C;CACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,MAAM,8BAA8B;EAClC,MAAM,gBAAgB,CAAC;EACvB,IAAI,cAAc,KAAA,GAChB,qBAAqB,aAAa;EAEpC,oBAAoB,aAAa;CACnC;CAEA,MAAM,gBAAgB,OAAe;EACnC,gBAAgB,YAAY;GAC1B,MAAM,OAAO,IAAI,IAAI,OAAO;GAC5B,IAAI,KAAK,IAAI,EAAE,GACb,KAAK,OAAO,EAAE;QAEd,KAAK,IAAI,EAAE;GAEb,OAAO;EACT,CAAC;CACH;CAEA,OACE,qBAAC,OAAD;EAAK,GAAI;EAAW,WAAW;EAAoB;EAAnD,UAAA;GACE,oBAAC,UAAD;IAAQ,WAAU;IAChB,UAAA,oBAAC,OAAD;KAAK,KAAI;KAAG,WAAU;KAA0B,KAAK;IAAO,CAAA;GACtD,CAAA;GACR,qBAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACE,oBAAC,OAAD;KAAK,cAAY;KAAiB,WAAU;KAC1C,UAAA,oBAAC,iBAAD;MACgB;MACd,WAAW;MACE;MACN;MACE;MACT,UAAU;MACV,UAAU;KACX,CAAA;IACE,CAAA,GACL,oBAAC,OAAD;KAAK,WAAU;KACb,UAAA,oBAAC,UAAD;MACE,cAAY,cAAc,cAAc;MACxC,WAAU;MACV,SAAS;MACT,MAAK;MAEL,UAAA,oBAAC,eAAD,EAAe,MAAK,aAAc,CAAA;KAC5B,CAAA;IACL,CAAA,CACA;;GACP,oBAAC,QAAD;IAAM,WAAU;IAA8B;GAAe,CAAA;EAC1D;;AAET,CACF;AAEA,UAAU,cAAc"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef, ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* @description A FontAwesome Classic icon name in its Regular form, used for
|
|
4
|
+
* navigation item icons in `AppLayout`.
|
|
5
|
+
*/
|
|
6
|
+
export type AppLayoutIcon = `fa-${string}`;
|
|
7
|
+
/**
|
|
8
|
+
* @description A single, recursively composable navigation entry in the
|
|
9
|
+
* `AppLayout` sidebar. Parent and child items each act as their own action
|
|
10
|
+
* button; a parent item with `children` additionally exposes a separate
|
|
11
|
+
* disclosure control, so activating the parent's own action and toggling its
|
|
12
|
+
* nested children are independent.
|
|
13
|
+
*/
|
|
14
|
+
export interface AppLayoutItem {
|
|
15
|
+
/**
|
|
16
|
+
* @description Stable identifier passed to `onClick` and compared against
|
|
17
|
+
* `activeItemId`.
|
|
18
|
+
*/
|
|
19
|
+
id: string;
|
|
20
|
+
/**
|
|
21
|
+
* @description Visible, localized navigation label. Remains accessible for
|
|
22
|
+
* each item even while the sidebar is collapsed.
|
|
23
|
+
*/
|
|
24
|
+
name: string;
|
|
25
|
+
/**
|
|
26
|
+
* @description FontAwesome Classic icon name shown for this item, including
|
|
27
|
+
* in the collapsed sidebar.
|
|
28
|
+
*/
|
|
29
|
+
icon: AppLayoutIcon;
|
|
30
|
+
/**
|
|
31
|
+
* @description Nested child items, rendered behind this item's own
|
|
32
|
+
* disclosure control and hidden while the sidebar is collapsed.
|
|
33
|
+
*/
|
|
34
|
+
children?: AppLayoutItem[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @description Props for the top-level application shell: a header with a
|
|
38
|
+
* logo, a collapsible sidebar of recursively nested navigation items, and a
|
|
39
|
+
* consumer-owned content area. Forwards all remaining native `div` props
|
|
40
|
+
* (except `children` and `onClick`, which have `AppLayout`-specific meaning),
|
|
41
|
+
* `className` and `ref`.
|
|
42
|
+
*/
|
|
43
|
+
export interface AppLayoutProps extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'onClick'> {
|
|
44
|
+
/**
|
|
45
|
+
* @description URL of an image or SVG resource rendered as the header logo.
|
|
46
|
+
* Always decorative (`alt=""`); it does not itself provide any accessible
|
|
47
|
+
* name.
|
|
48
|
+
*/
|
|
49
|
+
logo: string;
|
|
50
|
+
/**
|
|
51
|
+
* @description Top-level navigation items, recursively composable via each
|
|
52
|
+
* item's own `children`. An empty array renders an empty navigation list.
|
|
53
|
+
*/
|
|
54
|
+
items: AppLayoutItem[];
|
|
55
|
+
/**
|
|
56
|
+
* @description Called with an item's `id` when that item's own action is
|
|
57
|
+
* activated, for both parent and child items. Toggling a parent's
|
|
58
|
+
* disclosure control does not call `onClick`.
|
|
59
|
+
*/
|
|
60
|
+
onClick: (id: string) => void;
|
|
61
|
+
/**
|
|
62
|
+
* @description Localized accessible name for the navigation landmark.
|
|
63
|
+
*/
|
|
64
|
+
navigationLabel: string;
|
|
65
|
+
/**
|
|
66
|
+
* @description Localized accessible name for the sidebar toggle control
|
|
67
|
+
* when activating it will collapse the sidebar.
|
|
68
|
+
*/
|
|
69
|
+
collapseLabel: string;
|
|
70
|
+
/**
|
|
71
|
+
* @description Localized accessible name for the sidebar toggle control
|
|
72
|
+
* when activating it will expand the sidebar.
|
|
73
|
+
*/
|
|
74
|
+
expandLabel: string;
|
|
75
|
+
/**
|
|
76
|
+
* @description `id` of the currently active navigation item. `AppLayout`
|
|
77
|
+
* only renders the active visual state; it does not derive or own
|
|
78
|
+
* activeness from routing.
|
|
79
|
+
*/
|
|
80
|
+
activeItemId?: string;
|
|
81
|
+
/**
|
|
82
|
+
* @description Consumer-provided content rendered in the area beside the
|
|
83
|
+
* sidebar. Owns its own internal semantics and scrolling.
|
|
84
|
+
*/
|
|
85
|
+
children?: ReactNode;
|
|
86
|
+
/**
|
|
87
|
+
* @description Controlled collapsed state of the sidebar.
|
|
88
|
+
*/
|
|
89
|
+
collapsed?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* @description Uncontrolled initial collapsed state of the sidebar.
|
|
92
|
+
* Persisting the collapsed state across sessions is outside `AppLayout`'s
|
|
93
|
+
* responsibility.
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
defaultCollapsed?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* @description Called with the next collapsed state when the sidebar
|
|
99
|
+
* toggle is activated, for both the controlled and uncontrolled usage.
|
|
100
|
+
*/
|
|
101
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=AppLayout.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLayout.types.d.ts","sourceRoot":"","sources":["../../../src/components/app-layout/AppLayout.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,MAAM,EAAE,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,IAAI,EAAE,aAAa,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChG;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,KAAK,EAAE,aAAa,EAAE,CAAC;IAEvB;;;;OAIG;IACH,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9B;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAClD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLayoutIcon.d.ts","sourceRoot":"","sources":["../../../../src/components/app-layout/app-layout-icon/AppLayoutIcon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,QAAA,MAAM,aAAa,GAAI,UAAU;IAAE,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,gCAS/D,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
//#region src/components/app-layout/app-layout-icon/AppLayoutIcon.tsx
|
|
3
|
+
var AppLayoutIcon = ({ icon }) => /* @__PURE__ */ jsxs("span", {
|
|
4
|
+
"aria-hidden": "true",
|
|
5
|
+
className: "chayns-app-layout__icon",
|
|
6
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
7
|
+
className: "chayns-app-layout__weight",
|
|
8
|
+
children: /* @__PURE__ */ jsx("i", { className: `far ${icon}` })
|
|
9
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
10
|
+
className: "chayns-app-layout__weight chayns-app-layout__weight--active",
|
|
11
|
+
children: /* @__PURE__ */ jsx("i", { className: `fas ${icon}` })
|
|
12
|
+
})]
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { AppLayoutIcon as default };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=AppLayoutIcon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLayoutIcon.js","names":[],"sources":["../../../../src/components/app-layout/app-layout-icon/AppLayoutIcon.tsx"],"sourcesContent":["import type { AppLayoutItem } from '../AppLayout.types.js';\n\nconst AppLayoutIcon = ({ icon }: { icon: AppLayoutItem['icon'] }) => (\n <span aria-hidden=\"true\" className=\"chayns-app-layout__icon\">\n <span className=\"chayns-app-layout__weight\">\n <i className={`far ${icon}`} />\n </span>\n <span className=\"chayns-app-layout__weight chayns-app-layout__weight--active\">\n <i className={`fas ${icon}`} />\n </span>\n </span>\n);\n\nexport default AppLayoutIcon;\n"],"mappings":";;AAEA,IAAM,iBAAiB,EAAE,WACvB,qBAAC,QAAD;CAAM,eAAY;CAAO,WAAU;CAAnC,UAAA,CACE,oBAAC,QAAD;EAAM,WAAU;EACd,UAAA,oBAAC,KAAD,EAAG,WAAW,OAAO,OAAS,CAAA;CAC1B,CAAA,GACN,oBAAC,QAAD;EAAM,WAAU;EACd,UAAA,oBAAC,KAAD,EAAG,WAAW,OAAO,OAAS,CAAA;CAC1B,CAAA,CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AppLayoutItem } from '../AppLayout.types.js';
|
|
2
|
+
interface NavigationItemsProps {
|
|
3
|
+
items: AppLayoutItem[];
|
|
4
|
+
activeItemId?: string | undefined;
|
|
5
|
+
collapsed: boolean;
|
|
6
|
+
onClick: (id: string) => void;
|
|
7
|
+
expandedIds: ReadonlySet<string>;
|
|
8
|
+
onToggle: (id: string) => void;
|
|
9
|
+
parentId?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const NavigationItems: ({ activeItemId, collapsed, expandedIds, items, onClick, onToggle, parentId, }: NavigationItemsProps) => import("react").JSX.Element;
|
|
12
|
+
export default NavigationItems;
|
|
13
|
+
//# sourceMappingURL=NavigationItems.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavigationItems.d.ts","sourceRoot":"","sources":["../../../../src/components/app-layout/navigation-items/NavigationItems.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,UAAU,oBAAoB;IAC5B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,QAAA,MAAM,eAAe,GAAI,+EAQtB,oBAAoB,gCAoEtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import AppLayoutIcon from "../app-layout-icon/AppLayoutIcon.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/components/app-layout/navigation-items/NavigationItems.tsx
|
|
4
|
+
var NavigationItems = ({ activeItemId, collapsed, expandedIds, items, onClick, onToggle, parentId }) => /* @__PURE__ */ jsx("ul", {
|
|
5
|
+
className: "chayns-app-layout__list",
|
|
6
|
+
children: items.map((item) => {
|
|
7
|
+
const hasChildren = Boolean(item.children?.length);
|
|
8
|
+
const isExpanded = expandedIds.has(item.id);
|
|
9
|
+
const panelId = `${parentId ?? "root"}-${item.id}-children`;
|
|
10
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
11
|
+
className: `chayns-app-layout__item${activeItemId === item.id ? " chayns-app-layout__item--active" : ""}`,
|
|
12
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
13
|
+
className: "chayns-app-layout__row",
|
|
14
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
15
|
+
"aria-current": activeItemId === item.id ? "page" : void 0,
|
|
16
|
+
"aria-label": item.name,
|
|
17
|
+
className: "chayns-app-layout__action",
|
|
18
|
+
onClick: () => onClick(item.id),
|
|
19
|
+
type: "button",
|
|
20
|
+
children: [/* @__PURE__ */ jsx(AppLayoutIcon, { icon: item.icon }), /* @__PURE__ */ jsx("span", {
|
|
21
|
+
className: `chayns-app-layout__label${collapsed ? " chayns-app-layout__label--hidden" : ""}`,
|
|
22
|
+
children: item.name
|
|
23
|
+
})]
|
|
24
|
+
}), hasChildren && !collapsed ? /* @__PURE__ */ jsx("button", {
|
|
25
|
+
"aria-controls": panelId,
|
|
26
|
+
"aria-expanded": isExpanded,
|
|
27
|
+
"aria-label": item.name,
|
|
28
|
+
className: "chayns-app-layout__disclosure",
|
|
29
|
+
onClick: () => onToggle(item.id),
|
|
30
|
+
type: "button",
|
|
31
|
+
children: /* @__PURE__ */ jsx(AppLayoutIcon, { icon: "fa-chevron-down" })
|
|
32
|
+
}) : null]
|
|
33
|
+
}), hasChildren && !collapsed ? /* @__PURE__ */ jsx("div", {
|
|
34
|
+
"aria-hidden": !isExpanded,
|
|
35
|
+
className: `chayns-app-layout__children${isExpanded ? " chayns-app-layout__children--open" : ""}`,
|
|
36
|
+
id: panelId,
|
|
37
|
+
inert: !isExpanded,
|
|
38
|
+
children: /* @__PURE__ */ jsx(NavigationItems, {
|
|
39
|
+
activeItemId,
|
|
40
|
+
collapsed,
|
|
41
|
+
expandedIds,
|
|
42
|
+
items: item.children ?? [],
|
|
43
|
+
onClick,
|
|
44
|
+
onToggle,
|
|
45
|
+
parentId: item.id
|
|
46
|
+
})
|
|
47
|
+
}) : null]
|
|
48
|
+
}, item.id);
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
//#endregion
|
|
52
|
+
export { NavigationItems as default };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=NavigationItems.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavigationItems.js","names":[],"sources":["../../../../src/components/app-layout/navigation-items/NavigationItems.tsx"],"sourcesContent":["import AppLayoutIcon from '../app-layout-icon/AppLayoutIcon.js';\nimport type { AppLayoutItem } from '../AppLayout.types.js';\n\ninterface NavigationItemsProps {\n items: AppLayoutItem[];\n activeItemId?: string | undefined;\n collapsed: boolean;\n onClick: (id: string) => void;\n expandedIds: ReadonlySet<string>;\n onToggle: (id: string) => void;\n parentId?: string;\n}\n\nconst NavigationItems = ({\n activeItemId,\n collapsed,\n expandedIds,\n items,\n onClick,\n onToggle,\n parentId,\n}: NavigationItemsProps) => (\n <ul className=\"chayns-app-layout__list\">\n {items.map((item) => {\n const hasChildren = Boolean(item.children?.length);\n const isExpanded = expandedIds.has(item.id);\n const panelId = `${parentId ?? 'root'}-${item.id}-children`;\n\n return (\n <li\n className={`chayns-app-layout__item${\n activeItemId === item.id ? ' chayns-app-layout__item--active' : ''\n }`}\n key={item.id}\n >\n <div className=\"chayns-app-layout__row\">\n <button\n aria-current={activeItemId === item.id ? 'page' : undefined}\n aria-label={item.name}\n className=\"chayns-app-layout__action\"\n onClick={() => onClick(item.id)}\n type=\"button\"\n >\n <AppLayoutIcon icon={item.icon} />\n <span\n className={`chayns-app-layout__label${\n collapsed ? ' chayns-app-layout__label--hidden' : ''\n }`}\n >\n {item.name}\n </span>\n </button>\n {hasChildren && !collapsed ? (\n <button\n aria-controls={panelId}\n aria-expanded={isExpanded}\n aria-label={item.name}\n className=\"chayns-app-layout__disclosure\"\n onClick={() => onToggle(item.id)}\n type=\"button\"\n >\n <AppLayoutIcon icon=\"fa-chevron-down\" />\n </button>\n ) : null}\n </div>\n {hasChildren && !collapsed ? (\n <div\n aria-hidden={!isExpanded}\n className={`chayns-app-layout__children${\n isExpanded ? ' chayns-app-layout__children--open' : ''\n }`}\n id={panelId}\n inert={!isExpanded}\n >\n <NavigationItems\n activeItemId={activeItemId}\n collapsed={collapsed}\n expandedIds={expandedIds}\n items={item.children ?? []}\n onClick={onClick}\n onToggle={onToggle}\n parentId={item.id}\n />\n </div>\n ) : null}\n </li>\n );\n })}\n </ul>\n);\n\nexport default NavigationItems;\n"],"mappings":";;;AAaA,IAAM,mBAAmB,EACvB,cACA,WACA,aACA,OACA,SACA,UACA,eAEA,oBAAC,MAAD;CAAI,WAAU;CACX,UAAA,MAAM,KAAK,SAAS;EACnB,MAAM,cAAc,QAAQ,KAAK,UAAU,MAAM;EACjD,MAAM,aAAa,YAAY,IAAI,KAAK,EAAE;EAC1C,MAAM,UAAU,GAAG,YAAY,OAAO,GAAG,KAAK,GAAG;EAEjD,OACE,qBAAC,MAAD;GACE,WAAW,0BACT,iBAAiB,KAAK,KAAK,qCAAqC;GAFpE,UAAA,CAME,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,UAAD;KACE,gBAAc,iBAAiB,KAAK,KAAK,SAAS,KAAA;KAClD,cAAY,KAAK;KACjB,WAAU;KACV,eAAe,QAAQ,KAAK,EAAE;KAC9B,MAAK;KALP,UAAA,CAOE,oBAAC,eAAD,EAAe,MAAM,KAAK,KAAO,CAAA,GACjC,oBAAC,QAAD;MACE,WAAW,2BACT,YAAY,sCAAsC;MAGnD,UAAA,KAAK;KACF,CAAA,CACA;IACP,CAAA,GAAA,eAAe,CAAC,YACf,oBAAC,UAAD;KACE,iBAAe;KACf,iBAAe;KACf,cAAY,KAAK;KACjB,WAAU;KACV,eAAe,SAAS,KAAK,EAAE;KAC/B,MAAK;KAEL,UAAA,oBAAC,eAAD,EAAe,MAAK,kBAAmB,CAAA;IACjC,CAAA,IACN,IACD;GACJ,CAAA,GAAA,eAAe,CAAC,YACf,oBAAC,OAAD;IACE,eAAa,CAAC;IACd,WAAW,8BACT,aAAa,uCAAuC;IAEtD,IAAI;IACJ,OAAO,CAAC;IAER,UAAA,oBAAC,iBAAD;KACgB;KACH;KACE;KACb,OAAO,KAAK,YAAY,CAAC;KAChB;KACC;KACV,UAAU,KAAK;IAChB,CAAA;GACE,CAAA,IACH,IACF;EApDG,GAAA,KAAK,EAoDR;CAER,CAAC;AACC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../src/components/tabs/Tabs.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,eAAO,MAAM,IAAI,mHA6Gf,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import TabsIcon from "./tabs-icon/TabsIcon.js";
|
|
2
|
+
import { forwardRef, useId, useRef } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/tabs/Tabs.tsx
|
|
5
|
+
var Tabs = forwardRef(function Tabs({ addLabel, className, onAdd, tabs, ...rootProps }, ref) {
|
|
6
|
+
const baseId = useId();
|
|
7
|
+
const tabRefs = useRef([]);
|
|
8
|
+
const activeIndex = Math.max(0, tabs.findIndex((tab) => tab.isActive));
|
|
9
|
+
const rootClassName = ["chayns-tabs", className].filter(Boolean).join(" ");
|
|
10
|
+
const focusTab = (index) => {
|
|
11
|
+
const nextIndex = (index + tabs.length) % tabs.length;
|
|
12
|
+
tabs[nextIndex]?.onClick();
|
|
13
|
+
tabRefs.current[nextIndex]?.focus();
|
|
14
|
+
};
|
|
15
|
+
const handleKeyDown = (event, index) => {
|
|
16
|
+
if ((event.key === "Delete" || event.key === "Backspace") && tabs[index]?.onRemove) {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
tabs[index].onRemove();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (tabs.length === 0) return;
|
|
22
|
+
const nextIndex = event.key === "ArrowRight" || event.key === "ArrowDown" ? index + 1 : event.key === "ArrowLeft" || event.key === "ArrowUp" ? index - 1 : event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 : -1;
|
|
23
|
+
if (nextIndex === -1) return;
|
|
24
|
+
event.preventDefault();
|
|
25
|
+
focusTab(nextIndex);
|
|
26
|
+
};
|
|
27
|
+
const activeTab = tabs[activeIndex];
|
|
28
|
+
const activeTabId = activeTab ? `${baseId}-tab-${activeIndex}` : void 0;
|
|
29
|
+
const activePanelId = activeTab ? `${baseId}-panel-${activeIndex}` : void 0;
|
|
30
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
31
|
+
...rootProps,
|
|
32
|
+
className: rootClassName,
|
|
33
|
+
ref,
|
|
34
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
35
|
+
className: "chayns-tabs__bar",
|
|
36
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
37
|
+
className: "chayns-tabs__list",
|
|
38
|
+
role: "tablist",
|
|
39
|
+
children: tabs.map((tab, index) => {
|
|
40
|
+
const tabId = `${baseId}-tab-${index}`;
|
|
41
|
+
const panelId = `${baseId}-panel-${index}`;
|
|
42
|
+
const isSelected = index === activeIndex;
|
|
43
|
+
return /* @__PURE__ */ jsx("div", {
|
|
44
|
+
className: "chayns-tabs__item",
|
|
45
|
+
role: "presentation",
|
|
46
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
47
|
+
"aria-controls": isSelected ? panelId : void 0,
|
|
48
|
+
"aria-selected": isSelected,
|
|
49
|
+
className: `chayns-tabs__tab${isSelected ? " chayns-tabs__tab--active" : ""}`,
|
|
50
|
+
id: tabId,
|
|
51
|
+
onClick: (event) => {
|
|
52
|
+
if (event.target.closest("[data-tabs-remove]")) {
|
|
53
|
+
tab.onRemove?.();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
tab.onClick();
|
|
57
|
+
},
|
|
58
|
+
onKeyDown: (event) => handleKeyDown(event, index),
|
|
59
|
+
ref: (element) => {
|
|
60
|
+
tabRefs.current[index] = element;
|
|
61
|
+
},
|
|
62
|
+
role: "tab",
|
|
63
|
+
tabIndex: isSelected ? 0 : -1,
|
|
64
|
+
type: "button",
|
|
65
|
+
children: [
|
|
66
|
+
/* @__PURE__ */ jsx(TabsIcon, { icon: tab.icon }),
|
|
67
|
+
/* @__PURE__ */ jsx("span", {
|
|
68
|
+
className: "chayns-tabs__label",
|
|
69
|
+
children: tab.name
|
|
70
|
+
}),
|
|
71
|
+
tab.onRemove ? /* @__PURE__ */ jsx("span", {
|
|
72
|
+
"aria-hidden": "true",
|
|
73
|
+
className: "chayns-tabs__remove",
|
|
74
|
+
"data-tabs-remove": true,
|
|
75
|
+
children: /* @__PURE__ */ jsx(TabsIcon, { icon: "fa-xmark" })
|
|
76
|
+
}) : null
|
|
77
|
+
]
|
|
78
|
+
})
|
|
79
|
+
}, tabId);
|
|
80
|
+
})
|
|
81
|
+
}), onAdd && addLabel ? /* @__PURE__ */ jsx("button", {
|
|
82
|
+
"aria-label": addLabel,
|
|
83
|
+
className: "chayns-tabs__add",
|
|
84
|
+
onClick: onAdd,
|
|
85
|
+
type: "button",
|
|
86
|
+
children: /* @__PURE__ */ jsx(TabsIcon, { icon: "fa-plus" })
|
|
87
|
+
}) : null]
|
|
88
|
+
}), activeTab && activeTabId && activePanelId ? /* @__PURE__ */ jsx("div", {
|
|
89
|
+
"aria-labelledby": activeTabId,
|
|
90
|
+
className: "chayns-tabs__panel",
|
|
91
|
+
id: activePanelId,
|
|
92
|
+
role: "tabpanel",
|
|
93
|
+
tabIndex: 0,
|
|
94
|
+
children: activeTab.content
|
|
95
|
+
}) : null]
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
Tabs.displayName = "Tabs";
|
|
99
|
+
//#endregion
|
|
100
|
+
export { Tabs };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=Tabs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tabs.js","names":[],"sources":["../../../src/components/tabs/Tabs.tsx"],"sourcesContent":["import { forwardRef, useId, useRef } from 'react';\nimport type { KeyboardEvent } from 'react';\n\nimport type { TabsProps } from './Tabs.types.js';\nimport TabsIcon from './tabs-icon/TabsIcon.js';\n\nexport const Tabs = forwardRef<HTMLDivElement, TabsProps>(function Tabs(\n { addLabel, className, onAdd, tabs, ...rootProps },\n ref,\n) {\n const baseId = useId();\n const tabRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const activeIndex = Math.max(\n 0,\n tabs.findIndex((tab) => tab.isActive),\n );\n const rootClassName = ['chayns-tabs', className].filter(Boolean).join(' ');\n\n const focusTab = (index: number) => {\n const nextIndex = (index + tabs.length) % tabs.length;\n tabs[nextIndex]?.onClick();\n tabRefs.current[nextIndex]?.focus();\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n if ((event.key === 'Delete' || event.key === 'Backspace') && tabs[index]?.onRemove) {\n event.preventDefault();\n tabs[index].onRemove();\n return;\n }\n\n if (tabs.length === 0) return;\n\n const nextIndex =\n event.key === 'ArrowRight' || event.key === 'ArrowDown'\n ? index + 1\n : event.key === 'ArrowLeft' || event.key === 'ArrowUp'\n ? index - 1\n : event.key === 'Home'\n ? 0\n : event.key === 'End'\n ? tabs.length - 1\n : -1;\n\n if (nextIndex === -1) return;\n event.preventDefault();\n focusTab(nextIndex);\n };\n\n const activeTab = tabs[activeIndex];\n const activeTabId = activeTab ? `${baseId}-tab-${activeIndex}` : undefined;\n const activePanelId = activeTab ? `${baseId}-panel-${activeIndex}` : undefined;\n\n return (\n <div {...rootProps} className={rootClassName} ref={ref}>\n <div className=\"chayns-tabs__bar\">\n <div className=\"chayns-tabs__list\" role=\"tablist\">\n {tabs.map((tab, index) => {\n const tabId = `${baseId}-tab-${index}`;\n const panelId = `${baseId}-panel-${index}`;\n const isSelected = index === activeIndex;\n\n return (\n <div className=\"chayns-tabs__item\" key={tabId} role=\"presentation\">\n <button\n aria-controls={isSelected ? panelId : undefined}\n aria-selected={isSelected}\n className={`chayns-tabs__tab${isSelected ? ' chayns-tabs__tab--active' : ''}`}\n id={tabId}\n onClick={(event) => {\n if ((event.target as HTMLElement).closest('[data-tabs-remove]')) {\n tab.onRemove?.();\n return;\n }\n tab.onClick();\n }}\n onKeyDown={(event) => handleKeyDown(event, index)}\n ref={(element) => {\n tabRefs.current[index] = element;\n }}\n role=\"tab\"\n tabIndex={isSelected ? 0 : -1}\n type=\"button\"\n >\n <TabsIcon icon={tab.icon} />\n <span className=\"chayns-tabs__label\">{tab.name}</span>\n {tab.onRemove ? (\n <span aria-hidden=\"true\" className=\"chayns-tabs__remove\" data-tabs-remove>\n <TabsIcon icon=\"fa-xmark\" />\n </span>\n ) : null}\n </button>\n </div>\n );\n })}\n </div>\n {onAdd && addLabel ? (\n <button aria-label={addLabel} className=\"chayns-tabs__add\" onClick={onAdd} type=\"button\">\n <TabsIcon icon=\"fa-plus\" />\n </button>\n ) : null}\n </div>\n {activeTab && activeTabId && activePanelId ? (\n <div\n aria-labelledby={activeTabId}\n className=\"chayns-tabs__panel\"\n id={activePanelId}\n role=\"tabpanel\"\n tabIndex={0}\n >\n {activeTab.content}\n </div>\n ) : null}\n </div>\n );\n});\n\nTabs.displayName = 'Tabs';\n"],"mappings":";;;;AAMA,IAAa,OAAO,WAAsC,SAAS,KACjE,EAAE,UAAU,WAAW,OAAO,MAAM,GAAG,aACvC,KACA;CACA,MAAM,SAAS,MAAM;CACrB,MAAM,UAAU,OAAqC,CAAC,CAAC;CACvD,MAAM,cAAc,KAAK,IACvB,GACA,KAAK,WAAW,QAAQ,IAAI,QAAQ,CACtC;CACA,MAAM,gBAAgB,CAAC,eAAe,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAEzE,MAAM,YAAY,UAAkB;EAClC,MAAM,aAAa,QAAQ,KAAK,UAAU,KAAK;EAC/C,KAAK,UAAU,EAAE,QAAQ;EACzB,QAAQ,QAAQ,UAAU,EAAE,MAAM;CACpC;CAEA,MAAM,iBAAiB,OAAyC,UAAkB;EAChF,KAAK,MAAM,QAAQ,YAAY,MAAM,QAAQ,gBAAgB,KAAK,MAAM,EAAE,UAAU;GAClF,MAAM,eAAe;GACrB,KAAK,MAAM,CAAC,SAAS;GACrB;EACF;EAEA,IAAI,KAAK,WAAW,GAAG;EAEvB,MAAM,YACJ,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,cACxC,QAAQ,IACR,MAAM,QAAQ,eAAe,MAAM,QAAQ,YACzC,QAAQ,IACR,MAAM,QAAQ,SACZ,IACA,MAAM,QAAQ,QACZ,KAAK,SAAS,IACd;EAEZ,IAAI,cAAc,IAAI;EACtB,MAAM,eAAe;EACrB,SAAS,SAAS;CACpB;CAEA,MAAM,YAAY,KAAK;CACvB,MAAM,cAAc,YAAY,GAAG,OAAO,OAAO,gBAAgB,KAAA;CACjE,MAAM,gBAAgB,YAAY,GAAG,OAAO,SAAS,gBAAgB,KAAA;CAErE,OACE,qBAAC,OAAD;EAAK,GAAI;EAAW,WAAW;EAAoB;EAAnD,UAAA,CACE,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,oBAAC,OAAD;IAAK,WAAU;IAAoB,MAAK;IACrC,UAAA,KAAK,KAAK,KAAK,UAAU;KACxB,MAAM,QAAQ,GAAG,OAAO,OAAO;KAC/B,MAAM,UAAU,GAAG,OAAO,SAAS;KACnC,MAAM,aAAa,UAAU;KAE7B,OACE,oBAAC,OAAD;MAAK,WAAU;MAAgC,MAAK;MAClD,UAAA,qBAAC,UAAD;OACE,iBAAe,aAAa,UAAU,KAAA;OACtC,iBAAe;OACf,WAAW,mBAAmB,aAAa,8BAA8B;OACzE,IAAI;OACJ,UAAU,UAAU;QAClB,IAAK,MAAM,OAAuB,QAAQ,oBAAoB,GAAG;SAC/D,IAAI,WAAW;SACf;QACF;QACA,IAAI,QAAQ;OACd;OACA,YAAY,UAAU,cAAc,OAAO,KAAK;OAChD,MAAM,YAAY;QAChB,QAAQ,QAAQ,SAAS;OAC3B;OACA,MAAK;OACL,UAAU,aAAa,IAAI;OAC3B,MAAK;OAlBP,UAAA;QAoBE,oBAAC,UAAD,EAAU,MAAM,IAAI,KAAO,CAAA;QAC3B,oBAAC,QAAD;SAAM,WAAU;SAAsB,UAAA,IAAI;QAAW,CAAA;QACpD,IAAI,WACH,oBAAC,QAAD;SAAM,eAAY;SAAO,WAAU;SAAsB,oBAAA;SACvD,UAAA,oBAAC,UAAD,EAAU,MAAK,WAAY,CAAA;QACvB,CAAA,IACJ;OACE;;KACL,GA7BmC,KA6BnC;IAET,CAAC;GACE,CAAA,GACJ,SAAS,WACR,oBAAC,UAAD;IAAQ,cAAY;IAAU,WAAU;IAAmB,SAAS;IAAO,MAAK;IAC9E,UAAA,oBAAC,UAAD,EAAU,MAAK,UAAW,CAAA;GACpB,CAAA,IACN,IACD;EACJ,CAAA,GAAA,aAAa,eAAe,gBAC3B,oBAAC,OAAD;GACE,mBAAiB;GACjB,WAAU;GACV,IAAI;GACJ,MAAK;GACL,UAAU;GAET,UAAA,UAAU;EACR,CAAA,IACH,IACD;;AAET,CAAC;AAED,KAAK,cAAc"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef, ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* @description A FontAwesome Classic icon name in its Regular form, used for
|
|
4
|
+
* tab entry icons.
|
|
5
|
+
*/
|
|
6
|
+
export type TabsIcon = `fa-${string}`;
|
|
7
|
+
/**
|
|
8
|
+
* @description A single tab: its trigger button and the peer content it
|
|
9
|
+
* represents. Selection is externally owned — `Tabs` never infers or
|
|
10
|
+
* reconciles `isActive` itself; the consumer must update it on `onClick`.
|
|
11
|
+
*/
|
|
12
|
+
export interface TabsEntry {
|
|
13
|
+
/**
|
|
14
|
+
* @description FontAwesome Classic icon name shown on the tab trigger.
|
|
15
|
+
*/
|
|
16
|
+
icon: TabsIcon;
|
|
17
|
+
/**
|
|
18
|
+
* @description Visible, already-resolved localized tab label.
|
|
19
|
+
*/
|
|
20
|
+
name: string;
|
|
21
|
+
/**
|
|
22
|
+
* @description Whether this entry is the currently selected tab. Exactly
|
|
23
|
+
* one entry in a `tabs` array is expected to be active at a time; the
|
|
24
|
+
* consumer, not `Tabs`, is responsible for keeping this consistent.
|
|
25
|
+
*/
|
|
26
|
+
isActive: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* @description Called when this tab's trigger is activated. The consumer
|
|
29
|
+
* is expected to update `isActive` in response.
|
|
30
|
+
*/
|
|
31
|
+
onClick: () => void;
|
|
32
|
+
/**
|
|
33
|
+
* @description Optional remove handler. When supplied, a remove affordance
|
|
34
|
+
* is rendered as part of the same native tab button, and `Delete` or
|
|
35
|
+
* `Backspace` triggers it while the tab is focused.
|
|
36
|
+
*/
|
|
37
|
+
onRemove?: () => void;
|
|
38
|
+
/**
|
|
39
|
+
* @description Peer panel content for this tab. Only the active entry's
|
|
40
|
+
* `content` is rendered.
|
|
41
|
+
*/
|
|
42
|
+
content: ReactNode;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @description Props for a tabbed layout pattern that switches between peer
|
|
46
|
+
* content regions. `Tabs` owns no routing, persistence, business state or
|
|
47
|
+
* translation; selection state lives entirely in `tabs[].isActive`. Forwards
|
|
48
|
+
* remaining native `div` props (except `children`, which `Tabs` derives from
|
|
49
|
+
* `tabs`), `className` and `ref`.
|
|
50
|
+
*/
|
|
51
|
+
export interface TabsProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
|
|
52
|
+
/**
|
|
53
|
+
* @description Ordered tab entries; exactly one should have `isActive`
|
|
54
|
+
* `true`.
|
|
55
|
+
*/
|
|
56
|
+
tabs: TabsEntry[];
|
|
57
|
+
/**
|
|
58
|
+
* @description Optional handler that renders an add-tab trigger when
|
|
59
|
+
* supplied together with `addLabel`.
|
|
60
|
+
*/
|
|
61
|
+
onAdd?: () => void;
|
|
62
|
+
/**
|
|
63
|
+
* @description Localized accessible name for the add-tab trigger. Required
|
|
64
|
+
* for the add trigger to render; supplying `onAdd` without `addLabel` does
|
|
65
|
+
* not render the control.
|
|
66
|
+
*/
|
|
67
|
+
addLabel?: string;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=Tabs.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tabs.types.d.ts","sourceRoot":"","sources":["../../../src/components/tabs/Tabs.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,MAAM,EAAE,CAAC;AAEtC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,IAAI,EAAE,QAAQ,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB;;;OAGG;IACH,OAAO,EAAE,SAAS,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC/E;;;OAGG;IACH,IAAI,EAAE,SAAS,EAAE,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAEnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TabsIcon.d.ts","sourceRoot":"","sources":["../../../../src/components/tabs/tabs-icon/TabsIcon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,QAAA,MAAM,QAAQ,GAAI,UAAU;IAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;CAAE,gCAItD,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
//#region src/components/tabs/tabs-icon/TabsIcon.tsx
|
|
3
|
+
var TabsIcon = ({ icon }) => /* @__PURE__ */ jsx("span", {
|
|
4
|
+
"aria-hidden": "true",
|
|
5
|
+
className: "chayns-tabs__icon",
|
|
6
|
+
children: /* @__PURE__ */ jsx("i", { className: `far ${icon}` })
|
|
7
|
+
});
|
|
8
|
+
//#endregion
|
|
9
|
+
export { TabsIcon as default };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=TabsIcon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TabsIcon.js","names":[],"sources":["../../../../src/components/tabs/tabs-icon/TabsIcon.tsx"],"sourcesContent":["import type { TabsEntry } from '../Tabs.types.js';\n\nconst TabsIcon = ({ icon }: { icon: TabsEntry['icon'] }) => (\n <span aria-hidden=\"true\" className=\"chayns-tabs__icon\">\n <i className={`far ${icon}`} />\n </span>\n);\n\nexport default TabsIcon;\n"],"mappings":";;AAEA,IAAM,YAAY,EAAE,WAClB,oBAAC,QAAD;CAAM,eAAY;CAAO,WAAU;CACjC,UAAA,oBAAC,KAAD,EAAG,WAAW,OAAO,OAAS,CAAA;AAC1B,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AppLayout } from './components/app-layout/AppLayout.js';
|
|
2
|
+
export type { AppLayoutIcon, AppLayoutItem, AppLayoutProps, } from './components/app-layout/AppLayout.types.js';
|
|
3
|
+
export { Tabs } from './components/tabs/Tabs.js';
|
|
4
|
+
export type { TabsEntry, TabsIcon, TabsProps } from './components/tabs/Tabs.types.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AACjE,YAAY,EACV,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC"}
|
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chayns-ui/layout",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Accessible application layout components for chayns UI",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/TobitSoftware/chayns-ui.git",
|
|
10
|
+
"directory": "packages/layout"
|
|
11
|
+
},
|
|
12
|
+
"sideEffects": [
|
|
13
|
+
"**/*.css"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./app-layout.css": "./dist/app-layout.css",
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@chayns-ui/core": ">=0.2.1",
|
|
30
|
+
"react": ">=18 <20"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "vite build && node ../../tooling/copy-layout-css.mjs && tsc -b tsconfig.build.json --force",
|
|
37
|
+
"clean": "node ../../tooling/clean-package.mjs layout",
|
|
38
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
}
|
|
43
|
+
}
|