@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.
Files changed (77) hide show
  1. package/README.md +1 -0
  2. package/dist/components/NavList/NavList.d.ts +34 -0
  3. package/dist/components/NavList/NavList.d.ts.map +1 -0
  4. package/dist/components/NavList/NavList.js +113 -0
  5. package/dist/components/NavList/NavList.js.map +1 -0
  6. package/dist/components/NavList/NavList.mjs +111 -0
  7. package/dist/components/NavList/NavList.mjs.map +1 -0
  8. package/dist/components/NavList/NavList.styles.d.ts +90 -0
  9. package/dist/components/NavList/NavList.styles.d.ts.map +1 -0
  10. package/dist/components/NavList/NavList.styles.js +104 -0
  11. package/dist/components/NavList/NavList.styles.js.map +1 -0
  12. package/dist/components/NavList/NavList.styles.mjs +97 -0
  13. package/dist/components/NavList/NavList.styles.mjs.map +1 -0
  14. package/dist/components/NavList/NavList.translations.json.js +19 -0
  15. package/dist/components/NavList/NavList.translations.json.js.map +1 -0
  16. package/dist/components/NavList/NavList.translations.json.mjs +14 -0
  17. package/dist/components/NavList/NavList.translations.json.mjs.map +1 -0
  18. package/dist/components/NavList/NavList.types.d.ts +131 -0
  19. package/dist/components/NavList/NavList.types.d.ts.map +1 -0
  20. package/dist/components/NavList/NavListContext.d.ts +30 -0
  21. package/dist/components/NavList/NavListContext.d.ts.map +1 -0
  22. package/dist/components/NavList/NavListContext.js +16 -0
  23. package/dist/components/NavList/NavListContext.js.map +1 -0
  24. package/dist/components/NavList/NavListContext.mjs +13 -0
  25. package/dist/components/NavList/NavListContext.mjs.map +1 -0
  26. package/dist/components/NavList/NavListGroup.d.ts +31 -0
  27. package/dist/components/NavList/NavListGroup.d.ts.map +1 -0
  28. package/dist/components/NavList/NavListGroup.js +115 -0
  29. package/dist/components/NavList/NavListGroup.js.map +1 -0
  30. package/dist/components/NavList/NavListGroup.mjs +113 -0
  31. package/dist/components/NavList/NavListGroup.mjs.map +1 -0
  32. package/dist/components/NavList/NavListItem.d.ts +15 -0
  33. package/dist/components/NavList/NavListItem.d.ts.map +1 -0
  34. package/dist/components/NavList/NavListItem.js +269 -0
  35. package/dist/components/NavList/NavListItem.js.map +1 -0
  36. package/dist/components/NavList/NavListItem.mjs +267 -0
  37. package/dist/components/NavList/NavListItem.mjs.map +1 -0
  38. package/dist/components/NavList/NavListSkeleton.d.ts +17 -0
  39. package/dist/components/NavList/NavListSkeleton.d.ts.map +1 -0
  40. package/dist/components/NavList/NavListSkeleton.js +33 -0
  41. package/dist/components/NavList/NavListSkeleton.js.map +1 -0
  42. package/dist/components/NavList/NavListSkeleton.mjs +31 -0
  43. package/dist/components/NavList/NavListSkeleton.mjs.map +1 -0
  44. package/dist/components/NavList/index.d.ts +7 -0
  45. package/dist/components/NavList/index.d.ts.map +1 -0
  46. package/dist/index.d.ts +2 -1
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +12 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/index.mjs +6 -1
  51. package/dist/index.mjs.map +1 -1
  52. package/dist/utils/focusRing.d.ts +22 -0
  53. package/dist/utils/focusRing.d.ts.map +1 -1
  54. package/dist/utils/focusRing.js +22 -0
  55. package/dist/utils/focusRing.js.map +1 -1
  56. package/dist/utils/focusRing.mjs +21 -1
  57. package/dist/utils/focusRing.mjs.map +1 -1
  58. package/package.json +2 -1
  59. package/src/components/Menu/Menu.usage.mdx +8 -0
  60. package/src/components/NavList/NavList.stories.tsx +895 -0
  61. package/src/components/NavList/NavList.styles.ts +114 -0
  62. package/src/components/NavList/NavList.test.tsx +1254 -0
  63. package/src/components/NavList/NavList.translations.json +9 -0
  64. package/src/components/NavList/NavList.tsx +160 -0
  65. package/src/components/NavList/NavList.types.ts +145 -0
  66. package/src/components/NavList/NavList.usage.mdx +540 -0
  67. package/src/components/NavList/NavListContext.ts +25 -0
  68. package/src/components/NavList/NavListGroup.tsx +243 -0
  69. package/src/components/NavList/NavListItem.tsx +525 -0
  70. package/src/components/NavList/NavListSkeleton.tsx +57 -0
  71. package/src/components/NavList/index.ts +14 -0
  72. package/src/components/Tabs/Tabs.usage.mdx +58 -14
  73. package/src/components/Tooltip/Tooltip.usage.mdx +8 -0
  74. package/src/components/a11y.kwcag.test.tsx +66 -0
  75. package/src/components/claude.md +1 -1
  76. package/src/index.ts +7 -1
  77. 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';