@mindlogic-ai/logician-ui 5.0.0-alpha.12 → 5.0.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/components/NavList/NavList.d.ts +34 -0
- package/dist/components/NavList/NavList.d.ts.map +1 -0
- package/dist/components/NavList/NavList.js +113 -0
- package/dist/components/NavList/NavList.js.map +1 -0
- package/dist/components/NavList/NavList.mjs +111 -0
- package/dist/components/NavList/NavList.mjs.map +1 -0
- package/dist/components/NavList/NavList.styles.d.ts +90 -0
- package/dist/components/NavList/NavList.styles.d.ts.map +1 -0
- package/dist/components/NavList/NavList.styles.js +104 -0
- package/dist/components/NavList/NavList.styles.js.map +1 -0
- package/dist/components/NavList/NavList.styles.mjs +97 -0
- package/dist/components/NavList/NavList.styles.mjs.map +1 -0
- package/dist/components/NavList/NavList.translations.json.js +19 -0
- package/dist/components/NavList/NavList.translations.json.js.map +1 -0
- package/dist/components/NavList/NavList.translations.json.mjs +14 -0
- package/dist/components/NavList/NavList.translations.json.mjs.map +1 -0
- package/dist/components/NavList/NavList.types.d.ts +131 -0
- package/dist/components/NavList/NavList.types.d.ts.map +1 -0
- package/dist/components/NavList/NavListContext.d.ts +30 -0
- package/dist/components/NavList/NavListContext.d.ts.map +1 -0
- package/dist/components/NavList/NavListContext.js +16 -0
- package/dist/components/NavList/NavListContext.js.map +1 -0
- package/dist/components/NavList/NavListContext.mjs +13 -0
- package/dist/components/NavList/NavListContext.mjs.map +1 -0
- package/dist/components/NavList/NavListGroup.d.ts +31 -0
- package/dist/components/NavList/NavListGroup.d.ts.map +1 -0
- package/dist/components/NavList/NavListGroup.js +115 -0
- package/dist/components/NavList/NavListGroup.js.map +1 -0
- package/dist/components/NavList/NavListGroup.mjs +113 -0
- package/dist/components/NavList/NavListGroup.mjs.map +1 -0
- package/dist/components/NavList/NavListItem.d.ts +15 -0
- package/dist/components/NavList/NavListItem.d.ts.map +1 -0
- package/dist/components/NavList/NavListItem.js +269 -0
- package/dist/components/NavList/NavListItem.js.map +1 -0
- package/dist/components/NavList/NavListItem.mjs +267 -0
- package/dist/components/NavList/NavListItem.mjs.map +1 -0
- package/dist/components/NavList/NavListSkeleton.d.ts +17 -0
- package/dist/components/NavList/NavListSkeleton.d.ts.map +1 -0
- package/dist/components/NavList/NavListSkeleton.js +33 -0
- package/dist/components/NavList/NavListSkeleton.js.map +1 -0
- package/dist/components/NavList/NavListSkeleton.mjs +31 -0
- package/dist/components/NavList/NavListSkeleton.mjs.map +1 -0
- package/dist/components/NavList/index.d.ts +7 -0
- package/dist/components/NavList/index.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -1
- package/dist/index.mjs.map +1 -1
- package/dist/utils/focusRing.d.ts +22 -0
- package/dist/utils/focusRing.d.ts.map +1 -1
- package/dist/utils/focusRing.js +22 -0
- package/dist/utils/focusRing.js.map +1 -1
- package/dist/utils/focusRing.mjs +21 -1
- package/dist/utils/focusRing.mjs.map +1 -1
- package/package.json +2 -1
- package/src/components/Menu/Menu.usage.mdx +8 -0
- package/src/components/NavList/NavList.stories.tsx +895 -0
- package/src/components/NavList/NavList.styles.ts +114 -0
- package/src/components/NavList/NavList.test.tsx +1254 -0
- package/src/components/NavList/NavList.translations.json +9 -0
- package/src/components/NavList/NavList.tsx +160 -0
- package/src/components/NavList/NavList.types.ts +145 -0
- package/src/components/NavList/NavList.usage.mdx +540 -0
- package/src/components/NavList/NavListContext.ts +25 -0
- package/src/components/NavList/NavListGroup.tsx +243 -0
- package/src/components/NavList/NavListItem.tsx +525 -0
- package/src/components/NavList/NavListSkeleton.tsx +57 -0
- package/src/components/NavList/index.ts +14 -0
- package/src/components/Tabs/Tabs.usage.mdx +58 -14
- package/src/components/Tooltip/Tooltip.usage.mdx +8 -0
- package/src/components/a11y.kwcag.test.tsx +66 -0
- package/src/components/claude.md +1 -1
- package/src/index.ts +7 -1
- package/src/utils/focusRing.ts +23 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { forwardRef, useEffect, useId, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
chakra,
|
|
4
|
+
Collapsible as ChakraCollapsible,
|
|
5
|
+
LocaleProvider,
|
|
6
|
+
useLocaleContext,
|
|
7
|
+
} from '@chakra-ui/react';
|
|
8
|
+
|
|
9
|
+
import { ChevronDownIcon } from '@/components/Icon';
|
|
10
|
+
import { Overline } from '@/components/Typography/Overline';
|
|
11
|
+
import { focusRingInset } from '@/utils/focusRing';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
byOrientation,
|
|
15
|
+
inheritDirection,
|
|
16
|
+
listStyles,
|
|
17
|
+
localeFor,
|
|
18
|
+
NAV_LIST_RAMP,
|
|
19
|
+
pressedFill,
|
|
20
|
+
} from './NavList.styles';
|
|
21
|
+
import { NavListGroupProps } from './NavList.types';
|
|
22
|
+
import { useNavListContext } from './NavListContext';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A run of rows under an optional title.
|
|
26
|
+
*
|
|
27
|
+
* Rendered as `<li>` → title + nested `<ul aria-labelledby={title}>`, so a
|
|
28
|
+
* screen reader announces "목록, 공유 드라이브, 3개 항목" on entering it. The
|
|
29
|
+
* title is overline text, not `<h*>`: which heading level a rail's group is
|
|
30
|
+
* can only be known at the call site, and a typography default that decides
|
|
31
|
+
* the outline is exactly the `Subtitle as="h6"` defect in `ACCESSIBILITY.md`.
|
|
32
|
+
*
|
|
33
|
+
* `isCollapsible` turns the title into a disclosure button
|
|
34
|
+
* (`aria-expanded` + `aria-controls`); the list stays labelled by the title
|
|
35
|
+
* text. A collapsed RAIL (`NavList isCollapsed`) shows every group's rows and
|
|
36
|
+
* folds the titles away visually — a column of icons has no room for either.
|
|
37
|
+
*
|
|
38
|
+
* The rows open and close on Collapsible's own motion: Ark's collapsible
|
|
39
|
+
* measures the list into `--height`, the recipe's `expand-height` /
|
|
40
|
+
* `collapse-height` move it, and `animationStyle="presence"` sets the house
|
|
41
|
+
* clock (300ms in, 150ms out) with a fade instead of the height under reduced
|
|
42
|
+
* motion. Ark sets `hidden` once the close has finished, so closed rows are
|
|
43
|
+
* out of the tab order and the accessibility tree; `inert` covers the 150ms
|
|
44
|
+
* the list is still leaving, so a closing row cannot be tabbed into.
|
|
45
|
+
*/
|
|
46
|
+
export const NavListGroup = forwardRef<HTMLLIElement, NavListGroupProps>(
|
|
47
|
+
(
|
|
48
|
+
{
|
|
49
|
+
title,
|
|
50
|
+
children,
|
|
51
|
+
isCollapsible = false,
|
|
52
|
+
isOpen: isOpenProp,
|
|
53
|
+
defaultOpen = true,
|
|
54
|
+
onOpenChange,
|
|
55
|
+
...rest
|
|
56
|
+
},
|
|
57
|
+
ref
|
|
58
|
+
) => {
|
|
59
|
+
const { orientation, isCollapsed, tooltipDir } = useNavListContext();
|
|
60
|
+
const inherited = useLocaleContext();
|
|
61
|
+
const titleId = useId();
|
|
62
|
+
const listId = useId();
|
|
63
|
+
const hasTitle = title != null && title !== false;
|
|
64
|
+
const [isOpenState, setIsOpenState] = useState(defaultOpen);
|
|
65
|
+
// The disclosure exists whenever the caller asked for one; a collapsed
|
|
66
|
+
// rail only forces it open, so toggling the rail does not remount rows.
|
|
67
|
+
const isDisclosure = isCollapsible && hasTitle;
|
|
68
|
+
const canCollapse = isDisclosure && !isCollapsed;
|
|
69
|
+
const isOpen = !canCollapse || (isOpenProp ?? isOpenState);
|
|
70
|
+
const listRef = useRef<HTMLUListElement>(null);
|
|
71
|
+
// A DOM property rather than the attribute: React 18 does not know
|
|
72
|
+
// `inert`, and 19 reads it as a boolean.
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (listRef.current) listRef.current.inert = !isOpen;
|
|
75
|
+
}, [isOpen]);
|
|
76
|
+
|
|
77
|
+
const toggle = () => {
|
|
78
|
+
const next = !isOpen;
|
|
79
|
+
if (isOpenProp === undefined) setIsOpenState(next);
|
|
80
|
+
onOpenChange?.(next);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Space on the axis the rows stack along, on both sides of a group: before
|
|
84
|
+
// it unless it opens the list, and before whatever row follows it, so the
|
|
85
|
+
// row after a group does not read as one more member of it.
|
|
86
|
+
const gapBefore = {
|
|
87
|
+
marginTop: byOrientation(orientation, 3, 0),
|
|
88
|
+
marginInlineStart: byOrientation(orientation, 0, 3),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const titleText = (
|
|
92
|
+
<Overline
|
|
93
|
+
as="span"
|
|
94
|
+
id={titleId}
|
|
95
|
+
display="block"
|
|
96
|
+
color={NAV_LIST_RAMP.groupTitle}
|
|
97
|
+
whiteSpace="nowrap"
|
|
98
|
+
overflow="hidden"
|
|
99
|
+
textOverflow="ellipsis"
|
|
100
|
+
>
|
|
101
|
+
{title}
|
|
102
|
+
</Overline>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
let heading = null;
|
|
106
|
+
if (canCollapse) {
|
|
107
|
+
heading = (
|
|
108
|
+
<chakra.button
|
|
109
|
+
type="button"
|
|
110
|
+
aria-expanded={isOpen}
|
|
111
|
+
aria-controls={listId}
|
|
112
|
+
onClick={toggle}
|
|
113
|
+
display="flex"
|
|
114
|
+
alignItems="center"
|
|
115
|
+
justifyContent="space-between"
|
|
116
|
+
gap={2}
|
|
117
|
+
minH={8}
|
|
118
|
+
px={2.5}
|
|
119
|
+
borderRadius="md"
|
|
120
|
+
bg="transparent"
|
|
121
|
+
border="none"
|
|
122
|
+
cursor="pointer"
|
|
123
|
+
color={NAV_LIST_RAMP.groupTitle}
|
|
124
|
+
textAlign="start"
|
|
125
|
+
_hover={{ bg: NAV_LIST_RAMP.hoverFill }}
|
|
126
|
+
_active={pressedFill}
|
|
127
|
+
{...focusRingInset}
|
|
128
|
+
>
|
|
129
|
+
{titleText}
|
|
130
|
+
<ChevronDownIcon
|
|
131
|
+
aria-hidden
|
|
132
|
+
boxSize={4}
|
|
133
|
+
flexShrink={0}
|
|
134
|
+
// Closed points to the inline end: right, or left on an RTL page.
|
|
135
|
+
rotate={isOpen ? '0deg' : '-90deg'}
|
|
136
|
+
_rtl={{ rotate: isOpen ? '0deg' : '90deg' }}
|
|
137
|
+
// Turns with the list, on the same clock as its enter.
|
|
138
|
+
animationStyle="travel"
|
|
139
|
+
transitionProperty="rotate"
|
|
140
|
+
/>
|
|
141
|
+
</chakra.button>
|
|
142
|
+
);
|
|
143
|
+
} else if (hasTitle) {
|
|
144
|
+
heading = (
|
|
145
|
+
<chakra.span
|
|
146
|
+
display="block"
|
|
147
|
+
px={2.5}
|
|
148
|
+
py={byOrientation(orientation, 1, 0)}
|
|
149
|
+
>
|
|
150
|
+
{titleText}
|
|
151
|
+
</chakra.span>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
// A collapsed rail has no room for titles. The title folds to no height
|
|
155
|
+
// and fades on the rows' clock instead of being swapped out, so the rows
|
|
156
|
+
// under it slide up with the rail rather than jumping. It stays in the
|
|
157
|
+
// DOM either way: it is what names the group's list.
|
|
158
|
+
if (heading) {
|
|
159
|
+
heading = (
|
|
160
|
+
<chakra.div
|
|
161
|
+
display="grid"
|
|
162
|
+
gridTemplateRows={isCollapsed ? '0fr' : '1fr'}
|
|
163
|
+
opacity={isCollapsed ? 0 : 1}
|
|
164
|
+
animationStyle="feedback"
|
|
165
|
+
transitionProperty="grid-template-rows, opacity"
|
|
166
|
+
>
|
|
167
|
+
<chakra.div
|
|
168
|
+
minH={0}
|
|
169
|
+
overflow="hidden"
|
|
170
|
+
display="flex"
|
|
171
|
+
flexDirection="column"
|
|
172
|
+
>
|
|
173
|
+
{heading}
|
|
174
|
+
</chakra.div>
|
|
175
|
+
</chakra.div>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const listProps = {
|
|
180
|
+
id: listId,
|
|
181
|
+
'aria-labelledby': hasTitle ? titleId : undefined,
|
|
182
|
+
...listStyles(orientation),
|
|
183
|
+
alignItems: byOrientation(orientation, 'stretch', 'center'),
|
|
184
|
+
...inheritDirection,
|
|
185
|
+
} as const;
|
|
186
|
+
|
|
187
|
+
const list = isDisclosure ? (
|
|
188
|
+
<ChakraCollapsible.Content
|
|
189
|
+
asChild
|
|
190
|
+
animationStyle="presence"
|
|
191
|
+
// `hidden` loses to the list's own `display: flex`.
|
|
192
|
+
css={{ '&[hidden]': { display: 'none' } }}
|
|
193
|
+
>
|
|
194
|
+
<chakra.ul ref={listRef} {...listProps}>
|
|
195
|
+
{children}
|
|
196
|
+
</chakra.ul>
|
|
197
|
+
</ChakraCollapsible.Content>
|
|
198
|
+
) : (
|
|
199
|
+
<chakra.ul {...listProps}>{children}</chakra.ul>
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const group = (
|
|
203
|
+
<chakra.li
|
|
204
|
+
ref={ref}
|
|
205
|
+
display="flex"
|
|
206
|
+
flexDirection={byOrientation(orientation, 'column', 'row')}
|
|
207
|
+
alignItems={byOrientation(orientation, 'stretch', 'center')}
|
|
208
|
+
gap={0.5}
|
|
209
|
+
flexShrink={0}
|
|
210
|
+
minW={0}
|
|
211
|
+
css={{
|
|
212
|
+
'&:not(:first-child)': gapBefore,
|
|
213
|
+
'& + li': gapBefore,
|
|
214
|
+
}}
|
|
215
|
+
{...inheritDirection}
|
|
216
|
+
{...rest}
|
|
217
|
+
>
|
|
218
|
+
<LocaleProvider locale={inherited.locale}>
|
|
219
|
+
{heading}
|
|
220
|
+
{list}
|
|
221
|
+
</LocaleProvider>
|
|
222
|
+
</chakra.li>
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
if (!isDisclosure) return group;
|
|
226
|
+
// The title stays our own button, so `aria-expanded` flips on the click
|
|
227
|
+
// rather than when the closing animation ends, and the rail can force the
|
|
228
|
+
// group open without the button having a say.
|
|
229
|
+
return (
|
|
230
|
+
// Zag stamps `dir` on the group and its list; give it the measured
|
|
231
|
+
// direction so the attributes agree with the layout after mount. The
|
|
232
|
+
// layout does not read it (`inheritDirection`), and the rows inside get
|
|
233
|
+
// the caller's own locale back.
|
|
234
|
+
<LocaleProvider locale={localeFor(inherited, tooltipDir)}>
|
|
235
|
+
<ChakraCollapsible.Root asChild open={isOpen} ids={{ content: listId }}>
|
|
236
|
+
{group}
|
|
237
|
+
</ChakraCollapsible.Root>
|
|
238
|
+
</LocaleProvider>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
NavListGroup.displayName = 'NavList.Group';
|