oneslash-design-system 1.6.0 → 1.7.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/README.md +18 -0
- package/dist/components/alert.jsx +31 -16
- package/dist/components/button.jsx +30 -108
- package/dist/components/checkBox.d.ts +2 -1
- package/dist/components/checkBox.jsx +24 -24
- package/dist/components/emptyBox.jsx +6 -4
- package/dist/components/iconButton.d.ts +4 -1
- package/dist/components/iconButton.jsx +39 -36
- package/dist/components/link.jsx +29 -13
- package/dist/components/loadingScreen.jsx +4 -4
- package/dist/components/menu.jsx +3 -2
- package/dist/components/menuItem.d.ts +8 -1
- package/dist/components/menuItem.jsx +23 -94
- package/dist/components/modal.jsx +51 -12
- package/dist/components/navigation.jsx +1 -1
- package/dist/components/popover.jsx +22 -19
- package/dist/components/radioGroup.jsx +25 -15
- package/dist/components/select.jsx +102 -133
- package/dist/components/sharedStyles.d.ts +9 -0
- package/dist/components/sharedStyles.jsx +18 -0
- package/dist/components/tab.jsx +20 -92
- package/dist/components/tableCell.jsx +1 -1
- package/dist/components/tableContainer.jsx +1 -1
- package/dist/components/tableHeader.jsx +1 -1
- package/dist/components/tableHeaderCell.jsx +1 -1
- package/dist/components/tableRow.jsx +1 -1
- package/dist/components/tabsContainer.jsx +20 -2
- package/dist/components/tag.jsx +43 -26
- package/dist/components/textField.d.ts +3 -1
- package/dist/components/textField.jsx +23 -32
- package/dist/components/textarea.d.ts +3 -1
- package/dist/components/textarea.jsx +38 -41
- package/dist/components/timeStamp.jsx +6 -4
- package/dist/components/tooltip.d.ts +1 -1
- package/dist/components/tooltip.jsx +81 -44
- package/dist/components/useOverlayTransition.d.ts +9 -0
- package/dist/components/useOverlayTransition.js +31 -0
- package/dist/components/userImage.jsx +2 -2
- package/dist/designTokens.js +11 -11
- package/dist/output.css +382 -151
- package/dist/tailwind.config.js +12 -11
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/components/menu.jsx
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import React, { forwardRef } from 'react';
|
|
3
3
|
var Menu = forwardRef(function (_a, ref) {
|
|
4
4
|
var children = _a.children, width = _a.width, _b = _a.className, className = _b === void 0 ? '' : _b;
|
|
5
|
-
return (<div ref={ref} className={"\n bg-light-background-accent300 dark:bg-dark-background-accent300\n rounded-[
|
|
6
|
-
|
|
5
|
+
return (<div ref={ref} className={"\n bg-light-background-accent300 dark:bg-dark-background-accent300\n rounded-[12px]\n shadow-lg\n dark:ring-1 dark:ring-dark-misc-divider\n overflow-hidden\n ".concat(className, "\n ")} style={{ width: width || 'auto' }}>
|
|
6
|
+
{/* 4px padding keeps the 8px item corners concentric with the 12px menu corners */}
|
|
7
|
+
<div className="p-1">
|
|
7
8
|
{children}
|
|
8
9
|
</div>
|
|
9
10
|
</div>);
|
|
@@ -19,6 +19,13 @@ interface MenuItemProps {
|
|
|
19
19
|
};
|
|
20
20
|
iconRight?: string;
|
|
21
21
|
rightAction?: React.ReactNode;
|
|
22
|
+
/** ARIA role; Select passes 'option'. Defaults to 'menuitem'. */
|
|
23
|
+
role?: string;
|
|
24
|
+
id?: string;
|
|
25
|
+
/** Keyboard highlight used by Select's listbox */
|
|
26
|
+
isActive?: boolean;
|
|
27
|
+
/** Select keeps focus on its trigger and passes -1 */
|
|
28
|
+
tabIndex?: number;
|
|
22
29
|
}
|
|
23
|
-
export default function MenuItem({ href, iconName, icon, userHandle, userImgUrl, label, secondLabel, isSelected, onClick, className, size, tag, iconRight, rightAction, }: MenuItemProps): JSX.Element;
|
|
30
|
+
export default function MenuItem({ href, iconName, icon, userHandle, userImgUrl, label, secondLabel, isSelected, onClick, className, size, tag, iconRight, rightAction, role, id, isActive, tabIndex, }: MenuItemProps): JSX.Element;
|
|
24
31
|
export {};
|
|
@@ -1,106 +1,33 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
-
function step(op) {
|
|
16
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
-
switch (op[0]) {
|
|
21
|
-
case 0: case 1: t = op; break;
|
|
22
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
-
default:
|
|
26
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
-
if (t[2]) _.ops.pop();
|
|
31
|
-
_.trys.pop(); continue;
|
|
32
|
-
}
|
|
33
|
-
op = body.call(thisArg, _);
|
|
34
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import * as LucideIcons from 'lucide-react';
|
|
39
4
|
import NextLink from 'next/link';
|
|
40
5
|
import UserImage from './userImage';
|
|
41
6
|
import Tag from './tag';
|
|
7
|
+
import { focusRing, pressFeedbackAny, stateTransition } from './sharedStyles';
|
|
42
8
|
export default function MenuItem(_a) {
|
|
43
|
-
var
|
|
44
|
-
var href = _a.href, iconName = _a.iconName, icon = _a.icon, userHandle = _a.userHandle, userImgUrl = _a.userImgUrl, label = _a.label, secondLabel = _a.secondLabel, isSelected = _a.isSelected, onClick = _a.onClick,
|
|
45
|
-
|
|
46
|
-
var
|
|
47
|
-
var
|
|
48
|
-
var module_1, IconComponent, error_1;
|
|
49
|
-
return __generator(this, function (_a) {
|
|
50
|
-
switch (_a.label) {
|
|
51
|
-
case 0:
|
|
52
|
-
if (!iconName)
|
|
53
|
-
return [2 /*return*/, null];
|
|
54
|
-
_a.label = 1;
|
|
55
|
-
case 1:
|
|
56
|
-
_a.trys.push([1, 3, , 4]);
|
|
57
|
-
return [4 /*yield*/, import('lucide-react')];
|
|
58
|
-
case 2:
|
|
59
|
-
module_1 = _a.sent();
|
|
60
|
-
IconComponent = module_1[iconName];
|
|
61
|
-
return [2 /*return*/, IconComponent || null];
|
|
62
|
-
case 3:
|
|
63
|
-
error_1 = _a.sent();
|
|
64
|
-
console.error("Failed to load icon ".concat(iconName, ":"), error_1);
|
|
65
|
-
return [2 /*return*/, null];
|
|
66
|
-
case 4: return [2 /*return*/];
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}); }, []);
|
|
70
|
-
useEffect(function () {
|
|
71
|
-
var fetchIcon = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
72
|
-
var _a, _b;
|
|
73
|
-
return __generator(this, function (_c) {
|
|
74
|
-
switch (_c.label) {
|
|
75
|
-
case 0:
|
|
76
|
-
if (!iconName) return [3 /*break*/, 2];
|
|
77
|
-
_a = setIconLeft;
|
|
78
|
-
return [4 /*yield*/, loadIcon(iconName)];
|
|
79
|
-
case 1:
|
|
80
|
-
_a.apply(void 0, [_c.sent()]);
|
|
81
|
-
_c.label = 2;
|
|
82
|
-
case 2:
|
|
83
|
-
if (!iconRight) return [3 /*break*/, 4];
|
|
84
|
-
_b = setIconRight;
|
|
85
|
-
return [4 /*yield*/, loadIcon(iconRight)];
|
|
86
|
-
case 3:
|
|
87
|
-
_b.apply(void 0, [_c.sent()]);
|
|
88
|
-
_c.label = 4;
|
|
89
|
-
case 4: return [2 /*return*/];
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}); };
|
|
93
|
-
fetchIcon();
|
|
94
|
-
}, [iconName, iconRight, loadIcon]);
|
|
9
|
+
var _b;
|
|
10
|
+
var href = _a.href, iconName = _a.iconName, icon = _a.icon, userHandle = _a.userHandle, userImgUrl = _a.userImgUrl, label = _a.label, secondLabel = _a.secondLabel, isSelected = _a.isSelected, onClick = _a.onClick, _c = _a.className, className = _c === void 0 ? '' : _c, _d = _a.size, size = _d === void 0 ? 'medium' : _d, tag = _a.tag, iconRight = _a.iconRight, rightAction = _a.rightAction, _e = _a.role, role = _e === void 0 ? 'menuitem' : _e, id = _a.id, _f = _a.isActive, isActive = _f === void 0 ? false : _f, _g = _a.tabIndex, tabIndex = _g === void 0 ? 0 : _g;
|
|
11
|
+
// Synchronous lookup so icons render on the first paint
|
|
12
|
+
var IconLeft = iconName ? LucideIcons[iconName] : null;
|
|
13
|
+
var IconRight = iconRight ? LucideIcons[iconRight] : null;
|
|
95
14
|
// Size-based icon and text classes
|
|
96
15
|
var iconSize = { tiny: 'w-4 h-4', small: 'w-5 h-5', medium: 'w-6 h-6' }[size];
|
|
97
16
|
var labelClass = { tiny: 'text-caption', small: 'text-body2', medium: 'text-body1' }[size];
|
|
98
17
|
var secondLabelClass = size === 'tiny' ? 'text-caption' : 'text-body2';
|
|
99
|
-
|
|
18
|
+
// One step smaller than the row so a tag never makes its row taller
|
|
19
|
+
var tagSize = { tiny: 'tiny', small: 'tiny', medium: 'small' }[size];
|
|
100
20
|
var paddingClass = size === 'tiny' ? 'p-1' : 'p-2';
|
|
101
|
-
var content = (<div
|
|
21
|
+
var content = (<div id={id} role={href ? undefined : role}
|
|
22
|
+
// A link wrapper is already focusable; otherwise the row itself takes focus
|
|
23
|
+
tabIndex={href ? undefined : tabIndex} aria-selected={role === 'option' ? !!isSelected : undefined} className={"\n flex items-center ".concat(paddingClass, " rounded-[8px] cursor-pointer justify-between text-left ").concat(stateTransition, " ").concat(pressFeedbackAny, " ").concat(focusRing, "\n ").concat(isSelected
|
|
102
24
|
? 'bg-light-primary-dark dark:bg-dark-primary-dark text-light-text-contrast dark:text-dark-text-contrast'
|
|
103
|
-
:
|
|
25
|
+
: "hover:bg-light-background-accent200 dark:hover:bg-dark-background-accent200 ".concat(isActive ? 'bg-light-background-accent200 dark:bg-dark-background-accent200' : ''), "\n ").concat(className, "\n ")} style={{ width: '100%' }} onClick={onClick} onKeyDown={function (e) {
|
|
26
|
+
if (!href && e.target === e.currentTarget && (e.key === 'Enter' || e.key === ' ')) {
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
e.currentTarget.click();
|
|
29
|
+
}
|
|
30
|
+
}}>
|
|
104
31
|
{/* Left group: icon/userImg + label with 8px gap */}
|
|
105
32
|
<div className="flex items-center gap-2 min-w-0">
|
|
106
33
|
{userImgUrl ? (<UserImage userHandle={userHandle || ''} userImgUrl={userImgUrl}/>) : icon ? (<span className={"".concat(iconSize, " flex-shrink-0 flex items-center justify-center")}>{icon}</span>) : (IconLeft && (<IconLeft className={"".concat(iconSize, " flex-shrink-0 ").concat(isSelected ? 'text-light-text-contrast dark:text-dark-text-contrast' : 'text-light-text-secondary dark:text-dark-text-secondary')} strokeWidth={2}/>))}
|
|
@@ -116,12 +43,14 @@ export default function MenuItem(_a) {
|
|
|
116
43
|
|
|
117
44
|
{/* Right group: tag + icon aligned to the right */}
|
|
118
45
|
<div className="flex items-center gap-2 flex-shrink-0">
|
|
119
|
-
{tag && (<Tag variant={tag.variant || 'contained'} size={tagSize} label={tag.label} iconName={tag.iconName}
|
|
46
|
+
{tag && (<Tag variant={tag.variant || 'contained'} size={tagSize} label={tag.label} iconName={tag.iconName}
|
|
47
|
+
// 'default' has a fill that stays visible on the menu background
|
|
48
|
+
color={(_b = tag.color) !== null && _b !== void 0 ? _b : 'default'}/>)}
|
|
120
49
|
{IconRight && (<IconRight className={"".concat(iconSize, " ").concat(isSelected ? 'text-light-text-contrast dark:text-dark-text-contrast' : 'text-light-text-secondary dark:text-dark-text-secondary', " flex-shrink-0")} strokeWidth={2}/>)}
|
|
121
50
|
{rightAction && (<div onClick={function (e) { return e.stopPropagation(); }}>
|
|
122
51
|
{rightAction}
|
|
123
52
|
</div>)}
|
|
124
53
|
</div>
|
|
125
54
|
</div>);
|
|
126
|
-
return href ? <NextLink href={href}>{content}</NextLink> : content;
|
|
55
|
+
return href ? <NextLink href={href} className={"block rounded-[8px] ".concat(focusRing)}>{content}</NextLink> : content;
|
|
127
56
|
}
|
|
@@ -1,38 +1,77 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import React, { useEffect } from 'react';
|
|
2
|
+
import React, { useEffect, useId, useRef } from 'react';
|
|
3
|
+
import { overlayHidden, overlayTransition, overlayVisible } from './sharedStyles';
|
|
4
|
+
import { useOverlayTransition } from './useOverlayTransition';
|
|
5
|
+
var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
3
6
|
export default function Modal(_a) {
|
|
4
7
|
var isOpen = _a.isOpen, title = _a.title, secondTitle = _a.secondTitle, children = _a.children, onClose = _a.onClose, actions = _a.actions, _b = _a.size, size = _b === void 0 ? 'medium' : _b;
|
|
5
|
-
|
|
8
|
+
var panelRef = useRef(null);
|
|
9
|
+
var titleId = useId();
|
|
10
|
+
var _c = useOverlayTransition(isOpen), mounted = _c.mounted, visible = _c.visible;
|
|
11
|
+
// Keep onClose in a ref so a new function each render doesn't re-run the focus effect
|
|
12
|
+
var onCloseRef = useRef(onClose);
|
|
13
|
+
onCloseRef.current = onClose;
|
|
14
|
+
// Move focus into the panel, trap Tab inside it, close on Escape,
|
|
15
|
+
// lock page scroll, and give focus back to the trigger on close
|
|
6
16
|
useEffect(function () {
|
|
7
|
-
|
|
17
|
+
var _a;
|
|
18
|
+
// Wait for the panel to mount before moving focus into it
|
|
19
|
+
if (!isOpen || !mounted)
|
|
8
20
|
return;
|
|
21
|
+
var previouslyFocused = document.activeElement;
|
|
22
|
+
var panel = panelRef.current;
|
|
23
|
+
var firstFocusable = panel === null || panel === void 0 ? void 0 : panel.querySelector(FOCUSABLE);
|
|
24
|
+
(_a = (firstFocusable !== null && firstFocusable !== void 0 ? firstFocusable : panel)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
25
|
+
var previousOverflow = document.body.style.overflow;
|
|
26
|
+
document.body.style.overflow = 'hidden';
|
|
9
27
|
var handleKeyDown = function (e) {
|
|
10
28
|
if (e.key === 'Escape') {
|
|
11
|
-
|
|
29
|
+
onCloseRef.current();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (e.key !== 'Tab' || !panel)
|
|
33
|
+
return;
|
|
34
|
+
var focusable = Array.from(panel.querySelectorAll(FOCUSABLE));
|
|
35
|
+
if (focusable.length === 0) {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
var first = focusable[0];
|
|
40
|
+
var last = focusable[focusable.length - 1];
|
|
41
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
last.focus();
|
|
44
|
+
}
|
|
45
|
+
else if (!e.shiftKey && document.activeElement === last) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
first.focus();
|
|
12
48
|
}
|
|
13
49
|
};
|
|
14
50
|
window.addEventListener('keydown', handleKeyDown);
|
|
15
51
|
return function () {
|
|
52
|
+
var _a;
|
|
16
53
|
window.removeEventListener('keydown', handleKeyDown);
|
|
54
|
+
document.body.style.overflow = previousOverflow;
|
|
55
|
+
(_a = previouslyFocused === null || previouslyFocused === void 0 ? void 0 : previouslyFocused.focus) === null || _a === void 0 ? void 0 : _a.call(previouslyFocused);
|
|
17
56
|
};
|
|
18
|
-
}, [isOpen,
|
|
57
|
+
}, [isOpen, mounted]);
|
|
19
58
|
// Determine width based on size prop
|
|
20
59
|
var sizeConfig = {
|
|
21
60
|
small: { width: 'w-[480px]', maxWidth: '480px' },
|
|
22
61
|
medium: { width: 'w-[600px]', maxWidth: '600px' },
|
|
23
62
|
large: { width: 'w-[1024px]', maxWidth: '1024px' },
|
|
24
63
|
};
|
|
25
|
-
var
|
|
64
|
+
var _d = sizeConfig[size], modalWidth = _d.width, maxWidth = _d.maxWidth;
|
|
26
65
|
// close modal by clicking elsewhere
|
|
27
66
|
var handleOverlayClick = function (e) {
|
|
28
67
|
if (e.target === e.currentTarget) {
|
|
29
68
|
onClose();
|
|
30
69
|
}
|
|
31
70
|
};
|
|
32
|
-
if (!
|
|
71
|
+
if (!mounted)
|
|
33
72
|
return null;
|
|
34
|
-
return (<div className="fixed bg-light-misc-scrim/40 dark:bg-dark-misc-scrim/40 flex items-center justify-center z-50" style={{ top: 0, left: 0, right: 0, bottom: 0 }} onClick={handleOverlayClick}
|
|
35
|
-
<div className={"bg-light-background-default dark:bg-dark-background-
|
|
73
|
+
return (<div className={"fixed bg-light-misc-scrim/40 dark:bg-dark-misc-scrim/40 flex items-center justify-center z-50\n transition-opacity duration-150 ease-out motion-reduce:transition-none ".concat(visible ? 'opacity-100' : 'opacity-0')} style={{ top: 0, left: 0, right: 0, bottom: 0 }} onClick={handleOverlayClick}>
|
|
74
|
+
<div ref={panelRef} role="dialog" aria-modal="true" aria-labelledby={title ? titleId : undefined} tabIndex={-1} className={"bg-light-background-default dark:bg-dark-background-accent100 dark:ring-1 dark:ring-dark-misc-divider rounded-[8px] ".concat(modalWidth, " flex flex-col outline-none\n ").concat(overlayTransition, " ").concat(visible ? overlayVisible : overlayHidden)} style={{
|
|
36
75
|
maxWidth: maxWidth,
|
|
37
76
|
width: 'calc(100vw - 64px)',
|
|
38
77
|
maxHeight: '90vh',
|
|
@@ -40,7 +79,7 @@ export default function Modal(_a) {
|
|
|
40
79
|
}}>
|
|
41
80
|
{(title || actions) && (<div className="px-6 py-3 border-b border-light-misc-divider dark:border-dark-misc-divider flex justify-between items-start" style={{ flexShrink: 0 }}>
|
|
42
81
|
{title && (<div className="flex flex-col">
|
|
43
|
-
<h2 id=
|
|
82
|
+
<h2 id={titleId} className="text-h5 text-light-text-primary dark:text-dark-text-primary">
|
|
44
83
|
{title}
|
|
45
84
|
</h2>
|
|
46
85
|
{secondTitle && (<span className="text-body2 opacity-70 text-light-text-primary dark:text-dark-text-primary">
|
|
@@ -56,8 +95,8 @@ export default function Modal(_a) {
|
|
|
56
95
|
overflowY: 'auto',
|
|
57
96
|
flexGrow: 1,
|
|
58
97
|
minHeight: 0,
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
// A thin scrollbar is the only cue that long content scrolls
|
|
99
|
+
scrollbarWidth: 'thin',
|
|
61
100
|
}}>
|
|
62
101
|
{children}
|
|
63
102
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
export default function Navigation(_a) {
|
|
4
4
|
var children = _a.children, _b = _a.className, className = _b === void 0 ? '' : _b;
|
|
5
|
-
return (<nav className={"\n bg-light-background-default dark:bg-dark-background-default\n border-r border-light-misc-divider dark:border-dark-misc-divider\n p-
|
|
5
|
+
return (<nav className={"\n bg-light-background-default dark:bg-dark-background-default\n border-r border-light-misc-divider dark:border-dark-misc-divider\n p-2\n ".concat(className, "\n ")}>
|
|
6
6
|
{children}
|
|
7
7
|
</nav>);
|
|
8
8
|
}
|
|
@@ -1,52 +1,55 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
2
|
import React, { useState, useEffect } from 'react';
|
|
14
3
|
import { createPortal } from 'react-dom';
|
|
15
4
|
import { usePopper } from 'react-popper';
|
|
5
|
+
import { overlayHidden, overlayTransition, overlayVisible } from './sharedStyles';
|
|
6
|
+
import { useOverlayTransition } from './useOverlayTransition';
|
|
16
7
|
export default function Popover(_a) {
|
|
17
8
|
var id = _a.id, anchorEl = _a.anchorEl, open = _a.open, onClose = _a.onClose, children = _a.children;
|
|
18
9
|
var _b = useState(null), popoverElement = _b[0], setPopoverElement = _b[1];
|
|
19
10
|
var _c = useState(false), hasMounted = _c[0], setHasMounted = _c[1];
|
|
11
|
+
var _d = useOverlayTransition(open), mounted = _d.mounted, visible = _d.visible;
|
|
20
12
|
// Initialize Popper.js
|
|
21
|
-
var
|
|
13
|
+
var _e = usePopper(anchorEl, popoverElement, {
|
|
22
14
|
placement: 'bottom-start', // Default placement, can be customized
|
|
23
15
|
modifiers: [
|
|
24
|
-
{ name: 'offset', options: { offset: [0,
|
|
16
|
+
{ name: 'offset', options: { offset: [0, 4] } }, // Same 4px gap as the Select menu and Tooltip
|
|
25
17
|
],
|
|
26
|
-
}), styles =
|
|
27
|
-
//
|
|
18
|
+
}), styles = _e.styles, attributes = _e.attributes;
|
|
19
|
+
// Close on outside clicks and Escape. Clicks on the anchor are left to the
|
|
20
|
+
// anchor's own toggle, otherwise the popover would close and reopen.
|
|
28
21
|
useEffect(function () {
|
|
29
22
|
var handleClickOutside = function (event) {
|
|
30
|
-
|
|
23
|
+
var target = event.target;
|
|
24
|
+
if (popoverElement && !popoverElement.contains(target) && anchorEl && !anchorEl.contains(target)) {
|
|
31
25
|
onClose();
|
|
32
26
|
}
|
|
33
27
|
};
|
|
28
|
+
var handleKeyDown = function (event) {
|
|
29
|
+
if (event.key === 'Escape')
|
|
30
|
+
onClose();
|
|
31
|
+
};
|
|
34
32
|
if (open) {
|
|
35
33
|
document.addEventListener('mousedown', handleClickOutside);
|
|
34
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
36
35
|
}
|
|
37
36
|
return function () {
|
|
38
37
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
38
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
39
39
|
};
|
|
40
40
|
}, [open, anchorEl, popoverElement, onClose]);
|
|
41
41
|
// Ensure popover is only rendered after the component mounts
|
|
42
42
|
useEffect(function () {
|
|
43
43
|
setHasMounted(true);
|
|
44
44
|
}, []);
|
|
45
|
-
if (!
|
|
45
|
+
if (!mounted || !hasMounted || !anchorEl)
|
|
46
46
|
return null;
|
|
47
47
|
// Render popover in a portal to prevent layout shifts and positioning issues
|
|
48
|
-
return createPortal(<div id={id} ref={setPopoverElement} style={
|
|
49
|
-
{
|
|
48
|
+
return createPortal(<div id={id} ref={setPopoverElement} style={styles.popper} {...attributes.popper} className="z-50" role="dialog">
|
|
49
|
+
{/* Popper owns the outer transform, so the fade and scale go on this inner panel */}
|
|
50
|
+
<div className={"bg-light-background-accent300 dark:bg-dark-background-accent300 dark:ring-1 dark:ring-dark-misc-divider rounded-[8px] shadow-lg p-2 origin-top-left\n ".concat(overlayTransition, " ").concat(visible ? overlayVisible : overlayHidden)}>
|
|
51
|
+
{children}
|
|
52
|
+
</div>
|
|
50
53
|
</div>, document.body // Mounting the popover in the document body for isolation
|
|
51
54
|
);
|
|
52
55
|
}
|
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import React from 'react';
|
|
2
|
+
import React, { useId } from 'react';
|
|
3
|
+
import { popTransition, stateTransition } from './sharedStyles';
|
|
3
4
|
export default function RadioGroup(_a) {
|
|
4
5
|
var options = _a.options, selectedValue = _a.selectedValue, onChange = _a.onChange, _b = _a.direction, direction = _b === void 0 ? 'vertical' : _b;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
{
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
// One shared name gives native arrow-key movement between the options
|
|
7
|
+
var groupName = useId();
|
|
8
|
+
return (<div role="radiogroup" className={"flex ".concat(direction === 'horizontal' ? 'gap-4' : 'flex-col gap-2')}>
|
|
9
|
+
{options.map(function (option) {
|
|
10
|
+
var isSelected = selectedValue === option.value;
|
|
11
|
+
return (<label key={option.value} className="group flex items-start cursor-pointer">
|
|
12
|
+
<input type="radio" name={groupName} value={option.value} checked={isSelected} onChange={function () { return onChange(option.value); }} className="peer sr-only"/>
|
|
13
|
+
{/* Hover halo and hit area, same as Checkbox */}
|
|
14
|
+
<span className={"relative flex flex-shrink-0 items-center justify-center w-6 h-6 rounded-full ".concat(stateTransition, "\n group-hover:bg-light-action-selected dark:group-hover:bg-dark-action-selected\n peer-focus-visible:outline peer-focus-visible:outline-2 peer-focus-visible:outline-offset-0 peer-focus-visible:outline-light-accent-main dark:peer-focus-visible:outline-dark-accent-main")}>
|
|
15
|
+
{/* outer circle */}
|
|
16
|
+
<span className={"relative flex justify-center items-center w-4 h-4 rounded-full border-2 ".concat(stateTransition, "\n ").concat(isSelected
|
|
17
|
+
? 'border-light-text-primary dark:border-dark-text-primary'
|
|
18
|
+
: 'border-light-text-secondary dark:border-dark-text-secondary', "\n ")}>
|
|
19
|
+
{/* Inner circle; always rendered so it can scale in and out */}
|
|
20
|
+
<span className={"absolute w-2 h-2 rounded-full bg-light-text-primary dark:bg-dark-text-primary ".concat(popTransition, " ").concat(isSelected ? 'scale-100' : 'scale-0')}/>
|
|
21
|
+
</span>
|
|
22
|
+
</span>
|
|
23
|
+
<span className="flex flex-col ml-2">
|
|
24
|
+
<span className="text-body1 text-light-text-primary dark:text-dark-text-primary">{option.label}</span>
|
|
25
|
+
{option.secondLabel && (<span className="text-body2 opacity-70 text-light-text-primary dark:text-dark-text-primary">{option.secondLabel}</span>)}
|
|
26
|
+
</span>
|
|
27
|
+
</label>);
|
|
28
|
+
})}
|
|
19
29
|
</div>);
|
|
20
30
|
}
|