@openlearning/widget-framework 1.0.2 → 1.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/README.md +230 -0
- package/dist/components/Button.d.ts +41 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +47 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Checkbox.d.ts +19 -0
- package/dist/components/Checkbox.d.ts.map +1 -0
- package/dist/components/Checkbox.js +16 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/CheckboxGroup.d.ts +34 -0
- package/dist/components/CheckboxGroup.d.ts.map +1 -0
- package/dist/components/CheckboxGroup.js +18 -0
- package/dist/components/CheckboxGroup.js.map +1 -0
- package/dist/components/Menu.d.ts +45 -0
- package/dist/components/Menu.d.ts.map +1 -0
- package/dist/components/Menu.js +56 -0
- package/dist/components/Menu.js.map +1 -0
- package/dist/components/RadioGroup.d.ts +36 -0
- package/dist/components/RadioGroup.d.ts.map +1 -0
- package/dist/components/RadioGroup.js +18 -0
- package/dist/components/RadioGroup.js.map +1 -0
- package/dist/components/Spinner.d.ts +15 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +10 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/StatusTag.d.ts +16 -0
- package/dist/components/StatusTag.d.ts.map +1 -0
- package/dist/components/StatusTag.js +9 -0
- package/dist/components/StatusTag.js.map +1 -0
- package/dist/components/Switch.d.ts +22 -0
- package/dist/components/Switch.d.ts.map +1 -0
- package/dist/components/Switch.js +14 -0
- package/dist/components/Switch.js.map +1 -0
- package/dist/components/Tabs.d.ts +23 -0
- package/dist/components/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs.js +27 -0
- package/dist/components/Tabs.js.map +1 -0
- package/dist/components/TextField.d.ts +19 -0
- package/dist/components/TextField.d.ts.map +1 -0
- package/dist/components/TextField.js +17 -0
- package/dist/components/TextField.js.map +1 -0
- package/dist/components/Textarea.d.ts +19 -0
- package/dist/components/Textarea.d.ts.map +1 -0
- package/dist/components/Textarea.js +17 -0
- package/dist/components/Textarea.js.map +1 -0
- package/dist/components/icons/IconCaretDown.d.ts +7 -0
- package/dist/components/icons/IconCaretDown.d.ts.map +1 -0
- package/dist/components/icons/IconCaretDown.js +4 -0
- package/dist/components/icons/IconCaretDown.js.map +1 -0
- package/dist/components/icons/IconCaretLeft.d.ts +7 -0
- package/dist/components/icons/IconCaretLeft.d.ts.map +1 -0
- package/dist/components/icons/IconCaretLeft.js +4 -0
- package/dist/components/icons/IconCaretLeft.js.map +1 -0
- package/dist/components/icons/IconCaretRight.d.ts +7 -0
- package/dist/components/icons/IconCaretRight.d.ts.map +1 -0
- package/dist/components/icons/IconCaretRight.js +4 -0
- package/dist/components/icons/IconCaretRight.js.map +1 -0
- package/dist/components/icons/IconCaretUp.d.ts +7 -0
- package/dist/components/icons/IconCaretUp.d.ts.map +1 -0
- package/dist/components/icons/IconCaretUp.js +4 -0
- package/dist/components/icons/IconCaretUp.js.map +1 -0
- package/dist/components/icons/IconChevronLeft.d.ts +7 -0
- package/dist/components/icons/IconChevronLeft.d.ts.map +1 -0
- package/dist/components/icons/IconChevronLeft.js +4 -0
- package/dist/components/icons/IconChevronLeft.js.map +1 -0
- package/dist/components/icons/IconChevronRight.d.ts +7 -0
- package/dist/components/icons/IconChevronRight.d.ts.map +1 -0
- package/dist/components/icons/IconChevronRight.js +4 -0
- package/dist/components/icons/IconChevronRight.js.map +1 -0
- package/dist/components/icons/IconFastForward.d.ts +7 -0
- package/dist/components/icons/IconFastForward.d.ts.map +1 -0
- package/dist/components/icons/IconFastForward.js +4 -0
- package/dist/components/icons/IconFastForward.js.map +1 -0
- package/dist/components/icons/IconNext.d.ts +7 -0
- package/dist/components/icons/IconNext.d.ts.map +1 -0
- package/dist/components/icons/IconNext.js +4 -0
- package/dist/components/icons/IconNext.js.map +1 -0
- package/dist/components/icons/IconPause.d.ts +7 -0
- package/dist/components/icons/IconPause.d.ts.map +1 -0
- package/dist/components/icons/IconPause.js +4 -0
- package/dist/components/icons/IconPause.js.map +1 -0
- package/dist/components/icons/IconPlay.d.ts +7 -0
- package/dist/components/icons/IconPlay.d.ts.map +1 -0
- package/dist/components/icons/IconPlay.js +4 -0
- package/dist/components/icons/IconPlay.js.map +1 -0
- package/dist/components/icons/IconPlus.d.ts +7 -0
- package/dist/components/icons/IconPlus.d.ts.map +1 -0
- package/dist/components/icons/IconPlus.js +4 -0
- package/dist/components/icons/IconPlus.js.map +1 -0
- package/dist/components/icons/IconPrevious.d.ts +7 -0
- package/dist/components/icons/IconPrevious.d.ts.map +1 -0
- package/dist/components/icons/IconPrevious.js +4 -0
- package/dist/components/icons/IconPrevious.js.map +1 -0
- package/dist/components/icons/IconRedo.d.ts +7 -0
- package/dist/components/icons/IconRedo.d.ts.map +1 -0
- package/dist/components/icons/IconRedo.js +4 -0
- package/dist/components/icons/IconRedo.js.map +1 -0
- package/dist/components/icons/IconReload.d.ts +7 -0
- package/dist/components/icons/IconReload.d.ts.map +1 -0
- package/dist/components/icons/IconReload.js +4 -0
- package/dist/components/icons/IconReload.js.map +1 -0
- package/dist/components/icons/IconReplay.d.ts +3 -0
- package/dist/components/icons/IconReplay.d.ts.map +1 -0
- package/dist/components/icons/IconReplay.js +4 -0
- package/dist/components/icons/IconReplay.js.map +1 -0
- package/dist/components/icons/IconRewind.d.ts +7 -0
- package/dist/components/icons/IconRewind.d.ts.map +1 -0
- package/dist/components/icons/IconRewind.js +4 -0
- package/dist/components/icons/IconRewind.js.map +1 -0
- package/dist/components/icons/IconSkipBackward.d.ts +7 -0
- package/dist/components/icons/IconSkipBackward.d.ts.map +1 -0
- package/dist/components/icons/IconSkipBackward.js +4 -0
- package/dist/components/icons/IconSkipBackward.js.map +1 -0
- package/dist/components/icons/IconSkipForward.d.ts +7 -0
- package/dist/components/icons/IconSkipForward.d.ts.map +1 -0
- package/dist/components/icons/IconSkipForward.js +4 -0
- package/dist/components/icons/IconSkipForward.js.map +1 -0
- package/dist/components/icons/IconStop.d.ts +7 -0
- package/dist/components/icons/IconStop.d.ts.map +1 -0
- package/dist/components/icons/IconStop.js +4 -0
- package/dist/components/icons/IconStop.js.map +1 -0
- package/dist/components/icons/IconUndo.d.ts +7 -0
- package/dist/components/icons/IconUndo.d.ts.map +1 -0
- package/dist/components/icons/IconUndo.js +4 -0
- package/dist/components/icons/IconUndo.js.map +1 -0
- package/dist/components/icons/IconX.d.ts +7 -0
- package/dist/components/icons/IconX.d.ts.map +1 -0
- package/dist/components/icons/IconX.js +4 -0
- package/dist/components/icons/IconX.js.map +1 -0
- package/dist/components/icons/index.d.ts +21 -0
- package/dist/components/icons/index.d.ts.map +1 -0
- package/dist/components/icons/index.js +21 -0
- package/dist/components/icons/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/styles/button.css +381 -0
- package/dist/styles/checkbox.css +105 -0
- package/dist/styles/checkboxgroup.css +42 -0
- package/dist/styles/radiogroup.css +135 -0
- package/dist/styles/spinner.css +22 -0
- package/dist/styles/statustag.css +35 -0
- package/dist/styles/switch.css +74 -0
- package/dist/styles/tabs.css +49 -0
- package/dist/styles/textfield.css +72 -0
- package/package.json +12 -7
package/README.md
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Widget Framework
|
|
2
|
+
|
|
3
|
+
A comprehensive framework for building OpenLearning widgets with reusable UI components, parent-window messaging, and configuration management.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Reusable UI Components**: Pre-built, accessible components following the OpenLearning design system
|
|
8
|
+
- **Parent Messaging**: Secure two-way communication between widgets and parent applications
|
|
9
|
+
- **Configuration Management**: Type-safe widget configuration and setup
|
|
10
|
+
- **TypeScript Support**: Full TypeScript support with comprehensive type definitions
|
|
11
|
+
- **Consistent Styling**: CSS-based theming with customizable color variables
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @openlearning/widget-framework
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. Create Your Widget Component
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import React from "react";
|
|
25
|
+
import { Button } from "@openlearning/widget-framework";
|
|
26
|
+
import "@openlearning/widget-framework/styles/button.css";
|
|
27
|
+
|
|
28
|
+
export function MyWidget() {
|
|
29
|
+
return (
|
|
30
|
+
<div>
|
|
31
|
+
<h1>My First Widget</h1>
|
|
32
|
+
<Button theme="cta">Get Started</Button>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Set Up the Learner View
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { createLearnerEntry } from "@openlearning/widget-framework";
|
|
42
|
+
import MyWidget from "./MyWidget";
|
|
43
|
+
|
|
44
|
+
createLearnerEntry(MyWidget);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 3. Set Up the Setup View
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { createSetupEntry } from "@openlearning/widget-framework";
|
|
51
|
+
import MyWidgetSetup from "./MyWidgetSetup";
|
|
52
|
+
|
|
53
|
+
createSetupEntry(MyWidgetSetup);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Core Features
|
|
57
|
+
|
|
58
|
+
### UI Components
|
|
59
|
+
|
|
60
|
+
See [COMPONENTS.md](./COMPONENTS.md) for full component documentation.
|
|
61
|
+
|
|
62
|
+
### Parent Messaging
|
|
63
|
+
|
|
64
|
+
Communicate securely with the parent application:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { useParentMessaging } from "@openlearning/widget-framework";
|
|
68
|
+
|
|
69
|
+
function MyComponent() {
|
|
70
|
+
const { send, subscribe } = useParentMessaging();
|
|
71
|
+
|
|
72
|
+
const handleAction = () => {
|
|
73
|
+
send({
|
|
74
|
+
type: "action",
|
|
75
|
+
payload: { /* your data */ }
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
subscribe((message) => {
|
|
80
|
+
console.log("Parent message:", message);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return <button onClick={handleAction}>Send Message</button>;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Configuration Management
|
|
88
|
+
|
|
89
|
+
Access and manage widget configuration:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { useWidgetConfig } from "@openlearning/widget-framework";
|
|
93
|
+
|
|
94
|
+
function MyComponent() {
|
|
95
|
+
const config = useWidgetConfig();
|
|
96
|
+
|
|
97
|
+
return <div>Theme: {config.theme}</div>;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Development Tools
|
|
102
|
+
|
|
103
|
+
Use `DevApp` for development and testing:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { DevApp } from "@openlearning/widget-framework";
|
|
107
|
+
|
|
108
|
+
export default function App() {
|
|
109
|
+
return (
|
|
110
|
+
<DevApp
|
|
111
|
+
LearnerView={MyLearnerWidget}
|
|
112
|
+
SetupView={MySetupWidget}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Project Setup
|
|
119
|
+
|
|
120
|
+
Define your widget project configuration:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { defineWidgetProject } from "@openlearning/widget-framework";
|
|
124
|
+
|
|
125
|
+
export const widgetConfig = defineWidgetProject({
|
|
126
|
+
id: "my-widget",
|
|
127
|
+
name: "My Widget",
|
|
128
|
+
version: "1.0.0",
|
|
129
|
+
/* ... more config ... */
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Integration Guide
|
|
134
|
+
|
|
135
|
+
See [INTEGRATION_GUIDE.md](./INTEGRATION_GUIDE.md) for practical examples and common patterns.
|
|
136
|
+
|
|
137
|
+
## API Reference
|
|
138
|
+
|
|
139
|
+
### `createLearnerEntry(Component)`
|
|
140
|
+
Renders a component as the learner view of your widget.
|
|
141
|
+
|
|
142
|
+
### `createSetupEntry(Component)`
|
|
143
|
+
Renders a component as the setup/configuration view of your widget.
|
|
144
|
+
|
|
145
|
+
### `useParentMessaging()`
|
|
146
|
+
Hook for parent-window communication. Returns `{ send, subscribe }`.
|
|
147
|
+
|
|
148
|
+
### `useWidgetConfig()`
|
|
149
|
+
Hook to access widget configuration.
|
|
150
|
+
|
|
151
|
+
### `DevApp(props)`
|
|
152
|
+
Development wrapper component for testing widget views.
|
|
153
|
+
|
|
154
|
+
### `defineWidgetProject(config)`
|
|
155
|
+
Defines widget project configuration.
|
|
156
|
+
|
|
157
|
+
### UI Components
|
|
158
|
+
|
|
159
|
+
#### Button
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { Button } from "@openlearning/widget-framework";
|
|
163
|
+
import "@openlearning/widget-framework/styles/button.css";
|
|
164
|
+
|
|
165
|
+
<Button
|
|
166
|
+
variant="filled"
|
|
167
|
+
theme="primary"
|
|
168
|
+
size="medium"
|
|
169
|
+
icon={<MyIcon />}
|
|
170
|
+
iconPosition="left"
|
|
171
|
+
isLoading={false}
|
|
172
|
+
fullWidth={false}
|
|
173
|
+
onClick={handleClick}
|
|
174
|
+
>
|
|
175
|
+
Click Me
|
|
176
|
+
</Button>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## CSS Customization
|
|
180
|
+
|
|
181
|
+
Override styling by setting CSS variables:
|
|
182
|
+
|
|
183
|
+
```css
|
|
184
|
+
:root {
|
|
185
|
+
--btn-primary-bg: #4a90e2;
|
|
186
|
+
--btn-primary-bg-hover: #357abd;
|
|
187
|
+
--btn-cta-bg: #10b981;
|
|
188
|
+
/* ... more variables ... */
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
See [button.css](./src/styles/button.css) for all available variables.
|
|
193
|
+
|
|
194
|
+
## TypeScript Support
|
|
195
|
+
|
|
196
|
+
Full TypeScript support with type definitions:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import {
|
|
200
|
+
Button,
|
|
201
|
+
type ButtonProps,
|
|
202
|
+
type LearnerViewProps,
|
|
203
|
+
type WidgetConfigBase
|
|
204
|
+
} from "@openlearning/widget-framework";
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Browser Support
|
|
208
|
+
|
|
209
|
+
- Chrome/Edge (latest 2 versions)
|
|
210
|
+
- Firefox (latest 2 versions)
|
|
211
|
+
- Safari (latest 2 versions)
|
|
212
|
+
- iOS Safari 12+
|
|
213
|
+
- Android 5+
|
|
214
|
+
|
|
215
|
+
## Contributing
|
|
216
|
+
|
|
217
|
+
Contributions are welcome! Please follow these guidelines:
|
|
218
|
+
|
|
219
|
+
1. Create a feature branch
|
|
220
|
+
2. Make your changes
|
|
221
|
+
3. Write tests for new functionality
|
|
222
|
+
4. Submit a pull request
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT
|
|
227
|
+
|
|
228
|
+
## Support
|
|
229
|
+
|
|
230
|
+
For issues, questions, or feature requests, please contact the OpenLearning team.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import "../styles/button.css";
|
|
3
|
+
export type ButtonVariant = "round-filled" | "round-tonal" | "round-quiet" | "round-inline" | "primary" | "secondary";
|
|
4
|
+
export type ButtonSize = "small" | "medium" | "large";
|
|
5
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
|
+
/** Visual variant of the button */
|
|
7
|
+
variant?: ButtonVariant;
|
|
8
|
+
/** Size of the button */
|
|
9
|
+
size?: ButtonSize;
|
|
10
|
+
/** Icon to display */
|
|
11
|
+
icon?: ReactNode;
|
|
12
|
+
/** Icon position */
|
|
13
|
+
iconPosition?: "left" | "right";
|
|
14
|
+
/** Whether button is loading */
|
|
15
|
+
isLoading?: boolean;
|
|
16
|
+
/** Custom loading indicator component */
|
|
17
|
+
loadingIndicator?: ReactNode;
|
|
18
|
+
/** Whether button takes full width */
|
|
19
|
+
fullWidth?: boolean;
|
|
20
|
+
/** Custom className to merge with button classes */
|
|
21
|
+
className?: string;
|
|
22
|
+
/** Button content/children */
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reusable Button component following OpenLearning design system
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Basic usage
|
|
30
|
+
* <Button>Click me</Button>
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // With variant
|
|
34
|
+
* <Button variant="filled">Submit</Button>
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // With icon
|
|
38
|
+
* <Button icon={<IconComponent />} iconPosition="left">Action</Button>
|
|
39
|
+
*/
|
|
40
|
+
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
41
|
+
//# sourceMappingURL=Button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/components/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,sBAAsB,CAAC;AAG9B,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,aAAa,GAAG,cAAc,GAAG,SAAS,GAAG,WAAW,CAAC;AACtH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,WACf,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACrD,mCAAmC;IACnC,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB,yBAAyB;IACzB,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB,sBAAsB;IACtB,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAEhC,gCAAgC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAE7B,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,MAAM,uFAqElB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "../styles/button.css";
|
|
4
|
+
import { Spinner } from "./Spinner";
|
|
5
|
+
/**
|
|
6
|
+
* Reusable Button component following OpenLearning design system
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // Basic usage
|
|
10
|
+
* <Button>Click me</Button>
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // With variant
|
|
14
|
+
* <Button variant="filled">Submit</Button>
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // With icon
|
|
18
|
+
* <Button icon={<IconComponent />} iconPosition="left">Action</Button>
|
|
19
|
+
*/
|
|
20
|
+
export const Button = React.forwardRef(({ variant = "primary", size = "medium", icon, iconPosition = "left", isLoading = false, loadingIndicator, fullWidth = false, className = "", children, disabled, ...rest }, ref) => {
|
|
21
|
+
const isDisabled = disabled;
|
|
22
|
+
// Build class names
|
|
23
|
+
const classes = [
|
|
24
|
+
"btn",
|
|
25
|
+
`btn--${variant}`,
|
|
26
|
+
size !== "medium" && `btn--${size}`,
|
|
27
|
+
isLoading && "btn--loading",
|
|
28
|
+
fullWidth && "btn--full-width",
|
|
29
|
+
!children && icon && "btn--icon-only",
|
|
30
|
+
className,
|
|
31
|
+
]
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.join(" ");
|
|
34
|
+
// Build content
|
|
35
|
+
let content = children;
|
|
36
|
+
if (isLoading) {
|
|
37
|
+
content = (_jsxs(_Fragment, { children: [loadingIndicator || _jsx(Spinner, {}), children && _jsx("span", { children: children })] }));
|
|
38
|
+
}
|
|
39
|
+
else if (icon) {
|
|
40
|
+
const iconElement = _jsx("span", { className: "btn__icon", children: icon });
|
|
41
|
+
content =
|
|
42
|
+
iconPosition === "right" ? (_jsxs(_Fragment, { children: [children, iconElement] })) : (_jsxs(_Fragment, { children: [iconElement, children] }));
|
|
43
|
+
}
|
|
44
|
+
return (_jsx("button", { ref: ref, className: classes, disabled: isDisabled, ...rest, children: content }));
|
|
45
|
+
});
|
|
46
|
+
Button.displayName = "Button";
|
|
47
|
+
//# sourceMappingURL=Button.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.js","sourceRoot":"","sources":["../../src/components/Button.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AACzC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmCpC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CACpC,CACE,EACE,OAAO,GAAG,SAAS,EACnB,IAAI,GAAG,QAAQ,EACf,IAAI,EACJ,YAAY,GAAG,MAAM,EACrB,SAAS,GAAG,KAAK,EACjB,gBAAgB,EAChB,SAAS,GAAG,KAAK,EACjB,SAAS,GAAG,EAAE,EACd,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,EACD,GAAG,EACH,EAAE;IACF,MAAM,UAAU,GAAG,QAAQ,CAAC;IAE5B,oBAAoB;IACpB,MAAM,OAAO,GAAG;QACd,KAAK;QACL,QAAQ,OAAO,EAAE;QACjB,IAAI,KAAK,QAAQ,IAAI,QAAQ,IAAI,EAAE;QACnC,SAAS,IAAI,cAAc;QAC3B,SAAS,IAAI,iBAAiB;QAC9B,CAAC,QAAQ,IAAI,IAAI,IAAI,gBAAgB;QACrC,SAAS;KACV;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,gBAAgB;IAChB,IAAI,OAAO,GAAc,QAAQ,CAAC;IAElC,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,GAAG,CACR,8BACG,gBAAgB,IAAI,KAAC,OAAO,KAAG,EAC/B,QAAQ,IAAI,yBAAO,QAAQ,GAAQ,IACnC,CACJ,CAAC;IACJ,CAAC;SAAM,IAAI,IAAI,EAAE,CAAC;QAChB,MAAM,WAAW,GAAG,eAAM,SAAS,EAAC,WAAW,YAAE,IAAI,GAAQ,CAAC;QAC9D,OAAO;YACL,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,CACzB,8BACG,QAAQ,EACR,WAAW,IACX,CACJ,CAAC,CAAC,CAAC,CACF,8BACG,WAAW,EACX,QAAQ,IACR,CACJ,CAAC;IACN,CAAC;IAED,OAAO,CACL,iBACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,OAAO,EAClB,QAAQ,EAAE,UAAU,KAChB,IAAI,YAEP,OAAO,GACD,CACV,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "../styles/checkbox.css";
|
|
3
|
+
export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
|
|
4
|
+
/** Checkbox label */
|
|
5
|
+
label?: React.ReactNode;
|
|
6
|
+
/** Error message to display */
|
|
7
|
+
error?: string | null;
|
|
8
|
+
/** Helper text that appears below the checkbox */
|
|
9
|
+
description?: React.ReactNode;
|
|
10
|
+
/** Whether the checkbox is required */
|
|
11
|
+
required?: boolean;
|
|
12
|
+
/** Called when checked state changes */
|
|
13
|
+
onChange?: (checked: boolean) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Checkbox component matching OpenLearning UI design
|
|
17
|
+
*/
|
|
18
|
+
export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>>;
|
|
19
|
+
//# sourceMappingURL=Checkbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../src/components/Checkbox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,wBAAwB,CAAC;AAEhC,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC;IAClG,qBAAqB;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kDAAkD;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ,wFAkDpB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "../styles/checkbox.css";
|
|
4
|
+
/**
|
|
5
|
+
* Checkbox component matching OpenLearning UI design
|
|
6
|
+
*/
|
|
7
|
+
export const Checkbox = React.forwardRef(({ label, error, description, required, className = "", onChange, disabled, ...rest }, ref) => {
|
|
8
|
+
const handleChange = (event) => {
|
|
9
|
+
if (onChange) {
|
|
10
|
+
onChange(event.target.checked);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
return (_jsxs("div", { className: `checkbox-container ${className}`, children: [_jsxs("label", { className: `checkbox-label ${disabled ? 'checkbox-label--disabled' : ''}`, children: [_jsx("input", { ref: ref, type: "checkbox", className: "checkbox-input", disabled: disabled, onChange: handleChange, ...rest }), _jsx("span", { className: "checkbox-box", children: _jsx("svg", { viewBox: "0 0 20 20", fill: "none", stroke: disabled ? "rgba(0, 0, 0, 0.25)" : "rgba(0, 0, 0, 0.9)", strokeWidth: "3", children: _jsx("polyline", { points: "16 5 8 14 4 10" }) }) }), label && (_jsxs("span", { className: "checkbox-text", children: [label, required && _jsx("span", { className: "checkbox-required", children: " *" })] }))] }), description && !error && (_jsx("div", { className: "checkbox-description", children: description })), error && _jsx("div", { className: "checkbox-error", children: error })] }));
|
|
14
|
+
});
|
|
15
|
+
Checkbox.displayName = "Checkbox";
|
|
16
|
+
//# sourceMappingURL=Checkbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/components/Checkbox.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,wBAAwB,CAAC;AAehC;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CACtC,CACE,EACE,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,SAAS,GAAG,EAAE,EACd,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,EACD,GAAG,EACH,EAAE;IACF,MAAM,YAAY,GAAG,CAAC,KAA0C,EAAE,EAAE;QAClE,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAE,sBAAsB,SAAS,EAAE,aAC/C,iBAAO,SAAS,EAAE,kBAAkB,QAAQ,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE,EAAE,aAC9E,gBACE,GAAG,EAAE,GAAG,EACR,IAAI,EAAC,UAAU,EACf,SAAS,EAAC,gBAAgB,EAC1B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,YAAY,KAClB,IAAI,GACR,EACF,eAAM,SAAS,EAAC,cAAc,YAC5B,cAAK,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,oBAAoB,EAAE,WAAW,EAAC,GAAG,YACnH,mBAAU,MAAM,EAAC,gBAAgB,GAAG,GAChC,GACD,EACN,KAAK,IAAI,CACR,gBAAM,SAAS,EAAC,eAAe,aAC5B,KAAK,EACL,QAAQ,IAAI,eAAM,SAAS,EAAC,mBAAmB,mBAAU,IACrD,CACR,IACK,EACP,WAAW,IAAI,CAAC,KAAK,IAAI,CACxB,cAAK,SAAS,EAAC,sBAAsB,YAAE,WAAW,GAAO,CAC1D,EACA,KAAK,IAAI,cAAK,SAAS,EAAC,gBAAgB,YAAE,KAAK,GAAO,IACnD,CACP,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "../styles/checkboxgroup.css";
|
|
3
|
+
export interface CheckboxGroupOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: React.ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface CheckboxGroupProps {
|
|
9
|
+
/** List of checkbox options */
|
|
10
|
+
options: CheckboxGroupOption[];
|
|
11
|
+
/** Currently selected values */
|
|
12
|
+
values?: string[];
|
|
13
|
+
/** Called when selection changes */
|
|
14
|
+
onChange?: (values: string[]) => void;
|
|
15
|
+
/** Group label */
|
|
16
|
+
label?: React.ReactNode;
|
|
17
|
+
/** Error message */
|
|
18
|
+
error?: string | null;
|
|
19
|
+
/** Helper text */
|
|
20
|
+
description?: React.ReactNode;
|
|
21
|
+
/** Whether the group is required */
|
|
22
|
+
required?: boolean;
|
|
23
|
+
/** Whether all options are disabled */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
/** Layout direction for options */
|
|
26
|
+
layout?: 'horizontal' | 'vertical';
|
|
27
|
+
/** Additional class name */
|
|
28
|
+
className?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* CheckboxGroup component for multiple selections
|
|
32
|
+
*/
|
|
33
|
+
export declare const CheckboxGroup: React.FC<CheckboxGroupProps>;
|
|
34
|
+
//# sourceMappingURL=CheckboxGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CheckboxGroup.d.ts","sourceRoot":"","sources":["../../src/components/CheckboxGroup.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,6BAA6B,CAAC;AAErC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC,kBAAkB;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kBAAkB;IAClB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACnC,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA+CtD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Checkbox } from "./Checkbox";
|
|
3
|
+
import "../styles/checkboxgroup.css";
|
|
4
|
+
/**
|
|
5
|
+
* CheckboxGroup component for multiple selections
|
|
6
|
+
*/
|
|
7
|
+
export const CheckboxGroup = ({ options, values = [], onChange, label, error, description, required, disabled = false, layout = 'horizontal', className = "", }) => {
|
|
8
|
+
const handleChange = (optionValue, checked) => {
|
|
9
|
+
if (!disabled && onChange) {
|
|
10
|
+
const newValues = checked
|
|
11
|
+
? [...values, optionValue]
|
|
12
|
+
: values.filter((v) => v !== optionValue);
|
|
13
|
+
onChange(newValues);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
return (_jsxs("div", { className: `checkboxgroup-container ${className}`, children: [label && (_jsxs("div", { className: "checkboxgroup-label", children: [label, required && _jsx("span", { className: "checkboxgroup-required", children: " *" })] })), _jsx("div", { className: `checkboxgroup-options checkboxgroup-options--${layout}`, children: options.map((option, index) => (_jsx(Checkbox, { label: option.label, checked: values.includes(option.value), disabled: disabled || option.disabled, onChange: (checked) => handleChange(option.value, checked), className: index > 0 ? 'checkbox--spaced' : '' }, option.value))) }), description && !error && (_jsx("div", { className: "checkboxgroup-description", children: description })), error && _jsx("div", { className: "checkboxgroup-error", children: error })] }));
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=CheckboxGroup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CheckboxGroup.js","sourceRoot":"","sources":["../../src/components/CheckboxGroup.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,6BAA6B,CAAC;AA+BrC;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAiC,CAAC,EAC1D,OAAO,EACP,MAAM,GAAG,EAAE,EACX,QAAQ,EACR,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,MAAM,GAAG,YAAY,EACrB,SAAS,GAAG,EAAE,GACf,EAAE,EAAE;IACH,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAE,OAAgB,EAAE,EAAE;QAC7D,IAAI,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,OAAO;gBACvB,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,WAAW,CAAC;gBAC1B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC;YAC5C,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAE,2BAA2B,SAAS,EAAE,aACnD,KAAK,IAAI,CACR,eAAK,SAAS,EAAC,qBAAqB,aACjC,KAAK,EACL,QAAQ,IAAI,eAAM,SAAS,EAAC,wBAAwB,mBAAU,IAC3D,CACP,EACD,cAAK,SAAS,EAAE,gDAAgD,MAAM,EAAE,YACrE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAC9B,KAAC,QAAQ,IAEP,KAAK,EAAE,MAAM,CAAC,KAAK,EACnB,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EACtC,QAAQ,EAAE,QAAQ,IAAI,MAAM,CAAC,QAAQ,EACrC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAC1D,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,IALzC,MAAM,CAAC,KAAK,CAMjB,CACH,CAAC,GACE,EACL,WAAW,IAAI,CAAC,KAAK,IAAI,CACxB,cAAK,SAAS,EAAC,2BAA2B,YAAE,WAAW,GAAO,CAC/D,EACA,KAAK,IAAI,cAAK,SAAS,EAAC,qBAAqB,YAAE,KAAK,GAAO,IACxD,CACP,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
export interface MenuItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
icon?: ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
checked?: boolean;
|
|
8
|
+
divider?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface MenuProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
11
|
+
/** Menu trigger button content/label */
|
|
12
|
+
trigger: ReactNode;
|
|
13
|
+
/** Menu items to display */
|
|
14
|
+
items: MenuItem[];
|
|
15
|
+
/** Called when a menu item is clicked */
|
|
16
|
+
onItemClick?: (itemId: string) => void;
|
|
17
|
+
/** Trigger button icon */
|
|
18
|
+
triggerIcon?: ReactNode;
|
|
19
|
+
/** Custom trigger button class name */
|
|
20
|
+
triggerClassName?: string;
|
|
21
|
+
/** Disable the menu trigger */
|
|
22
|
+
triggerDisabled?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Menu component for displaying dropdown menus
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Basic usage
|
|
29
|
+
* const items = [
|
|
30
|
+
* { id: 'edit', label: 'Edit' },
|
|
31
|
+
* { id: 'delete', label: 'Delete' },
|
|
32
|
+
* ];
|
|
33
|
+
* <Menu trigger="Actions" items={items} onItemClick={(id) => handleAction(id)} />
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // With icons and divider
|
|
37
|
+
* const items = [
|
|
38
|
+
* { id: 'edit', label: 'Edit', icon: <EditIcon /> },
|
|
39
|
+
* { divider: true },
|
|
40
|
+
* { id: 'delete', label: 'Delete', icon: <TrashIcon /> },
|
|
41
|
+
* ];
|
|
42
|
+
* <Menu trigger={<MoreIcon />} items={items} />
|
|
43
|
+
*/
|
|
44
|
+
export declare const Menu: React.ForwardRefExoticComponent<MenuProps & React.RefAttributes<HTMLDivElement>>;
|
|
45
|
+
//# sourceMappingURL=Menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../../src/components/Menu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAA+B,MAAM,OAAO,CAAC;AAEtE,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACrE,wCAAwC;IACxC,OAAO,EAAE,SAAS,CAAC;IAEnB,4BAA4B;IAC5B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAElB,yCAAyC;IACzC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAEvC,0BAA0B;IAC1B,WAAW,CAAC,EAAE,SAAS,CAAC;IAExB,uCAAuC;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,+BAA+B;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,IAAI,kFA2HhB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Menu component for displaying dropdown menus
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Basic usage
|
|
8
|
+
* const items = [
|
|
9
|
+
* { id: 'edit', label: 'Edit' },
|
|
10
|
+
* { id: 'delete', label: 'Delete' },
|
|
11
|
+
* ];
|
|
12
|
+
* <Menu trigger="Actions" items={items} onItemClick={(id) => handleAction(id)} />
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // With icons and divider
|
|
16
|
+
* const items = [
|
|
17
|
+
* { id: 'edit', label: 'Edit', icon: <EditIcon /> },
|
|
18
|
+
* { divider: true },
|
|
19
|
+
* { id: 'delete', label: 'Delete', icon: <TrashIcon /> },
|
|
20
|
+
* ];
|
|
21
|
+
* <Menu trigger={<MoreIcon />} items={items} />
|
|
22
|
+
*/
|
|
23
|
+
export const Menu = React.forwardRef(({ trigger, items, onItemClick, triggerIcon, triggerClassName = "", triggerDisabled = false, className = "", ...rest }, ref) => {
|
|
24
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
25
|
+
const menuRef = useRef(null);
|
|
26
|
+
const triggerRef = useRef(null);
|
|
27
|
+
// Close menu when clicking outside
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const handleClickOutside = (event) => {
|
|
30
|
+
if (menuRef.current &&
|
|
31
|
+
!menuRef.current.contains(event.target) &&
|
|
32
|
+
!triggerRef.current?.contains(event.target)) {
|
|
33
|
+
setIsOpen(false);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
if (isOpen) {
|
|
37
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
38
|
+
return () => {
|
|
39
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}, [isOpen]);
|
|
43
|
+
const handleItemClick = (itemId) => {
|
|
44
|
+
onItemClick?.(itemId);
|
|
45
|
+
setIsOpen(false);
|
|
46
|
+
};
|
|
47
|
+
const hasCheckedItems = items.some((item) => item.checked);
|
|
48
|
+
return (_jsxs("div", { ref: ref, className: `menu ${className}`.trim(), ...rest, children: [_jsxs("button", { ref: triggerRef, className: `menu__trigger ${isOpen ? "menu__trigger--open" : ""} ${triggerClassName}`.trim(), onClick: () => setIsOpen(!isOpen), disabled: triggerDisabled, "aria-haspopup": "true", "aria-expanded": isOpen, children: [triggerIcon && _jsx("span", { className: "menu__trigger-icon", children: triggerIcon }), trigger, !triggerIcon && (_jsx("svg", { className: "menu__trigger-arrow", width: "1em", height: "1em", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("polyline", { points: "6 9 12 15 18 9" }) }))] }), _jsx("div", { className: `menu__content ${isOpen ? "menu__content--open" : ""}`, role: "menu", children: items.map((item, index) => {
|
|
49
|
+
if (item.divider) {
|
|
50
|
+
return (_jsx("div", { className: "menu__divider", role: "separator" }, `divider-${index}`));
|
|
51
|
+
}
|
|
52
|
+
return (_jsxs("button", { className: `menu__item ${item.checked ? "menu__item--checked" : ""}`.trim(), onClick: () => handleItemClick(item.id), disabled: item.disabled, role: "menuitem", "aria-disabled": item.disabled, children: [item.icon && (_jsx("span", { className: "menu__item-icon", children: item.icon })), _jsx("span", { children: item.label }), hasCheckedItems && item.checked && (_jsx("span", { className: "menu__item-checkmark", role: "presentation", children: "\u2713" }))] }, item.id));
|
|
53
|
+
}) })] }));
|
|
54
|
+
});
|
|
55
|
+
Menu.displayName = "Menu";
|
|
56
|
+
//# sourceMappingURL=Menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menu.js","sourceRoot":"","sources":["../../src/components/Menu.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "../styles/radiogroup.css";
|
|
3
|
+
export interface RadioOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: React.ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface RadioGroupProps {
|
|
9
|
+
/** List of radio options */
|
|
10
|
+
options: RadioOption[];
|
|
11
|
+
/** Currently selected value */
|
|
12
|
+
value?: string;
|
|
13
|
+
/** Called when selection changes */
|
|
14
|
+
onChange?: (value: string) => void;
|
|
15
|
+
/** Group label */
|
|
16
|
+
label?: React.ReactNode;
|
|
17
|
+
/** Group name (for form) */
|
|
18
|
+
name?: string;
|
|
19
|
+
/** Error message */
|
|
20
|
+
error?: string | null;
|
|
21
|
+
/** Helper text */
|
|
22
|
+
description?: React.ReactNode;
|
|
23
|
+
/** Whether the group is required */
|
|
24
|
+
required?: boolean;
|
|
25
|
+
/** Whether all options are disabled */
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
/** Layout direction for options */
|
|
28
|
+
layout?: 'horizontal' | 'vertical';
|
|
29
|
+
/** Additional class name */
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* RadioGroup component matching OpenLearning UI design
|
|
34
|
+
*/
|
|
35
|
+
export declare const RadioGroup: React.FC<RadioGroupProps>;
|
|
36
|
+
//# sourceMappingURL=RadioGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioGroup.d.ts","sourceRoot":"","sources":["../../src/components/RadioGroup.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,0BAA0B,CAAC;AAElC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,kBAAkB;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kBAAkB;IAClB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACnC,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA4DhD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/radiogroup.css";
|
|
3
|
+
/**
|
|
4
|
+
* RadioGroup component matching OpenLearning UI design
|
|
5
|
+
*/
|
|
6
|
+
export const RadioGroup = ({ options, value, onChange, label, name, error, description, required, disabled = false, layout = 'horizontal', className = "", }) => {
|
|
7
|
+
const handleChange = (optionValue) => {
|
|
8
|
+
if (!disabled && onChange) {
|
|
9
|
+
onChange(optionValue);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
return (_jsxs("div", { className: `radiogroup-container ${className}`, children: [label && (_jsxs("div", { className: "radiogroup-label", children: [label, required && _jsx("span", { className: "radiogroup-required", children: " *" })] })), _jsx("div", { className: `radiogroup-options radiogroup-options--${layout}`, children: options.map((option, index) => {
|
|
13
|
+
const isDisabled = disabled || option.disabled;
|
|
14
|
+
const isChecked = value === option.value;
|
|
15
|
+
return (_jsxs("label", { className: `radiogroup-option ${isDisabled ? 'radiogroup-option--disabled' : ''} ${index > 0 ? 'radiogroup-option--spaced' : ''}`, children: [_jsx("input", { type: "radio", className: "radiogroup-input", name: name, value: option.value, checked: isChecked, disabled: isDisabled, onChange: () => handleChange(option.value) }), _jsx("div", { className: "radiogroup-radio-container", children: _jsx("div", { className: "radiogroup-radio" }) }), _jsx("span", { className: "radiogroup-option-label", children: option.label })] }, option.value));
|
|
16
|
+
}) }), description && !error && (_jsx("div", { className: "radiogroup-description", children: description })), error && _jsx("div", { className: "radiogroup-error", children: error })] }));
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=RadioGroup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioGroup.js","sourceRoot":"","sources":["../../src/components/RadioGroup.tsx"],"names":[],"mappings":";AACA,OAAO,0BAA0B,CAAC;AAiClC;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAA8B,CAAC,EACpD,OAAO,EACP,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,KAAK,EACL,WAAW,EACX,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,MAAM,GAAG,YAAY,EACrB,SAAS,GAAG,EAAE,GACf,EAAE,EAAE;IACH,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAE,EAAE;QAC3C,IAAI,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC;YAC1B,QAAQ,CAAC,WAAW,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAE,wBAAwB,SAAS,EAAE,aAChD,KAAK,IAAI,CACR,eAAK,SAAS,EAAC,kBAAkB,aAC9B,KAAK,EACL,QAAQ,IAAI,eAAM,SAAS,EAAC,qBAAqB,mBAAU,IACxD,CACP,EACD,cAAK,SAAS,EAAE,0CAA0C,MAAM,EAAE,YAC/D,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;oBAC7B,MAAM,UAAU,GAAG,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC;oBAC/C,MAAM,SAAS,GAAG,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC;oBAEzC,OAAO,CACL,iBAEE,SAAS,EAAE,qBAAqB,UAAU,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,EAAE,EAAE,aAEjI,gBACE,IAAI,EAAC,OAAO,EACZ,SAAS,EAAC,kBAAkB,EAC5B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,CAAC,KAAK,EACnB,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,UAAU,EACpB,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,GAC1C,EACF,cAAK,SAAS,EAAC,4BAA4B,YACzC,cAAK,SAAS,EAAC,kBAAkB,GAAO,GACpC,EACN,eAAM,SAAS,EAAC,yBAAyB,YAAE,MAAM,CAAC,KAAK,GAAQ,KAf1D,MAAM,CAAC,KAAK,CAgBX,CACT,CAAC;gBACJ,CAAC,CAAC,GACE,EACL,WAAW,IAAI,CAAC,KAAK,IAAI,CACxB,cAAK,SAAS,EAAC,wBAAwB,YAAE,WAAW,GAAO,CAC5D,EACA,KAAK,IAAI,cAAK,SAAS,EAAC,kBAAkB,YAAE,KAAK,GAAO,IACrD,CACP,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "../styles/spinner.css";
|
|
3
|
+
export interface SpinnerProps {
|
|
4
|
+
/** Optional label to show next to spinner */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Color of the spinner (defaults to currentColor) */
|
|
7
|
+
color?: string;
|
|
8
|
+
/** Font size of the spinner icon */
|
|
9
|
+
fontSize?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Loading spinner component matching OpenLearning UI design
|
|
13
|
+
*/
|
|
14
|
+
export declare const Spinner: React.FC<SpinnerProps>;
|
|
15
|
+
//# sourceMappingURL=Spinner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Spinner.d.ts","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,uBAAuB,CAAC;AAE/B,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAwB1C,CAAC"}
|