@rebasepro/ui 0.17.3 → 0.18.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 +4 -0
- package/dist/components/MultiSelect.d.ts +10 -0
- package/dist/index.es.js +2 -1
- package/dist/index.es.js.map +1 -1
- package/package.json +32 -23
- package/src/components/Alert.tsx +0 -118
- package/src/components/Autocomplete.tsx +0 -93
- package/src/components/Avatar.tsx +0 -73
- package/src/components/Badge.tsx +0 -44
- package/src/components/BooleanSwitch.tsx +0 -116
- package/src/components/BooleanSwitchWithLabel.tsx +0 -120
- package/src/components/Button.tsx +0 -110
- package/src/components/Card.tsx +0 -43
- package/src/components/CenteredView.tsx +0 -35
- package/src/components/Checkbox.tsx +0 -129
- package/src/components/Chip.tsx +0 -131
- package/src/components/CircularProgress.tsx +0 -53
- package/src/components/CircularProgressCenter.tsx +0 -27
- package/src/components/Collapse.tsx +0 -66
- package/src/components/ColorPicker.tsx +0 -140
- package/src/components/Container.tsx +0 -51
- package/src/components/DateTimeField.tsx +0 -363
- package/src/components/DebouncedTextField.tsx +0 -76
- package/src/components/Dialog.tsx +0 -159
- package/src/components/DialogActions.tsx +0 -29
- package/src/components/DialogContent.tsx +0 -27
- package/src/components/DialogTitle.tsx +0 -41
- package/src/components/ErrorBoundary.tsx +0 -236
- package/src/components/ExpandablePanel.tsx +0 -130
- package/src/components/FileUpload.tsx +0 -103
- package/src/components/FilterChip.tsx +0 -89
- package/src/components/IconButton.tsx +0 -101
- package/src/components/InfoLabel.tsx +0 -23
- package/src/components/InputLabel.tsx +0 -40
- package/src/components/Label.tsx +0 -32
- package/src/components/LoadingButton.tsx +0 -32
- package/src/components/Markdown.tsx +0 -55
- package/src/components/Menu.tsx +0 -114
- package/src/components/Menubar.tsx +0 -355
- package/src/components/MultiSelect.tsx +0 -433
- package/src/components/Paper.tsx +0 -23
- package/src/components/Popover.tsx +0 -162
- package/src/components/RadioGroup.tsx +0 -74
- package/src/components/ResizablePanels.tsx +0 -189
- package/src/components/SearchBar.tsx +0 -136
- package/src/components/Select.tsx +0 -326
- package/src/components/Separator.tsx +0 -28
- package/src/components/Sheet.tsx +0 -135
- package/src/components/Skeleton.tsx +0 -54
- package/src/components/Slider.tsx +0 -110
- package/src/components/Table.tsx +0 -178
- package/src/components/Tabs.tsx +0 -185
- package/src/components/TextField.tsx +0 -270
- package/src/components/TextareaAutosize.tsx +0 -368
- package/src/components/ToggleButtonGroup.tsx +0 -69
- package/src/components/Tooltip.tsx +0 -153
- package/src/components/Typography.tsx +0 -132
- package/src/components/VirtualTable/VirtualTable.performance.test.tsx +0 -90
- package/src/components/VirtualTable/VirtualTable.tsx +0 -700
- package/src/components/VirtualTable/VirtualTableCell.tsx +0 -59
- package/src/components/VirtualTable/VirtualTableHeader.tsx +0 -327
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +0 -250
- package/src/components/VirtualTable/VirtualTableProps.tsx +0 -339
- package/src/components/VirtualTable/VirtualTableRow.tsx +0 -51
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +0 -44
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -89
- package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +0 -71
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +0 -107
- package/src/components/VirtualTable/fields/VirtualTableSwitch.tsx +0 -28
- package/src/components/VirtualTable/index.tsx +0 -10
- package/src/components/VirtualTable/selection/SelectionContext.tsx +0 -36
- package/src/components/VirtualTable/selection/SelectionStore.ts +0 -54
- package/src/components/VirtualTable/types.tsx +0 -47
- package/src/components/common/SelectInputLabel.tsx +0 -23
- package/src/components/index.tsx +0 -62
- package/src/hooks/PortalContainerContext.tsx +0 -50
- package/src/hooks/index.ts +0 -7
- package/src/hooks/useDebounceCallback.tsx +0 -21
- package/src/hooks/useDebounceValue.tsx +0 -20
- package/src/hooks/useDebouncedCallback.ts +0 -39
- package/src/hooks/useInjectStyles.tsx +0 -31
- package/src/hooks/useOutsideAlerter.tsx +0 -46
- package/src/icons/GitHubIcon.tsx +0 -21
- package/src/icons/HandleIcon.tsx +0 -23
- package/src/icons/Icon.tsx +0 -33
- package/src/icons/LucideIconByName.tsx +0 -117
- package/src/icons/cool_icon_keys.ts +0 -12
- package/src/icons/icon_keys.ts +0 -1936
- package/src/icons/index.ts +0 -179
- package/src/index.css +0 -169
- package/src/index.ts +0 -47
- package/src/scripts/fixIconSizes.cjs +0 -92
- package/src/scripts/fixIconSizesToConst.cjs +0 -106
- package/src/scripts/generateIconKeys.ts +0 -41
- package/src/scripts/generateIcons.ts +0 -4
- package/src/scripts/saveIconFiles.ts +0 -89
- package/src/styles.ts +0 -141
- package/src/theme.css +0 -168
- package/src/util/chip_colors.ts +0 -242
- package/src/util/cls.ts +0 -8
- package/src/util/debounce.ts +0 -28
- package/src/util/hash.ts +0 -11
- package/src/util/index.ts +0 -4
- package/src/util/key_to_icon_component.ts +0 -11
- package/src/util/lazy_chunk.ts +0 -101
- package/src/views/CardView.tsx +0 -281
- package/src/views/CollectionView/CollectionCardView.tsx +0 -265
- package/src/views/CollectionView/CollectionKanbanView.tsx +0 -246
- package/src/views/CollectionView/CollectionListView.tsx +0 -240
- package/src/views/CollectionView/CollectionTableView.tsx +0 -229
- package/src/views/CollectionView/CollectionView.tsx +0 -450
- package/src/views/CollectionView/CollectionViewToolbar.tsx +0 -242
- package/src/views/CollectionView/CollectionViewTypes.ts +0 -181
- package/src/views/CollectionView/DefaultCellRenderer.tsx +0 -404
- package/src/views/CollectionView/index.ts +0 -39
- package/src/views/CollectionView/resolveTitleProperty.ts +0 -52
- package/src/views/CollectionView/utils.ts +0 -14
- package/src/views/Kanban/Board.tsx +0 -384
- package/src/views/Kanban/BoardColumn.tsx +0 -138
- package/src/views/Kanban/BoardColumnTitle.tsx +0 -47
- package/src/views/Kanban/BoardSortableList.tsx +0 -182
- package/src/views/Kanban/board_types.ts +0 -80
- package/src/views/Kanban/index.tsx +0 -5
- package/src/views/Kanban/placement.ts +0 -62
- package/src/views/KanbanView.tsx +0 -32
- package/src/views/ListView.tsx +0 -296
- package/src/views/index.ts +0 -5
package/package.json
CHANGED
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/ui",
|
|
3
|
-
"
|
|
4
|
-
"version": "0.17.3",
|
|
3
|
+
"version": "0.18.0",
|
|
5
4
|
"description": "Rebase design system — React components and design tokens built on Radix UI, Tailwind CSS and lucide-react",
|
|
6
|
-
"funding": {
|
|
7
|
-
"url": "https://github.com/sponsors/rebaseco"
|
|
8
|
-
},
|
|
9
|
-
"author": "Rebase",
|
|
10
|
-
"license": "MIT",
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "https://github.com/rebasepro/rebase.git",
|
|
14
|
-
"directory": "packages/ui"
|
|
15
|
-
},
|
|
16
|
-
"main": "./dist/index.es.js",
|
|
17
|
-
"module": "./dist/index.es.js",
|
|
18
|
-
"types": "./dist/index.d.ts",
|
|
19
|
-
"source": "src/index.ts",
|
|
20
|
-
"engines": {
|
|
21
|
-
"node": ">=20"
|
|
22
|
-
},
|
|
23
5
|
"keywords": [
|
|
24
6
|
"rebase",
|
|
25
7
|
"cms",
|
|
@@ -31,11 +13,38 @@
|
|
|
31
13
|
"content manager",
|
|
32
14
|
"react"
|
|
33
15
|
],
|
|
16
|
+
"homepage": "https://rebase.pro",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/rebasepro/rebase/issues"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/rebasepro/rebase.git",
|
|
23
|
+
"directory": "packages/ui"
|
|
24
|
+
},
|
|
25
|
+
"funding": {
|
|
26
|
+
"url": "https://github.com/sponsors/rebasepro"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"author": "Rebase",
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./dist/index.es.js",
|
|
32
|
+
"module": "./dist/index.es.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"//sideEffects": "The stylesheet is the side effect. `index.css` and the CSS the build emits are imported for what they do, not for what they export, so a bundler told this package is side-effect-free would drop them and ship an unstyled admin panel.",
|
|
35
|
+
"sideEffects": [
|
|
36
|
+
"**/*.css",
|
|
37
|
+
"./index.css"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=22.22.0"
|
|
41
|
+
},
|
|
34
42
|
"exports": {
|
|
35
43
|
".": {
|
|
36
44
|
"types": "./dist/index.d.ts",
|
|
37
45
|
"development": "./dist/index.es.js",
|
|
38
|
-
"import": "./dist/index.es.js"
|
|
46
|
+
"import": "./dist/index.es.js",
|
|
47
|
+
"default": "./dist/index.es.js"
|
|
39
48
|
},
|
|
40
49
|
"./package.json": "./package.json",
|
|
41
50
|
"./index.css": "./dist/index.css",
|
|
@@ -74,8 +83,8 @@
|
|
|
74
83
|
"tailwind-merge": "^3.6.0"
|
|
75
84
|
},
|
|
76
85
|
"peerDependencies": {
|
|
77
|
-
"react": "
|
|
78
|
-
"react-dom": "
|
|
86
|
+
"react": "^19.2.7",
|
|
87
|
+
"react-dom": "^19.2.7"
|
|
79
88
|
},
|
|
80
89
|
"devDependencies": {
|
|
81
90
|
"@jest/globals": "^30.4.1",
|
|
@@ -104,7 +113,6 @@
|
|
|
104
113
|
},
|
|
105
114
|
"files": [
|
|
106
115
|
"dist",
|
|
107
|
-
"src",
|
|
108
116
|
"index.css"
|
|
109
117
|
],
|
|
110
118
|
"gitHead": "d935eefa5aa8d1009a2398cfac2c1e4ee9aeb6b6",
|
|
@@ -116,6 +124,7 @@
|
|
|
116
124
|
"build": "rm -rf dist || true && vite build && cp dist/src/index.css dist/src/theme.css dist/ && tsc --emitDeclarationOnly -p tsconfig.prod.json && node ../../tooling/scripts/add-dts-extensions.mjs dist && node ../../tooling/scripts/assert-build-output.mjs",
|
|
117
125
|
"test:lint": "eslint \"src/**\" --quiet",
|
|
118
126
|
"test": "jest --passWithNoTests",
|
|
127
|
+
"test:watch": "jest --watch",
|
|
119
128
|
"clean": "rm -rf dist && find ./src \\( -name '*.js' -o -name '*.d.ts' \\) -type f | xargs rm -f",
|
|
120
129
|
"generateIcons": "bun --esm src/scripts/generateIcons.ts"
|
|
121
130
|
}
|
package/src/components/Alert.tsx
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { cls } from "../util";
|
|
3
|
-
import { AlertCircleIcon, AlertTriangleIcon, CheckCircleIcon, InfoIcon, XIcon } from "../icons";
|
|
4
|
-
|
|
5
|
-
export interface AlertProps {
|
|
6
|
-
children: React.ReactNode;
|
|
7
|
-
onDismiss?: () => void;
|
|
8
|
-
color?: "error" | "warning" | "info" | "success" | "base";
|
|
9
|
-
size?: "small" | "medium" | "large";
|
|
10
|
-
action?: React.ReactNode;
|
|
11
|
-
className?: string;
|
|
12
|
-
outerClassName?: string;
|
|
13
|
-
style?: React.CSSProperties;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const getSizeClasses = (size: "small" | "medium" | "large") => {
|
|
17
|
-
switch (size) {
|
|
18
|
-
case "small":
|
|
19
|
-
return "px-3 py-1.5 text-xs gap-2 rounded-md";
|
|
20
|
-
case "large":
|
|
21
|
-
return "px-4 py-3.5 text-sm gap-3 rounded-lg";
|
|
22
|
-
case "medium":
|
|
23
|
-
default:
|
|
24
|
-
return "px-3.5 py-2.5 text-sm gap-2.5 rounded-lg";
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const getIconSize = (size: "small" | "medium" | "large") => size === "small" ? 16 : 18;
|
|
29
|
-
|
|
30
|
-
const getColorClasses = (severity: string) => {
|
|
31
|
-
switch (severity) {
|
|
32
|
-
case "error":
|
|
33
|
-
return "bg-red-500/8 dark:bg-red-500/12 text-red-800 dark:text-red-200 border border-red-500/20 dark:border-red-500/25";
|
|
34
|
-
case "warning":
|
|
35
|
-
return "bg-amber-500/8 dark:bg-amber-500/12 text-amber-800 dark:text-amber-200 border border-amber-500/20 dark:border-amber-500/25";
|
|
36
|
-
case "info":
|
|
37
|
-
return "bg-blue-500/8 dark:bg-blue-500/12 text-blue-800 dark:text-blue-200 border border-blue-500/20 dark:border-blue-500/25";
|
|
38
|
-
case "success":
|
|
39
|
-
return "bg-emerald-500/8 dark:bg-emerald-500/12 text-emerald-800 dark:text-emerald-200 border border-emerald-500/20 dark:border-emerald-500/25";
|
|
40
|
-
case "base":
|
|
41
|
-
default:
|
|
42
|
-
return "bg-surface-accent-500/8 dark:bg-surface-accent-400/10 text-surface-accent-800 dark:text-surface-accent-100 border border-surface-accent-500/15 dark:border-surface-accent-400/20";
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const getIconColor = (severity: string) => {
|
|
47
|
-
switch (severity) {
|
|
48
|
-
case "error":
|
|
49
|
-
return "text-red-500 dark:text-red-400";
|
|
50
|
-
case "warning":
|
|
51
|
-
return "text-amber-500 dark:text-amber-400";
|
|
52
|
-
case "info":
|
|
53
|
-
return "text-blue-500 dark:text-blue-400";
|
|
54
|
-
case "success":
|
|
55
|
-
return "text-emerald-500 dark:text-emerald-400";
|
|
56
|
-
case "base":
|
|
57
|
-
default:
|
|
58
|
-
return "text-surface-accent-500 dark:text-surface-accent-400";
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const getIcon = (severity: string, size: number, className: string) => {
|
|
63
|
-
switch (severity) {
|
|
64
|
-
case "error":
|
|
65
|
-
return <AlertCircleIcon size={size} className={className}/>;
|
|
66
|
-
case "warning":
|
|
67
|
-
return <AlertTriangleIcon size={size} className={className}/>;
|
|
68
|
-
case "success":
|
|
69
|
-
return <CheckCircleIcon size={size} className={className}/>;
|
|
70
|
-
case "info":
|
|
71
|
-
case "base":
|
|
72
|
-
default:
|
|
73
|
-
return <InfoIcon size={size} className={className}/>;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const Alert: React.FC<AlertProps> = ({
|
|
78
|
-
children,
|
|
79
|
-
onDismiss,
|
|
80
|
-
color = "info",
|
|
81
|
-
size = "medium",
|
|
82
|
-
action,
|
|
83
|
-
outerClassName,
|
|
84
|
-
className,
|
|
85
|
-
style
|
|
86
|
-
}) => {
|
|
87
|
-
const classes = getColorClasses(color);
|
|
88
|
-
const iconSize = getIconSize(size);
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<div
|
|
92
|
-
style={style}
|
|
93
|
-
className={cls(
|
|
94
|
-
getSizeClasses(size),
|
|
95
|
-
"w-full",
|
|
96
|
-
"font-medium leading-snug",
|
|
97
|
-
"flex items-start",
|
|
98
|
-
classes,
|
|
99
|
-
outerClassName)}>
|
|
100
|
-
<span className={cls("shrink-0 mt-px", getIconColor(color))}>
|
|
101
|
-
{getIcon(color, iconSize, "")}
|
|
102
|
-
</span>
|
|
103
|
-
<div className={cls("grow min-w-0 self-center", className)}>{children}</div>
|
|
104
|
-
{action}
|
|
105
|
-
{onDismiss && (
|
|
106
|
-
<button
|
|
107
|
-
type="button"
|
|
108
|
-
aria-label="Dismiss"
|
|
109
|
-
className={cls(
|
|
110
|
-
"shrink-0 rounded p-0.5 opacity-60 transition-opacity hover:opacity-100",
|
|
111
|
-
"focus:outline-none focus-visible:ring-2 focus-visible:ring-current/40")}
|
|
112
|
-
onClick={onDismiss}>
|
|
113
|
-
<XIcon size={iconSize - 2}/>
|
|
114
|
-
</button>
|
|
115
|
-
)}
|
|
116
|
-
</div>
|
|
117
|
-
);
|
|
118
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import React from "react";
|
|
3
|
-
|
|
4
|
-
import { paperMixin } from "../styles";
|
|
5
|
-
import { Collapse } from "./Collapse";
|
|
6
|
-
import { cls } from "../util";
|
|
7
|
-
import { useOutsideAlerter } from "../hooks";
|
|
8
|
-
|
|
9
|
-
export type AutocompleteProps = {
|
|
10
|
-
children: React.ReactNode;
|
|
11
|
-
open: boolean;
|
|
12
|
-
setOpen: (open: boolean) => void;
|
|
13
|
-
className?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const useAutoComplete = ({ ref }: {
|
|
17
|
-
ref: React.MutableRefObject<HTMLDivElement | null>;
|
|
18
|
-
}) => {
|
|
19
|
-
|
|
20
|
-
const [autoCompleteOpen, setAutoCompleteOpen] = React.useState(false);
|
|
21
|
-
const [inputFocused, setInputFocused] = React.useState(false);
|
|
22
|
-
|
|
23
|
-
// if ref is not focused, close autocomplete
|
|
24
|
-
React.useEffect(() => {
|
|
25
|
-
if (ref.current) {
|
|
26
|
-
ref.current.onfocus = () => {
|
|
27
|
-
setAutoCompleteOpen(true);
|
|
28
|
-
setInputFocused(true);
|
|
29
|
-
}
|
|
30
|
-
ref.current.onblur = () => {
|
|
31
|
-
setInputFocused(false);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}, [ref]);
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
inputFocused,
|
|
38
|
-
autoCompleteOpen,
|
|
39
|
-
setAutoCompleteOpen
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function Autocomplete({
|
|
44
|
-
children,
|
|
45
|
-
open,
|
|
46
|
-
setOpen,
|
|
47
|
-
className
|
|
48
|
-
}: AutocompleteProps) {
|
|
49
|
-
|
|
50
|
-
const autocompleteRef = React.useRef<HTMLDivElement>(null);
|
|
51
|
-
useOutsideAlerter(autocompleteRef, () => setOpen(false));
|
|
52
|
-
|
|
53
|
-
return <Collapse
|
|
54
|
-
in={open}
|
|
55
|
-
duration={30}
|
|
56
|
-
className={cls(
|
|
57
|
-
"absolute top-full left-0 right-0 overflow-visible",
|
|
58
|
-
open ? "shadow-2xs" : "",
|
|
59
|
-
"my-2",
|
|
60
|
-
"z-20",
|
|
61
|
-
"w-full")}>
|
|
62
|
-
<div ref={autocompleteRef}
|
|
63
|
-
className={cls(
|
|
64
|
-
open ? paperMixin : "",
|
|
65
|
-
"bg-surface-50 dark:bg-surface-800",
|
|
66
|
-
className
|
|
67
|
-
)}>
|
|
68
|
-
{children}
|
|
69
|
-
</div>
|
|
70
|
-
</Collapse>;
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export type AutocompleteItemProps = {
|
|
75
|
-
children: React.ReactNode,
|
|
76
|
-
onClick?: () => void,
|
|
77
|
-
className?: string
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const AutocompleteItem = React.memo(function AutocompleteItem({
|
|
81
|
-
children,
|
|
82
|
-
onClick,
|
|
83
|
-
className
|
|
84
|
-
}: AutocompleteItemProps) {
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<div
|
|
88
|
-
className={cls("flex w-full items-center h-[48px] cursor-pointer transition-colors duration-150 hover:bg-surface-accent-100 dark:hover:bg-surface-accent-800", className)}
|
|
89
|
-
onClick={onClick}>
|
|
90
|
-
{children}
|
|
91
|
-
</div>
|
|
92
|
-
)
|
|
93
|
-
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import React, { useState } from "react";
|
|
3
|
-
import { cls } from "../util";
|
|
4
|
-
|
|
5
|
-
export interface AvatarProps {
|
|
6
|
-
src?: string;
|
|
7
|
-
alt?: string;
|
|
8
|
-
children?: React.ReactNode;
|
|
9
|
-
className?: string;
|
|
10
|
-
outerClassName?: string;
|
|
11
|
-
hover?: boolean;
|
|
12
|
-
style?: React.CSSProperties;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const AvatarInner: React.ForwardRefRenderFunction<HTMLButtonElement, AvatarProps> = (
|
|
16
|
-
{
|
|
17
|
-
src,
|
|
18
|
-
alt,
|
|
19
|
-
children,
|
|
20
|
-
className,
|
|
21
|
-
style,
|
|
22
|
-
outerClassName,
|
|
23
|
-
hover = true,
|
|
24
|
-
...props
|
|
25
|
-
},
|
|
26
|
-
ref
|
|
27
|
-
) => {
|
|
28
|
-
const [isImageError, setIsImageError] = useState(false);
|
|
29
|
-
|
|
30
|
-
const handleImageError = () => {
|
|
31
|
-
setIsImageError(true);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<button
|
|
36
|
-
ref={ref}
|
|
37
|
-
style={style}
|
|
38
|
-
{...props}
|
|
39
|
-
className={cls(
|
|
40
|
-
"rounded-full flex items-center justify-center overflow-hidden",
|
|
41
|
-
"p-1 w-12 h-12 min-w-12 min-h-12",
|
|
42
|
-
hover && "transition-colors duration-150 hover:bg-surface-accent-200 dark:hover:bg-surface-accent-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50",
|
|
43
|
-
outerClassName
|
|
44
|
-
)}
|
|
45
|
-
>
|
|
46
|
-
{src && !isImageError ? (
|
|
47
|
-
<img
|
|
48
|
-
className={cls(
|
|
49
|
-
"bg-surface-accent-100 dark:bg-surface-accent-800",
|
|
50
|
-
"w-full h-full object-cover rounded-full",
|
|
51
|
-
className
|
|
52
|
-
)}
|
|
53
|
-
src={src}
|
|
54
|
-
alt={alt}
|
|
55
|
-
onError={handleImageError}
|
|
56
|
-
/>
|
|
57
|
-
) : (
|
|
58
|
-
<span
|
|
59
|
-
className={cls(
|
|
60
|
-
"bg-surface-accent-100 dark:bg-surface-accent-800",
|
|
61
|
-
"flex items-center justify-center",
|
|
62
|
-
"w-full h-full py-1.5 text-lg font-medium text-surface-accent-900 dark:text-text-primary-dark rounded-full",
|
|
63
|
-
className
|
|
64
|
-
)}
|
|
65
|
-
>
|
|
66
|
-
{children}
|
|
67
|
-
</span>
|
|
68
|
-
)}
|
|
69
|
-
</button>
|
|
70
|
-
);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const Avatar = React.forwardRef(AvatarInner);
|
package/src/components/Badge.tsx
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
export type BadgeColor = "primary" | "secondary" | "warning" | "error";
|
|
4
|
-
|
|
5
|
-
export interface BadgeProps {
|
|
6
|
-
color?: BadgeColor;
|
|
7
|
-
children: React.ReactNode;
|
|
8
|
-
invisible?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const getColor = (color: BadgeColor) => {
|
|
12
|
-
switch (color) {
|
|
13
|
-
case "primary":
|
|
14
|
-
return "bg-primary";
|
|
15
|
-
case "secondary":
|
|
16
|
-
return "bg-secondary";
|
|
17
|
-
case "warning":
|
|
18
|
-
return "bg-orange-500";
|
|
19
|
-
case "error":
|
|
20
|
-
return "bg-red-500";
|
|
21
|
-
default:
|
|
22
|
-
return "bg-surface-accent-300 dark:bg-surface-accent-700";
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(({
|
|
27
|
-
color = "primary",
|
|
28
|
-
invisible = false,
|
|
29
|
-
children
|
|
30
|
-
}, ref) => {
|
|
31
|
-
return (
|
|
32
|
-
<div ref={ref} className="relative inline-block w-fit">
|
|
33
|
-
{children}
|
|
34
|
-
<span
|
|
35
|
-
className={`absolute top-0.5 right-0.5 transform translate-x-1/2 -translate-y-1/2 rounded-full
|
|
36
|
-
${getColor(color)}
|
|
37
|
-
transition-all duration-200 ease-out
|
|
38
|
-
${invisible ? "w-0 h-0" : "w-2 h-2"}`}
|
|
39
|
-
/>
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
Badge.displayName = "Badge";
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { cls } from "../util";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Switch geometry, one row per size: track w x h, knob k, and the knob's
|
|
7
|
-
* off/on x-offsets. `on` is always `w - k - inset` so the knob is inset by the
|
|
8
|
-
* same amount at both ends and vertically centred.
|
|
9
|
-
*
|
|
10
|
-
* This is a table rather than inline `cls({...})` conditions on purpose. The
|
|
11
|
-
* offsets used to be computed object keys — `[value ? "translate-x-[24px]" :
|
|
12
|
-
* "translate-x-[3px]"]: size === "large"` — and `large` and `medium` both
|
|
13
|
-
* produced the key `translate-x-[3px]` when off. The later literal overwrote
|
|
14
|
-
* the earlier, so the off-state class was silently dropped for `large` and the
|
|
15
|
-
* knob sat flush against the edge.
|
|
16
|
-
*/
|
|
17
|
-
const SWITCH_GEOMETRY = {
|
|
18
|
-
smallest: {
|
|
19
|
-
track: "w-[34px] h-[18px] min-w-[34px] min-h-[18px]",
|
|
20
|
-
knob: "w-[16px] h-[16px]", off: "translate-x-[1px]", on: "translate-x-[17px]",
|
|
21
|
-
bar: "w-[16px] h-[6px]", barX: "translate-x-[9px]"
|
|
22
|
-
},
|
|
23
|
-
small: {
|
|
24
|
-
track: "w-[38px] h-[22px] min-w-[38px] min-h-[22px]",
|
|
25
|
-
knob: "w-[18px] h-[18px]", off: "translate-x-[2px]", on: "translate-x-[18px]",
|
|
26
|
-
bar: "w-[18px] h-[8px]", barX: "translate-x-[10px]"
|
|
27
|
-
},
|
|
28
|
-
medium: {
|
|
29
|
-
track: "w-[44px] h-[26px] min-w-[44px] min-h-[26px]",
|
|
30
|
-
knob: "w-[20px] h-[20px]", off: "translate-x-[3px]", on: "translate-x-[21px]",
|
|
31
|
-
bar: "w-[20px] h-[10px]", barX: "translate-x-[12px]"
|
|
32
|
-
},
|
|
33
|
-
large: {
|
|
34
|
-
track: "w-[48px] h-[28px] min-w-[48px] min-h-[28px]",
|
|
35
|
-
knob: "w-[22px] h-[22px]", off: "translate-x-[3px]", on: "translate-x-[23px]",
|
|
36
|
-
bar: "w-[22px] h-[11px]", barX: "translate-x-[13px]"
|
|
37
|
-
}
|
|
38
|
-
} as const;
|
|
39
|
-
|
|
40
|
-
export type BooleanSwitchProps = {
|
|
41
|
-
value: boolean | null;
|
|
42
|
-
className?: string;
|
|
43
|
-
disabled?: boolean;
|
|
44
|
-
size?: "smallest" | "small" | "medium" | "large";
|
|
45
|
-
} & ({
|
|
46
|
-
allowIndeterminate: true;
|
|
47
|
-
onValueChange?: (newValue: boolean | null) => void;
|
|
48
|
-
} | {
|
|
49
|
-
allowIndeterminate?: false;
|
|
50
|
-
onValueChange?: (newValue: boolean) => void;
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
export const BooleanSwitch = React.forwardRef(function BooleanSwitch({
|
|
54
|
-
value,
|
|
55
|
-
allowIndeterminate,
|
|
56
|
-
className,
|
|
57
|
-
onValueChange,
|
|
58
|
-
disabled = false,
|
|
59
|
-
size = "medium",
|
|
60
|
-
...props
|
|
61
|
-
}: BooleanSwitchProps, ref: React.Ref<HTMLButtonElement>) {
|
|
62
|
-
return <button
|
|
63
|
-
type="button"
|
|
64
|
-
role="switch"
|
|
65
|
-
aria-checked={allowIndeterminate && (value === null || value === undefined) ? "mixed" : !!value}
|
|
66
|
-
aria-disabled={disabled || undefined}
|
|
67
|
-
ref={ref}
|
|
68
|
-
tabIndex={disabled ? -1 : undefined}
|
|
69
|
-
onClick={disabled
|
|
70
|
-
? (e) => e.preventDefault()
|
|
71
|
-
: (e) => {
|
|
72
|
-
e.preventDefault();
|
|
73
|
-
if (allowIndeterminate) {
|
|
74
|
-
if (value === null || value === undefined) onValueChange?.(true)
|
|
75
|
-
else if (value) onValueChange?.(false)
|
|
76
|
-
else onValueChange?.(null);
|
|
77
|
-
} else {
|
|
78
|
-
onValueChange?.(!value);
|
|
79
|
-
}
|
|
80
|
-
}}
|
|
81
|
-
className={cls(
|
|
82
|
-
// `large` used to share the `medium` branch, so the two sizes
|
|
83
|
-
// rendered identically and the prop looked inert. Geometry is
|
|
84
|
-
// now derived from one table (see SWITCH_GEOMETRY) so the knob
|
|
85
|
-
// inset is symmetric at every size.
|
|
86
|
-
SWITCH_GEOMETRY[size].track,
|
|
87
|
-
"outline-none outline-hidden rounded-full relative shadow-sm",
|
|
88
|
-
value ? (disabled
|
|
89
|
-
? "bg-white bg-opacity-54 bg-white/54 dark:bg-surface-accent-950 border-surface-accent-100 dark:border-surface-accent-700 ring-1 ring-surface-accent-200 dark:ring-surface-accent-700"
|
|
90
|
-
: "ring-secondary ring-1 bg-secondary dark:bg-secondary") : "bg-white bg-opacity-54 bg-white/54 dark:bg-surface-accent-900 ring-1 ring-surface-accent-200 dark:ring-surface-accent-700",
|
|
91
|
-
className
|
|
92
|
-
)}
|
|
93
|
-
{...props}
|
|
94
|
-
>
|
|
95
|
-
{allowIndeterminate && (value === null || value === undefined) && <div
|
|
96
|
-
key={"knob"}
|
|
97
|
-
className={cls(
|
|
98
|
-
"block rounded-full transition-transform duration-100 ease-out transform will-change-auto shadow-sm",
|
|
99
|
-
disabled ? "bg-surface-accent-400 dark:bg-surface-accent-600" : "bg-surface-accent-400 dark:bg-surface-accent-600",
|
|
100
|
-
SWITCH_GEOMETRY[size].bar,
|
|
101
|
-
SWITCH_GEOMETRY[size].barX
|
|
102
|
-
)}
|
|
103
|
-
/>}
|
|
104
|
-
|
|
105
|
-
{!(allowIndeterminate && (value === null || value === undefined)) && <div
|
|
106
|
-
key={"knob"}
|
|
107
|
-
className={cls(
|
|
108
|
-
"block rounded-full transition-transform duration-100 ease-out transform will-change-auto shadow-sm",
|
|
109
|
-
disabled ? "bg-surface-accent-300 dark:bg-surface-accent-700" : (value ? "bg-white shadow" : "bg-surface-accent-600 dark:bg-surface-accent-400"),
|
|
110
|
-
SWITCH_GEOMETRY[size].knob,
|
|
111
|
-
value ? SWITCH_GEOMETRY[size].on : SWITCH_GEOMETRY[size].off
|
|
112
|
-
)}
|
|
113
|
-
/>}
|
|
114
|
-
</button>;
|
|
115
|
-
}
|
|
116
|
-
) as React.ForwardRefExoticComponent<BooleanSwitchProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import {
|
|
4
|
-
controlHeightMixin,
|
|
5
|
-
fieldBackgroundDisabledMixin,
|
|
6
|
-
fieldBackgroundHoverMixin,
|
|
7
|
-
fieldBackgroundMixin,
|
|
8
|
-
focusedClasses
|
|
9
|
-
} from "../styles";
|
|
10
|
-
import { BooleanSwitch, BooleanSwitchProps } from "./BooleanSwitch";
|
|
11
|
-
import { cls } from "../util";
|
|
12
|
-
|
|
13
|
-
export type BooleanSwitchWithLabelProps = BooleanSwitchProps & {
|
|
14
|
-
position?: "start" | "end",
|
|
15
|
-
invisible?: boolean,
|
|
16
|
-
label?: React.ReactNode,
|
|
17
|
-
error?: boolean,
|
|
18
|
-
autoFocus?: boolean,
|
|
19
|
-
fullWidth?: boolean,
|
|
20
|
-
className?: string,
|
|
21
|
-
inputClassName?: string,
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Simple boolean switch.
|
|
26
|
-
*
|
|
27
|
-
*/
|
|
28
|
-
export const BooleanSwitchWithLabel = function BooleanSwitchWithLabel({
|
|
29
|
-
value,
|
|
30
|
-
position = "end",
|
|
31
|
-
size = "medium",
|
|
32
|
-
invisible,
|
|
33
|
-
onValueChange,
|
|
34
|
-
error,
|
|
35
|
-
label,
|
|
36
|
-
autoFocus,
|
|
37
|
-
disabled,
|
|
38
|
-
className,
|
|
39
|
-
fullWidth = true,
|
|
40
|
-
inputClassName,
|
|
41
|
-
...props
|
|
42
|
-
}: BooleanSwitchWithLabelProps) {
|
|
43
|
-
|
|
44
|
-
const ref = React.useRef<HTMLDivElement | null>(null);
|
|
45
|
-
const refInput = React.useRef<HTMLButtonElement | null>(null);
|
|
46
|
-
const switchLabelId = React.useId();
|
|
47
|
-
const [_, setFocused] = React.useState(autoFocus)
|
|
48
|
-
const onFocus = () => setFocused(true);
|
|
49
|
-
const onBlur = () => setFocused(false);
|
|
50
|
-
|
|
51
|
-
React.useEffect(() => {
|
|
52
|
-
if (autoFocus) {
|
|
53
|
-
// refInput.current?.focus();
|
|
54
|
-
}
|
|
55
|
-
}, []);
|
|
56
|
-
|
|
57
|
-
const focus = document.activeElement === refInput?.current || document.activeElement === ref?.current
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<div
|
|
61
|
-
ref={ref}
|
|
62
|
-
onFocus={onFocus}
|
|
63
|
-
onBlur={onBlur}
|
|
64
|
-
role="switch"
|
|
65
|
-
aria-checked={props.allowIndeterminate && (value === null || value === undefined) ? "mixed" : !!value}
|
|
66
|
-
aria-disabled={disabled || undefined}
|
|
67
|
-
aria-labelledby={label ? switchLabelId : undefined}
|
|
68
|
-
tabIndex={-1}
|
|
69
|
-
className={cls(
|
|
70
|
-
!invisible && fieldBackgroundMixin,
|
|
71
|
-
!invisible && (disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin),
|
|
72
|
-
disabled ? "cursor-default" : "cursor-pointer",
|
|
73
|
-
"rounded-lg max-w-full justify-between box-border relative inline-flex items-center",
|
|
74
|
-
!invisible && focus && !disabled ? focusedClasses : "",
|
|
75
|
-
error ? "text-red-500 dark:text-red-600" : (focus && !disabled ? "text-primary" : (!disabled ? "text-text-primary dark:text-text-primary-dark" : "text-text-secondary dark:text-text-secondary-dark")),
|
|
76
|
-
// The one control size scale — 28/32/40/48. This component still
|
|
77
|
-
// carried its own map (…/44/64), so a switch at `large` stood
|
|
78
|
-
// 16px taller than the text field beside it, which read as a
|
|
79
|
-
// giant empty box once the label moved out of the control.
|
|
80
|
-
controlHeightMixin[size],
|
|
81
|
-
size === "small" || size === "smallest" ? "pl-2" : "pl-4",
|
|
82
|
-
size === "small" || size === "smallest" ? "pr-4" : "pr-6",
|
|
83
|
-
position === "end" ? "flex-row-reverse" : "flex-row",
|
|
84
|
-
fullWidth ? "w-full" : "",
|
|
85
|
-
className
|
|
86
|
-
)}
|
|
87
|
-
onClick={disabled ? undefined : (e) => {
|
|
88
|
-
if (props.allowIndeterminate) {
|
|
89
|
-
const onChange = onValueChange as ((newValue: boolean | null) => void) | undefined;
|
|
90
|
-
if (value === null || value === undefined) onChange?.(true)
|
|
91
|
-
else if (value) onChange?.(false)
|
|
92
|
-
else onChange?.(null);
|
|
93
|
-
} else {
|
|
94
|
-
onValueChange?.(!value);
|
|
95
|
-
}
|
|
96
|
-
// refInput.current?.focus();
|
|
97
|
-
}}
|
|
98
|
-
>
|
|
99
|
-
|
|
100
|
-
<BooleanSwitch
|
|
101
|
-
value={value}
|
|
102
|
-
ref={refInput}
|
|
103
|
-
size={size}
|
|
104
|
-
className={cls(invisible && focus ? focusedClasses : "", inputClassName)}
|
|
105
|
-
disabled={disabled}
|
|
106
|
-
{...props}
|
|
107
|
-
/>
|
|
108
|
-
|
|
109
|
-
<div id={switchLabelId} className={cls(
|
|
110
|
-
"grow",
|
|
111
|
-
position === "end" ? "mr-4" : "ml-4",
|
|
112
|
-
size === "small" || size === "smallest" ? "text-sm" : "text-base"
|
|
113
|
-
)}>
|
|
114
|
-
{label}
|
|
115
|
-
</div>
|
|
116
|
-
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
);
|
|
120
|
-
};
|