@nixxie-cms/core 2.1.0 → 2.2.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/admin-ui/components/dist/nixxie-cms-core-admin-ui-components.cjs.js +7 -7
- package/admin-ui/components/dist/nixxie-cms-core-admin-ui-components.esm.js +7 -7
- package/admin-ui/context/dist/nixxie-cms-core-admin-ui-context.cjs.js +2 -2
- package/admin-ui/context/dist/nixxie-cms-core-admin-ui-context.esm.js +2 -2
- package/admin-ui/utils/dist/nixxie-cms-core-admin-ui-utils.cjs.js +5 -5
- package/admin-ui/utils/dist/nixxie-cms-core-admin-ui-utils.esm.js +3 -3
- package/dist/{CreateItemDialog-7008b050.esm.js → CreateItemDialog-66621fe8.esm.js} +3 -3
- package/dist/{CreateItemDialog-a0cab315.cjs.js → CreateItemDialog-96b044ce.cjs.js} +4 -4
- package/dist/{Field-47f85161.esm.js → Field-1820c4e6.esm.js} +1 -0
- package/dist/{Field-ed8d7627.cjs.js → Field-38d3cdf9.cjs.js} +1 -0
- package/dist/GraphQLErrorNotice-7594a9f8.esm.js +64 -0
- package/dist/GraphQLErrorNotice-c8890f80.cjs.js +66 -0
- package/dist/{PageContainer-5ae731cc.esm.js → PageContainer-355cfbfa.esm.js} +362 -156
- package/dist/{PageContainer-abd7159f.cjs.js → PageContainer-4095555a.cjs.js} +361 -155
- package/dist/{context-af9957ed.esm.js → context-2924eaaa.esm.js} +53 -58
- package/dist/{context-b5204629.cjs.js → context-2ce61d0b.cjs.js} +53 -58
- package/dist/declarations/src/admin-ui/components/GraphQLErrorNotice.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/components/PageContainer.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/context.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/bigInt/views/index.d.ts +2 -1
- package/dist/declarations/src/fields/types/bigInt/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/bytes/views/index.d.ts +2 -1
- package/dist/declarations/src/fields/types/bytes/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/calendarDay/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/decimal/views/index.d.ts +2 -1
- package/dist/declarations/src/fields/types/decimal/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/float/views/index.d.ts +2 -1
- package/dist/declarations/src/fields/types/float/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/integer/views/index.d.ts +2 -1
- package/dist/declarations/src/fields/types/integer/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/json/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/multiselect/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/relationship/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/select/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/text/views/index.d.ts +2 -1
- package/dist/declarations/src/fields/types/text/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/timestamp/views/index.d.ts.map +1 -1
- package/dist/declarations/src/fields/types/virtual/views/index.d.ts.map +1 -1
- package/dist/declarations/src/internal-unstable/admin-ui/pages/HomePage/index.d.ts.map +1 -1
- package/dist/declarations/src/internal-unstable/admin-ui/pages/ItemPage/index.d.ts.map +1 -1
- package/dist/pick-4c785a54.esm.js +34 -0
- package/dist/pick-906341bb.cjs.js +37 -0
- package/dist/{useCreateItem-1f94d252.esm.js → useCreateItem-36a75f1c.esm.js} +26 -26
- package/dist/{useCreateItem-1be4987e.cjs.js → useCreateItem-acf06f77.cjs.js} +37 -37
- package/dist/{useFilter-acc9d413.cjs.js → useFilter-c29f17a8.cjs.js} +1 -1
- package/dist/{useFilter-9b6db1f9.esm.js → useFilter-f79b2abb.esm.js} +1 -1
- package/dist/{Fields-956d9a14.esm.js → usePreventNavigation-093389dd.esm.js} +28 -2
- package/dist/{Fields-e2c28056.cjs.js → usePreventNavigation-d4f9f4fa.cjs.js} +27 -0
- package/fields/types/bigInt/views/dist/nixxie-cms-core-fields-types-bigInt-views.cjs.js +8 -0
- package/fields/types/bigInt/views/dist/nixxie-cms-core-fields-types-bigInt-views.esm.js +8 -1
- package/fields/types/bytes/views/dist/nixxie-cms-core-fields-types-bytes-views.cjs.js +14 -3
- package/fields/types/bytes/views/dist/nixxie-cms-core-fields-types-bytes-views.esm.js +15 -5
- package/fields/types/calendarDay/views/dist/nixxie-cms-core-fields-types-calendarDay-views.cjs.js +2 -1
- package/fields/types/calendarDay/views/dist/nixxie-cms-core-fields-types-calendarDay-views.esm.js +2 -1
- package/fields/types/decimal/views/dist/nixxie-cms-core-fields-types-decimal-views.cjs.js +10 -1
- package/fields/types/decimal/views/dist/nixxie-cms-core-fields-types-decimal-views.esm.js +10 -2
- package/fields/types/file/views/dist/nixxie-cms-core-fields-types-file-views.cjs.js +1 -1
- package/fields/types/file/views/dist/nixxie-cms-core-fields-types-file-views.esm.js +2 -2
- package/fields/types/float/views/dist/nixxie-cms-core-fields-types-float-views.cjs.js +10 -1
- package/fields/types/float/views/dist/nixxie-cms-core-fields-types-float-views.esm.js +10 -2
- package/fields/types/image/views/dist/nixxie-cms-core-fields-types-image-views.cjs.js +2 -1
- package/fields/types/image/views/dist/nixxie-cms-core-fields-types-image-views.esm.js +2 -1
- package/fields/types/integer/views/dist/nixxie-cms-core-fields-types-integer-views.cjs.js +8 -0
- package/fields/types/integer/views/dist/nixxie-cms-core-fields-types-integer-views.esm.js +8 -1
- package/fields/types/json/views/dist/nixxie-cms-core-fields-types-json-views.cjs.js +19 -4
- package/fields/types/json/views/dist/nixxie-cms-core-fields-types-json-views.esm.js +19 -4
- package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.cjs.js +18 -3
- package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.esm.js +18 -3
- package/fields/types/password/views/dist/nixxie-cms-core-fields-types-password-views.cjs.js +1 -1
- package/fields/types/password/views/dist/nixxie-cms-core-fields-types-password-views.esm.js +1 -1
- package/fields/types/relationship/views/dist/nixxie-cms-core-fields-types-relationship-views.cjs.js +9 -7
- package/fields/types/relationship/views/dist/nixxie-cms-core-fields-types-relationship-views.esm.js +9 -7
- package/fields/types/select/views/dist/nixxie-cms-core-fields-types-select-views.cjs.js +3 -2
- package/fields/types/select/views/dist/nixxie-cms-core-fields-types-select-views.esm.js +3 -2
- package/fields/types/text/views/dist/nixxie-cms-core-fields-types-text-views.cjs.js +14 -3
- package/fields/types/text/views/dist/nixxie-cms-core-fields-types-text-views.esm.js +15 -5
- package/fields/types/timestamp/views/dist/nixxie-cms-core-fields-types-timestamp-views.cjs.js +2 -1
- package/fields/types/timestamp/views/dist/nixxie-cms-core-fields-types-timestamp-views.esm.js +2 -1
- package/fields/types/virtual/views/dist/nixxie-cms-core-fields-types-virtual-views.cjs.js +13 -1
- package/fields/types/virtual/views/dist/nixxie-cms-core-fields-types-virtual-views.esm.js +14 -2
- package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.cjs.js +3 -3
- package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.esm.js +3 -3
- package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.cjs.js +7 -7
- package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.esm.js +6 -6
- package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.cjs.js +34 -33
- package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.esm.js +35 -34
- package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.cjs.js +53 -13
- package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.esm.js +52 -12
- package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.cjs.js +36 -25
- package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.esm.js +36 -25
- package/package.json +2 -2
- package/src/admin-ui/components/CommandPalette.tsx +134 -27
- package/src/admin-ui/components/CreateButtonLink.tsx +20 -46
- package/src/admin-ui/components/GraphQLErrorNotice.tsx +39 -33
- package/src/admin-ui/components/Logo.tsx +5 -5
- package/src/admin-ui/components/Navigation.tsx +41 -27
- package/src/admin-ui/components/PageContainer.tsx +171 -15
- package/src/admin-ui/components/WelcomeDialog.tsx +14 -14
- package/src/admin-ui/context.tsx +5 -2
- package/src/admin-ui/utils/useCreateItem.ts +21 -1
- package/src/fields/types/bigInt/views/index.tsx +10 -1
- package/src/fields/types/bytes/views/index.tsx +14 -1
- package/src/fields/types/calendarDay/views/index.tsx +2 -1
- package/src/fields/types/decimal/views/index.tsx +7 -1
- package/src/fields/types/file/views/Field.tsx +1 -1
- package/src/fields/types/float/views/index.tsx +7 -1
- package/src/fields/types/image/views/index.tsx +1 -1
- package/src/fields/types/integer/views/index.tsx +5 -0
- package/src/fields/types/json/views/index.tsx +20 -2
- package/src/fields/types/multiselect/views/index.tsx +7 -3
- package/src/fields/types/password/views/index.tsx +1 -1
- package/src/fields/types/relationship/views/index.tsx +1 -0
- package/src/fields/types/select/views/index.tsx +2 -1
- package/src/fields/types/text/views/index.tsx +14 -1
- package/src/fields/types/timestamp/views/index.tsx +2 -1
- package/src/fields/types/virtual/views/index.tsx +17 -2
- package/src/internal-unstable/admin-ui/pages/HomePage/index.tsx +40 -31
- package/src/internal-unstable/admin-ui/pages/ItemPage/index.tsx +36 -3
- package/src/internal-unstable/admin-ui/pages/ListPage/PaginationControls.tsx +20 -33
- package/src/internal-unstable/admin-ui/pages/ListPage/index.tsx +24 -16
- package/dist/GraphQLErrorNotice-cd74180d.cjs.js +0 -57
- package/dist/GraphQLErrorNotice-d9f0931b.esm.js +0 -55
- package/dist/pick-5fe45878.cjs.js +0 -71
- package/dist/pick-b7ef3115.esm.js +0 -68
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import NextHead from 'next/head';
|
|
2
2
|
import { useState, useRef, useMemo, useEffect } from 'react';
|
|
3
|
-
import { css } from '@keystar/ui/style';
|
|
3
|
+
import { css, tokenSchema } from '@keystar/ui/style';
|
|
4
4
|
import NextLink from 'next/link';
|
|
5
|
-
import { u as useNixxie } from './context-
|
|
5
|
+
import { u as useNixxie } from './context-2924eaaa.esm.js';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
import { useRouter } from 'next/router';
|
|
8
|
+
import { useId } from '@react-aria/utils';
|
|
8
9
|
|
|
9
10
|
function Logo() {
|
|
10
11
|
var _adminConfig$componen;
|
|
@@ -25,7 +26,7 @@ function DefaultLogo() {
|
|
|
25
26
|
outline: 0,
|
|
26
27
|
flexShrink: 0,
|
|
27
28
|
'&:focus-visible': {
|
|
28
|
-
outline:
|
|
29
|
+
outline: `2px solid ${tokenSchema.color.scale.black}`,
|
|
29
30
|
outlineOffset: 3,
|
|
30
31
|
borderRadius: 4
|
|
31
32
|
}
|
|
@@ -38,7 +39,7 @@ function DefaultLogo() {
|
|
|
38
39
|
width: 28,
|
|
39
40
|
height: 28,
|
|
40
41
|
borderRadius: 6,
|
|
41
|
-
backgroundColor:
|
|
42
|
+
backgroundColor: tokenSchema.color.scale.black,
|
|
42
43
|
flexShrink: 0
|
|
43
44
|
}),
|
|
44
45
|
children: /*#__PURE__*/jsx("svg", {
|
|
@@ -48,7 +49,7 @@ function DefaultLogo() {
|
|
|
48
49
|
fill: "none",
|
|
49
50
|
children: /*#__PURE__*/jsx("path", {
|
|
50
51
|
d: "M2.5 11V3L11.5 11V3",
|
|
51
|
-
stroke:
|
|
52
|
+
stroke: tokenSchema.color.scale.white,
|
|
52
53
|
strokeWidth: "1.8",
|
|
53
54
|
strokeLinecap: "round",
|
|
54
55
|
strokeLinejoin: "round"
|
|
@@ -59,7 +60,7 @@ function DefaultLogo() {
|
|
|
59
60
|
fontSize: 14,
|
|
60
61
|
fontWeight: 600,
|
|
61
62
|
letterSpacing: '-0.03em',
|
|
62
|
-
color:
|
|
63
|
+
color: tokenSchema.color.foreground.neutralEmphasis,
|
|
63
64
|
lineHeight: 1,
|
|
64
65
|
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif"
|
|
65
66
|
}),
|
|
@@ -83,8 +84,10 @@ function NavItem({
|
|
|
83
84
|
indent
|
|
84
85
|
}) {
|
|
85
86
|
const router = useRouter();
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
// Active on an exact match, or when the current route is nested under `href`
|
|
88
|
+
// (prefix match on a path boundary). Dashboard ('/') is only active on an
|
|
89
|
+
// exact match so it doesn't light up for every nested route.
|
|
90
|
+
const isActive = router.pathname === href || href !== '/' && router.pathname.startsWith(href + '/');
|
|
88
91
|
return /*#__PURE__*/jsxs("a", {
|
|
89
92
|
href: href,
|
|
90
93
|
onClick: e => {
|
|
@@ -101,8 +104,8 @@ function NavItem({
|
|
|
101
104
|
paddingBlock: '7px',
|
|
102
105
|
fontSize: 13,
|
|
103
106
|
fontWeight: isActive ? 500 : 400,
|
|
104
|
-
color: isActive ?
|
|
105
|
-
backgroundColor: isActive ?
|
|
107
|
+
color: isActive ? tokenSchema.color.foreground.neutralEmphasis : tokenSchema.color.foreground.neutralSecondary,
|
|
108
|
+
backgroundColor: isActive ? tokenSchema.color.background.surfaceSecondary : 'transparent',
|
|
106
109
|
textDecoration: 'none',
|
|
107
110
|
borderRadius: '0 6px 6px 0',
|
|
108
111
|
marginRight: '12px',
|
|
@@ -111,8 +114,8 @@ function NavItem({
|
|
|
111
114
|
userSelect: 'none',
|
|
112
115
|
transition: 'color 120ms, background-color 120ms',
|
|
113
116
|
'&:hover': {
|
|
114
|
-
color:
|
|
115
|
-
backgroundColor:
|
|
117
|
+
color: tokenSchema.color.foreground.neutralEmphasis,
|
|
118
|
+
backgroundColor: tokenSchema.color.background.surfaceSecondary
|
|
116
119
|
}
|
|
117
120
|
}),
|
|
118
121
|
children: [isActive && /*#__PURE__*/jsx("span", {
|
|
@@ -123,7 +126,7 @@ function NavItem({
|
|
|
123
126
|
bottom: '20%',
|
|
124
127
|
width: 2,
|
|
125
128
|
borderRadius: '0 2px 2px 0',
|
|
126
|
-
backgroundColor:
|
|
129
|
+
backgroundColor: tokenSchema.color.scale.black
|
|
127
130
|
})
|
|
128
131
|
}), /*#__PURE__*/jsx("span", {
|
|
129
132
|
className: css({
|
|
@@ -158,7 +161,7 @@ function SectionLabel({
|
|
|
158
161
|
fontWeight: 600,
|
|
159
162
|
letterSpacing: '0.10em',
|
|
160
163
|
textTransform: 'uppercase',
|
|
161
|
-
color:
|
|
164
|
+
color: tokenSchema.color.foreground.neutralSecondary
|
|
162
165
|
}),
|
|
163
166
|
children: children
|
|
164
167
|
}), action]
|
|
@@ -175,7 +178,7 @@ function NavDivider() {
|
|
|
175
178
|
height: 1,
|
|
176
179
|
marginInline: '16px',
|
|
177
180
|
marginBlock: '6px',
|
|
178
|
-
backgroundColor:
|
|
181
|
+
backgroundColor: tokenSchema.color.border.muted
|
|
179
182
|
})
|
|
180
183
|
});
|
|
181
184
|
}
|
|
@@ -199,20 +202,20 @@ function SearchTrigger({
|
|
|
199
202
|
paddingInline: '10px',
|
|
200
203
|
paddingBlock: '7px',
|
|
201
204
|
borderRadius: 6,
|
|
202
|
-
border:
|
|
203
|
-
backgroundColor:
|
|
204
|
-
color:
|
|
205
|
+
border: `1px solid ${tokenSchema.color.border.muted}`,
|
|
206
|
+
backgroundColor: tokenSchema.color.background.surfaceSecondary,
|
|
207
|
+
color: tokenSchema.color.foreground.neutralSecondary,
|
|
205
208
|
fontSize: 12.5,
|
|
206
209
|
cursor: 'pointer',
|
|
207
210
|
textAlign: 'left',
|
|
208
211
|
transition: 'border-color 140ms, background 140ms, color 140ms',
|
|
209
212
|
'&:hover': {
|
|
210
|
-
borderColor:
|
|
211
|
-
backgroundColor:
|
|
212
|
-
color:
|
|
213
|
+
borderColor: tokenSchema.color.border.emphasis,
|
|
214
|
+
backgroundColor: tokenSchema.color.background.surfaceSecondary,
|
|
215
|
+
color: tokenSchema.color.foreground.neutralSecondary
|
|
213
216
|
},
|
|
214
217
|
'&:focus-visible': {
|
|
215
|
-
outline:
|
|
218
|
+
outline: `2px solid ${tokenSchema.color.scale.black}`,
|
|
216
219
|
outlineOffset: 2
|
|
217
220
|
}
|
|
218
221
|
}),
|
|
@@ -244,7 +247,7 @@ function SearchTrigger({
|
|
|
244
247
|
}), /*#__PURE__*/jsx("kbd", {
|
|
245
248
|
className: css({
|
|
246
249
|
fontSize: 10.5,
|
|
247
|
-
color:
|
|
250
|
+
color: tokenSchema.color.foreground.neutralSecondary,
|
|
248
251
|
fontFamily: 'inherit',
|
|
249
252
|
letterSpacing: '0.02em'
|
|
250
253
|
}),
|
|
@@ -276,18 +279,18 @@ function NavSearch({
|
|
|
276
279
|
paddingInline: '10px',
|
|
277
280
|
paddingBlock: '5px',
|
|
278
281
|
fontSize: 12.5,
|
|
279
|
-
border:
|
|
282
|
+
border: `1px solid ${tokenSchema.color.border.muted}`,
|
|
280
283
|
borderRadius: 5,
|
|
281
|
-
backgroundColor:
|
|
282
|
-
color:
|
|
284
|
+
backgroundColor: tokenSchema.color.background.surfaceSecondary,
|
|
285
|
+
color: tokenSchema.color.foreground.neutralEmphasis,
|
|
283
286
|
outline: 'none',
|
|
284
287
|
boxSizing: 'border-box',
|
|
285
288
|
transition: 'border-color 140ms',
|
|
286
289
|
'&:focus': {
|
|
287
|
-
borderColor:
|
|
290
|
+
borderColor: tokenSchema.color.alias.borderHovered
|
|
288
291
|
},
|
|
289
292
|
'&::placeholder': {
|
|
290
|
-
color:
|
|
293
|
+
color: tokenSchema.color.foreground.neutralTertiary
|
|
291
294
|
},
|
|
292
295
|
'&::-webkit-search-cancel-button': {
|
|
293
296
|
display: 'none'
|
|
@@ -321,7 +324,7 @@ function CollectionsSection({
|
|
|
321
324
|
className: css({
|
|
322
325
|
margin: '4px 16px',
|
|
323
326
|
fontSize: 12.5,
|
|
324
|
-
color:
|
|
327
|
+
color: tokenSchema.color.foreground.neutralSecondary
|
|
325
328
|
}),
|
|
326
329
|
children: "No results"
|
|
327
330
|
}) : filtered.map(list => /*#__PURE__*/jsxs(NavItem, {
|
|
@@ -332,7 +335,7 @@ function CollectionsSection({
|
|
|
332
335
|
marginLeft: 6,
|
|
333
336
|
fontSize: 10,
|
|
334
337
|
fontWeight: 500,
|
|
335
|
-
color:
|
|
338
|
+
color: tokenSchema.color.foreground.neutralSecondary,
|
|
336
339
|
letterSpacing: '0.04em',
|
|
337
340
|
textTransform: 'uppercase'
|
|
338
341
|
}),
|
|
@@ -394,7 +397,7 @@ function Navigation({
|
|
|
394
397
|
className: css({
|
|
395
398
|
margin: '8px 16px',
|
|
396
399
|
fontSize: 12.5,
|
|
397
|
-
color:
|
|
400
|
+
color: tokenSchema.color.foreground.neutralSecondary
|
|
398
401
|
}),
|
|
399
402
|
children: "No collections"
|
|
400
403
|
}), process.env.NODE_ENV !== 'production' && apiPath && /*#__PURE__*/jsxs(Fragment, {
|
|
@@ -441,7 +444,7 @@ function NavFooter({
|
|
|
441
444
|
className: css({
|
|
442
445
|
paddingInline: '12px',
|
|
443
446
|
paddingBlock: '10px',
|
|
444
|
-
borderTop:
|
|
447
|
+
borderTop: `1px solid ${tokenSchema.color.border.muted}`,
|
|
445
448
|
marginTop: 'auto'
|
|
446
449
|
}),
|
|
447
450
|
children: children
|
|
@@ -498,6 +501,8 @@ function CommandPalette({
|
|
|
498
501
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
499
502
|
const inputRef = useRef(null);
|
|
500
503
|
const listRef = useRef(null);
|
|
504
|
+
const panelRef = useRef(null);
|
|
505
|
+
const listboxId = useId();
|
|
501
506
|
const commands = useCommands();
|
|
502
507
|
const filtered = useMemo(() => {
|
|
503
508
|
if (!query.trim()) return commands;
|
|
@@ -525,11 +530,29 @@ function CommandPalette({
|
|
|
525
530
|
if (!isOpen) return;
|
|
526
531
|
setQuery('');
|
|
527
532
|
setActiveIndex(0);
|
|
533
|
+
|
|
534
|
+
// FOCUS RESTORE: remember what was focused before the palette opened.
|
|
535
|
+
const previouslyFocused = document.activeElement;
|
|
536
|
+
|
|
537
|
+
// SCROLL LOCK: prevent the page behind the modal from scrolling.
|
|
538
|
+
const prevOverflow = document.body.style.overflow;
|
|
539
|
+
document.body.style.overflow = 'hidden';
|
|
528
540
|
const t = setTimeout(() => {
|
|
529
541
|
var _inputRef$current;
|
|
530
542
|
return (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
|
|
531
543
|
}, 10);
|
|
532
|
-
return () =>
|
|
544
|
+
return () => {
|
|
545
|
+
clearTimeout(t);
|
|
546
|
+
// Restore scroll position behaviour.
|
|
547
|
+
document.body.style.overflow = prevOverflow;
|
|
548
|
+
// Restore focus to whatever held it before we opened.
|
|
549
|
+
try {
|
|
550
|
+
var _previouslyFocused$fo;
|
|
551
|
+
previouslyFocused === null || previouslyFocused === void 0 || (_previouslyFocused$fo = previouslyFocused.focus) === null || _previouslyFocused$fo === void 0 || _previouslyFocused$fo.call(previouslyFocused);
|
|
552
|
+
} catch {
|
|
553
|
+
/* element may be gone from the DOM — ignore */
|
|
554
|
+
}
|
|
555
|
+
};
|
|
533
556
|
}, [isOpen]);
|
|
534
557
|
useEffect(() => {
|
|
535
558
|
setActiveIndex(0);
|
|
@@ -551,6 +574,41 @@ function CommandPalette({
|
|
|
551
574
|
cmd.action();
|
|
552
575
|
}
|
|
553
576
|
}
|
|
577
|
+
|
|
578
|
+
// FOCUS TRAP + panel-level ESCAPE. Runs for any child of the panel so the
|
|
579
|
+
// modal closes regardless of which element currently holds focus, and Tab /
|
|
580
|
+
// Shift+Tab stay contained within the panel's focusable descendants.
|
|
581
|
+
function onPanelKeyDown(e) {
|
|
582
|
+
if (e.key === 'Escape') {
|
|
583
|
+
e.preventDefault();
|
|
584
|
+
onClose();
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (e.key !== 'Tab') return;
|
|
588
|
+
const panel = panelRef.current;
|
|
589
|
+
if (!panel) return;
|
|
590
|
+
let focusables;
|
|
591
|
+
try {
|
|
592
|
+
focusables = Array.from(panel.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])')).filter(el => el.offsetParent !== null || el === document.activeElement);
|
|
593
|
+
} catch {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
if (focusables.length === 0) return;
|
|
597
|
+
const first = focusables[0];
|
|
598
|
+
const last = focusables[focusables.length - 1];
|
|
599
|
+
const current = document.activeElement;
|
|
600
|
+
if (e.shiftKey) {
|
|
601
|
+
if (current === first || !panel.contains(current)) {
|
|
602
|
+
e.preventDefault();
|
|
603
|
+
last.focus();
|
|
604
|
+
}
|
|
605
|
+
} else {
|
|
606
|
+
if (current === last || !panel.contains(current)) {
|
|
607
|
+
e.preventDefault();
|
|
608
|
+
first.focus();
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
554
612
|
function onKeyDown(e) {
|
|
555
613
|
if (e.key === 'ArrowDown') {
|
|
556
614
|
e.preventDefault();
|
|
@@ -574,6 +632,12 @@ function CommandPalette({
|
|
|
574
632
|
flatList.forEach((cmd, i) => map.set(cmd.id, i));
|
|
575
633
|
return map;
|
|
576
634
|
}, [flatList]);
|
|
635
|
+
|
|
636
|
+
// Stable per-option id derived from the listbox id, used for both the rendered
|
|
637
|
+
// <li role="option"> ids and the input's aria-activedescendant.
|
|
638
|
+
const optionId = cmdId => `${listboxId}-opt-${cmdId}`;
|
|
639
|
+
const activeCommand = flatList[activeIndex];
|
|
640
|
+
const activeOptionId = activeCommand ? optionId(activeCommand.id) : undefined;
|
|
577
641
|
if (!isOpen) return null;
|
|
578
642
|
return /*#__PURE__*/jsxs(Fragment, {
|
|
579
643
|
children: [/*#__PURE__*/jsx("div", {
|
|
@@ -581,14 +645,16 @@ function CommandPalette({
|
|
|
581
645
|
className: css({
|
|
582
646
|
position: 'fixed',
|
|
583
647
|
inset: 0,
|
|
584
|
-
backgroundColor:
|
|
648
|
+
backgroundColor: tokenSchema.color.alias.blanket,
|
|
585
649
|
zIndex: 200,
|
|
586
650
|
backdropFilter: 'blur(3px)'
|
|
587
651
|
})
|
|
588
652
|
}), /*#__PURE__*/jsxs("div", {
|
|
653
|
+
ref: panelRef,
|
|
589
654
|
role: "dialog",
|
|
590
655
|
"aria-modal": "true",
|
|
591
656
|
"aria-label": "Command palette",
|
|
657
|
+
onKeyDown: onPanelKeyDown,
|
|
592
658
|
className: css({
|
|
593
659
|
position: 'fixed',
|
|
594
660
|
top: '14vh',
|
|
@@ -599,7 +665,7 @@ function CommandPalette({
|
|
|
599
665
|
maxHeight: '62vh',
|
|
600
666
|
display: 'flex',
|
|
601
667
|
flexDirection: 'column',
|
|
602
|
-
backgroundColor:
|
|
668
|
+
backgroundColor: tokenSchema.color.background.canvas,
|
|
603
669
|
borderRadius: 10,
|
|
604
670
|
boxShadow: '0 24px 64px rgba(0,0,0,0.22), 0 0 0 1px rgba(0,0,0,0.07)',
|
|
605
671
|
zIndex: 201,
|
|
@@ -611,16 +677,17 @@ function CommandPalette({
|
|
|
611
677
|
alignItems: 'center',
|
|
612
678
|
gap: 10,
|
|
613
679
|
padding: '12px 16px',
|
|
614
|
-
borderBottom:
|
|
680
|
+
borderBottom: `1px solid ${tokenSchema.color.border.muted}`
|
|
615
681
|
}),
|
|
616
682
|
children: [/*#__PURE__*/jsxs("svg", {
|
|
617
683
|
width: "15",
|
|
618
684
|
height: "15",
|
|
619
685
|
viewBox: "0 0 15 15",
|
|
620
686
|
fill: "none",
|
|
687
|
+
"aria-hidden": "true",
|
|
621
688
|
style: {
|
|
622
689
|
flexShrink: 0,
|
|
623
|
-
color:
|
|
690
|
+
color: tokenSchema.color.foreground.neutralSecondary
|
|
624
691
|
},
|
|
625
692
|
children: [/*#__PURE__*/jsx("circle", {
|
|
626
693
|
cx: "6.5",
|
|
@@ -643,16 +710,22 @@ function CommandPalette({
|
|
|
643
710
|
onChange: e => setQuery(e.target.value),
|
|
644
711
|
onKeyDown: onKeyDown,
|
|
645
712
|
placeholder: "Search commands, lists, actions\u2026",
|
|
713
|
+
role: "combobox",
|
|
714
|
+
"aria-label": "Search commands",
|
|
715
|
+
"aria-expanded": flatList.length > 0,
|
|
716
|
+
"aria-controls": listboxId,
|
|
717
|
+
"aria-autocomplete": "list",
|
|
718
|
+
"aria-activedescendant": activeOptionId,
|
|
646
719
|
className: css({
|
|
647
720
|
flex: 1,
|
|
648
721
|
border: 'none',
|
|
649
722
|
outline: 'none',
|
|
650
723
|
fontSize: 14.5,
|
|
651
|
-
color:
|
|
724
|
+
color: tokenSchema.color.foreground.neutralEmphasis,
|
|
652
725
|
background: 'transparent',
|
|
653
726
|
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif",
|
|
654
727
|
'&::placeholder': {
|
|
655
|
-
color:
|
|
728
|
+
color: tokenSchema.color.foreground.neutralSecondary
|
|
656
729
|
}
|
|
657
730
|
})
|
|
658
731
|
}), /*#__PURE__*/jsx("kbd", {
|
|
@@ -661,11 +734,11 @@ function CommandPalette({
|
|
|
661
734
|
alignItems: 'center',
|
|
662
735
|
gap: 2,
|
|
663
736
|
padding: '2px 7px',
|
|
664
|
-
backgroundColor:
|
|
665
|
-
border:
|
|
737
|
+
backgroundColor: tokenSchema.color.background.surfaceSecondary,
|
|
738
|
+
border: `1px solid ${tokenSchema.color.border.muted}`,
|
|
666
739
|
borderRadius: 5,
|
|
667
740
|
fontSize: 11,
|
|
668
|
-
color:
|
|
741
|
+
color: tokenSchema.color.foreground.neutralSecondary,
|
|
669
742
|
fontFamily: 'inherit',
|
|
670
743
|
flexShrink: 0
|
|
671
744
|
}),
|
|
@@ -673,7 +746,9 @@ function CommandPalette({
|
|
|
673
746
|
})]
|
|
674
747
|
}), /*#__PURE__*/jsx("ul", {
|
|
675
748
|
ref: listRef,
|
|
749
|
+
id: listboxId,
|
|
676
750
|
role: "listbox",
|
|
751
|
+
"aria-label": "Commands",
|
|
677
752
|
className: css({
|
|
678
753
|
flex: 1,
|
|
679
754
|
overflowY: 'auto',
|
|
@@ -684,7 +759,7 @@ function CommandPalette({
|
|
|
684
759
|
width: 4
|
|
685
760
|
},
|
|
686
761
|
'&::-webkit-scrollbar-thumb': {
|
|
687
|
-
background:
|
|
762
|
+
background: tokenSchema.color.border.muted,
|
|
688
763
|
borderRadius: 4
|
|
689
764
|
}
|
|
690
765
|
}),
|
|
@@ -692,124 +767,131 @@ function CommandPalette({
|
|
|
692
767
|
className: css({
|
|
693
768
|
padding: '28px 16px',
|
|
694
769
|
textAlign: 'center',
|
|
695
|
-
color:
|
|
770
|
+
color: tokenSchema.color.foreground.neutralSecondary,
|
|
696
771
|
fontSize: 13.5
|
|
697
772
|
}),
|
|
698
773
|
children: ["No results for \u201C", query, "\u201D"]
|
|
699
|
-
}) : Array.from(grouped.entries()).map(([category, items]) =>
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
backgroundColor: isActive ? '#0a0a0a' : 'transparent',
|
|
736
|
-
transition: 'background 100ms',
|
|
737
|
-
'&:hover': {
|
|
738
|
-
backgroundColor: isActive ? '#0a0a0a' : '#f5f5f5'
|
|
739
|
-
}
|
|
740
|
-
}),
|
|
741
|
-
children: [/*#__PURE__*/jsx("span", {
|
|
774
|
+
}) : Array.from(grouped.entries()).map(([category, items], groupIdx) => {
|
|
775
|
+
const headerId = `${listboxId}-cat-${groupIdx}`;
|
|
776
|
+
return /*#__PURE__*/jsxs("li", {
|
|
777
|
+
role: "presentation",
|
|
778
|
+
children: [/*#__PURE__*/jsx("p", {
|
|
779
|
+
id: headerId,
|
|
780
|
+
role: "presentation",
|
|
781
|
+
className: css({
|
|
782
|
+
margin: 0,
|
|
783
|
+
padding: '8px 16px 3px',
|
|
784
|
+
fontSize: 10,
|
|
785
|
+
fontWeight: 600,
|
|
786
|
+
letterSpacing: '0.10em',
|
|
787
|
+
textTransform: 'uppercase',
|
|
788
|
+
color: tokenSchema.color.foreground.neutralSecondary
|
|
789
|
+
}),
|
|
790
|
+
children: category
|
|
791
|
+
}), /*#__PURE__*/jsx("ul", {
|
|
792
|
+
role: "group",
|
|
793
|
+
"aria-labelledby": headerId,
|
|
794
|
+
className: css({
|
|
795
|
+
listStyle: 'none',
|
|
796
|
+
margin: 0,
|
|
797
|
+
padding: 0
|
|
798
|
+
}),
|
|
799
|
+
children: items.map(cmd => {
|
|
800
|
+
var _cmdFlatIndex$get;
|
|
801
|
+
const idx = (_cmdFlatIndex$get = cmdFlatIndex.get(cmd.id)) !== null && _cmdFlatIndex$get !== void 0 ? _cmdFlatIndex$get : 0;
|
|
802
|
+
const isActive = idx === activeIndex;
|
|
803
|
+
return /*#__PURE__*/jsxs("li", {
|
|
804
|
+
id: optionId(cmd.id),
|
|
805
|
+
role: "option",
|
|
806
|
+
"aria-selected": isActive,
|
|
807
|
+
"data-active": isActive,
|
|
808
|
+
onClick: () => runCommand(cmd),
|
|
809
|
+
onMouseEnter: () => setActiveIndex(idx),
|
|
742
810
|
className: css({
|
|
743
|
-
display: '
|
|
811
|
+
display: 'flex',
|
|
744
812
|
alignItems: 'center',
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
flexShrink: 0,
|
|
754
|
-
transition: 'background 100ms, color 100ms',
|
|
755
|
-
fontFamily: 'monospace'
|
|
756
|
-
}),
|
|
757
|
-
children: cmd.icon
|
|
758
|
-
}), /*#__PURE__*/jsxs("span", {
|
|
759
|
-
className: css({
|
|
760
|
-
flex: 1,
|
|
761
|
-
minWidth: 0
|
|
813
|
+
gap: 10,
|
|
814
|
+
padding: '7px 16px',
|
|
815
|
+
cursor: 'pointer',
|
|
816
|
+
backgroundColor: isActive ? tokenSchema.color.scale.black : 'transparent',
|
|
817
|
+
transition: 'background 100ms',
|
|
818
|
+
'&:hover': {
|
|
819
|
+
backgroundColor: isActive ? tokenSchema.color.scale.black : tokenSchema.color.background.surfaceSecondary
|
|
820
|
+
}
|
|
762
821
|
}),
|
|
763
822
|
children: [/*#__PURE__*/jsx("span", {
|
|
764
823
|
className: css({
|
|
765
|
-
display: '
|
|
766
|
-
|
|
824
|
+
display: 'inline-flex',
|
|
825
|
+
alignItems: 'center',
|
|
826
|
+
justifyContent: 'center',
|
|
827
|
+
width: 28,
|
|
828
|
+
height: 28,
|
|
829
|
+
borderRadius: 6,
|
|
830
|
+
backgroundColor: isActive ? '#2a2a2a' : tokenSchema.color.background.surfaceSecondary,
|
|
831
|
+
fontSize: 12,
|
|
767
832
|
fontWeight: 500,
|
|
768
|
-
color: isActive ?
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
transition: 'color 100ms'
|
|
833
|
+
color: isActive ? tokenSchema.color.foreground.onEmphasis : tokenSchema.color.foreground.neutralSecondary,
|
|
834
|
+
flexShrink: 0,
|
|
835
|
+
transition: 'background 100ms, color 100ms',
|
|
836
|
+
fontFamily: 'monospace'
|
|
773
837
|
}),
|
|
774
|
-
children: cmd.
|
|
775
|
-
}),
|
|
838
|
+
children: cmd.icon
|
|
839
|
+
}), /*#__PURE__*/jsxs("span", {
|
|
776
840
|
className: css({
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
841
|
+
flex: 1,
|
|
842
|
+
minWidth: 0
|
|
843
|
+
}),
|
|
844
|
+
children: [/*#__PURE__*/jsx("span", {
|
|
845
|
+
className: css({
|
|
846
|
+
display: 'block',
|
|
847
|
+
fontSize: 13.5,
|
|
848
|
+
fontWeight: 500,
|
|
849
|
+
color: isActive ? tokenSchema.color.foreground.onEmphasis : tokenSchema.color.foreground.neutralEmphasis,
|
|
850
|
+
whiteSpace: 'nowrap',
|
|
851
|
+
overflow: 'hidden',
|
|
852
|
+
textOverflow: 'ellipsis',
|
|
853
|
+
transition: 'color 100ms'
|
|
854
|
+
}),
|
|
855
|
+
children: cmd.label
|
|
856
|
+
}), cmd.description && /*#__PURE__*/jsx("span", {
|
|
857
|
+
className: css({
|
|
858
|
+
display: 'block',
|
|
859
|
+
fontSize: 12,
|
|
860
|
+
color: isActive ? tokenSchema.color.foreground.inverseSecondary : tokenSchema.color.foreground.neutralSecondary,
|
|
861
|
+
whiteSpace: 'nowrap',
|
|
862
|
+
overflow: 'hidden',
|
|
863
|
+
textOverflow: 'ellipsis',
|
|
864
|
+
transition: 'color 100ms'
|
|
865
|
+
}),
|
|
866
|
+
children: cmd.description
|
|
867
|
+
})]
|
|
868
|
+
}), isActive && /*#__PURE__*/jsx("kbd", {
|
|
869
|
+
className: css({
|
|
870
|
+
fontSize: 11,
|
|
871
|
+
color: tokenSchema.color.foreground.inverseSecondary,
|
|
872
|
+
background: '#2a2a2a',
|
|
873
|
+
border: '1px solid #3a3a3a',
|
|
874
|
+
borderRadius: 4,
|
|
875
|
+
padding: '2px 6px',
|
|
876
|
+
fontFamily: 'inherit',
|
|
877
|
+
flexShrink: 0
|
|
784
878
|
}),
|
|
785
|
-
children:
|
|
879
|
+
children: "\u21B5"
|
|
786
880
|
})]
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
border: '1px solid #3a3a3a',
|
|
793
|
-
borderRadius: 4,
|
|
794
|
-
padding: '2px 6px',
|
|
795
|
-
fontFamily: 'inherit',
|
|
796
|
-
flexShrink: 0
|
|
797
|
-
}),
|
|
798
|
-
children: "\u21B5"
|
|
799
|
-
})]
|
|
800
|
-
}, cmd.id);
|
|
801
|
-
})
|
|
802
|
-
})]
|
|
803
|
-
}, category))
|
|
881
|
+
}, cmd.id);
|
|
882
|
+
})
|
|
883
|
+
})]
|
|
884
|
+
}, category);
|
|
885
|
+
})
|
|
804
886
|
}), /*#__PURE__*/jsxs("div", {
|
|
805
887
|
className: css({
|
|
806
888
|
display: 'flex',
|
|
807
889
|
alignItems: 'center',
|
|
808
890
|
gap: 14,
|
|
809
891
|
padding: '8px 16px',
|
|
810
|
-
borderTop:
|
|
892
|
+
borderTop: `1px solid ${tokenSchema.color.border.muted}`,
|
|
811
893
|
fontSize: 11,
|
|
812
|
-
color:
|
|
894
|
+
color: tokenSchema.color.foreground.neutralSecondary
|
|
813
895
|
}),
|
|
814
896
|
children: [/*#__PURE__*/jsxs("span", {
|
|
815
897
|
children: [/*#__PURE__*/jsx("kbd", {
|
|
@@ -856,13 +938,54 @@ function PageWrapper(props) {
|
|
|
856
938
|
display: 'flex',
|
|
857
939
|
height: '100vh',
|
|
858
940
|
overflow: 'hidden',
|
|
859
|
-
backgroundColor:
|
|
941
|
+
backgroundColor: tokenSchema.color.background.surface,
|
|
860
942
|
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
|
|
861
943
|
}),
|
|
862
944
|
...props
|
|
863
945
|
});
|
|
864
946
|
}
|
|
865
947
|
|
|
948
|
+
// ---- Skip to content (WCAG 2.4.1 Bypass Blocks) ----
|
|
949
|
+
function SkipLink() {
|
|
950
|
+
return /*#__PURE__*/jsx("a", {
|
|
951
|
+
href: "#main-content",
|
|
952
|
+
className: css({
|
|
953
|
+
// Visually hidden, off-screen by default — kept out of the layout for mouse users.
|
|
954
|
+
position: 'absolute',
|
|
955
|
+
top: 0,
|
|
956
|
+
left: 0,
|
|
957
|
+
width: 1,
|
|
958
|
+
height: 1,
|
|
959
|
+
padding: 0,
|
|
960
|
+
margin: -1,
|
|
961
|
+
overflow: 'hidden',
|
|
962
|
+
clip: 'rect(0 0 0 0)',
|
|
963
|
+
whiteSpace: 'nowrap',
|
|
964
|
+
border: 0,
|
|
965
|
+
zIndex: 60,
|
|
966
|
+
// Reveal on keyboard focus.
|
|
967
|
+
'&:focus, &:focus-visible': {
|
|
968
|
+
width: 'auto',
|
|
969
|
+
height: 'auto',
|
|
970
|
+
padding: '10px 16px',
|
|
971
|
+
margin: 8,
|
|
972
|
+
clip: 'auto',
|
|
973
|
+
overflow: 'visible',
|
|
974
|
+
backgroundColor: tokenSchema.color.background.canvas,
|
|
975
|
+
color: tokenSchema.color.foreground.neutralEmphasis,
|
|
976
|
+
borderRadius: 6,
|
|
977
|
+
boxShadow: `0 2px 8px ${tokenSchema.color.shadow.regular}`,
|
|
978
|
+
outline: `2px solid ${tokenSchema.color.foreground.neutralEmphasis}`,
|
|
979
|
+
outlineOffset: 2,
|
|
980
|
+
textDecoration: 'none',
|
|
981
|
+
fontSize: 14,
|
|
982
|
+
fontWeight: 500
|
|
983
|
+
}
|
|
984
|
+
}),
|
|
985
|
+
children: "Skip to content"
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
866
989
|
// ---- Mobile overlay ----
|
|
867
990
|
function Backdrop({
|
|
868
991
|
isVisible,
|
|
@@ -874,7 +997,7 @@ function Backdrop({
|
|
|
874
997
|
className: css({
|
|
875
998
|
position: 'fixed',
|
|
876
999
|
inset: 0,
|
|
877
|
-
backgroundColor:
|
|
1000
|
+
backgroundColor: tokenSchema.color.alias.blanket,
|
|
878
1001
|
zIndex: 40,
|
|
879
1002
|
transition: 'opacity 200ms',
|
|
880
1003
|
opacity: isVisible ? 1 : 0,
|
|
@@ -888,16 +1011,91 @@ function Backdrop({
|
|
|
888
1011
|
}
|
|
889
1012
|
|
|
890
1013
|
// ---- Sidebar — pure white, precise ----
|
|
1014
|
+
const FOCUSABLE_SELECTOR = 'a[href],button:not([disabled]),input:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
891
1015
|
function Sidebar({
|
|
892
1016
|
isOpen,
|
|
893
1017
|
onClose,
|
|
894
1018
|
onCmdK
|
|
895
1019
|
}) {
|
|
1020
|
+
const asideRef = useRef(null);
|
|
1021
|
+
// Element to restore focus to (the hamburger) once the drawer closes.
|
|
1022
|
+
const lastFocusedRef = useRef(null);
|
|
1023
|
+
|
|
1024
|
+
// Mobile drawer a11y: focus trap, Escape-to-close, initial focus, and focus
|
|
1025
|
+
// restore. `isOpen` is only ever true on mobile (the resize handler forces it
|
|
1026
|
+
// false at >=768px), so gating on it doubles as a mobile gate.
|
|
1027
|
+
useEffect(() => {
|
|
1028
|
+
if (!isOpen) return;
|
|
1029
|
+
const aside = asideRef.current;
|
|
1030
|
+
if (!aside) return;
|
|
1031
|
+
|
|
1032
|
+
// Save the currently focused element so we can restore it on close.
|
|
1033
|
+
try {
|
|
1034
|
+
const active = document.activeElement;
|
|
1035
|
+
lastFocusedRef.current = active instanceof HTMLElement ? active : null;
|
|
1036
|
+
} catch {
|
|
1037
|
+
lastFocusedRef.current = null;
|
|
1038
|
+
}
|
|
1039
|
+
const getFocusable = () => {
|
|
1040
|
+
try {
|
|
1041
|
+
return Array.from(aside.querySelectorAll(FOCUSABLE_SELECTOR)).filter(el => el.offsetParent !== null || el === document.activeElement);
|
|
1042
|
+
} catch {
|
|
1043
|
+
return [];
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
// Move initial focus into the drawer.
|
|
1048
|
+
try {
|
|
1049
|
+
var _getFocusable$;
|
|
1050
|
+
(_getFocusable$ = getFocusable()[0]) === null || _getFocusable$ === void 0 || _getFocusable$.focus();
|
|
1051
|
+
} catch {
|
|
1052
|
+
/* noop */
|
|
1053
|
+
}
|
|
1054
|
+
function onKeyDown(e) {
|
|
1055
|
+
if (e.key === 'Escape') {
|
|
1056
|
+
e.preventDefault();
|
|
1057
|
+
onClose();
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
if (e.key !== 'Tab') return;
|
|
1061
|
+
const focusable = getFocusable();
|
|
1062
|
+
if (focusable.length === 0) {
|
|
1063
|
+
// Nothing focusable inside — keep focus from escaping anyway.
|
|
1064
|
+
e.preventDefault();
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
const first = focusable[0];
|
|
1068
|
+
const last = focusable[focusable.length - 1];
|
|
1069
|
+
const current = document.activeElement;
|
|
1070
|
+
if (e.shiftKey) {
|
|
1071
|
+
if (current === first || !(aside !== null && aside !== void 0 && aside.contains(current))) {
|
|
1072
|
+
e.preventDefault();
|
|
1073
|
+
last.focus();
|
|
1074
|
+
}
|
|
1075
|
+
} else if (current === last || !(aside !== null && aside !== void 0 && aside.contains(current))) {
|
|
1076
|
+
e.preventDefault();
|
|
1077
|
+
first.focus();
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
document.addEventListener('keydown', onKeyDown, true);
|
|
1081
|
+
return () => {
|
|
1082
|
+
document.removeEventListener('keydown', onKeyDown, true);
|
|
1083
|
+
// Restore focus to the trigger that opened the drawer.
|
|
1084
|
+
try {
|
|
1085
|
+
var _lastFocusedRef$curre;
|
|
1086
|
+
(_lastFocusedRef$curre = lastFocusedRef.current) === null || _lastFocusedRef$curre === void 0 || _lastFocusedRef$curre.focus();
|
|
1087
|
+
} catch {
|
|
1088
|
+
/* noop */
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
}, [isOpen, onClose]);
|
|
896
1092
|
return /*#__PURE__*/jsxs(Fragment, {
|
|
897
1093
|
children: [/*#__PURE__*/jsx(Backdrop, {
|
|
898
1094
|
isVisible: isOpen,
|
|
899
1095
|
onClick: onClose
|
|
900
1096
|
}), /*#__PURE__*/jsxs("aside", {
|
|
1097
|
+
id: "nixxie-sidebar",
|
|
1098
|
+
ref: asideRef,
|
|
901
1099
|
className: css({
|
|
902
1100
|
position: 'fixed',
|
|
903
1101
|
top: 0,
|
|
@@ -907,13 +1105,18 @@ function Sidebar({
|
|
|
907
1105
|
zIndex: 50,
|
|
908
1106
|
display: 'flex',
|
|
909
1107
|
flexDirection: 'column',
|
|
910
|
-
backgroundColor:
|
|
911
|
-
borderRight:
|
|
1108
|
+
backgroundColor: tokenSchema.color.background.canvas,
|
|
1109
|
+
borderRight: `1px solid ${tokenSchema.color.border.muted}`,
|
|
912
1110
|
transform: isOpen ? 'translateX(0)' : `translateX(-${SIDEBAR_WIDTH}px)`,
|
|
1111
|
+
// Closed off-canvas drawer must leave the tab order / AT tree on
|
|
1112
|
+
// mobile; `visibility:hidden` removes its descendants from both.
|
|
1113
|
+
visibility: isOpen ? 'visible' : 'hidden',
|
|
913
1114
|
transition: 'transform 220ms cubic-bezier(0.4,0,0.2,1)',
|
|
914
1115
|
'@media (min-width: 768px)': {
|
|
915
1116
|
position: 'relative',
|
|
916
1117
|
transform: 'translateX(0)',
|
|
1118
|
+
// Persistent desktop rail is always reachable.
|
|
1119
|
+
visibility: 'visible',
|
|
917
1120
|
flexShrink: 0
|
|
918
1121
|
}
|
|
919
1122
|
}),
|
|
@@ -923,7 +1126,7 @@ function Sidebar({
|
|
|
923
1126
|
alignItems: 'center',
|
|
924
1127
|
height: TOPBAR_HEIGHT,
|
|
925
1128
|
paddingInline: '16px',
|
|
926
|
-
borderBottom:
|
|
1129
|
+
borderBottom: `1px solid ${tokenSchema.color.border.muted}`,
|
|
927
1130
|
flexShrink: 0
|
|
928
1131
|
}),
|
|
929
1132
|
children: /*#__PURE__*/jsx(Logo, {})
|
|
@@ -937,7 +1140,7 @@ function Sidebar({
|
|
|
937
1140
|
width: 3
|
|
938
1141
|
},
|
|
939
1142
|
'&::-webkit-scrollbar-thumb': {
|
|
940
|
-
background:
|
|
1143
|
+
background: tokenSchema.color.border.muted,
|
|
941
1144
|
borderRadius: 3
|
|
942
1145
|
}
|
|
943
1146
|
}),
|
|
@@ -963,8 +1166,8 @@ function TopBar({
|
|
|
963
1166
|
height: TOPBAR_HEIGHT,
|
|
964
1167
|
paddingInline: '24px',
|
|
965
1168
|
gap: '14px',
|
|
966
|
-
backgroundColor:
|
|
967
|
-
borderBottom:
|
|
1169
|
+
backgroundColor: tokenSchema.color.background.canvas,
|
|
1170
|
+
borderBottom: `1px solid ${tokenSchema.color.border.muted}`,
|
|
968
1171
|
flexShrink: 0,
|
|
969
1172
|
zIndex: 30
|
|
970
1173
|
}),
|
|
@@ -972,6 +1175,7 @@ function TopBar({
|
|
|
972
1175
|
onClick: onMenuClick,
|
|
973
1176
|
"aria-label": isSidebarOpen ? 'Close menu' : 'Open menu',
|
|
974
1177
|
"aria-expanded": isSidebarOpen,
|
|
1178
|
+
"aria-controls": "nixxie-sidebar",
|
|
975
1179
|
className: css({
|
|
976
1180
|
display: 'inline-flex',
|
|
977
1181
|
alignItems: 'center',
|
|
@@ -979,15 +1183,15 @@ function TopBar({
|
|
|
979
1183
|
width: 32,
|
|
980
1184
|
height: 32,
|
|
981
1185
|
borderRadius: 6,
|
|
982
|
-
border:
|
|
1186
|
+
border: `1px solid ${tokenSchema.color.border.muted}`,
|
|
983
1187
|
background: 'transparent',
|
|
984
1188
|
cursor: 'pointer',
|
|
985
1189
|
flexShrink: 0,
|
|
986
|
-
color:
|
|
1190
|
+
color: tokenSchema.color.foreground.neutralTertiary,
|
|
987
1191
|
transition: 'color 130ms, background 130ms',
|
|
988
1192
|
'&:hover': {
|
|
989
|
-
background:
|
|
990
|
-
color:
|
|
1193
|
+
background: tokenSchema.color.background.surfaceSecondary,
|
|
1194
|
+
color: tokenSchema.color.foreground.neutralEmphasis
|
|
991
1195
|
},
|
|
992
1196
|
'@media (min-width: 768px)': {
|
|
993
1197
|
display: 'none'
|
|
@@ -1048,6 +1252,8 @@ function TopBar({
|
|
|
1048
1252
|
// ---- Main content area ----
|
|
1049
1253
|
function MainContent(props) {
|
|
1050
1254
|
return /*#__PURE__*/jsx("main", {
|
|
1255
|
+
id: "main-content",
|
|
1256
|
+
tabIndex: -1,
|
|
1051
1257
|
className: css({
|
|
1052
1258
|
flex: 1,
|
|
1053
1259
|
display: 'flex',
|
|
@@ -1071,11 +1277,11 @@ function ContentScroller(props) {
|
|
|
1071
1277
|
width: 5
|
|
1072
1278
|
},
|
|
1073
1279
|
'&::-webkit-scrollbar-thumb': {
|
|
1074
|
-
background:
|
|
1280
|
+
background: tokenSchema.color.border.muted,
|
|
1075
1281
|
borderRadius: 4
|
|
1076
1282
|
},
|
|
1077
1283
|
'&::-webkit-scrollbar-thumb:hover': {
|
|
1078
|
-
background:
|
|
1284
|
+
background: tokenSchema.color.border.emphasis
|
|
1079
1285
|
}
|
|
1080
1286
|
}),
|
|
1081
1287
|
...props
|
|
@@ -1114,7 +1320,7 @@ function PageContainer({
|
|
|
1114
1320
|
children: /*#__PURE__*/jsx("title", {
|
|
1115
1321
|
children: title ? `Nixxie – ${title}` : 'Nixxie'
|
|
1116
1322
|
}, "title")
|
|
1117
|
-
}), /*#__PURE__*/jsx(Sidebar, {
|
|
1323
|
+
}), /*#__PURE__*/jsx(SkipLink, {}), /*#__PURE__*/jsx(Sidebar, {
|
|
1118
1324
|
isOpen: isSidebarOpen,
|
|
1119
1325
|
onClose: () => setSidebarOpen(false),
|
|
1120
1326
|
onCmdK: () => setCmdOpen(v => !v)
|