oneslash-design-system 1.5.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.d.ts +1 -1
- package/dist/components/button.jsx +32 -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/icon.d.ts +1 -1
- package/dist/components/icon.jsx +1 -0
- package/dist/components/iconButton.d.ts +4 -1
- package/dist/components/iconButton.jsx +40 -40
- 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 +9 -2
- package/dist/components/menuItem.jsx +28 -97
- 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.d.ts +2 -1
- package/dist/components/tab.jsx +38 -99
- 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.d.ts +1 -0
- package/dist/components/tabsContainer.jsx +86 -4
- package/dist/components/tag.d.ts +1 -1
- package/dist/components/tag.jsx +48 -27
- package/dist/components/textField.d.ts +4 -2
- package/dist/components/textField.jsx +24 -32
- package/dist/components/textarea.d.ts +4 -2
- package/dist/components/textarea.jsx +39 -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 +16 -16
- package/dist/output.css +439 -136
- package/dist/stories/Button.stories.jsx +1 -1
- package/dist/stories/Icon.stories.jsx +1 -1
- package/dist/stories/MenuItem.stories.jsx +1 -1
- package/dist/stories/TabsContainer.stories.d.ts +2 -0
- package/dist/stories/TabsContainer.stories.jsx +14 -2
- package/dist/stories/Tag.stories.jsx +1 -1
- package/dist/stories/TextField.stories.jsx +1 -1
- package/dist/stories/Textarea.stories.jsx +1 -1
- package/dist/tailwind.config.js +12 -11
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/components/link.jsx
CHANGED
|
@@ -12,29 +12,31 @@ var __assign = (this && this.__assign) || function () {
|
|
|
12
12
|
};
|
|
13
13
|
import React from 'react';
|
|
14
14
|
import NextLink from 'next/link';
|
|
15
|
+
import { focusRing } from './sharedStyles';
|
|
15
16
|
export default function Link(_a) {
|
|
16
17
|
var href = _a.href, children = _a.children, _b = _a.color, color = _b === void 0 ? 'info' : _b, _c = _a.size, size = _c === void 0 ? 'body1' : _c, _d = _a.openInNewTab, openInNewTab = _d === void 0 ? false : _d, _e = _a.className, className = _e === void 0 ? '' : _e;
|
|
17
18
|
// Color classes based on the color prop
|
|
19
|
+
// Light mode uses the -dark shade so link text keeps 4.5:1 on light backgrounds
|
|
18
20
|
var colorClasses = {
|
|
19
21
|
info: {
|
|
20
|
-
default: 'text-light-info-
|
|
21
|
-
hover: 'hover:
|
|
22
|
-
visited: 'visited:text-light-info-
|
|
22
|
+
default: 'text-light-info-dark dark:text-dark-info-main',
|
|
23
|
+
hover: 'hover:underline-offset-[4px] dark:hover:text-dark-info-light',
|
|
24
|
+
visited: 'visited:text-light-info-dark dark:visited:text-dark-info-light',
|
|
23
25
|
},
|
|
24
26
|
error: {
|
|
25
|
-
default: 'text-light-error-
|
|
26
|
-
hover: 'hover:
|
|
27
|
-
visited: 'visited:text-light-error-
|
|
27
|
+
default: 'text-light-error-dark dark:text-dark-error-main',
|
|
28
|
+
hover: 'hover:underline-offset-[4px] dark:hover:text-dark-error-light',
|
|
29
|
+
visited: 'visited:text-light-error-dark dark:visited:text-dark-error-light',
|
|
28
30
|
},
|
|
29
31
|
warning: {
|
|
30
|
-
default: 'text-light-warning-
|
|
31
|
-
hover: 'hover:
|
|
32
|
-
visited: 'visited:text-light-warning-
|
|
32
|
+
default: 'text-light-warning-dark dark:text-dark-warning-main',
|
|
33
|
+
hover: 'hover:underline-offset-[4px] dark:hover:text-dark-warning-light',
|
|
34
|
+
visited: 'visited:text-light-warning-dark dark:visited:text-dark-warning-light',
|
|
33
35
|
},
|
|
34
36
|
success: {
|
|
35
|
-
default: 'text-light-success-
|
|
36
|
-
hover: 'hover:
|
|
37
|
-
visited: 'visited:text-light-success-
|
|
37
|
+
default: 'text-light-success-dark dark:text-dark-success-main',
|
|
38
|
+
hover: 'hover:underline-offset-[4px] dark:hover:text-dark-success-light',
|
|
39
|
+
visited: 'visited:text-light-success-dark dark:visited:text-dark-success-light',
|
|
38
40
|
},
|
|
39
41
|
};
|
|
40
42
|
var selectedColorClasses = colorClasses[color];
|
|
@@ -42,7 +44,21 @@ export default function Link(_a) {
|
|
|
42
44
|
var isExternal = href.startsWith('http://') || href.startsWith('https://');
|
|
43
45
|
// Determine if we should open in new tab
|
|
44
46
|
var shouldOpenInNewTab = openInNewTab || isExternal;
|
|
45
|
-
|
|
47
|
+
// Literal class names so Tailwind can find them when scanning this file
|
|
48
|
+
var sizeClass = {
|
|
49
|
+
h1: 'text-h1',
|
|
50
|
+
h2: 'text-h2',
|
|
51
|
+
h3: 'text-h3',
|
|
52
|
+
h4: 'text-h4',
|
|
53
|
+
h5: 'text-h5',
|
|
54
|
+
h6: 'text-h6',
|
|
55
|
+
subtitle1: 'text-subtitle1',
|
|
56
|
+
subtitle2: 'text-subtitle2',
|
|
57
|
+
body1: 'text-body1',
|
|
58
|
+
body2: 'text-body2',
|
|
59
|
+
caption: 'text-caption',
|
|
60
|
+
}[size];
|
|
61
|
+
var linkClasses = "\n ".concat(sizeClass, "\n ").concat(selectedColorClasses.default, "\n ").concat(selectedColorClasses.hover, "\n ").concat(selectedColorClasses.visited, "\n underline\n underline-offset-[2px]\n decoration-2\n cursor-pointer\n rounded-[2px]\n transition-[color,text-underline-offset]\n duration-150\n ease-out\n motion-reduce:transition-none\n ").concat(focusRing, "\n ").concat(className, "\n ").trim().replace(/\s+/g, ' ');
|
|
46
62
|
var commonProps = __assign({ className: linkClasses }, (shouldOpenInNewTab && {
|
|
47
63
|
target: '_blank',
|
|
48
64
|
rel: 'noopener noreferrer',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
export default function LoadingScreen() {
|
|
4
|
-
return (<div className="flex justify-center items-center h-full w-full min-h-[200px]">
|
|
5
|
-
<div className="w-12 h-12 rounded-full border-4 border-
|
|
4
|
+
return (<div role="status" aria-label="Loading" className="flex justify-center items-center h-full w-full min-h-[200px]">
|
|
5
|
+
<div className="w-12 h-12 rounded-full border-4 border-current border-t-transparent text-light-text-primary dark:text-dark-text-primary animate-spin"></div>
|
|
6
6
|
</div>);
|
|
7
7
|
}
|
|
8
8
|
;
|
|
@@ -14,7 +14,7 @@ export function LoadingSmall(_a) {
|
|
|
14
14
|
small: 'w-5 h-5 border-2', // 20px, slightly thinner border
|
|
15
15
|
tiny: 'w-4 h-4 border-2', // 16px
|
|
16
16
|
};
|
|
17
|
-
return (<div className="flex justify-center items-center">
|
|
18
|
-
<div className={"rounded-full border-
|
|
17
|
+
return (<div role="status" aria-label="Loading" className="flex justify-center items-center">
|
|
18
|
+
<div className={"rounded-full border-current border-t-transparent text-light-text-primary dark:text-dark-text-primary animate-spin ".concat(spinnerSizeClasses[size])}/>
|
|
19
19
|
</div>);
|
|
20
20
|
}
|
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>);
|
|
@@ -10,7 +10,7 @@ interface MenuItemProps {
|
|
|
10
10
|
isSelected?: boolean;
|
|
11
11
|
onClick?: any;
|
|
12
12
|
className?: string;
|
|
13
|
-
size?: 'small' | 'medium';
|
|
13
|
+
size?: 'tiny' | 'small' | 'medium';
|
|
14
14
|
tag?: {
|
|
15
15
|
label: React.ReactNode;
|
|
16
16
|
iconName?: string;
|
|
@@ -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,104 +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
|
-
var iconSize =
|
|
97
|
-
var labelClass =
|
|
98
|
-
var
|
|
99
|
-
|
|
15
|
+
var iconSize = { tiny: 'w-4 h-4', small: 'w-5 h-5', medium: 'w-6 h-6' }[size];
|
|
16
|
+
var labelClass = { tiny: 'text-caption', small: 'text-body2', medium: 'text-body1' }[size];
|
|
17
|
+
var secondLabelClass = size === 'tiny' ? 'text-caption' : 'text-body2';
|
|
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];
|
|
20
|
+
var paddingClass = size === 'tiny' ? 'p-1' : 'p-2';
|
|
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
|
|
100
24
|
? 'bg-light-primary-dark dark:bg-dark-primary-dark text-light-text-contrast dark:text-dark-text-contrast'
|
|
101
|
-
:
|
|
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
|
+
}}>
|
|
102
31
|
{/* Left group: icon/userImg + label with 8px gap */}
|
|
103
32
|
<div className="flex items-center gap-2 min-w-0">
|
|
104
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}/>))}
|
|
@@ -106,7 +35,7 @@ export default function MenuItem(_a) {
|
|
|
106
35
|
<span className={"truncate ".concat(labelClass, " ").concat(isSelected ? 'text-light-text-contrast dark:text-dark-text-contrast' : 'text-light-text-primary dark:text-dark-text-primary')}>
|
|
107
36
|
{label}
|
|
108
37
|
</span>
|
|
109
|
-
{secondLabel && (<span className={"truncate
|
|
38
|
+
{secondLabel && (<span className={"truncate ".concat(secondLabelClass, " opacity-70 ").concat(isSelected ? 'text-light-text-contrast dark:text-dark-text-contrast' : 'text-light-text-primary dark:text-dark-text-primary')}>
|
|
110
39
|
{secondLabel}
|
|
111
40
|
</span>)}
|
|
112
41
|
</div>
|
|
@@ -114,12 +43,14 @@ export default function MenuItem(_a) {
|
|
|
114
43
|
|
|
115
44
|
{/* Right group: tag + icon aligned to the right */}
|
|
116
45
|
<div className="flex items-center gap-2 flex-shrink-0">
|
|
117
|
-
{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'}/>)}
|
|
118
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}/>)}
|
|
119
50
|
{rightAction && (<div onClick={function (e) { return e.stopPropagation(); }}>
|
|
120
51
|
{rightAction}
|
|
121
52
|
</div>)}
|
|
122
53
|
</div>
|
|
123
54
|
</div>);
|
|
124
|
-
return href ? <NextLink href={href}>{content}</NextLink> : content;
|
|
55
|
+
return href ? <NextLink href={href} className={"block rounded-[8px] ".concat(focusRing)}>{content}</NextLink> : content;
|
|
125
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
|
}
|