@ews-admin/global-design-system 1.1.1 → 1.1.5
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/dist/components/ThemeToggle/ThemeToggle.d.ts +6 -0
- package/dist/components/ThemeToggle/ThemeToggle.d.ts.map +1 -0
- package/dist/components/ThemeToggle/index.d.ts +3 -0
- package/dist/components/ThemeToggle/index.d.ts.map +1 -0
- package/dist/icons/DoctorIcon.d.ts +4 -0
- package/dist/icons/DoctorIcon.d.ts.map +1 -0
- package/dist/icons/Icon.d.ts +18 -0
- package/dist/icons/Icon.d.ts.map +1 -1
- package/dist/icons/PatientIcon.d.ts +4 -0
- package/dist/icons/PatientIcon.d.ts.map +1 -0
- package/dist/icons/UserIcon.d.ts +4 -0
- package/dist/icons/UserIcon.d.ts.map +1 -0
- package/dist/icons/index.d.ts +4 -1
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/index.css +3 -2
- package/dist/index.d.ts +62 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.css +3 -2
- package/dist/index.esm.js +156 -20
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +160 -18
- package/dist/index.js.map +1 -1
- package/dist/theme/ThemeProvider.d.ts +10 -0
- package/dist/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/theme/themeConfig.d.ts +8 -0
- package/dist/theme/themeConfig.d.ts.map +1 -0
- package/dist/types/theme.d.ts +23 -0
- package/dist/types/theme.d.ts.map +1 -0
- package/package.json +7 -3
- package/src/components/ThemeToggle/ThemeToggle.tsx +43 -0
- package/src/components/ThemeToggle/index.ts +2 -0
- package/src/icons/DoctorIcon.tsx +14 -0
- package/src/icons/Icon.tsx +20 -0
- package/src/icons/PatientIcon.tsx +12 -0
- package/src/icons/UserIcon.tsx +12 -0
- package/src/icons/index.ts +6 -1
- package/src/index.ts +17 -2
- package/src/styles/index.css +162 -8
- package/src/styles/theme-variables.css +47 -0
- package/src/theme/ThemeProvider.tsx +69 -0
- package/src/theme/themeConfig.ts +40 -0
- package/src/types/theme.ts +24 -0
- package/tailwind.preset.js +166 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/types/theme.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,KAAK,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,KAAK,CAAC;IACZ,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC;CAC1B"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ews-admin/global-design-system",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "EWS Global Design System - Reusable components for EWS applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
|
-
"src"
|
|
10
|
+
"src",
|
|
11
|
+
"tailwind.preset.js"
|
|
11
12
|
],
|
|
12
13
|
"keywords": [
|
|
13
14
|
"design-system",
|
|
@@ -80,6 +81,9 @@
|
|
|
80
81
|
"version:major": "pnpm version major",
|
|
81
82
|
"view": "pnpm view @ews-admin/global-design-system",
|
|
82
83
|
"clean": "rm -rf dist",
|
|
83
|
-
"rebuild": "pnpm clean && pnpm build"
|
|
84
|
+
"rebuild": "pnpm clean && pnpm build",
|
|
85
|
+
"link": "pnpm build && pnpm link --global",
|
|
86
|
+
"unlink": "pnpm unlink --global",
|
|
87
|
+
"dev:watch": "pnpm build && pnpm dev"
|
|
84
88
|
}
|
|
85
89
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useTheme } from "../../theme/ThemeProvider";
|
|
3
|
+
import { Theme } from "../../types/theme";
|
|
4
|
+
|
|
5
|
+
export interface ThemeToggleProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ThemeToggle: React.FC<ThemeToggleProps> = ({ className }) => {
|
|
10
|
+
const { theme, setTheme } = useTheme();
|
|
11
|
+
|
|
12
|
+
const handleThemeChange = (newTheme: Theme) => {
|
|
13
|
+
setTheme(newTheme);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className={`flex items-center space-x-2 ${className || ""}`}>
|
|
18
|
+
<span className="text-sm font-medium text-gray-700">Theme:</span>
|
|
19
|
+
<div className="flex bg-gray-100 rounded-lg p-1">
|
|
20
|
+
<button
|
|
21
|
+
onClick={() => handleThemeChange("PROMED")}
|
|
22
|
+
className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${
|
|
23
|
+
theme === "PROMED"
|
|
24
|
+
? "bg-ews-primary text-white"
|
|
25
|
+
: "text-gray-600 hover:text-gray-900"
|
|
26
|
+
}`}
|
|
27
|
+
>
|
|
28
|
+
PROMED
|
|
29
|
+
</button>
|
|
30
|
+
<button
|
|
31
|
+
onClick={() => handleThemeChange("MED")}
|
|
32
|
+
className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${
|
|
33
|
+
theme === "MED"
|
|
34
|
+
? "bg-ews-primary text-white"
|
|
35
|
+
: "text-gray-600 hover:text-gray-900"
|
|
36
|
+
}`}
|
|
37
|
+
>
|
|
38
|
+
MED
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Stethoscope } from "lucide-react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { SimpleIconProps } from "./Icon";
|
|
4
|
+
|
|
5
|
+
export const DoctorIcon: React.FC<SimpleIconProps> = ({
|
|
6
|
+
size = 24,
|
|
7
|
+
color = "currentColor",
|
|
8
|
+
className = "",
|
|
9
|
+
...props
|
|
10
|
+
}) => {
|
|
11
|
+
return (
|
|
12
|
+
<Stethoscope size={size} color={color} className={className} {...props} />
|
|
13
|
+
);
|
|
14
|
+
};
|
package/src/icons/Icon.tsx
CHANGED
|
@@ -12,6 +12,26 @@ export interface IconProps extends Omit<LucideProps, "size"> {
|
|
|
12
12
|
icon: React.ComponentType<LucideProps>;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// Simple icon props for individual icon components
|
|
16
|
+
export interface SimpleIconProps {
|
|
17
|
+
/**
|
|
18
|
+
* Icon size in pixels
|
|
19
|
+
*/
|
|
20
|
+
size?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Icon color (can be hex, rgb, or Tailwind class)
|
|
23
|
+
*/
|
|
24
|
+
color?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Additional CSS classes (supports Tailwind classes)
|
|
27
|
+
*/
|
|
28
|
+
className?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Other props passed to the underlying icon
|
|
31
|
+
*/
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}
|
|
34
|
+
|
|
15
35
|
const Icon = React.forwardRef<SVGSVGElement, IconProps>(
|
|
16
36
|
({ size = "md", icon: IconComponent, className, ...props }, ref) => {
|
|
17
37
|
const sizes = {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Heart } from "lucide-react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { SimpleIconProps } from "./Icon";
|
|
4
|
+
|
|
5
|
+
export const PatientIcon: React.FC<SimpleIconProps> = ({
|
|
6
|
+
size = 24,
|
|
7
|
+
color = "currentColor",
|
|
8
|
+
className = "",
|
|
9
|
+
...props
|
|
10
|
+
}) => {
|
|
11
|
+
return <Heart size={size} color={color} className={className} {...props} />;
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { User } from "lucide-react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { SimpleIconProps } from "./Icon";
|
|
4
|
+
|
|
5
|
+
export const UserIcon: React.FC<SimpleIconProps> = ({
|
|
6
|
+
size = 24,
|
|
7
|
+
color = "currentColor",
|
|
8
|
+
className = "",
|
|
9
|
+
...props
|
|
10
|
+
}) => {
|
|
11
|
+
return <User size={size} color={color} className={className} {...props} />;
|
|
12
|
+
};
|
package/src/icons/index.ts
CHANGED
|
@@ -38,4 +38,9 @@ export {
|
|
|
38
38
|
|
|
39
39
|
// Keep our custom Icon component for backward compatibility
|
|
40
40
|
export { Icon } from "./Icon";
|
|
41
|
-
export type { IconProps } from "./Icon";
|
|
41
|
+
export type { IconProps, SimpleIconProps } from "./Icon";
|
|
42
|
+
|
|
43
|
+
// Custom EWS Icons
|
|
44
|
+
export { DoctorIcon } from "./DoctorIcon";
|
|
45
|
+
export { PatientIcon } from "./PatientIcon";
|
|
46
|
+
export { UserIcon } from "./UserIcon";
|
package/src/index.ts
CHANGED
|
@@ -16,13 +16,24 @@ export type { ErrorField, ErrorObject, ModalProps } from "./components/Modal";
|
|
|
16
16
|
export { Logo } from "./components/Logo";
|
|
17
17
|
export type { LogoProps } from "./components/Logo";
|
|
18
18
|
|
|
19
|
+
export { ThemeToggle } from "./components/ThemeToggle";
|
|
20
|
+
export type { ThemeToggleProps } from "./components/ThemeToggle";
|
|
21
|
+
|
|
19
22
|
// Molecules
|
|
20
23
|
export { SpecialtySearchAutocomplete } from "./molecules";
|
|
21
24
|
export type { Specialty, SpecialtySearchAutocompleteProps } from "./molecules";
|
|
22
25
|
|
|
23
26
|
// Icons
|
|
24
|
-
export {
|
|
25
|
-
|
|
27
|
+
export {
|
|
28
|
+
ArrowRight,
|
|
29
|
+
Check,
|
|
30
|
+
DoctorIcon,
|
|
31
|
+
Icon,
|
|
32
|
+
PatientIcon,
|
|
33
|
+
Search,
|
|
34
|
+
UserIcon,
|
|
35
|
+
} from "./icons";
|
|
36
|
+
export type { IconProps, SimpleIconProps } from "./icons";
|
|
26
37
|
|
|
27
38
|
// Utils
|
|
28
39
|
export { cn, debounce, formatCurrency, formatDate, generateId } from "./utils";
|
|
@@ -30,6 +41,10 @@ export { cn, debounce, formatCurrency, formatDate, generateId } from "./utils";
|
|
|
30
41
|
// Hooks
|
|
31
42
|
export { useDebounce, useDebouncedCallback } from "./hooks";
|
|
32
43
|
|
|
44
|
+
// Theme
|
|
45
|
+
export { ThemeProvider, useTheme } from "./theme/ThemeProvider";
|
|
46
|
+
export type { Theme, ThemeConfig, ThemeContextType } from "./types/theme";
|
|
47
|
+
|
|
33
48
|
// Styles
|
|
34
49
|
import "./styles/index.css";
|
|
35
50
|
import "./styles/tailwind.css";
|
package/src/styles/index.css
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* EWS Global Design System - Base Styles */
|
|
2
|
+
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap");
|
|
2
3
|
@tailwind base;
|
|
3
4
|
@tailwind components;
|
|
4
5
|
@tailwind utilities;
|
|
@@ -73,15 +74,14 @@
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
/* Reset and Base Styles */
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
@layer base {
|
|
78
|
+
html {
|
|
79
|
+
font-family: "Inter", system-ui, sans-serif;
|
|
80
|
+
}
|
|
79
81
|
|
|
80
|
-
body {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
color: var(--ews-gray-900);
|
|
84
|
-
background-color: var(--ews-white);
|
|
82
|
+
body {
|
|
83
|
+
@apply bg-neutral-50 text-neutral-900;
|
|
84
|
+
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/* Utility Classes */
|
|
@@ -96,3 +96,157 @@ body {
|
|
|
96
96
|
white-space: nowrap;
|
|
97
97
|
border: 0;
|
|
98
98
|
}
|
|
99
|
+
@layer base {
|
|
100
|
+
html {
|
|
101
|
+
font-family: "Inter", system-ui, sans-serif;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
body {
|
|
105
|
+
@apply bg-neutral-50 text-neutral-900;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
@layer utilities {
|
|
109
|
+
/* Custom shadow utilities */
|
|
110
|
+
.shadow-soft {
|
|
111
|
+
box-shadow: 0 2px 15px -3px rgba(0, 0, 0, 0.07),
|
|
112
|
+
0 10px 20px -2px rgba(0, 0, 0, 0.04);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.shadow-medium {
|
|
116
|
+
box-shadow: 0 4px 25px -5px rgba(0, 0, 0, 0.1),
|
|
117
|
+
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.shadow-large {
|
|
121
|
+
box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.15),
|
|
122
|
+
0 2px 10px -2px rgba(0, 0, 0, 0.05);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@layer components {
|
|
127
|
+
/* Custom scrollbar */
|
|
128
|
+
::-webkit-scrollbar {
|
|
129
|
+
width: 6px;
|
|
130
|
+
}
|
|
131
|
+
::-webkit-scrollbar-track {
|
|
132
|
+
@apply bg-neutral-100;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
::-webkit-scrollbar-thumb {
|
|
136
|
+
@apply rounded-full bg-neutral-300;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
::-webkit-scrollbar-thumb:hover {
|
|
140
|
+
@apply bg-neutral-400;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Card base styles */
|
|
144
|
+
.card-base {
|
|
145
|
+
@apply bg-white rounded-xl border shadow-soft border-neutral-200;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Sidebar navigation styles */
|
|
149
|
+
.sidebar-item {
|
|
150
|
+
@apply relative transition-all duration-200;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.sidebar-item-active {
|
|
154
|
+
@apply border-l-4 shadow-sm bg-primary-50 text-primary-700 border-l-primary-500;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.sidebar-item-hover {
|
|
158
|
+
@apply hover:bg-neutral-50 hover:text-neutral-900 hover:border-l-4 hover:border-l-neutral-300;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.sidebar-item-icon {
|
|
162
|
+
@apply transition-colors duration-200;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.sidebar-item-icon-active {
|
|
166
|
+
@apply text-primary-600;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.sidebar-item-icon-inactive {
|
|
170
|
+
@apply text-neutral-500;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Dashboard animations */
|
|
174
|
+
@keyframes fadeIn {
|
|
175
|
+
from {
|
|
176
|
+
opacity: 0;
|
|
177
|
+
transform: translateY(20px);
|
|
178
|
+
}
|
|
179
|
+
to {
|
|
180
|
+
opacity: 1;
|
|
181
|
+
transform: translateY(0);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@keyframes slideInFromLeft {
|
|
186
|
+
from {
|
|
187
|
+
opacity: 0;
|
|
188
|
+
transform: translateX(-20px);
|
|
189
|
+
}
|
|
190
|
+
to {
|
|
191
|
+
opacity: 1;
|
|
192
|
+
transform: translateX(0);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@keyframes slideInFromRight {
|
|
197
|
+
from {
|
|
198
|
+
opacity: 0;
|
|
199
|
+
transform: translateX(20px);
|
|
200
|
+
}
|
|
201
|
+
to {
|
|
202
|
+
opacity: 1;
|
|
203
|
+
transform: translateX(0);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@keyframes scaleIn {
|
|
208
|
+
from {
|
|
209
|
+
opacity: 0;
|
|
210
|
+
transform: scale(0.9);
|
|
211
|
+
}
|
|
212
|
+
to {
|
|
213
|
+
opacity: 1;
|
|
214
|
+
transform: scale(1);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.animate-fadeIn {
|
|
219
|
+
animation: fadeIn 0.6s ease-out;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.animate-slideInLeft {
|
|
223
|
+
animation: slideInFromLeft 0.6s ease-out;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.animate-slideInRight {
|
|
227
|
+
animation: slideInFromRight 0.6s ease-out;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.animate-scaleIn {
|
|
231
|
+
animation: scaleIn 0.6s ease-out;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* Hover animations */
|
|
235
|
+
.hover-lift {
|
|
236
|
+
transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.hover-lift:hover {
|
|
240
|
+
transform: translateY(-2px);
|
|
241
|
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1),
|
|
242
|
+
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.hover-scale {
|
|
246
|
+
transition: transform 0.2s ease-out;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.hover-scale:hover {
|
|
250
|
+
transform: scale(1.02);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* EWS Design System - Theme CSS Variables */
|
|
2
|
+
/* This file should be imported in consuming applications */
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
/* PROMED Theme (Default) - Professional theme for doctors/managers */
|
|
6
|
+
--ews-primary: #21596c;
|
|
7
|
+
--ews-primary-hover: #1a4756;
|
|
8
|
+
--ews-primary-light: #c0d0d4;
|
|
9
|
+
--ews-secondary: #3ba1a1;
|
|
10
|
+
--ews-secondary-hover: #308181;
|
|
11
|
+
--ews-success: #059669;
|
|
12
|
+
--ews-success-hover: #047857;
|
|
13
|
+
--ews-warning: #d97706;
|
|
14
|
+
--ews-warning-hover: #b45309;
|
|
15
|
+
--ews-error: #dc2626;
|
|
16
|
+
--ews-error-hover: #b91c1c;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* MED Theme - Patient-friendly theme */
|
|
20
|
+
[data-theme="MED"] {
|
|
21
|
+
--ews-primary: #3ba1a1;
|
|
22
|
+
--ews-primary-hover: #308181;
|
|
23
|
+
--ews-primary-light: #a8d5d5;
|
|
24
|
+
--ews-secondary: #6b73ff;
|
|
25
|
+
--ews-secondary-hover: #5a61e6;
|
|
26
|
+
--ews-success: #059669;
|
|
27
|
+
--ews-success-hover: #047857;
|
|
28
|
+
--ews-warning: #d97706;
|
|
29
|
+
--ews-warning-hover: #b45309;
|
|
30
|
+
--ews-error: #dc2626;
|
|
31
|
+
--ews-error-hover: #b91c1c;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* PROMED Theme - Professional theme */
|
|
35
|
+
[data-theme="PROMED"] {
|
|
36
|
+
--ews-primary: #21596c;
|
|
37
|
+
--ews-primary-hover: #1a4756;
|
|
38
|
+
--ews-primary-light: #c0d0d4;
|
|
39
|
+
--ews-secondary: #3ba1a1;
|
|
40
|
+
--ews-secondary-hover: #308181;
|
|
41
|
+
--ews-success: #059669;
|
|
42
|
+
--ews-success-hover: #047857;
|
|
43
|
+
--ews-warning: #d97706;
|
|
44
|
+
--ews-warning-hover: #b45309;
|
|
45
|
+
--ews-error: #dc2626;
|
|
46
|
+
--ews-error-hover: #b91c1c;
|
|
47
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
ReactNode,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { Theme, ThemeContextType } from "../types/theme";
|
|
9
|
+
import { THEMES } from "./themeConfig";
|
|
10
|
+
|
|
11
|
+
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
12
|
+
|
|
13
|
+
interface ThemeProviderProps {
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
defaultTheme?: Theme;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
19
|
+
children,
|
|
20
|
+
defaultTheme = "PROMED",
|
|
21
|
+
}) => {
|
|
22
|
+
const [theme, setTheme] = useState<Theme>(defaultTheme);
|
|
23
|
+
const [themeConfig, setThemeConfig] = useState(THEMES[defaultTheme]);
|
|
24
|
+
|
|
25
|
+
// Update theme config when theme changes
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
setThemeConfig(THEMES[theme]);
|
|
28
|
+
|
|
29
|
+
// Update data-theme attribute for CSS variable switching
|
|
30
|
+
const root = document.documentElement;
|
|
31
|
+
root.setAttribute("data-theme", theme);
|
|
32
|
+
|
|
33
|
+
// Also update CSS custom properties for backward compatibility
|
|
34
|
+
const colors = THEMES[theme].colors;
|
|
35
|
+
root.style.setProperty("--ews-primary", colors.primary);
|
|
36
|
+
root.style.setProperty("--ews-primary-hover", colors.primaryHover);
|
|
37
|
+
root.style.setProperty("--ews-primary-light", colors.primaryLight);
|
|
38
|
+
root.style.setProperty("--ews-secondary", colors.secondary);
|
|
39
|
+
root.style.setProperty("--ews-secondary-hover", colors.secondaryHover);
|
|
40
|
+
root.style.setProperty("--ews-success", colors.success);
|
|
41
|
+
root.style.setProperty("--ews-success-hover", colors.successHover);
|
|
42
|
+
root.style.setProperty("--ews-warning", colors.warning);
|
|
43
|
+
root.style.setProperty("--ews-warning-hover", colors.warningHover);
|
|
44
|
+
root.style.setProperty("--ews-error", colors.error);
|
|
45
|
+
root.style.setProperty("--ews-error-hover", colors.errorHover);
|
|
46
|
+
}, [theme]);
|
|
47
|
+
|
|
48
|
+
const handleSetTheme = (newTheme: Theme) => {
|
|
49
|
+
setTheme(newTheme);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const value: ThemeContextType = {
|
|
53
|
+
theme,
|
|
54
|
+
setTheme: handleSetTheme,
|
|
55
|
+
themeConfig,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const useTheme = (): ThemeContextType => {
|
|
64
|
+
const context = useContext(ThemeContext);
|
|
65
|
+
if (context === undefined) {
|
|
66
|
+
throw new Error("useTheme must be used within a ThemeProvider");
|
|
67
|
+
}
|
|
68
|
+
return context;
|
|
69
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ThemeConfig } from "../types/theme";
|
|
2
|
+
|
|
3
|
+
export const PROMED_THEME: ThemeConfig = {
|
|
4
|
+
name: "PROMED",
|
|
5
|
+
colors: {
|
|
6
|
+
primary: "#21596C",
|
|
7
|
+
secondary: "#3BA1A1",
|
|
8
|
+
primaryHover: "#1a4756",
|
|
9
|
+
secondaryHover: "#308181",
|
|
10
|
+
primaryLight: "#c0d0d4",
|
|
11
|
+
success: "#059669",
|
|
12
|
+
successHover: "#047857",
|
|
13
|
+
warning: "#d97706",
|
|
14
|
+
warningHover: "#b45309",
|
|
15
|
+
error: "#dc2626",
|
|
16
|
+
errorHover: "#b91c1c",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const MED_THEME: ThemeConfig = {
|
|
21
|
+
name: "MED",
|
|
22
|
+
colors: {
|
|
23
|
+
primary: "#3BA1A1", // Teal for patients (calming, trustworthy)
|
|
24
|
+
secondary: "#6B73FF", // Soft purple-blue (suggested secondary for MED)
|
|
25
|
+
primaryHover: "#308181",
|
|
26
|
+
secondaryHover: "#5a61e6",
|
|
27
|
+
primaryLight: "#a8d5d5",
|
|
28
|
+
success: "#059669",
|
|
29
|
+
successHover: "#047857",
|
|
30
|
+
warning: "#d97706",
|
|
31
|
+
warningHover: "#b45309",
|
|
32
|
+
error: "#dc2626",
|
|
33
|
+
errorHover: "#b91c1c",
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const THEMES = {
|
|
38
|
+
PROMED: PROMED_THEME,
|
|
39
|
+
MED: MED_THEME,
|
|
40
|
+
} as const;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type Theme = "PROMED" | "MED";
|
|
2
|
+
|
|
3
|
+
export interface ThemeConfig {
|
|
4
|
+
name: Theme;
|
|
5
|
+
colors: {
|
|
6
|
+
primary: string;
|
|
7
|
+
secondary: string;
|
|
8
|
+
primaryHover: string;
|
|
9
|
+
secondaryHover: string;
|
|
10
|
+
primaryLight: string;
|
|
11
|
+
success: string;
|
|
12
|
+
successHover: string;
|
|
13
|
+
warning: string;
|
|
14
|
+
warningHover: string;
|
|
15
|
+
error: string;
|
|
16
|
+
errorHover: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ThemeContextType {
|
|
21
|
+
theme: Theme;
|
|
22
|
+
setTheme: (theme: Theme) => void;
|
|
23
|
+
themeConfig: ThemeConfig;
|
|
24
|
+
}
|