@altruist03/ui 0.0.0-stage → 0.2.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/assets/images/noData.webp +0 -0
- package/dist/index.js +15318 -0
- package/dist/index.js.map +1 -0
- package/dist/noData-XPRHKMFR.webp +0 -0
- package/package.json +51 -4
- package/src/ThemeProvider/ThemeContext.tsx +75 -0
- package/src/ThemeProvider/index.ts +7 -0
- package/src/assets/images/noData.webp +0 -0
- package/src/assets.d.ts +10 -0
- package/src/components/Accordion/Accordion.stories.tsx +64 -0
- package/src/components/Accordion/Accordion.tsx +219 -0
- package/src/components/Accordion/Accordion.types.ts +16 -0
- package/src/components/Avatar/Avatar.stories.tsx +304 -0
- package/src/components/Avatar/Avatar.tsx +211 -0
- package/src/components/Avatar/Avatar.types.ts +33 -0
- package/src/components/Badge/Badge.stories.tsx +143 -0
- package/src/components/Badge/Badge.tsx +138 -0
- package/src/components/Badge/Badge.types.ts +24 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +120 -0
- package/src/components/Breadcrumb/Breadcrumb.types.ts +22 -0
- package/src/components/Button/Button.stories.tsx +457 -0
- package/src/components/Button/Button.tsx +202 -0
- package/src/components/Button/Button.types.ts +18 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +254 -0
- package/src/components/Checkbox/Checkbox.tsx +352 -0
- package/src/components/Checkbox/Checkbox.types.ts +123 -0
- package/src/components/CloseButton/CloseButton.stories.tsx +100 -0
- package/src/components/CloseButton/CloseButton.tsx +117 -0
- package/src/components/CloseButton/CloseButton.types.ts +15 -0
- package/src/components/CollapsibleSearch/CollapsibleSearch.tsx +120 -0
- package/src/components/DataTable/DataTable.stories.tsx +233 -0
- package/src/components/DataTable/DataTable.tsx +968 -0
- package/src/components/DataTable/DataTable.types.ts +49 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +341 -0
- package/src/components/DatePicker/DatePicker.tsx +669 -0
- package/src/components/DatePicker/DatePicker.types.ts +105 -0
- package/src/components/DatePicker/DateTimePicker.tsx +35 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +179 -0
- package/src/components/DateRangePicker/DateRangePicker.types.ts +34 -0
- package/src/components/Drawer/Drawer.stories.tsx +243 -0
- package/src/components/Drawer/Drawer.tsx +196 -0
- package/src/components/Drawer/Drawer.types.ts +60 -0
- package/src/components/FileUpload/FileUpload.stories.tsx +162 -0
- package/src/components/FileUpload/FileUpload.tsx +216 -0
- package/src/components/FileUpload/FileUpload.types.ts +80 -0
- package/src/components/FilterPopover/FilterPopover.stories.tsx +173 -0
- package/src/components/FilterPopover/FilterPopover.tsx +310 -0
- package/src/components/FilterPopover/FilterPopover.types.ts +26 -0
- package/src/components/ImageViewer/ImageViewerToolbar.jsx +81 -0
- package/src/components/ImageViewer/useImagePanRotate.js +102 -0
- package/src/components/IpWhitelistField/IpWhitelistField.stories.tsx +138 -0
- package/src/components/IpWhitelistField/IpWhitelistField.tsx +164 -0
- package/src/components/IpWhitelistField/IpWhitelistField.types.ts +42 -0
- package/src/components/Loader/Loader.stories.tsx +95 -0
- package/src/components/Loader/Loader.tsx +132 -0
- package/src/components/Loader/Loader.types.ts +6 -0
- package/src/components/Menu/Menu.tsx +143 -0
- package/src/components/Menu/Menu.types.ts +28 -0
- package/src/components/MenuButton/MenuButton.stories.tsx +140 -0
- package/src/components/MenuButton/MenuButton.tsx +45 -0
- package/src/components/MenuButton/MenuButton.types.ts +21 -0
- package/src/components/Modal/Modal.stories.tsx +266 -0
- package/src/components/Modal/Modal.tsx +399 -0
- package/src/components/Modal/index.ts +2 -0
- package/src/components/Modal/types.ts +126 -0
- package/src/components/Pagination/Pagination.stories.tsx +167 -0
- package/src/components/Pagination/Pagination.tsx +266 -0
- package/src/components/Pagination/Pagination.types.ts +20 -0
- package/src/components/PeriodSelector/PeriodSelector.stories.tsx +50 -0
- package/src/components/PeriodSelector/PeriodSelector.tsx +47 -0
- package/src/components/PeriodSelector/PeriodSelector.types.ts +10 -0
- package/src/components/PhoneField/PhoneField.stories.tsx +141 -0
- package/src/components/PhoneField/PhoneField.tsx +284 -0
- package/src/components/PhoneField/PhoneField.types.ts +89 -0
- package/src/components/PhoneField/countryCodes.ts +236 -0
- package/src/components/Progress/Progress.stories.tsx +83 -0
- package/src/components/Progress/Progress.tsx +91 -0
- package/src/components/Progress/Progress.types.ts +22 -0
- package/src/components/RadioButton/RadioButton.stories.tsx +332 -0
- package/src/components/RadioButton/RadioButton.tsx +427 -0
- package/src/components/RadioButton/RadioButton.types.ts +117 -0
- package/src/components/ReportCard/ReportCard.tsx +183 -0
- package/src/components/ReportCard/ReportCard.types.ts +17 -0
- package/src/components/SectionIcon/SectionIcon.tsx +87 -0
- package/src/components/SectionIcon/SectionIcon.types.ts +23 -0
- package/src/components/SegmentedInputField/SegmentedInputField.stories.tsx +165 -0
- package/src/components/SegmentedInputField/SegmentedInputField.tsx +174 -0
- package/src/components/SegmentedInputField/SegmentedInputField.types.ts +51 -0
- package/src/components/SelectScroll/SelectScroll.stories.tsx +336 -0
- package/src/components/SelectScroll/SelectScroll.tsx +1119 -0
- package/src/components/SelectScroll/SelectScroll.types.ts +60 -0
- package/src/components/Selector/Selector.stories.tsx +382 -0
- package/src/components/Selector/Selector.tsx +1310 -0
- package/src/components/Selector/Selector.types.ts +71 -0
- package/src/components/Skeleton/Skeleton.stories.tsx +133 -0
- package/src/components/Skeleton/Skeleton.tsx +20 -0
- package/src/components/Skeleton/Skeleton.types.ts +3 -0
- package/src/components/Spinner/Spinner.stories.tsx +84 -0
- package/src/components/Spinner/Spinner.tsx +79 -0
- package/src/components/Spinner/Spinner.types.ts +8 -0
- package/src/components/StatCard/StatCard.stories.tsx +125 -0
- package/src/components/StatCard/StatCard.tsx +167 -0
- package/src/components/StatCard/StatCard.types.ts +20 -0
- package/src/components/StatusBadge/StatusBadge.stories.tsx +179 -0
- package/src/components/StatusBadge/StatusBadge.tsx +74 -0
- package/src/components/StatusBadge/StatusBadge.types.ts +23 -0
- package/src/components/Stepper/Stepper.stories.tsx +85 -0
- package/src/components/Stepper/Stepper.tsx +138 -0
- package/src/components/Stepper/Stepper.types.ts +16 -0
- package/src/components/SubTabs/SubTabs.stories.tsx +128 -0
- package/src/components/SubTabs/SubTabs.tsx +279 -0
- package/src/components/SubTabs/SubTabs.types.ts +44 -0
- package/src/components/SummaryCard/SummaryCard.stories.tsx +100 -0
- package/src/components/SummaryCard/SummaryCard.tsx +141 -0
- package/src/components/SummaryCard/SummaryCard.types.ts +22 -0
- package/src/components/Switch/Switch.stories.tsx +59 -0
- package/src/components/Switch/Switch.tsx +217 -0
- package/src/components/Switch/Switch.types.ts +22 -0
- package/src/components/Tabs/Tabs.stories.tsx +178 -0
- package/src/components/Tabs/Tabs.tsx +197 -0
- package/src/components/Tabs/Tabs.types.ts +34 -0
- package/src/components/TextField/TextField.stories.tsx +249 -0
- package/src/components/TextField/TextField.tsx +462 -0
- package/src/components/TextField/TextField.types.ts +141 -0
- package/src/components/Toast/Toast.stories.tsx +237 -0
- package/src/components/Toast/Toast.tsx +246 -0
- package/src/components/Toast/Toast.types.ts +31 -0
- package/src/components/ToggleButton/ToggleButton.stories.tsx +105 -0
- package/src/components/ToggleButton/ToggleButton.tsx +267 -0
- package/src/components/ToggleButton/ToggleButton.types.ts +19 -0
- package/src/components/UploadDropzone/UploadDropzone.jsx +78 -0
- package/src/components/UploadDropzone2/UploadDropzone.stories.tsx +94 -0
- package/src/components/UploadDropzone2/UploadDropzone.tsx +222 -0
- package/src/components/UploadDropzone2/UploadDropzone.types.ts +54 -0
- package/src/components/ViewEditToggle/ViewEditToggle.stories.tsx +40 -0
- package/src/components/ViewEditToggle/ViewEditToggle.tsx +239 -0
- package/src/components/ViewEditToggle/ViewEditToggle.types.ts +27 -0
- package/src/components/index.ts +157 -0
- package/src/icons/ReportDollarIcon.tsx +71 -0
- package/src/icons/index.ts +27 -0
- package/src/index.ts +214 -0
- package/src/storybook/preview.tsx +64 -0
- package/src/theme/index.ts +4 -0
- package/src/theme/types.d.ts +1 -0
- package/src/themes/cpc.ts +4 -0
- package/src/themes/firebird.ts +4 -0
- package/src/tokens/brands/cpc/themes/dark.json +82 -0
- package/src/tokens/brands/cpc/themes/light.json +1 -0
- package/src/tokens/brands/default/brand.json +20 -0
- package/src/tokens/brands/firebird/themes/dark.json +28 -0
- package/src/tokens/brands/firebird/themes/light.json +20 -0
- package/src/tokens/button.ts +24 -0
- package/src/tokens/components/accordion.json +52 -0
- package/src/tokens/components/avatar.json +40 -0
- package/src/tokens/components/badge.json +41 -0
- package/src/tokens/components/breadcrumb.json +13 -0
- package/src/tokens/components/button.json +67 -0
- package/src/tokens/components/card.json +39 -0
- package/src/tokens/components/checkbox.json +64 -0
- package/src/tokens/components/closebutton.json +55 -0
- package/src/tokens/components/datatable.json +40 -0
- package/src/tokens/components/datepicker.json +99 -0
- package/src/tokens/components/drawer.json +23 -0
- package/src/tokens/components/field.json +29 -0
- package/src/tokens/components/fileupload.json +57 -0
- package/src/tokens/components/filterpopover.json +29 -0
- package/src/tokens/components/input.json +31 -0
- package/src/tokens/components/ipwhitelistfield.json +16 -0
- package/src/tokens/components/loader.json +15 -0
- package/src/tokens/components/menu.json +21 -0
- package/src/tokens/components/modal.json +25 -0
- package/src/tokens/components/pagination.json +45 -0
- package/src/tokens/components/phonefield.json +41 -0
- package/src/tokens/components/progress.json +12 -0
- package/src/tokens/components/radio.json +87 -0
- package/src/tokens/components/reportcard.json +17 -0
- package/src/tokens/components/selector.json +87 -0
- package/src/tokens/components/selectscroll.json +65 -0
- package/src/tokens/components/skeleton.json +6 -0
- package/src/tokens/components/stepper.json +38 -0
- package/src/tokens/components/subtabs.json +45 -0
- package/src/tokens/components/switch.json +55 -0
- package/src/tokens/components/tabs.json +38 -0
- package/src/tokens/components/textfield.json +59 -0
- package/src/tokens/components/togglebutton.json +48 -0
- package/src/tokens/components/uploaddropzone.json +61 -0
- package/src/tokens/drawer.ts +37 -0
- package/src/tokens/foundation/breakpoints.json +10 -0
- package/src/tokens/foundation/colors.json +122 -0
- package/src/tokens/foundation/opacity.json +16 -0
- package/src/tokens/foundation/radius.json +14 -0
- package/src/tokens/foundation/size.json +38 -0
- package/src/tokens/foundation/spacing.json +19 -0
- package/src/tokens/foundation/typography.json +43 -0
- package/src/tokens/index.ts +3665 -0
- package/src/tokens/modal.ts +42 -0
- package/src/tokens/semantic/color.json +148 -0
- package/src/tokens/semantic/field.json +28 -0
- package/src/tokens/semantic/opacity.json +5 -0
- package/src/tokens/semantic/size.json +722 -0
- package/src/tokens/semantic/spacing.json +27 -0
- package/src/tokens/semantic/typography.json +87 -0
- package/src/utils/scrollLock.ts +44 -0
- package/README.md +0 -3
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altruist03/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Altruist design system components (ported from Tawseel Storybook).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"**/*.css",
|
|
8
|
+
"**/*.json"
|
|
9
|
+
],
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"module": "./dist/index.js",
|
|
12
|
+
"types": "./src/index.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./src/index.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./datepicker.css": "react-datepicker/dist/react-datepicker.css"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src",
|
|
24
|
+
"assets"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@emotion/react": "^11.13.3",
|
|
31
|
+
"@emotion/styled": "^11.13.0",
|
|
32
|
+
"@mui/icons-material": "^6.1.0",
|
|
33
|
+
"@mui/material": "^6.1.0",
|
|
34
|
+
"@types/react-table": "^7.7.20",
|
|
35
|
+
"date-fns": "^2.30.0",
|
|
36
|
+
"react-datepicker": "^7.6.0",
|
|
37
|
+
"react-loading-skeleton": "^3.5.0",
|
|
38
|
+
"react-table": "^7.8.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"react": ">=18",
|
|
42
|
+
"react-dom": ">=18"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/react": "^19.2.2",
|
|
46
|
+
"@types/react-dom": "^19.2.2",
|
|
47
|
+
"react": "^19.2.0",
|
|
48
|
+
"react-dom": "^19.2.0"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, useState, useEffect, ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
getTokens,
|
|
4
|
+
getScreen,
|
|
5
|
+
getBreakpoints,
|
|
6
|
+
type ResolvedTokens,
|
|
7
|
+
type BrandKey,
|
|
8
|
+
type ThemeMode,
|
|
9
|
+
} from '../tokens';
|
|
10
|
+
|
|
11
|
+
export interface ThemeContextValue {
|
|
12
|
+
brand: BrandKey;
|
|
13
|
+
mode: ThemeMode;
|
|
14
|
+
tokens: ResolvedTokens;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const defaultTokens = getTokens({
|
|
18
|
+
brand: 'cpc',
|
|
19
|
+
mode: 'light',
|
|
20
|
+
screen: 'desktop',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const ThemeContext = createContext<ThemeContextValue>({
|
|
24
|
+
brand: 'cpc',
|
|
25
|
+
mode: 'light',
|
|
26
|
+
tokens: defaultTokens,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export interface DesignSystemThemeProviderProps {
|
|
30
|
+
brand?: BrandKey;
|
|
31
|
+
mode?: ThemeMode;
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function DesignSystemThemeProvider({
|
|
36
|
+
brand = 'cpc',
|
|
37
|
+
mode = 'light',
|
|
38
|
+
children,
|
|
39
|
+
}: DesignSystemThemeProviderProps) {
|
|
40
|
+
const breakpoints = useMemo(() => getBreakpoints(), []);
|
|
41
|
+
// Start with "desktop" so we never use a wrong read from before viewport is applied
|
|
42
|
+
const [screen, setScreen] = useState<ReturnType<typeof getScreen>>('desktop');
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const updateScreen = () => setScreen(getScreen(breakpoints));
|
|
46
|
+
// Run immediately (correct on many loads)
|
|
47
|
+
updateScreen();
|
|
48
|
+
// Run again after paint so we catch the real viewport when it was wrong on first read
|
|
49
|
+
const rafId = requestAnimationFrame(updateScreen);
|
|
50
|
+
// One more time after a short delay for slow viewport application (e.g. mobile)
|
|
51
|
+
const timeoutId = window.setTimeout(updateScreen, 100);
|
|
52
|
+
window.addEventListener('resize', updateScreen);
|
|
53
|
+
return () => {
|
|
54
|
+
cancelAnimationFrame(rafId);
|
|
55
|
+
window.clearTimeout(timeoutId);
|
|
56
|
+
window.removeEventListener('resize', updateScreen);
|
|
57
|
+
};
|
|
58
|
+
}, [breakpoints]);
|
|
59
|
+
|
|
60
|
+
const value = useMemo<ThemeContextValue>(
|
|
61
|
+
() => ({
|
|
62
|
+
brand,
|
|
63
|
+
mode,
|
|
64
|
+
tokens: getTokens({ brand, mode, screen }),
|
|
65
|
+
}),
|
|
66
|
+
[brand, mode, screen]
|
|
67
|
+
);
|
|
68
|
+
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Hook to read resolved tokens (mode + brand) from ThemeProvider. Falls back to default tokens if no provider. */
|
|
72
|
+
export function useTokens(): ResolvedTokens {
|
|
73
|
+
const ctx = useContext(ThemeContext);
|
|
74
|
+
return ctx.tokens;
|
|
75
|
+
}
|
|
Binary file
|
package/src/assets.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Accordion } from "./Accordion";
|
|
3
|
+
import { ToggleButton } from "../ToggleButton/ToggleButton";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Accordion> = {
|
|
6
|
+
title: "Design System/Accordion",
|
|
7
|
+
component: Accordion,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
decorators: [
|
|
10
|
+
(Story) => (
|
|
11
|
+
<div style={{ padding: "24px", maxWidth: "1024px" }}>
|
|
12
|
+
<Story />
|
|
13
|
+
</div>
|
|
14
|
+
),
|
|
15
|
+
],
|
|
16
|
+
args: {
|
|
17
|
+
title: "Nationality Category",
|
|
18
|
+
subtitle: "Expatriate",
|
|
19
|
+
showStatusIcon: true,
|
|
20
|
+
showActionIcon: true,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
type Story = StoryObj<typeof Accordion>;
|
|
26
|
+
|
|
27
|
+
const Content = () => (
|
|
28
|
+
<div style={{ width: "100%", maxWidth: "420px" }}>
|
|
29
|
+
<div
|
|
30
|
+
style={{
|
|
31
|
+
marginBottom: "12px",
|
|
32
|
+
fontWeight: 600,
|
|
33
|
+
color: "#22313A",
|
|
34
|
+
fontSize: "14px",
|
|
35
|
+
lineHeight: "40px",
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
What is the nationality category? <span style={{ color: "#D61F1F" }}>*</span>
|
|
39
|
+
</div>
|
|
40
|
+
<ToggleButton
|
|
41
|
+
value="expatriate"
|
|
42
|
+
options={[
|
|
43
|
+
{ value: "citizen", label: "Citizen" },
|
|
44
|
+
{ value: "expatriate", label: "Expatriate" },
|
|
45
|
+
]}
|
|
46
|
+
onChange={() => {}}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export const Default: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
defaultExpanded: false,
|
|
54
|
+
children: <Content />,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const Expanded: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
defaultExpanded: true,
|
|
61
|
+
children: <Content />,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useTokens } from "../../ThemeProvider/ThemeContext";
|
|
3
|
+
import { getAccordionTokensFromResolved } from "../../tokens";
|
|
4
|
+
import type { AccordionProps } from "./Accordion.types";
|
|
5
|
+
|
|
6
|
+
export const Accordion = ({
|
|
7
|
+
title,
|
|
8
|
+
subtitle,
|
|
9
|
+
children,
|
|
10
|
+
expanded,
|
|
11
|
+
defaultExpanded = false,
|
|
12
|
+
onToggle,
|
|
13
|
+
disabled = false,
|
|
14
|
+
showStatusIcon = true,
|
|
15
|
+
showActionIcon = true,
|
|
16
|
+
actionLabel = "Edit section",
|
|
17
|
+
onActionClick,
|
|
18
|
+
style,
|
|
19
|
+
...rest
|
|
20
|
+
}: AccordionProps) => {
|
|
21
|
+
const resolvedTokens = useTokens();
|
|
22
|
+
const t = getAccordionTokensFromResolved(resolvedTokens);
|
|
23
|
+
|
|
24
|
+
const isControlled = typeof expanded === "boolean";
|
|
25
|
+
const [internalExpanded, setInternalExpanded] = React.useState(defaultExpanded);
|
|
26
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
27
|
+
const [contentHeight, setContentHeight] = React.useState("0px");
|
|
28
|
+
const contentInnerRef = React.useRef<HTMLDivElement>(null);
|
|
29
|
+
const isExpanded = isControlled ? expanded : internalExpanded;
|
|
30
|
+
|
|
31
|
+
React.useEffect(() => {
|
|
32
|
+
const nextHeight = contentInnerRef.current?.scrollHeight ?? 0;
|
|
33
|
+
setContentHeight(`${nextHeight}px`);
|
|
34
|
+
}, [isExpanded, children]);
|
|
35
|
+
|
|
36
|
+
const toggleExpanded = () => {
|
|
37
|
+
if (disabled) return;
|
|
38
|
+
const next = !isExpanded;
|
|
39
|
+
if (!isControlled) setInternalExpanded(next);
|
|
40
|
+
onToggle?.(next);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const headerBackground = isExpanded
|
|
44
|
+
? t.header.expandedBackground
|
|
45
|
+
: isHovered
|
|
46
|
+
? t.header.hoverBackground
|
|
47
|
+
: t.header.defaultBackground;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
style={{
|
|
52
|
+
width: t.container.width,
|
|
53
|
+
border: `${t.container.borderWidth} solid ${t.container.borderColor}`,
|
|
54
|
+
borderRadius: t.container.borderRadius,
|
|
55
|
+
overflow: "hidden",
|
|
56
|
+
backgroundColor: t.container.background,
|
|
57
|
+
...style,
|
|
58
|
+
}}
|
|
59
|
+
{...rest}
|
|
60
|
+
>
|
|
61
|
+
<div
|
|
62
|
+
role="button"
|
|
63
|
+
tabIndex={disabled ? -1 : 0}
|
|
64
|
+
aria-expanded={isExpanded}
|
|
65
|
+
aria-disabled={disabled}
|
|
66
|
+
onClick={toggleExpanded}
|
|
67
|
+
onKeyDown={(event) => {
|
|
68
|
+
if (disabled) return;
|
|
69
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
toggleExpanded();
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
75
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
76
|
+
style={{
|
|
77
|
+
width: "100%",
|
|
78
|
+
backgroundColor: headerBackground,
|
|
79
|
+
border: "none",
|
|
80
|
+
borderBottom:
|
|
81
|
+
isExpanded && children
|
|
82
|
+
? `${t.container.borderWidth} solid ${t.container.borderColor}`
|
|
83
|
+
: "none",
|
|
84
|
+
padding: t.header.padding,
|
|
85
|
+
display: "flex",
|
|
86
|
+
alignItems: "center",
|
|
87
|
+
justifyContent: "space-between",
|
|
88
|
+
gap: t.header.gap,
|
|
89
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
90
|
+
textAlign: "left",
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<span
|
|
94
|
+
style={{
|
|
95
|
+
display: "flex",
|
|
96
|
+
alignItems: "center",
|
|
97
|
+
gap: t.header.contentGap,
|
|
98
|
+
minWidth: 0,
|
|
99
|
+
flex: 1,
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{showStatusIcon ? (
|
|
103
|
+
<span
|
|
104
|
+
aria-hidden
|
|
105
|
+
style={{
|
|
106
|
+
width: t.statusIcon.size,
|
|
107
|
+
height: t.statusIcon.size,
|
|
108
|
+
minWidth: t.statusIcon.size,
|
|
109
|
+
borderRadius: "50%",
|
|
110
|
+
backgroundColor: t.statusIcon.background,
|
|
111
|
+
color: t.statusIcon.color,
|
|
112
|
+
display: "inline-flex",
|
|
113
|
+
alignItems: "center",
|
|
114
|
+
justifyContent: "center",
|
|
115
|
+
fontSize: t.statusIcon.fontSize,
|
|
116
|
+
lineHeight: t.statusIcon.lineHeight,
|
|
117
|
+
fontWeight: t.statusIcon.fontWeight,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
{t.statusIcon.symbol}
|
|
121
|
+
</span>
|
|
122
|
+
) : null}
|
|
123
|
+
|
|
124
|
+
<span style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
|
|
125
|
+
<span
|
|
126
|
+
style={{
|
|
127
|
+
color: t.title.color,
|
|
128
|
+
fontFamily: t.title.fontFamily,
|
|
129
|
+
fontSize: t.title.fontSize,
|
|
130
|
+
fontWeight: t.title.fontWeight,
|
|
131
|
+
lineHeight: t.title.lineHeight,
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
{title}
|
|
135
|
+
</span>
|
|
136
|
+
{subtitle ? (
|
|
137
|
+
<span
|
|
138
|
+
style={{
|
|
139
|
+
color: t.subtitle.color,
|
|
140
|
+
fontFamily: t.subtitle.fontFamily,
|
|
141
|
+
fontSize: t.subtitle.fontSize,
|
|
142
|
+
fontStyle: t.subtitle.fontStyle,
|
|
143
|
+
fontWeight: t.subtitle.fontWeight,
|
|
144
|
+
lineHeight: t.subtitle.lineHeight,
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
{subtitle}
|
|
148
|
+
</span>
|
|
149
|
+
) : null}
|
|
150
|
+
</span>
|
|
151
|
+
</span>
|
|
152
|
+
|
|
153
|
+
{showActionIcon ? (
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
aria-label={actionLabel}
|
|
157
|
+
onClick={(event) => {
|
|
158
|
+
event.stopPropagation();
|
|
159
|
+
onActionClick?.(event);
|
|
160
|
+
}}
|
|
161
|
+
style={{
|
|
162
|
+
width: t.actionIcon.size,
|
|
163
|
+
height: t.actionIcon.size,
|
|
164
|
+
minWidth: t.actionIcon.size,
|
|
165
|
+
border: "none",
|
|
166
|
+
background: "transparent",
|
|
167
|
+
color: t.actionIcon.color,
|
|
168
|
+
display: "inline-flex",
|
|
169
|
+
alignItems: "center",
|
|
170
|
+
justifyContent: "center",
|
|
171
|
+
padding: "0",
|
|
172
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
173
|
+
}}
|
|
174
|
+
>
|
|
175
|
+
<svg
|
|
176
|
+
width={t.actionIcon.size}
|
|
177
|
+
height={t.actionIcon.size}
|
|
178
|
+
viewBox="0 0 24 24"
|
|
179
|
+
fill="none"
|
|
180
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
181
|
+
aria-hidden
|
|
182
|
+
>
|
|
183
|
+
<path
|
|
184
|
+
d="M14.06 4.94L19.06 9.94M5 19H10L20.06 8.94C20.651 8.34911 20.9831 7.54761 20.9831 6.712C20.9831 5.87639 20.651 5.07489 20.06 4.484C19.4691 3.89311 18.6676 3.56102 17.832 3.56102C16.9964 3.56102 16.1949 3.89311 15.604 4.484L5.544 14.544C5.189 14.899 5 15.38 5 15.882V19Z"
|
|
185
|
+
stroke="currentColor"
|
|
186
|
+
strokeWidth={t.actionIcon.strokeWidth}
|
|
187
|
+
strokeLinecap="round"
|
|
188
|
+
strokeLinejoin="round"
|
|
189
|
+
/>
|
|
190
|
+
</svg>
|
|
191
|
+
</button>
|
|
192
|
+
) : null}
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
{children ? (
|
|
196
|
+
<div
|
|
197
|
+
aria-hidden={!isExpanded}
|
|
198
|
+
style={{
|
|
199
|
+
maxHeight: isExpanded ? contentHeight : "0px",
|
|
200
|
+
opacity: isExpanded ? 1 : 0,
|
|
201
|
+
overflow: "hidden",
|
|
202
|
+
transition: "max-height 280ms ease, opacity 220ms ease",
|
|
203
|
+
willChange: "max-height, opacity",
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
<div
|
|
207
|
+
ref={contentInnerRef}
|
|
208
|
+
style={{
|
|
209
|
+
padding: t.content.padding,
|
|
210
|
+
backgroundColor: t.content.background,
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
{children}
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
) : null}
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
|
|
3
|
+
export interface AccordionProps
|
|
4
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "children" | "onToggle"> {
|
|
5
|
+
title: string;
|
|
6
|
+
subtitle?: string;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
expanded?: boolean;
|
|
9
|
+
defaultExpanded?: boolean;
|
|
10
|
+
onToggle?: (expanded: boolean) => void;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
showStatusIcon?: boolean;
|
|
13
|
+
showActionIcon?: boolean;
|
|
14
|
+
actionLabel?: string;
|
|
15
|
+
onActionClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
16
|
+
}
|