@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.
Files changed (205) hide show
  1. package/assets/images/noData.webp +0 -0
  2. package/dist/index.js +15318 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/noData-XPRHKMFR.webp +0 -0
  5. package/package.json +51 -4
  6. package/src/ThemeProvider/ThemeContext.tsx +75 -0
  7. package/src/ThemeProvider/index.ts +7 -0
  8. package/src/assets/images/noData.webp +0 -0
  9. package/src/assets.d.ts +10 -0
  10. package/src/components/Accordion/Accordion.stories.tsx +64 -0
  11. package/src/components/Accordion/Accordion.tsx +219 -0
  12. package/src/components/Accordion/Accordion.types.ts +16 -0
  13. package/src/components/Avatar/Avatar.stories.tsx +304 -0
  14. package/src/components/Avatar/Avatar.tsx +211 -0
  15. package/src/components/Avatar/Avatar.types.ts +33 -0
  16. package/src/components/Badge/Badge.stories.tsx +143 -0
  17. package/src/components/Badge/Badge.tsx +138 -0
  18. package/src/components/Badge/Badge.types.ts +24 -0
  19. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
  20. package/src/components/Breadcrumb/Breadcrumb.tsx +120 -0
  21. package/src/components/Breadcrumb/Breadcrumb.types.ts +22 -0
  22. package/src/components/Button/Button.stories.tsx +457 -0
  23. package/src/components/Button/Button.tsx +202 -0
  24. package/src/components/Button/Button.types.ts +18 -0
  25. package/src/components/Checkbox/Checkbox.stories.tsx +254 -0
  26. package/src/components/Checkbox/Checkbox.tsx +352 -0
  27. package/src/components/Checkbox/Checkbox.types.ts +123 -0
  28. package/src/components/CloseButton/CloseButton.stories.tsx +100 -0
  29. package/src/components/CloseButton/CloseButton.tsx +117 -0
  30. package/src/components/CloseButton/CloseButton.types.ts +15 -0
  31. package/src/components/CollapsibleSearch/CollapsibleSearch.tsx +120 -0
  32. package/src/components/DataTable/DataTable.stories.tsx +233 -0
  33. package/src/components/DataTable/DataTable.tsx +968 -0
  34. package/src/components/DataTable/DataTable.types.ts +49 -0
  35. package/src/components/DatePicker/DatePicker.stories.tsx +341 -0
  36. package/src/components/DatePicker/DatePicker.tsx +669 -0
  37. package/src/components/DatePicker/DatePicker.types.ts +105 -0
  38. package/src/components/DatePicker/DateTimePicker.tsx +35 -0
  39. package/src/components/DateRangePicker/DateRangePicker.tsx +179 -0
  40. package/src/components/DateRangePicker/DateRangePicker.types.ts +34 -0
  41. package/src/components/Drawer/Drawer.stories.tsx +243 -0
  42. package/src/components/Drawer/Drawer.tsx +196 -0
  43. package/src/components/Drawer/Drawer.types.ts +60 -0
  44. package/src/components/FileUpload/FileUpload.stories.tsx +162 -0
  45. package/src/components/FileUpload/FileUpload.tsx +216 -0
  46. package/src/components/FileUpload/FileUpload.types.ts +80 -0
  47. package/src/components/FilterPopover/FilterPopover.stories.tsx +173 -0
  48. package/src/components/FilterPopover/FilterPopover.tsx +310 -0
  49. package/src/components/FilterPopover/FilterPopover.types.ts +26 -0
  50. package/src/components/ImageViewer/ImageViewerToolbar.jsx +81 -0
  51. package/src/components/ImageViewer/useImagePanRotate.js +102 -0
  52. package/src/components/IpWhitelistField/IpWhitelistField.stories.tsx +138 -0
  53. package/src/components/IpWhitelistField/IpWhitelistField.tsx +164 -0
  54. package/src/components/IpWhitelistField/IpWhitelistField.types.ts +42 -0
  55. package/src/components/Loader/Loader.stories.tsx +95 -0
  56. package/src/components/Loader/Loader.tsx +132 -0
  57. package/src/components/Loader/Loader.types.ts +6 -0
  58. package/src/components/Menu/Menu.tsx +143 -0
  59. package/src/components/Menu/Menu.types.ts +28 -0
  60. package/src/components/MenuButton/MenuButton.stories.tsx +140 -0
  61. package/src/components/MenuButton/MenuButton.tsx +45 -0
  62. package/src/components/MenuButton/MenuButton.types.ts +21 -0
  63. package/src/components/Modal/Modal.stories.tsx +266 -0
  64. package/src/components/Modal/Modal.tsx +399 -0
  65. package/src/components/Modal/index.ts +2 -0
  66. package/src/components/Modal/types.ts +126 -0
  67. package/src/components/Pagination/Pagination.stories.tsx +167 -0
  68. package/src/components/Pagination/Pagination.tsx +266 -0
  69. package/src/components/Pagination/Pagination.types.ts +20 -0
  70. package/src/components/PeriodSelector/PeriodSelector.stories.tsx +50 -0
  71. package/src/components/PeriodSelector/PeriodSelector.tsx +47 -0
  72. package/src/components/PeriodSelector/PeriodSelector.types.ts +10 -0
  73. package/src/components/PhoneField/PhoneField.stories.tsx +141 -0
  74. package/src/components/PhoneField/PhoneField.tsx +284 -0
  75. package/src/components/PhoneField/PhoneField.types.ts +89 -0
  76. package/src/components/PhoneField/countryCodes.ts +236 -0
  77. package/src/components/Progress/Progress.stories.tsx +83 -0
  78. package/src/components/Progress/Progress.tsx +91 -0
  79. package/src/components/Progress/Progress.types.ts +22 -0
  80. package/src/components/RadioButton/RadioButton.stories.tsx +332 -0
  81. package/src/components/RadioButton/RadioButton.tsx +427 -0
  82. package/src/components/RadioButton/RadioButton.types.ts +117 -0
  83. package/src/components/ReportCard/ReportCard.tsx +183 -0
  84. package/src/components/ReportCard/ReportCard.types.ts +17 -0
  85. package/src/components/SectionIcon/SectionIcon.tsx +87 -0
  86. package/src/components/SectionIcon/SectionIcon.types.ts +23 -0
  87. package/src/components/SegmentedInputField/SegmentedInputField.stories.tsx +165 -0
  88. package/src/components/SegmentedInputField/SegmentedInputField.tsx +174 -0
  89. package/src/components/SegmentedInputField/SegmentedInputField.types.ts +51 -0
  90. package/src/components/SelectScroll/SelectScroll.stories.tsx +336 -0
  91. package/src/components/SelectScroll/SelectScroll.tsx +1119 -0
  92. package/src/components/SelectScroll/SelectScroll.types.ts +60 -0
  93. package/src/components/Selector/Selector.stories.tsx +382 -0
  94. package/src/components/Selector/Selector.tsx +1310 -0
  95. package/src/components/Selector/Selector.types.ts +71 -0
  96. package/src/components/Skeleton/Skeleton.stories.tsx +133 -0
  97. package/src/components/Skeleton/Skeleton.tsx +20 -0
  98. package/src/components/Skeleton/Skeleton.types.ts +3 -0
  99. package/src/components/Spinner/Spinner.stories.tsx +84 -0
  100. package/src/components/Spinner/Spinner.tsx +79 -0
  101. package/src/components/Spinner/Spinner.types.ts +8 -0
  102. package/src/components/StatCard/StatCard.stories.tsx +125 -0
  103. package/src/components/StatCard/StatCard.tsx +167 -0
  104. package/src/components/StatCard/StatCard.types.ts +20 -0
  105. package/src/components/StatusBadge/StatusBadge.stories.tsx +179 -0
  106. package/src/components/StatusBadge/StatusBadge.tsx +74 -0
  107. package/src/components/StatusBadge/StatusBadge.types.ts +23 -0
  108. package/src/components/Stepper/Stepper.stories.tsx +85 -0
  109. package/src/components/Stepper/Stepper.tsx +138 -0
  110. package/src/components/Stepper/Stepper.types.ts +16 -0
  111. package/src/components/SubTabs/SubTabs.stories.tsx +128 -0
  112. package/src/components/SubTabs/SubTabs.tsx +279 -0
  113. package/src/components/SubTabs/SubTabs.types.ts +44 -0
  114. package/src/components/SummaryCard/SummaryCard.stories.tsx +100 -0
  115. package/src/components/SummaryCard/SummaryCard.tsx +141 -0
  116. package/src/components/SummaryCard/SummaryCard.types.ts +22 -0
  117. package/src/components/Switch/Switch.stories.tsx +59 -0
  118. package/src/components/Switch/Switch.tsx +217 -0
  119. package/src/components/Switch/Switch.types.ts +22 -0
  120. package/src/components/Tabs/Tabs.stories.tsx +178 -0
  121. package/src/components/Tabs/Tabs.tsx +197 -0
  122. package/src/components/Tabs/Tabs.types.ts +34 -0
  123. package/src/components/TextField/TextField.stories.tsx +249 -0
  124. package/src/components/TextField/TextField.tsx +462 -0
  125. package/src/components/TextField/TextField.types.ts +141 -0
  126. package/src/components/Toast/Toast.stories.tsx +237 -0
  127. package/src/components/Toast/Toast.tsx +246 -0
  128. package/src/components/Toast/Toast.types.ts +31 -0
  129. package/src/components/ToggleButton/ToggleButton.stories.tsx +105 -0
  130. package/src/components/ToggleButton/ToggleButton.tsx +267 -0
  131. package/src/components/ToggleButton/ToggleButton.types.ts +19 -0
  132. package/src/components/UploadDropzone/UploadDropzone.jsx +78 -0
  133. package/src/components/UploadDropzone2/UploadDropzone.stories.tsx +94 -0
  134. package/src/components/UploadDropzone2/UploadDropzone.tsx +222 -0
  135. package/src/components/UploadDropzone2/UploadDropzone.types.ts +54 -0
  136. package/src/components/ViewEditToggle/ViewEditToggle.stories.tsx +40 -0
  137. package/src/components/ViewEditToggle/ViewEditToggle.tsx +239 -0
  138. package/src/components/ViewEditToggle/ViewEditToggle.types.ts +27 -0
  139. package/src/components/index.ts +157 -0
  140. package/src/icons/ReportDollarIcon.tsx +71 -0
  141. package/src/icons/index.ts +27 -0
  142. package/src/index.ts +214 -0
  143. package/src/storybook/preview.tsx +64 -0
  144. package/src/theme/index.ts +4 -0
  145. package/src/theme/types.d.ts +1 -0
  146. package/src/themes/cpc.ts +4 -0
  147. package/src/themes/firebird.ts +4 -0
  148. package/src/tokens/brands/cpc/themes/dark.json +82 -0
  149. package/src/tokens/brands/cpc/themes/light.json +1 -0
  150. package/src/tokens/brands/default/brand.json +20 -0
  151. package/src/tokens/brands/firebird/themes/dark.json +28 -0
  152. package/src/tokens/brands/firebird/themes/light.json +20 -0
  153. package/src/tokens/button.ts +24 -0
  154. package/src/tokens/components/accordion.json +52 -0
  155. package/src/tokens/components/avatar.json +40 -0
  156. package/src/tokens/components/badge.json +41 -0
  157. package/src/tokens/components/breadcrumb.json +13 -0
  158. package/src/tokens/components/button.json +67 -0
  159. package/src/tokens/components/card.json +39 -0
  160. package/src/tokens/components/checkbox.json +64 -0
  161. package/src/tokens/components/closebutton.json +55 -0
  162. package/src/tokens/components/datatable.json +40 -0
  163. package/src/tokens/components/datepicker.json +99 -0
  164. package/src/tokens/components/drawer.json +23 -0
  165. package/src/tokens/components/field.json +29 -0
  166. package/src/tokens/components/fileupload.json +57 -0
  167. package/src/tokens/components/filterpopover.json +29 -0
  168. package/src/tokens/components/input.json +31 -0
  169. package/src/tokens/components/ipwhitelistfield.json +16 -0
  170. package/src/tokens/components/loader.json +15 -0
  171. package/src/tokens/components/menu.json +21 -0
  172. package/src/tokens/components/modal.json +25 -0
  173. package/src/tokens/components/pagination.json +45 -0
  174. package/src/tokens/components/phonefield.json +41 -0
  175. package/src/tokens/components/progress.json +12 -0
  176. package/src/tokens/components/radio.json +87 -0
  177. package/src/tokens/components/reportcard.json +17 -0
  178. package/src/tokens/components/selector.json +87 -0
  179. package/src/tokens/components/selectscroll.json +65 -0
  180. package/src/tokens/components/skeleton.json +6 -0
  181. package/src/tokens/components/stepper.json +38 -0
  182. package/src/tokens/components/subtabs.json +45 -0
  183. package/src/tokens/components/switch.json +55 -0
  184. package/src/tokens/components/tabs.json +38 -0
  185. package/src/tokens/components/textfield.json +59 -0
  186. package/src/tokens/components/togglebutton.json +48 -0
  187. package/src/tokens/components/uploaddropzone.json +61 -0
  188. package/src/tokens/drawer.ts +37 -0
  189. package/src/tokens/foundation/breakpoints.json +10 -0
  190. package/src/tokens/foundation/colors.json +122 -0
  191. package/src/tokens/foundation/opacity.json +16 -0
  192. package/src/tokens/foundation/radius.json +14 -0
  193. package/src/tokens/foundation/size.json +38 -0
  194. package/src/tokens/foundation/spacing.json +19 -0
  195. package/src/tokens/foundation/typography.json +43 -0
  196. package/src/tokens/index.ts +3665 -0
  197. package/src/tokens/modal.ts +42 -0
  198. package/src/tokens/semantic/color.json +148 -0
  199. package/src/tokens/semantic/field.json +28 -0
  200. package/src/tokens/semantic/opacity.json +5 -0
  201. package/src/tokens/semantic/size.json +722 -0
  202. package/src/tokens/semantic/spacing.json +27 -0
  203. package/src/tokens/semantic/typography.json +87 -0
  204. package/src/utils/scrollLock.ts +44 -0
  205. 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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ DesignSystemThemeProvider,
3
+ useTokens,
4
+ ThemeContext,
5
+ type ThemeContextValue,
6
+ type DesignSystemThemeProviderProps,
7
+ } from './ThemeContext';
Binary file
@@ -0,0 +1,10 @@
1
+ declare module '*.webp' {
2
+ const src: string
3
+ export default src
4
+ }
5
+
6
+ declare module '*.css'
7
+ declare module '*.json' {
8
+ const value: Record<string, unknown>
9
+ export default value
10
+ }
@@ -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
+ }