@molecule/app-ui-react 1.0.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 +115 -0
- package/README.md +2786 -0
- package/dist/components/Accordion.d.ts +12 -0
- package/dist/components/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion.js +80 -0
- package/dist/components/Accordion.js.map +1 -0
- package/dist/components/Alert.d.ts +26 -0
- package/dist/components/Alert.d.ts.map +1 -0
- package/dist/components/Alert.js +53 -0
- package/dist/components/Alert.js.map +1 -0
- package/dist/components/AuthGuard.d.ts +66 -0
- package/dist/components/AuthGuard.d.ts.map +1 -0
- package/dist/components/AuthGuard.js +42 -0
- package/dist/components/AuthGuard.js.map +1 -0
- package/dist/components/Avatar.d.ts +12 -0
- package/dist/components/Avatar.d.ts.map +1 -0
- package/dist/components/Avatar.js +25 -0
- package/dist/components/Avatar.js.map +1 -0
- package/dist/components/Badge.d.ts +12 -0
- package/dist/components/Badge.d.ts.map +1 -0
- package/dist/components/Badge.js +18 -0
- package/dist/components/Badge.js.map +1 -0
- package/dist/components/Button.d.ts +12 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +19 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Card.d.ts +34 -0
- package/dist/components/Card.d.ts.map +1 -0
- package/dist/components/Card.js +71 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/Checkbox.d.ts +12 -0
- package/dist/components/Checkbox.d.ts.map +1 -0
- package/dist/components/Checkbox.js +41 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/Dropdown.d.ts +38 -0
- package/dist/components/Dropdown.d.ts.map +1 -0
- package/dist/components/Dropdown.js +352 -0
- package/dist/components/Dropdown.js.map +1 -0
- package/dist/components/EmptyState.d.ts +15 -0
- package/dist/components/EmptyState.d.ts.map +1 -0
- package/dist/components/EmptyState.js +20 -0
- package/dist/components/EmptyState.js.map +1 -0
- package/dist/components/FloatingInput.d.ts +22 -0
- package/dist/components/FloatingInput.d.ts.map +1 -0
- package/dist/components/FloatingInput.js +38 -0
- package/dist/components/FloatingInput.js.map +1 -0
- package/dist/components/Form.d.ts +22 -0
- package/dist/components/Form.d.ts.map +1 -0
- package/dist/components/Form.js +81 -0
- package/dist/components/Form.js.map +1 -0
- package/dist/components/Icon.d.ts +48 -0
- package/dist/components/Icon.d.ts.map +1 -0
- package/dist/components/Icon.js +46 -0
- package/dist/components/Icon.js.map +1 -0
- package/dist/components/Input.d.ts +12 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +25 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/LanguagePicker.d.ts +60 -0
- package/dist/components/LanguagePicker.d.ts.map +1 -0
- package/dist/components/LanguagePicker.js +45 -0
- package/dist/components/LanguagePicker.js.map +1 -0
- package/dist/components/Layout.d.ts +24 -0
- package/dist/components/Layout.d.ts.map +1 -0
- package/dist/components/Layout.js +104 -0
- package/dist/components/Layout.js.map +1 -0
- package/dist/components/Modal.d.ts +32 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.js +235 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/PageHeader.d.ts +15 -0
- package/dist/components/PageHeader.d.ts.map +1 -0
- package/dist/components/PageHeader.js +20 -0
- package/dist/components/PageHeader.js.map +1 -0
- package/dist/components/PageShell.d.ts +15 -0
- package/dist/components/PageShell.d.ts.map +1 -0
- package/dist/components/PageShell.js +20 -0
- package/dist/components/PageShell.js.map +1 -0
- package/dist/components/Pagination.d.ts +12 -0
- package/dist/components/Pagination.d.ts.map +1 -0
- package/dist/components/Pagination.js +78 -0
- package/dist/components/Pagination.js.map +1 -0
- package/dist/components/PanelClose.d.ts +40 -0
- package/dist/components/PanelClose.d.ts.map +1 -0
- package/dist/components/PanelClose.js +32 -0
- package/dist/components/PanelClose.js.map +1 -0
- package/dist/components/Progress.d.ts +45 -0
- package/dist/components/Progress.d.ts.map +1 -0
- package/dist/components/Progress.js +21 -0
- package/dist/components/Progress.js.map +1 -0
- package/dist/components/RadioGroup.d.ts +12 -0
- package/dist/components/RadioGroup.d.ts.map +1 -0
- package/dist/components/RadioGroup.js +34 -0
- package/dist/components/RadioGroup.js.map +1 -0
- package/dist/components/Select.d.ts +12 -0
- package/dist/components/Select.d.ts.map +1 -0
- package/dist/components/Select.js +57 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/Separator.d.ts +25 -0
- package/dist/components/Separator.d.ts.map +1 -0
- package/dist/components/Separator.js +18 -0
- package/dist/components/Separator.js.map +1 -0
- package/dist/components/SidebarUserCard.d.ts +60 -0
- package/dist/components/SidebarUserCard.d.ts.map +1 -0
- package/dist/components/SidebarUserCard.js +45 -0
- package/dist/components/SidebarUserCard.js.map +1 -0
- package/dist/components/Skeleton.d.ts +26 -0
- package/dist/components/Skeleton.d.ts.map +1 -0
- package/dist/components/Skeleton.js +45 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/Spinner.d.ts +17 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +32 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/Switch.d.ts +12 -0
- package/dist/components/Switch.d.ts.map +1 -0
- package/dist/components/Switch.js +52 -0
- package/dist/components/Switch.js.map +1 -0
- package/dist/components/Table.d.ts +12 -0
- package/dist/components/Table.d.ts.map +1 -0
- package/dist/components/Table.js +53 -0
- package/dist/components/Table.js.map +1 -0
- package/dist/components/Tabs.d.ts +12 -0
- package/dist/components/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs.js +72 -0
- package/dist/components/Tabs.js.map +1 -0
- package/dist/components/Textarea.d.ts +12 -0
- package/dist/components/Textarea.d.ts.map +1 -0
- package/dist/components/Textarea.js +66 -0
- package/dist/components/Textarea.js.map +1 -0
- package/dist/components/ThemeToggle.d.ts +43 -0
- package/dist/components/ThemeToggle.d.ts.map +1 -0
- package/dist/components/ThemeToggle.js +30 -0
- package/dist/components/ThemeToggle.js.map +1 -0
- package/dist/components/Toast.d.ts +61 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/Toast.js +159 -0
- package/dist/components/Toast.js.map +1 -0
- package/dist/components/Tooltip.d.ts +21 -0
- package/dist/components/Tooltip.d.ts.map +1 -0
- package/dist/components/Tooltip.js +245 -0
- package/dist/components/Tooltip.js.map +1 -0
- package/dist/components/UserMenu.d.ts +59 -0
- package/dist/components/UserMenu.d.ts.map +1 -0
- package/dist/components/UserMenu.js +34 -0
- package/dist/components/UserMenu.js.map +1 -0
- package/dist/components/UserMenuPopover.d.ts +139 -0
- package/dist/components/UserMenuPopover.d.ts.map +1 -0
- package/dist/components/UserMenuPopover.js +173 -0
- package/dist/components/UserMenuPopover.js.map +1 -0
- package/dist/components/index.d.ts +43 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +43 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.d.ts +131 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utilities/index.d.ts +8 -0
- package/dist/utilities/index.d.ts.map +1 -0
- package/dist/utilities/index.js +8 -0
- package/dist/utilities/index.js.map +1 -0
- package/dist/utilities/initials.d.ts +13 -0
- package/dist/utilities/initials.d.ts.map +1 -0
- package/dist/utilities/initials.js +23 -0
- package/dist/utilities/initials.js.map +1 -0
- package/dist/utilities/renderIcon.d.ts +21 -0
- package/dist/utilities/renderIcon.d.ts.map +1 -0
- package/dist/utilities/renderIcon.js +40 -0
- package/dist/utilities/renderIcon.js.map +1 -0
- package/dist/utilities.d.ts +12 -0
- package/dist/utilities.d.ts.map +1 -0
- package/dist/utilities.js +14 -0
- package/dist/utilities.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Tooltip component.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import React, { forwardRef, useCallback, useEffect, useId, useLayoutEffect, useRef, useState, } from 'react';
|
|
8
|
+
import { createPortal } from 'react-dom';
|
|
9
|
+
import { getClassMap } from '@molecule/app-ui';
|
|
10
|
+
/** Arrow square size (px) before the 45° rotation that turns it into a diamond. */
|
|
11
|
+
const ARROW_SIZE = 8;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the arrow's inline position style for a given placement. The arrow
|
|
14
|
+
* has no dedicated ClassMap resolver (`tooltip()` takes no options), so its
|
|
15
|
+
* shape/position is expressed via a small inline style — the sanctioned
|
|
16
|
+
* exception for values ClassMap cannot express. Its color is NOT hardcoded:
|
|
17
|
+
* `var(--color-surface)` / `var(--color-border)` are the same CSS custom
|
|
18
|
+
* properties the `bg-surface`/`border` Tailwind utilities in `tooltipContent`
|
|
19
|
+
* resolve to (see `@molecule/app-ui-tailwind/base.css`), so the arrow always
|
|
20
|
+
* matches the tooltip body in both themes.
|
|
21
|
+
* @param placement - The tooltip's resolved placement.
|
|
22
|
+
* @returns Inline style positioning the rotated-square arrow for that placement.
|
|
23
|
+
*/
|
|
24
|
+
function getArrowStyle(placement) {
|
|
25
|
+
const half = ARROW_SIZE / 2;
|
|
26
|
+
const base = {
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
width: ARROW_SIZE,
|
|
29
|
+
height: ARROW_SIZE,
|
|
30
|
+
background: 'var(--color-surface)',
|
|
31
|
+
border: '1px solid var(--color-border)',
|
|
32
|
+
};
|
|
33
|
+
switch (placement) {
|
|
34
|
+
case 'top':
|
|
35
|
+
return { ...base, bottom: -half, left: '50%', transform: 'translateX(-50%) rotate(45deg)' };
|
|
36
|
+
case 'top-start':
|
|
37
|
+
return { ...base, bottom: -half, left: 12, transform: 'rotate(45deg)' };
|
|
38
|
+
case 'top-end':
|
|
39
|
+
return { ...base, bottom: -half, right: 12, transform: 'rotate(45deg)' };
|
|
40
|
+
case 'bottom':
|
|
41
|
+
return { ...base, top: -half, left: '50%', transform: 'translateX(-50%) rotate(45deg)' };
|
|
42
|
+
case 'bottom-start':
|
|
43
|
+
return { ...base, top: -half, left: 12, transform: 'rotate(45deg)' };
|
|
44
|
+
case 'bottom-end':
|
|
45
|
+
return { ...base, top: -half, right: 12, transform: 'rotate(45deg)' };
|
|
46
|
+
case 'left':
|
|
47
|
+
return { ...base, right: -half, top: '50%', transform: 'translateY(-50%) rotate(45deg)' };
|
|
48
|
+
case 'right':
|
|
49
|
+
return { ...base, left: -half, top: '50%', transform: 'translateY(-50%) rotate(45deg)' };
|
|
50
|
+
default:
|
|
51
|
+
return base;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Position the tooltip in a LAYOUT effect (synchronous, before the browser paints) so
|
|
55
|
+
// it's measured + placed in the same frame it mounts. With a plain useEffect (which
|
|
56
|
+
// runs AFTER paint) the tooltip paints once at the default {0,0} top-left and then jumps
|
|
57
|
+
// to the correct spot — the flicker. Fall back to useEffect on the server (no DOM) to
|
|
58
|
+
// avoid React's SSR useLayoutEffect warning.
|
|
59
|
+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
60
|
+
/**
|
|
61
|
+
* Calculate tooltip position based on trigger element and placement.
|
|
62
|
+
* @param triggerRect - The bounding rectangle of the trigger element.
|
|
63
|
+
* @param tooltipRect - The bounding rectangle of the tooltip element.
|
|
64
|
+
* @param placement - The desired tooltip placement direction.
|
|
65
|
+
* @param offset - The pixel offset from the trigger element.
|
|
66
|
+
* @returns The computed top and left position.
|
|
67
|
+
*/
|
|
68
|
+
function calculatePosition(triggerRect, tooltipRect, placement, offset = 8) {
|
|
69
|
+
const { top, left, width, height } = triggerRect;
|
|
70
|
+
const tooltipWidth = tooltipRect.width;
|
|
71
|
+
const tooltipHeight = tooltipRect.height;
|
|
72
|
+
const positions = {
|
|
73
|
+
top: {
|
|
74
|
+
top: top + window.scrollY - tooltipHeight - offset,
|
|
75
|
+
left: left + window.scrollX + width / 2 - tooltipWidth / 2,
|
|
76
|
+
},
|
|
77
|
+
bottom: {
|
|
78
|
+
top: top + window.scrollY + height + offset,
|
|
79
|
+
left: left + window.scrollX + width / 2 - tooltipWidth / 2,
|
|
80
|
+
},
|
|
81
|
+
left: {
|
|
82
|
+
top: top + window.scrollY + height / 2 - tooltipHeight / 2,
|
|
83
|
+
left: left + window.scrollX - tooltipWidth - offset,
|
|
84
|
+
},
|
|
85
|
+
right: {
|
|
86
|
+
top: top + window.scrollY + height / 2 - tooltipHeight / 2,
|
|
87
|
+
left: left + window.scrollX + width + offset,
|
|
88
|
+
},
|
|
89
|
+
'top-start': {
|
|
90
|
+
top: top + window.scrollY - tooltipHeight - offset,
|
|
91
|
+
left: left + window.scrollX,
|
|
92
|
+
},
|
|
93
|
+
'top-end': {
|
|
94
|
+
top: top + window.scrollY - tooltipHeight - offset,
|
|
95
|
+
left: left + window.scrollX + width - tooltipWidth,
|
|
96
|
+
},
|
|
97
|
+
'bottom-start': {
|
|
98
|
+
top: top + window.scrollY + height + offset,
|
|
99
|
+
left: left + window.scrollX,
|
|
100
|
+
},
|
|
101
|
+
'bottom-end': {
|
|
102
|
+
top: top + window.scrollY + height + offset,
|
|
103
|
+
left: left + window.scrollX + width - tooltipWidth,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
return positions[placement];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Tooltip component.
|
|
110
|
+
*
|
|
111
|
+
* `children` must be a single valid React element (e.g. one `<button>` or
|
|
112
|
+
* one custom component that forwards `aria-describedby`) for the tooltip's
|
|
113
|
+
* content to be programmatically associated with it — this is what lets a
|
|
114
|
+
* screen reader announce the tooltip text for the actually-focused control.
|
|
115
|
+
* A non-element `children` (plain text, a fragment, multiple nodes) still
|
|
116
|
+
* shows the tooltip visually on hover/focus, but without that association.
|
|
117
|
+
* `hasArrow` renders a small themed pointer at the resolved `placement`
|
|
118
|
+
* edge.
|
|
119
|
+
*/
|
|
120
|
+
export const Tooltip = forwardRef(({ content, children, placement = 'top', delay = 0, hasArrow = false, className, style, testId }, _ref) => {
|
|
121
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
122
|
+
const [position, setPosition] = useState({ top: 0, left: 0 });
|
|
123
|
+
const triggerRef = useRef(null);
|
|
124
|
+
const tooltipRef = useRef(null);
|
|
125
|
+
const timeoutRef = useRef(undefined);
|
|
126
|
+
// Programmatically associates the tooltip with its trigger. A previous
|
|
127
|
+
// version relied on the WRAPPER div's hover/focus — but the wrapper is
|
|
128
|
+
// not itself the focused element, so its `aria-describedby` would never
|
|
129
|
+
// be announced for the child that actually receives focus (see the
|
|
130
|
+
// single-child cloning below).
|
|
131
|
+
const tooltipId = useId();
|
|
132
|
+
const cm = getClassMap();
|
|
133
|
+
const updatePosition = useCallback(() => {
|
|
134
|
+
// Guard against the effect firing without a DOM — SSR, or after the test
|
|
135
|
+
// environment / portal root has been torn down while a show timer or a
|
|
136
|
+
// pending effect was still queued. Accessing window/document then throws a
|
|
137
|
+
// leaked "window is not defined" async error (the component already guards
|
|
138
|
+
// `typeof document` at its portal site for the same reason).
|
|
139
|
+
if (typeof window === 'undefined' || typeof document === 'undefined')
|
|
140
|
+
return;
|
|
141
|
+
if (triggerRef.current && tooltipRef.current) {
|
|
142
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
143
|
+
const tooltipRect = tooltipRef.current.getBoundingClientRect();
|
|
144
|
+
const newPosition = calculatePosition(triggerRect, tooltipRect, placement);
|
|
145
|
+
// Clamp into the viewport so a wide/edge tooltip never spills off the page
|
|
146
|
+
// (which would spawn a horizontal scrollbar). Positions are in document
|
|
147
|
+
// space (they already include scrollX/scrollY); use clientWidth/clientHeight
|
|
148
|
+
// — NOT innerWidth/innerHeight — so the page scrollbar is excluded. The
|
|
149
|
+
// Math.max(min, max) guard keeps it on-screen even when the tooltip is wider
|
|
150
|
+
// than the viewport.
|
|
151
|
+
const margin = 8;
|
|
152
|
+
const minLeft = window.scrollX + margin;
|
|
153
|
+
const maxLeft = window.scrollX + document.documentElement.clientWidth - tooltipRect.width - margin;
|
|
154
|
+
const minTop = window.scrollY + margin;
|
|
155
|
+
const maxTop = window.scrollY + document.documentElement.clientHeight - tooltipRect.height - margin;
|
|
156
|
+
newPosition.left = Math.min(Math.max(newPosition.left, minLeft), Math.max(minLeft, maxLeft));
|
|
157
|
+
newPosition.top = Math.min(Math.max(newPosition.top, minTop), Math.max(minTop, maxTop));
|
|
158
|
+
setPosition(newPosition);
|
|
159
|
+
}
|
|
160
|
+
}, [placement]);
|
|
161
|
+
const show = useCallback(() => {
|
|
162
|
+
if (delay > 0) {
|
|
163
|
+
timeoutRef.current = setTimeout(() => {
|
|
164
|
+
setIsVisible(true);
|
|
165
|
+
}, delay);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
setIsVisible(true);
|
|
169
|
+
}
|
|
170
|
+
}, [delay]);
|
|
171
|
+
const hide = useCallback(() => {
|
|
172
|
+
if (timeoutRef.current) {
|
|
173
|
+
clearTimeout(timeoutRef.current);
|
|
174
|
+
}
|
|
175
|
+
setIsVisible(false);
|
|
176
|
+
}, []);
|
|
177
|
+
// Show on focus ONLY for keyboard focus (:focus-visible). A mouse click or a
|
|
178
|
+
// programmatic .focus() — e.g. a dropdown/menu returning focus to its trigger
|
|
179
|
+
// after it closes — must NOT re-show a tooltip the click was meant to dismiss
|
|
180
|
+
// (which is why the "Device frame" tooltip lingered after opening + selecting).
|
|
181
|
+
// Guarded: engines without :focus-visible support (older browsers, jsdom) throw
|
|
182
|
+
// on the unknown selector — there we simply skip the focus-show (hover still
|
|
183
|
+
// works), the safe default.
|
|
184
|
+
const handleFocus = useCallback((event) => {
|
|
185
|
+
const target = event.target;
|
|
186
|
+
try {
|
|
187
|
+
if (typeof target.matches === 'function' && target.matches(':focus-visible')) {
|
|
188
|
+
show();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (_error) {
|
|
192
|
+
// :focus-visible unsupported here — don't show on focus; hover still applies.
|
|
193
|
+
}
|
|
194
|
+
}, [show]);
|
|
195
|
+
useIsomorphicLayoutEffect(() => {
|
|
196
|
+
if (isVisible) {
|
|
197
|
+
updatePosition();
|
|
198
|
+
}
|
|
199
|
+
}, [isVisible, updatePosition]);
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
return () => {
|
|
202
|
+
if (timeoutRef.current) {
|
|
203
|
+
clearTimeout(timeoutRef.current);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}, []);
|
|
207
|
+
// Outer positioned wrapper carries the top/left/zIndex placement; the
|
|
208
|
+
// arrow renders as its SIBLING (not nested inside the visible box),
|
|
209
|
+
// because `tooltipContent` sets `overflow-hidden` — an arrow meant to
|
|
210
|
+
// poke past the box's own edge would be clipped invisible if it were a
|
|
211
|
+
// descendant of that element instead.
|
|
212
|
+
const tooltipElement = isVisible && (_jsxs("div", { style: { position: 'absolute', top: position.top, left: position.left, zIndex: 9999 }, children: [_jsx("div", { ref: tooltipRef, id: tooltipId, role: "tooltip", className: cm.cn(cm.tooltipContent, className), style: style, "data-testid": testId, children: content }), hasArrow && _jsx("span", { "aria-hidden": "true", style: getArrowStyle(placement) })] }));
|
|
213
|
+
// Single-child API (mirrors every mainstream tooltip library): when
|
|
214
|
+
// `children` is exactly one valid element, clone it to inject
|
|
215
|
+
// `aria-describedby` onto the actual focused/hovered node while the
|
|
216
|
+
// tooltip is visible — that id is what a screen reader announces
|
|
217
|
+
// alongside the element's accessible name. `children` is typed as
|
|
218
|
+
// `ReactNode` (it could be a string, a fragment, or multiple nodes); for
|
|
219
|
+
// anything other than a single element there is no single DOM node to
|
|
220
|
+
// attach the id to, so the association is skipped rather than guessed —
|
|
221
|
+
// documented in the module JSDoc as the supported contract.
|
|
222
|
+
const isCloneableChild = React.isValidElement(children) && children.type !== React.Fragment;
|
|
223
|
+
const describedChildren = isCloneableChild
|
|
224
|
+
? React.cloneElement(children, {
|
|
225
|
+
'aria-describedby': isVisible ? tooltipId : undefined,
|
|
226
|
+
})
|
|
227
|
+
: children;
|
|
228
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { ref: triggerRef, onMouseEnter: show, onMouseLeave: hide, onFocus: handleFocus, onBlur: hide,
|
|
229
|
+
// WCAG 1.4.13: hover/focus-triggered content must be dismissable
|
|
230
|
+
// WITHOUT moving the pointer or focus — Escape hides the tooltip
|
|
231
|
+
// (the keydown on the focused child bubbles up to this wrapper).
|
|
232
|
+
onKeyDown: (e) => {
|
|
233
|
+
if (e.key === 'Escape')
|
|
234
|
+
hide();
|
|
235
|
+
},
|
|
236
|
+
// Any click on a tooltip-wrapped control hides the tooltip — clicking a
|
|
237
|
+
// button shouldn't leave its hover/focus tooltip lingering. The wrapper
|
|
238
|
+
// wraps children directly, so this single handler covers every
|
|
239
|
+
// button-with-tooltip consumer.
|
|
240
|
+
onClick: hide, className: cm.tooltipTrigger, children: describedChildren }), typeof document !== 'undefined' &&
|
|
241
|
+
tooltipElement &&
|
|
242
|
+
createPortal(tooltipElement, document.body)] }));
|
|
243
|
+
});
|
|
244
|
+
Tooltip.displayName = 'Tooltip';
|
|
245
|
+
//# sourceMappingURL=Tooltip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tooltip.js","sourceRoot":"","sources":["../../src/components/Tooltip.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
import type { IconName } from '@molecule/app-icons';
|
|
3
|
+
/**
|
|
4
|
+
* Props for {@link UserMenu}.
|
|
5
|
+
*
|
|
6
|
+
* The inner trigger is a Molecule `<Button>` whose own prop set
|
|
7
|
+
* conflicts with raw `ButtonHTMLAttributes` (color, value, size enums),
|
|
8
|
+
* so this component exposes a curated set of customization points
|
|
9
|
+
* instead of extending HTML attributes. For one-off extra attributes,
|
|
10
|
+
* pass them via `dataMolId` / `className` / `style` props.
|
|
11
|
+
*/
|
|
12
|
+
export interface UserMenuProps {
|
|
13
|
+
/**
|
|
14
|
+
* Panel content shown inside the drawer/modal (typically the app's
|
|
15
|
+
* `SettingsPanel`). It can dismiss the drawer by calling
|
|
16
|
+
* `usePanelClose()` — no `onClose` prop-threading required.
|
|
17
|
+
*/
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* i18n key for the trigger button's aria-label. Defaults to
|
|
21
|
+
* `'userMenu.open'`. Apps whose existing locales use a different key
|
|
22
|
+
* (e.g. `'userMenu.openButton'`) can override.
|
|
23
|
+
*/
|
|
24
|
+
ariaLabelKey?: string;
|
|
25
|
+
/** Fallback aria-label if the i18n key is missing. Defaults to `"Open user menu"`. */
|
|
26
|
+
ariaLabelDefault?: string;
|
|
27
|
+
/** Icon name for the trigger button. Defaults to `'user'`. */
|
|
28
|
+
triggerIcon?: IconName;
|
|
29
|
+
/** Pixel size for the trigger icon. Defaults to 20. */
|
|
30
|
+
triggerIconSize?: number;
|
|
31
|
+
/**
|
|
32
|
+
* `data-mol-id` for the trigger button. Defaults to `'user-menu'`.
|
|
33
|
+
* Pass an explicit value to disambiguate when the same page mounts
|
|
34
|
+
* more than one UserMenu.
|
|
35
|
+
*/
|
|
36
|
+
dataMolId?: string;
|
|
37
|
+
/** Extra className composed onto the trigger button. */
|
|
38
|
+
className?: string;
|
|
39
|
+
/** Whether the trigger button is disabled. */
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Override the trigger button's click handler. When provided, called
|
|
43
|
+
* instead of opening the panel — hosts use this to intercept the click
|
|
44
|
+
* (e.g. to open an auth modal for guest users).
|
|
45
|
+
*/
|
|
46
|
+
onClick?: () => void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Avatar-style trigger that opens the app's settings panel in a drawer.
|
|
50
|
+
*
|
|
51
|
+
* The panel content is passed as `children` so apps can mount their own
|
|
52
|
+
* `SettingsPanel` (which diverges per app) inside the shared drawer
|
|
53
|
+
* chrome. Panel content dismisses the drawer via `usePanelClose()`.
|
|
54
|
+
*
|
|
55
|
+
* Ships with `data-mol-id="user-menu"` on the trigger button by
|
|
56
|
+
* default for AI-agent / e2e selection.
|
|
57
|
+
*/
|
|
58
|
+
export declare function UserMenu({ children, ariaLabelKey, ariaLabelDefault, triggerIcon, triggerIconSize, dataMolId, className, disabled, onClick, }: UserMenuProps): JSX.Element;
|
|
59
|
+
//# sourceMappingURL=UserMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserMenu.d.ts","sourceRoot":"","sources":["../../src/components/UserMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAI3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AASnD;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,YAA8B,EAC9B,gBAAmC,EACnC,WAAoB,EACpB,eAAoB,EACpB,SAAuB,EACvB,SAAS,EACT,QAAQ,EACR,OAAO,GACR,EAAE,aAAa,GAAG,GAAG,CAAC,OAAO,CAuC7B"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useLocation } from 'react-router';
|
|
4
|
+
import { useTranslation } from '@molecule/app-react';
|
|
5
|
+
import { getClassMap } from '@molecule/app-ui';
|
|
6
|
+
import { Button } from './Button.js';
|
|
7
|
+
import { Icon } from './Icon.js';
|
|
8
|
+
import { Modal } from './Modal.js';
|
|
9
|
+
import { PanelCloseProvider } from './PanelClose.js';
|
|
10
|
+
/**
|
|
11
|
+
* Avatar-style trigger that opens the app's settings panel in a drawer.
|
|
12
|
+
*
|
|
13
|
+
* The panel content is passed as `children` so apps can mount their own
|
|
14
|
+
* `SettingsPanel` (which diverges per app) inside the shared drawer
|
|
15
|
+
* chrome. Panel content dismisses the drawer via `usePanelClose()`.
|
|
16
|
+
*
|
|
17
|
+
* Ships with `data-mol-id="user-menu"` on the trigger button by
|
|
18
|
+
* default for AI-agent / e2e selection.
|
|
19
|
+
*/
|
|
20
|
+
export function UserMenu({ children, ariaLabelKey = 'userMenu.open', ariaLabelDefault = 'Open user menu', triggerIcon = 'user', triggerIconSize = 20, dataMolId = 'user-menu', className, disabled, onClick, }) {
|
|
21
|
+
const cm = getClassMap();
|
|
22
|
+
const { t } = useTranslation();
|
|
23
|
+
const [open, setOpen] = useState(false);
|
|
24
|
+
const location = useLocation();
|
|
25
|
+
// Close the drawer whenever the route changes. React Router updates
|
|
26
|
+
// `location` on both in-app navigation and browser back/forward
|
|
27
|
+
// (popstate), so a single effect covers both — without it the drawer
|
|
28
|
+
// stays mounted over the next page and blocks clicks underneath.
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
setOpen(false);
|
|
31
|
+
}, [location.pathname, location.search]);
|
|
32
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: onClick ?? (() => setOpen(true)), "aria-label": t(ariaLabelKey, {}, { defaultValue: ariaLabelDefault }), "data-mol-id": dataMolId, className: className, disabled: disabled, children: _jsx(Icon, { name: triggerIcon, size: triggerIconSize }) }), _jsx(Modal, { open: open, onClose: () => setOpen(false), className: cm.drawer, "aria-label": t('userMenu.panelLabel', {}, { defaultValue: 'Account menu' }), children: _jsx(PanelCloseProvider, { close: () => setOpen(false), children: children }) })] }));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=UserMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserMenu.js","sourceRoot":"","sources":["../../src/components/UserMenu.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG1C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAgDpD;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,EACvB,QAAQ,EACR,YAAY,GAAG,eAAe,EAC9B,gBAAgB,GAAG,gBAAgB,EACnC,WAAW,GAAG,MAAM,EACpB,eAAe,GAAG,EAAE,EACpB,SAAS,GAAG,WAAW,EACvB,SAAS,EACT,QAAQ,EACR,OAAO,GACO;IACd,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IACxB,MAAM,EAAE,CAAC,EAAE,GAAG,cAAc,EAAE,CAAA;IAC9B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAE9B,oEAAoE;IACpE,gEAAgE;IAChE,qEAAqE;IACrE,iEAAiE;IACjE,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,CAAA;IAChB,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;IAExC,OAAO,CACL,8BACE,KAAC,MAAM,IACL,OAAO,EAAC,OAAO,EACf,IAAI,EAAC,IAAI,EACT,OAAO,EAAE,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,gBAC7B,CAAC,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC,iBACtD,SAAS,EACtB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,YAElB,KAAC,IAAI,IAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,GAAI,GAC3C,EACT,KAAC,KAAK,IACJ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAC7B,SAAS,EAAE,EAAE,CAAC,MAAM,gBAGR,CAAC,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC,YAE1E,KAAC,kBAAkB,IAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,YAAG,QAAQ,GAAsB,GAC1E,IACP,CACJ,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserMenuPopover — an inline (non-modal) popover account menu.
|
|
3
|
+
*
|
|
4
|
+
* Unlike `UserMenu` / `SidebarUserCard` (which open their panel in a
|
|
5
|
+
* `<Modal>` drawer), this family renders the panel inline as an
|
|
6
|
+
* absolutely-positioned popover. It auto-dismisses on route change, raw
|
|
7
|
+
* `popstate`, outside click, and `Escape` — so an SPA navigation never
|
|
8
|
+
* leaves the menu hovering over the next page.
|
|
9
|
+
*
|
|
10
|
+
* Compose it:
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* <UserMenuPopover guestName={t('userMenu.guestName', {}, { defaultValue: 'Analyst' })}>
|
|
14
|
+
* <UserMenuPopoverTrigger />
|
|
15
|
+
* <UserMenuPopoverPanel>
|
|
16
|
+
* <Link to="/settings">Settings</Link>
|
|
17
|
+
* <Link to="/datasets">My datasets</Link>
|
|
18
|
+
* <UserMenuPopoverSignOut />
|
|
19
|
+
* </UserMenuPopoverPanel>
|
|
20
|
+
* </UserMenuPopover>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import React, { type ReactNode } from 'react';
|
|
26
|
+
/**
|
|
27
|
+
* Props for {@link UserMenuPopover}.
|
|
28
|
+
*/
|
|
29
|
+
export interface UserMenuPopoverProps {
|
|
30
|
+
/**
|
|
31
|
+
* The trigger and panel — typically `<UserMenuPopoverTrigger />` and
|
|
32
|
+
* `<UserMenuPopoverPanel>`.
|
|
33
|
+
*/
|
|
34
|
+
children: ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* Label shown when there is no authenticated user. Defaults to the
|
|
37
|
+
* `userMenuPopover.guest` translation (English fallback `"Account"`).
|
|
38
|
+
*/
|
|
39
|
+
guestName?: string;
|
|
40
|
+
/** Extra className composed onto the relative-positioned container. */
|
|
41
|
+
className?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Container for the inline popover account menu. Owns the open state and
|
|
45
|
+
* the auto-dismiss behaviour (route change, `popstate`, outside click,
|
|
46
|
+
* `Escape`), and provides the resolved account identity to its
|
|
47
|
+
* sub-components via context.
|
|
48
|
+
*
|
|
49
|
+
* @param props - The popover children, optional guest label, and className.
|
|
50
|
+
* @returns The relative-positioned popover container.
|
|
51
|
+
*/
|
|
52
|
+
export declare function UserMenuPopover({ children, guestName, className, }: UserMenuPopoverProps): React.JSX.Element;
|
|
53
|
+
/**
|
|
54
|
+
* Props for {@link UserMenuPopoverTrigger}.
|
|
55
|
+
*/
|
|
56
|
+
export interface UserMenuPopoverTriggerProps {
|
|
57
|
+
/** i18n key for the trigger's aria-label. Default: `'userMenu.open'`. */
|
|
58
|
+
ariaLabelKey?: string;
|
|
59
|
+
/** Fallback aria-label if the i18n key is missing. Default: `'Open user menu'`. */
|
|
60
|
+
ariaLabelDefault?: string;
|
|
61
|
+
/** `data-mol-id` for the trigger button. Default: `'user-menu'`. */
|
|
62
|
+
dataMolId?: string;
|
|
63
|
+
/** Extra className composed onto the trigger button. */
|
|
64
|
+
className?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The trigger button: an initials avatar plus the account name and email,
|
|
68
|
+
* styled as a full-width sidebar card. Toggles the popover panel.
|
|
69
|
+
*
|
|
70
|
+
* @param props - aria-label overrides, `data-mol-id`, and className.
|
|
71
|
+
* @returns The popover trigger button.
|
|
72
|
+
*/
|
|
73
|
+
export declare function UserMenuPopoverTrigger({ ariaLabelKey, ariaLabelDefault, dataMolId, className, }: UserMenuPopoverTriggerProps): React.JSX.Element;
|
|
74
|
+
/**
|
|
75
|
+
* Props for {@link UserMenuPopoverPanel}.
|
|
76
|
+
*/
|
|
77
|
+
export interface UserMenuPopoverPanelProps {
|
|
78
|
+
/**
|
|
79
|
+
* Panel body — typically a set of `<Link>` nav items and a
|
|
80
|
+
* `<UserMenuPopoverSignOut />`. Rendered inside a `<nav>` below the
|
|
81
|
+
* built-in identity header.
|
|
82
|
+
*/
|
|
83
|
+
children: ReactNode;
|
|
84
|
+
/** Extra className composed onto the absolute-positioned panel. */
|
|
85
|
+
className?: string;
|
|
86
|
+
/** i18n key for the panel's aria-label. Default: `'userMenu.panelLabel'`. */
|
|
87
|
+
ariaLabelKey?: string;
|
|
88
|
+
/** Fallback aria-label if the i18n key is missing. Default: `'Account menu'`. */
|
|
89
|
+
ariaLabelDefault?: string;
|
|
90
|
+
/** `data-mol-id` for the panel. Default: `'user-menu-panel'`. */
|
|
91
|
+
dataMolId?: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The popover panel: an absolutely-positioned card with a built-in
|
|
95
|
+
* identity header (name + email) and a `<nav>` wrapping the caller's nav
|
|
96
|
+
* items. Renders nothing while the popover is closed.
|
|
97
|
+
*
|
|
98
|
+
* Provides only the structural concerns (absolute positioning above the
|
|
99
|
+
* trigger, `rounded-xl` border frame, the header/nav layout). Cosmetic
|
|
100
|
+
* choices — width, background, padding, shadow — are per-app: pass them
|
|
101
|
+
* via `className`. `cn()` concatenates (it does not tailwind-merge), so
|
|
102
|
+
* the panel never bakes a width/background the caller would have to
|
|
103
|
+
* fight.
|
|
104
|
+
*
|
|
105
|
+
* @param props - The nav children, className, and aria-label overrides.
|
|
106
|
+
* @returns The popover panel, or `null` when closed.
|
|
107
|
+
*/
|
|
108
|
+
export declare function UserMenuPopoverPanel({ children, className, ariaLabelKey, ariaLabelDefault, dataMolId, }: UserMenuPopoverPanelProps): React.JSX.Element | null;
|
|
109
|
+
/**
|
|
110
|
+
* Returns a callback that closes the enclosing `UserMenuPopover`. Useful
|
|
111
|
+
* for nav-item `onClick` handlers that should dismiss the popover even
|
|
112
|
+
* when they don't change the route. Returns a no-op when called outside
|
|
113
|
+
* a `UserMenuPopover`.
|
|
114
|
+
*
|
|
115
|
+
* @returns A function that closes the popover.
|
|
116
|
+
*/
|
|
117
|
+
export declare function useUserMenuPopoverClose(): () => void;
|
|
118
|
+
/**
|
|
119
|
+
* Props for {@link UserMenuPopoverSignOut}.
|
|
120
|
+
*/
|
|
121
|
+
export interface UserMenuPopoverSignOutProps {
|
|
122
|
+
/** i18n key for the button label. Default: `'userMenu.signOut'`. */
|
|
123
|
+
labelKey?: string;
|
|
124
|
+
/** Fallback label if the i18n key is missing. Default: `'Sign out'`. */
|
|
125
|
+
labelDefault?: string;
|
|
126
|
+
/** `data-mol-id` for the button. Default: `'user-menu-sign-out'`. */
|
|
127
|
+
dataMolId?: string;
|
|
128
|
+
/** Extra className composed onto the button. */
|
|
129
|
+
className?: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The sign-out nav item — closes the popover and calls `auth.logout()`.
|
|
133
|
+
* Drop it in as the last child of `<UserMenuPopoverPanel>`.
|
|
134
|
+
*
|
|
135
|
+
* @param props - Label overrides, `data-mol-id`, and className.
|
|
136
|
+
* @returns The sign-out button.
|
|
137
|
+
*/
|
|
138
|
+
export declare function UserMenuPopoverSignOut({ labelKey, labelDefault, dataMolId, className, }: UserMenuPopoverSignOutProps): React.JSX.Element;
|
|
139
|
+
//# sourceMappingURL=UserMenuPopover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserMenuPopover.d.ts","sourceRoot":"","sources":["../../src/components/UserMenuPopover.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,EAEZ,KAAK,SAAS,EAMf,MAAM,OAAO,CAAA;AA4Cd;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAA;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,SAAS,EACT,SAAS,GACV,EAAE,oBAAoB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAqD1C;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,YAA8B,EAC9B,gBAAmC,EACnC,SAAuB,EACvB,SAAS,GACV,EAAE,2BAA2B,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA+DjD;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAA;IACnB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,EACT,YAAoC,EACpC,gBAAiC,EACjC,SAA6B,GAC9B,EAAE,yBAAyB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAkCtD;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,IAAI,CAIpD;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,QAA6B,EAC7B,YAAyB,EACzB,SAAgC,EAChC,SAAS,GACV,EAAE,2BAA2B,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAkCjD"}
|