mal-ui 0.1.3
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 +244 -0
- package/dist/carousel/extensions.d.ts +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +424 -0
- package/dist/carousel/index.js.map +15 -0
- package/dist/charts/extensions.d.ts +1 -0
- package/dist/charts/index.d.ts +2 -0
- package/dist/charts/index.js +3499 -0
- package/dist/charts/index.js.map +53 -0
- package/dist/chunks/index-3ffbn0gc.js +29976 -0
- package/dist/chunks/index-3ffbn0gc.js.map +637 -0
- package/dist/chunks/index-6b5mapr4.js +2 -0
- package/dist/chunks/index-6b5mapr4.js.map +9 -0
- package/dist/chunks/index-6tm93gww.js +38 -0
- package/dist/chunks/index-6tm93gww.js.map +10 -0
- package/dist/chunks/index-cg10ezdx.js +2 -0
- package/dist/chunks/index-cg10ezdx.js.map +9 -0
- package/dist/chunks/index-mc01z7m9.js +48 -0
- package/dist/chunks/index-mc01z7m9.js.map +9 -0
- package/dist/chunks/index-rz8zh7g4.js +5015 -0
- package/dist/chunks/index-rz8zh7g4.js.map +113 -0
- package/dist/chunks/index-syjq3515.js +4893 -0
- package/dist/chunks/index-syjq3515.js.map +101 -0
- package/dist/chunks/index-w9ekv5h5.js +733 -0
- package/dist/chunks/index-w9ekv5h5.js.map +17 -0
- package/dist/code-highlight/extensions.d.ts +1 -0
- package/dist/code-highlight/index.d.ts +2 -0
- package/dist/code-highlight/index.js +1685 -0
- package/dist/code-highlight/index.js.map +25 -0
- package/dist/core/extensions.d.ts +2 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +844 -0
- package/dist/core/index.js.map +9 -0
- package/dist/dates/extensions.d.ts +1 -0
- package/dist/dates/index.d.ts +2 -0
- package/dist/dates/index.js +107 -0
- package/dist/dates/index.js.map +9 -0
- package/dist/dropzone/extensions.d.ts +1 -0
- package/dist/dropzone/index.d.ts +2 -0
- package/dist/dropzone/index.js +2681 -0
- package/dist/dropzone/index.js.map +21 -0
- package/dist/form/extensions.d.ts +1 -0
- package/dist/form/index.d.ts +2 -0
- package/dist/form/index.js +1605 -0
- package/dist/form/index.js.map +51 -0
- package/dist/hooks/extensions.d.ts +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +214 -0
- package/dist/hooks/index.js.map +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1052 -0
- package/dist/index.js.map +9 -0
- package/dist/modals/extensions.d.ts +1 -0
- package/dist/modals/index.d.ts +2 -0
- package/dist/modals/index.js +387 -0
- package/dist/modals/index.js.map +15 -0
- package/dist/notifications/extensions.d.ts +1 -0
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.js +1122 -0
- package/dist/notifications/index.js.map +28 -0
- package/dist/nprogress/extensions.d.ts +1 -0
- package/dist/nprogress/index.d.ts +2 -0
- package/dist/nprogress/index.js +207 -0
- package/dist/nprogress/index.js.map +12 -0
- package/dist/schedule/extensions.d.ts +1 -0
- package/dist/schedule/index.d.ts +2 -0
- package/dist/schedule/index.js +8569 -0
- package/dist/schedule/index.js.map +135 -0
- package/dist/spotlight/extensions.d.ts +1 -0
- package/dist/spotlight/index.d.ts +2 -0
- package/dist/spotlight/index.js +666 -0
- package/dist/spotlight/index.js.map +24 -0
- package/dist/styles.css +1 -0
- package/dist/theme/index.d.ts +13 -0
- package/dist/theme/index.js +202 -0
- package/dist/theme/index.js.map +11 -0
- package/dist/theme/tokens.d.ts +65 -0
- package/dist/tiptap/extensions.d.ts +1 -0
- package/dist/tiptap/index.d.ts +2 -0
- package/dist/tiptap/index.js +1549 -0
- package/dist/tiptap/index.js.map +25 -0
- package/package.json +163 -0
package/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# mal-ui
|
|
2
|
+
|
|
3
|
+
A React component library built on [Mantine v9](https://mantine.dev), providing a complete design system for MAL Devs projects — components, hooks, form utilities, charts, a rich-text editor, file uploads, and more, all pre-wired to the MAL brand theme.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Quick Start](#quick-start)
|
|
11
|
+
- [Subpath Imports](#subpath-imports)
|
|
12
|
+
- [Peer Dependencies](#peer-dependencies)
|
|
13
|
+
- [Theme](#theme)
|
|
14
|
+
- [Available Subpaths](#available-subpaths)
|
|
15
|
+
- [Development](#development)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# npm
|
|
23
|
+
npm install mal-ui
|
|
24
|
+
|
|
25
|
+
# bun
|
|
26
|
+
bun add mal-ui
|
|
27
|
+
|
|
28
|
+
# yarn
|
|
29
|
+
yarn add mal-ui
|
|
30
|
+
|
|
31
|
+
# pnpm
|
|
32
|
+
pnpm add mal-ui
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### 1 — Wrap your app with `MALUIProvider`
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// app/providers.tsx (Next.js App Router example)
|
|
43
|
+
'use client';
|
|
44
|
+
|
|
45
|
+
import { MALUIProvider } from 'mal-ui/core';
|
|
46
|
+
import { malTheme } from 'mal-ui/theme';
|
|
47
|
+
import 'mal-ui/styles.css';
|
|
48
|
+
|
|
49
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
50
|
+
return (
|
|
51
|
+
<MALUIProvider theme={malTheme}>
|
|
52
|
+
{children}
|
|
53
|
+
</MALUIProvider>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2 — Use components
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { Button, TextInput, Card } from 'mal-ui/core';
|
|
62
|
+
|
|
63
|
+
export function LoginCard() {
|
|
64
|
+
return (
|
|
65
|
+
<Card>
|
|
66
|
+
<TextInput label="Email" placeholder="you@example.com" />
|
|
67
|
+
<Button mt="sm" fullWidth>
|
|
68
|
+
Sign in
|
|
69
|
+
</Button>
|
|
70
|
+
</Card>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Subpath Imports
|
|
78
|
+
|
|
79
|
+
Import only what you need — each subpath is independently tree-shakeable:
|
|
80
|
+
|
|
81
|
+
| Subpath | What it contains |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `mal-ui` | Re-exports `core` + `hooks` (most common components and hooks) |
|
|
84
|
+
| `mal-ui/core` | All Mantine core components + MALUI branded aliases |
|
|
85
|
+
| `mal-ui/hooks` | All Mantine hooks + MALUI branded aliases |
|
|
86
|
+
| `mal-ui/form` | `useForm`, form fields, validation helpers |
|
|
87
|
+
| `mal-ui/charts` | Line, Bar, Area, Donut, Pie, Radar, Scatter, Bubble, Sparkline |
|
|
88
|
+
| `mal-ui/notifications` | Notification system (`showNotification`, `updateNotification`, etc.) |
|
|
89
|
+
| `mal-ui/modals` | Modal manager (`openConfirmModal`, `openModal`, etc.) |
|
|
90
|
+
| `mal-ui/spotlight` | `⌘K` / `Ctrl+K` spotlight search |
|
|
91
|
+
| `mal-ui/code-highlight` | Syntax-highlighted code blocks (powered by Shiki) |
|
|
92
|
+
| `mal-ui/tiptap` | Rich-text editor (Tiptap + Mantine toolbar) |
|
|
93
|
+
| `mal-ui/dropzone` | File upload dropzone |
|
|
94
|
+
| `mal-ui/carousel` | Embla-powered carousel |
|
|
95
|
+
| `mal-ui/nprogress` | Top-of-page progress bar |
|
|
96
|
+
| `mal-ui/dates` | Date pickers, calendars, time pickers |
|
|
97
|
+
| `mal-ui/schedule` | Weekly/day schedule view |
|
|
98
|
+
| `mal-ui/theme` | `malTheme` object + raw design tokens |
|
|
99
|
+
| `mal-ui/styles.css` | Required global CSS (import once at your app root) |
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Peer Dependencies
|
|
104
|
+
|
|
105
|
+
Install the peer dependencies you actually use. Only `react` and `react-dom` are always required; the rest are **optional** and only needed if you import the corresponding subpath.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Always required
|
|
109
|
+
npm install react react-dom
|
|
110
|
+
|
|
111
|
+
# Required for mal-ui/charts
|
|
112
|
+
npm install recharts
|
|
113
|
+
|
|
114
|
+
# Required for mal-ui/dates, mal-ui/schedule
|
|
115
|
+
npm install dayjs
|
|
116
|
+
|
|
117
|
+
# Required for mal-ui/carousel
|
|
118
|
+
npm install embla-carousel-react
|
|
119
|
+
|
|
120
|
+
# Required for mal-ui/tiptap
|
|
121
|
+
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit @tiptap/extension-link
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Theme
|
|
127
|
+
|
|
128
|
+
`mal-ui/theme` exports a ready-to-use Mantine theme override pre-configured with:
|
|
129
|
+
|
|
130
|
+
- **Brand colors** — `mal-brand` (purple) and `mal-secondary` palettes
|
|
131
|
+
- **Typography** — System font stack, tuned heading sizes and weights
|
|
132
|
+
- **Spacing & Radius** — Design-token-based scale
|
|
133
|
+
- **Shadows** — Consistent elevation scale
|
|
134
|
+
- **Component defaults** — Buttons, inputs, cards, and modals all default to `radius="md"`
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { malTheme } from 'mal-ui/theme';
|
|
138
|
+
// malTheme is a MantineThemeOverride — pass it to MantineProvider
|
|
139
|
+
|
|
140
|
+
// Raw tokens are also exported if you need them directly:
|
|
141
|
+
import { malColors, malSpacingTokens, malBreakpoints } from 'mal-ui/theme';
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Both Mantine names (`MantineProvider`, `useMantineTheme`) and MALUI aliases (`MALUIProvider`, `useMALUITheme`) are exported from `mal-ui/core` — use whichever you prefer.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Available Subpaths
|
|
149
|
+
|
|
150
|
+
### Core + Hooks
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { Button, TextInput, Select, Modal, Tabs } from 'mal-ui/core';
|
|
154
|
+
import { useDisclosure, useLocalStorage, useMediaQuery } from 'mal-ui/hooks';
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Form
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { useForm, isEmail, isNotEmpty } from 'mal-ui/form';
|
|
161
|
+
|
|
162
|
+
const form = useForm({
|
|
163
|
+
initialValues: { email: '', name: '' },
|
|
164
|
+
validate: {
|
|
165
|
+
email: isEmail('Invalid email'),
|
|
166
|
+
name: isNotEmpty('Name is required'),
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Charts
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import { LineChart, BarChart, DonutChart } from 'mal-ui/charts';
|
|
175
|
+
|
|
176
|
+
<LineChart
|
|
177
|
+
h={300}
|
|
178
|
+
data={data}
|
|
179
|
+
dataKey="date"
|
|
180
|
+
series={[{ name: 'Revenue', color: 'mal-brand.5' }]}
|
|
181
|
+
/>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Notifications
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { notifications } from 'mal-ui/notifications';
|
|
188
|
+
// In your layout, render: <Notifications />
|
|
189
|
+
|
|
190
|
+
notifications.show({ title: 'Done!', message: 'Upload complete.' });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Modals
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { modals } from 'mal-ui/modals';
|
|
197
|
+
// In your layout, render: <ModalsProvider>
|
|
198
|
+
|
|
199
|
+
modals.openConfirmModal({
|
|
200
|
+
title: 'Delete item',
|
|
201
|
+
children: <Text>Are you sure?</Text>,
|
|
202
|
+
onConfirm: () => deleteItem(),
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Development
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Install dependencies
|
|
212
|
+
bun install
|
|
213
|
+
|
|
214
|
+
# Build the library
|
|
215
|
+
bun run build
|
|
216
|
+
|
|
217
|
+
# Type-check
|
|
218
|
+
bun run typecheck
|
|
219
|
+
|
|
220
|
+
# Run tests
|
|
221
|
+
bun test
|
|
222
|
+
|
|
223
|
+
# Lint & format
|
|
224
|
+
bun run lint
|
|
225
|
+
bun run format
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Running the demo app
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# 1. Build the library
|
|
232
|
+
bun run build
|
|
233
|
+
|
|
234
|
+
# 2. Start the Next.js demo
|
|
235
|
+
cd examples/nextjs-demo
|
|
236
|
+
bun install
|
|
237
|
+
bun run dev
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Open [http://localhost:3000](http://localhost:3000) — every subpath has its own demo route.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
> Built with [Mantine](https://mantine.dev) · Bundled with [Bun](https://bun.sh)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
AccordionChevron,
|
|
4
|
+
Box,
|
|
5
|
+
InlineStyles,
|
|
6
|
+
UnstyledButton,
|
|
7
|
+
VisuallyHidden,
|
|
8
|
+
createSafeContext,
|
|
9
|
+
createVarsResolver,
|
|
10
|
+
factory,
|
|
11
|
+
filterProps,
|
|
12
|
+
getBaseValue,
|
|
13
|
+
getSortedBreakpoints,
|
|
14
|
+
getSpacing,
|
|
15
|
+
keys,
|
|
16
|
+
px,
|
|
17
|
+
rem,
|
|
18
|
+
useDirection,
|
|
19
|
+
useMantineTheme,
|
|
20
|
+
useProps,
|
|
21
|
+
useRandomClassName,
|
|
22
|
+
useStyles
|
|
23
|
+
} from "../chunks/index-3ffbn0gc.js";
|
|
24
|
+
import {
|
|
25
|
+
clamp,
|
|
26
|
+
useId$1
|
|
27
|
+
} from "../chunks/index-syjq3515.js";
|
|
28
|
+
import"../chunks/index-mc01z7m9.js";
|
|
29
|
+
|
|
30
|
+
// node_modules/@mantine/carousel/esm/Carousel.context.mjs
|
|
31
|
+
|
|
32
|
+
var [CarouselProvider, useCarouselContext] = createSafeContext("Carousel component was not found in tree");
|
|
33
|
+
|
|
34
|
+
// node_modules/@mantine/carousel/esm/Carousel.module.mjs
|
|
35
|
+
|
|
36
|
+
var Carousel_module_default = {
|
|
37
|
+
root: "m_17884d0f",
|
|
38
|
+
viewport: "m_a2dae653",
|
|
39
|
+
container: "m_fcd81474",
|
|
40
|
+
controls: "m_39bc3463",
|
|
41
|
+
control: "m_64f58e10",
|
|
42
|
+
indicators: "m_71ea3ab1",
|
|
43
|
+
indicator: "m_eae68602",
|
|
44
|
+
slide: "m_d98df724"
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// node_modules/@mantine/carousel/esm/CarouselSlide/CarouselSlide.mjs
|
|
48
|
+
import { jsx } from "react/jsx-runtime";
|
|
49
|
+
|
|
50
|
+
var CarouselSlide = factory((props) => {
|
|
51
|
+
const { classNames, className, style, styles, vars, mod, ...others } = useProps("CarouselSlide", null, props);
|
|
52
|
+
const ctx = useCarouselContext();
|
|
53
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
54
|
+
mod: [{ orientation: ctx.orientation }, mod],
|
|
55
|
+
role: "group",
|
|
56
|
+
"aria-roledescription": "slide",
|
|
57
|
+
"aria-label": "Carousel slide",
|
|
58
|
+
...ctx.getStyles("slide", {
|
|
59
|
+
className,
|
|
60
|
+
style,
|
|
61
|
+
classNames,
|
|
62
|
+
styles
|
|
63
|
+
}),
|
|
64
|
+
...others
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
CarouselSlide.classes = Carousel_module_default;
|
|
68
|
+
CarouselSlide.displayName = "@mantine/carousel/CarouselSlide";
|
|
69
|
+
|
|
70
|
+
// node_modules/@mantine/carousel/esm/CarouselVariables/CarouselVariables.mjs
|
|
71
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
72
|
+
|
|
73
|
+
function CarouselVariables({ slideGap, slideSize, selector }) {
|
|
74
|
+
const theme = useMantineTheme();
|
|
75
|
+
const baseStyles = filterProps({
|
|
76
|
+
"--carousel-slide-gap": getSpacing(getBaseValue(slideGap)),
|
|
77
|
+
"--carousel-slide-size": rem(getBaseValue(slideSize))
|
|
78
|
+
});
|
|
79
|
+
const queries = keys(theme.breakpoints).reduce((acc, breakpoint) => {
|
|
80
|
+
if (!acc[breakpoint])
|
|
81
|
+
acc[breakpoint] = {};
|
|
82
|
+
if (typeof slideGap === "object" && slideGap[breakpoint] !== undefined)
|
|
83
|
+
acc[breakpoint]["--carousel-slide-gap"] = getSpacing(slideGap[breakpoint]);
|
|
84
|
+
if (typeof slideSize === "object" && slideSize[breakpoint] !== undefined)
|
|
85
|
+
acc[breakpoint]["--carousel-slide-size"] = getSpacing(slideSize[breakpoint]);
|
|
86
|
+
return acc;
|
|
87
|
+
}, {});
|
|
88
|
+
return /* @__PURE__ */ jsx2(InlineStyles, {
|
|
89
|
+
styles: baseStyles,
|
|
90
|
+
media: getSortedBreakpoints(keys(queries), theme.breakpoints).filter((breakpoint) => keys(queries[breakpoint.value]).length > 0).map((breakpoint) => ({
|
|
91
|
+
query: `(min-width: ${theme.breakpoints[breakpoint.value]})`,
|
|
92
|
+
styles: queries[breakpoint.value]
|
|
93
|
+
})),
|
|
94
|
+
selector
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function getBreakpoints(values) {
|
|
98
|
+
if (typeof values === "object" && values !== null)
|
|
99
|
+
return keys(values);
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
function sortBreakpoints(breakpoints) {
|
|
103
|
+
return breakpoints.sort((a, b) => px(a) - px(b));
|
|
104
|
+
}
|
|
105
|
+
function getUniqueBreakpoints({ slideGap, slideSize }) {
|
|
106
|
+
return sortBreakpoints(Array.from(new Set([...getBreakpoints(slideGap), ...getBreakpoints(slideSize)])));
|
|
107
|
+
}
|
|
108
|
+
function CarouselContainerVariables({ slideGap, slideSize, selector }) {
|
|
109
|
+
const baseStyles = filterProps({
|
|
110
|
+
"--carousel-slide-gap": getSpacing(getBaseValue(slideGap)),
|
|
111
|
+
"--carousel-slide-size": rem(getBaseValue(slideSize))
|
|
112
|
+
});
|
|
113
|
+
const queries = getUniqueBreakpoints({
|
|
114
|
+
slideGap,
|
|
115
|
+
slideSize
|
|
116
|
+
}).reduce((acc, breakpoint) => {
|
|
117
|
+
if (!acc[breakpoint])
|
|
118
|
+
acc[breakpoint] = {};
|
|
119
|
+
if (typeof slideGap === "object" && slideGap[breakpoint] !== undefined)
|
|
120
|
+
acc[breakpoint]["--carousel-slide-gap"] = getSpacing(slideGap[breakpoint]);
|
|
121
|
+
if (typeof slideSize === "object" && slideSize[breakpoint] !== undefined)
|
|
122
|
+
acc[breakpoint]["--carousel-slide-size"] = getSpacing(slideSize[breakpoint]);
|
|
123
|
+
return acc;
|
|
124
|
+
}, {});
|
|
125
|
+
return /* @__PURE__ */ jsx2(InlineStyles, {
|
|
126
|
+
styles: baseStyles,
|
|
127
|
+
container: Object.keys(queries).map((breakpoint) => ({
|
|
128
|
+
query: `carousel (min-width: ${breakpoint})`,
|
|
129
|
+
styles: queries[breakpoint]
|
|
130
|
+
})),
|
|
131
|
+
selector
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// node_modules/@mantine/carousel/esm/get-chevron-rotation.mjs
|
|
136
|
+
|
|
137
|
+
function getChevronRotation({ dir, orientation, direction }) {
|
|
138
|
+
if (direction === "previous")
|
|
139
|
+
return orientation === "horizontal" ? 90 * (dir === "ltr" ? 1 : -1) : -180;
|
|
140
|
+
return orientation === "horizontal" ? 90 * (dir === "ltr" ? -1 : 1) : 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// node_modules/@mantine/carousel/esm/Carousel.mjs
|
|
144
|
+
import { Children, createElement, useCallback, useEffect, useState } from "react";
|
|
145
|
+
import useEmblaCarousel from "embla-carousel-react";
|
|
146
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
147
|
+
|
|
148
|
+
var defaultProps = {
|
|
149
|
+
controlSize: 26,
|
|
150
|
+
controlsOffset: "sm",
|
|
151
|
+
slideSize: "100%",
|
|
152
|
+
slideGap: 0,
|
|
153
|
+
orientation: "horizontal",
|
|
154
|
+
includeGapInSize: true,
|
|
155
|
+
initialSlide: 0,
|
|
156
|
+
withControls: true,
|
|
157
|
+
withIndicators: false,
|
|
158
|
+
withKeyboardEvents: true,
|
|
159
|
+
type: "media"
|
|
160
|
+
};
|
|
161
|
+
var defaultEmblaOptions = {
|
|
162
|
+
align: "center",
|
|
163
|
+
loop: false,
|
|
164
|
+
slidesToScroll: 1,
|
|
165
|
+
dragFree: false,
|
|
166
|
+
inViewThreshold: 0,
|
|
167
|
+
skipSnaps: false,
|
|
168
|
+
containScroll: "trimSnaps"
|
|
169
|
+
};
|
|
170
|
+
var varsResolver = createVarsResolver((_, { height, controlSize, controlsOffset }) => ({ root: {
|
|
171
|
+
"--carousel-height": rem(height),
|
|
172
|
+
"--carousel-control-size": rem(controlSize),
|
|
173
|
+
"--carousel-controls-offset": getSpacing(controlsOffset)
|
|
174
|
+
} }));
|
|
175
|
+
var Carousel = factory((_props) => {
|
|
176
|
+
const props = useProps("Carousel", defaultProps, _props);
|
|
177
|
+
const { classNames, className, style, styles, unstyled, vars, children, getEmblaApi, onNextSlide, onPreviousSlide, onSlideChange, nextControlProps, previousControlProps, controlSize, controlsOffset, slideSize, slideGap, orientation, height, includeGapInSize, draggable, initialSlide, withControls, withIndicators, plugins, nextControlIcon, previousControlIcon, withKeyboardEvents, mod, type, emblaOptions, attributes, getIndicatorProps, id, ...others } = props;
|
|
178
|
+
const getStyles = useStyles({
|
|
179
|
+
name: "Carousel",
|
|
180
|
+
classes: Carousel_module_default,
|
|
181
|
+
props,
|
|
182
|
+
className,
|
|
183
|
+
style,
|
|
184
|
+
classNames,
|
|
185
|
+
styles,
|
|
186
|
+
unstyled,
|
|
187
|
+
attributes,
|
|
188
|
+
vars,
|
|
189
|
+
varsResolver
|
|
190
|
+
});
|
|
191
|
+
const _id = useId$1(id);
|
|
192
|
+
const responsiveClassName = useRandomClassName();
|
|
193
|
+
const { dir } = useDirection();
|
|
194
|
+
const [emblaRefElement, embla] = useEmblaCarousel({
|
|
195
|
+
axis: orientation === "horizontal" ? "x" : "y",
|
|
196
|
+
direction: orientation === "horizontal" ? dir : undefined,
|
|
197
|
+
startIndex: initialSlide,
|
|
198
|
+
...defaultEmblaOptions,
|
|
199
|
+
...emblaOptions
|
|
200
|
+
}, plugins);
|
|
201
|
+
const [selected, setSelected] = useState(0);
|
|
202
|
+
const [slidesCount, setSlidesCount] = useState(0);
|
|
203
|
+
const handleScroll = useCallback((index) => embla && embla.scrollTo(index), [embla]);
|
|
204
|
+
const handleSelect = useCallback(() => {
|
|
205
|
+
if (!embla)
|
|
206
|
+
return;
|
|
207
|
+
const slide = embla.selectedScrollSnap();
|
|
208
|
+
setSelected(slide);
|
|
209
|
+
slide !== selected && onSlideChange?.(slide);
|
|
210
|
+
}, [
|
|
211
|
+
embla,
|
|
212
|
+
setSelected,
|
|
213
|
+
onSlideChange,
|
|
214
|
+
selected
|
|
215
|
+
]);
|
|
216
|
+
const handlePrevious = useCallback(() => {
|
|
217
|
+
embla?.scrollPrev();
|
|
218
|
+
onPreviousSlide?.();
|
|
219
|
+
}, [embla]);
|
|
220
|
+
const handleNext = useCallback(() => {
|
|
221
|
+
embla?.scrollNext();
|
|
222
|
+
onNextSlide?.();
|
|
223
|
+
}, [embla]);
|
|
224
|
+
const handleKeydown = useCallback((event) => {
|
|
225
|
+
if (withKeyboardEvents) {
|
|
226
|
+
if (event.key === "ArrowRight") {
|
|
227
|
+
event.preventDefault();
|
|
228
|
+
handleNext();
|
|
229
|
+
}
|
|
230
|
+
if (event.key === "ArrowLeft") {
|
|
231
|
+
event.preventDefault();
|
|
232
|
+
handlePrevious();
|
|
233
|
+
}
|
|
234
|
+
if (event.key === "Home") {
|
|
235
|
+
event.preventDefault();
|
|
236
|
+
embla?.scrollTo(0);
|
|
237
|
+
}
|
|
238
|
+
if (event.key === "End") {
|
|
239
|
+
event.preventDefault();
|
|
240
|
+
embla?.scrollTo(embla.scrollSnapList().length - 1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}, [
|
|
244
|
+
embla,
|
|
245
|
+
handleNext,
|
|
246
|
+
handlePrevious
|
|
247
|
+
]);
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
if (embla) {
|
|
250
|
+
getEmblaApi?.(embla);
|
|
251
|
+
handleSelect();
|
|
252
|
+
setSlidesCount(embla.scrollSnapList().length);
|
|
253
|
+
embla.on("select", handleSelect);
|
|
254
|
+
return () => {
|
|
255
|
+
embla.off("select", handleSelect);
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}, [
|
|
259
|
+
embla,
|
|
260
|
+
emblaOptions?.slidesToScroll,
|
|
261
|
+
handleSelect
|
|
262
|
+
]);
|
|
263
|
+
useEffect(() => {
|
|
264
|
+
if (embla) {
|
|
265
|
+
embla.reInit();
|
|
266
|
+
setSlidesCount(embla.scrollSnapList().length);
|
|
267
|
+
setSelected((currentSelected) => clamp(currentSelected, 0, Children.toArray(children).length - 1));
|
|
268
|
+
}
|
|
269
|
+
}, [Children.toArray(children).length, emblaOptions?.slidesToScroll]);
|
|
270
|
+
const canScrollPrev = embla?.canScrollPrev() || false;
|
|
271
|
+
const canScrollNext = embla?.canScrollNext() || false;
|
|
272
|
+
const handleIndicatorKeyDown = useCallback((event, index) => {
|
|
273
|
+
const isHorizontal = orientation === "horizontal";
|
|
274
|
+
const nextKey = isHorizontal ? "ArrowRight" : "ArrowDown";
|
|
275
|
+
const prevKey = isHorizontal ? "ArrowLeft" : "ArrowUp";
|
|
276
|
+
if (event.key === nextKey) {
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
const nextIndex = index < slidesCount - 1 ? index + 1 : 0;
|
|
279
|
+
handleScroll(nextIndex);
|
|
280
|
+
event.currentTarget.parentElement?.children[nextIndex]?.focus();
|
|
281
|
+
}
|
|
282
|
+
if (event.key === prevKey) {
|
|
283
|
+
event.preventDefault();
|
|
284
|
+
const prevIndex = index > 0 ? index - 1 : slidesCount - 1;
|
|
285
|
+
handleScroll(prevIndex);
|
|
286
|
+
event.currentTarget.parentElement?.children[prevIndex]?.focus();
|
|
287
|
+
}
|
|
288
|
+
if (event.key === "Home") {
|
|
289
|
+
event.preventDefault();
|
|
290
|
+
handleScroll(0);
|
|
291
|
+
event.currentTarget.parentElement?.children[0]?.focus();
|
|
292
|
+
}
|
|
293
|
+
if (event.key === "End") {
|
|
294
|
+
event.preventDefault();
|
|
295
|
+
handleScroll(slidesCount - 1);
|
|
296
|
+
event.currentTarget.parentElement?.children[slidesCount - 1]?.focus();
|
|
297
|
+
}
|
|
298
|
+
}, [
|
|
299
|
+
orientation,
|
|
300
|
+
slidesCount,
|
|
301
|
+
handleScroll
|
|
302
|
+
]);
|
|
303
|
+
const indicators = Array(slidesCount).fill(0).map((_, index) => /* @__PURE__ */ createElement(UnstyledButton, {
|
|
304
|
+
...getStyles("indicator"),
|
|
305
|
+
key: index,
|
|
306
|
+
role: "tab",
|
|
307
|
+
"aria-label": `Go to slide ${index + 1}`,
|
|
308
|
+
"aria-selected": index === selected,
|
|
309
|
+
tabIndex: index === selected ? 0 : -1,
|
|
310
|
+
"data-active": index === selected || undefined,
|
|
311
|
+
onClick: () => handleScroll(index),
|
|
312
|
+
onKeyDown: (event) => handleIndicatorKeyDown(event, index),
|
|
313
|
+
"data-orientation": orientation,
|
|
314
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
315
|
+
...getIndicatorProps?.(index)
|
|
316
|
+
}));
|
|
317
|
+
return /* @__PURE__ */ jsxs(CarouselProvider, {
|
|
318
|
+
value: {
|
|
319
|
+
getStyles,
|
|
320
|
+
orientation
|
|
321
|
+
},
|
|
322
|
+
children: [type === "container" ? /* @__PURE__ */ jsx3(CarouselContainerVariables, {
|
|
323
|
+
...props,
|
|
324
|
+
selector: `.${responsiveClassName}`
|
|
325
|
+
}) : /* @__PURE__ */ jsx3(CarouselVariables, {
|
|
326
|
+
...props,
|
|
327
|
+
selector: `.${responsiveClassName}`
|
|
328
|
+
}), /* @__PURE__ */ jsxs(Box, {
|
|
329
|
+
role: "region",
|
|
330
|
+
"aria-roledescription": "carousel",
|
|
331
|
+
...getStyles("root", { className: responsiveClassName }),
|
|
332
|
+
...others,
|
|
333
|
+
id: _id,
|
|
334
|
+
mod: [{
|
|
335
|
+
orientation,
|
|
336
|
+
"include-gap-in-size": includeGapInSize
|
|
337
|
+
}, mod],
|
|
338
|
+
onKeyDownCapture: handleKeydown,
|
|
339
|
+
children: [
|
|
340
|
+
/* @__PURE__ */ jsx3(VisuallyHidden, {
|
|
341
|
+
role: "status",
|
|
342
|
+
"aria-live": "polite",
|
|
343
|
+
"aria-atomic": "true",
|
|
344
|
+
children: slidesCount > 0 && `Slide ${selected + 1} of ${slidesCount}`
|
|
345
|
+
}),
|
|
346
|
+
withControls && /* @__PURE__ */ jsxs("div", {
|
|
347
|
+
...getStyles("controls"),
|
|
348
|
+
"data-orientation": orientation,
|
|
349
|
+
children: [/* @__PURE__ */ jsx3(UnstyledButton, {
|
|
350
|
+
"aria-controls": _id,
|
|
351
|
+
"aria-label": "Previous slide",
|
|
352
|
+
"aria-disabled": !canScrollPrev,
|
|
353
|
+
"data-inactive": !canScrollPrev || undefined,
|
|
354
|
+
"data-type": "previous",
|
|
355
|
+
tabIndex: canScrollPrev ? 0 : -1,
|
|
356
|
+
...previousControlProps,
|
|
357
|
+
...getStyles("control", {
|
|
358
|
+
className: previousControlProps?.className,
|
|
359
|
+
style: previousControlProps?.style
|
|
360
|
+
}),
|
|
361
|
+
onClick: (event) => {
|
|
362
|
+
handlePrevious();
|
|
363
|
+
previousControlProps?.onClick?.(event);
|
|
364
|
+
},
|
|
365
|
+
children: typeof previousControlIcon !== "undefined" ? previousControlIcon : /* @__PURE__ */ jsx3(AccordionChevron, { style: { transform: `rotate(${getChevronRotation({
|
|
366
|
+
dir,
|
|
367
|
+
orientation,
|
|
368
|
+
direction: "previous"
|
|
369
|
+
})}deg)` } })
|
|
370
|
+
}), /* @__PURE__ */ jsx3(UnstyledButton, {
|
|
371
|
+
"aria-controls": _id,
|
|
372
|
+
"aria-label": "Next slide",
|
|
373
|
+
"aria-disabled": !canScrollNext,
|
|
374
|
+
"data-inactive": !canScrollNext || undefined,
|
|
375
|
+
"data-type": "next",
|
|
376
|
+
tabIndex: canScrollNext ? 0 : -1,
|
|
377
|
+
...getStyles("control", {
|
|
378
|
+
className: nextControlProps?.className,
|
|
379
|
+
style: nextControlProps?.style
|
|
380
|
+
}),
|
|
381
|
+
...nextControlProps,
|
|
382
|
+
onClick: (event) => {
|
|
383
|
+
handleNext();
|
|
384
|
+
nextControlProps?.onClick?.(event);
|
|
385
|
+
},
|
|
386
|
+
children: typeof nextControlIcon !== "undefined" ? nextControlIcon : /* @__PURE__ */ jsx3(AccordionChevron, { style: { transform: `rotate(${getChevronRotation({
|
|
387
|
+
dir,
|
|
388
|
+
orientation,
|
|
389
|
+
direction: "next"
|
|
390
|
+
})}deg)` } })
|
|
391
|
+
})]
|
|
392
|
+
}),
|
|
393
|
+
/* @__PURE__ */ jsx3("div", {
|
|
394
|
+
...getStyles("viewport"),
|
|
395
|
+
ref: emblaRefElement,
|
|
396
|
+
"data-type": type,
|
|
397
|
+
children: /* @__PURE__ */ jsx3("div", {
|
|
398
|
+
...getStyles("container", { className: responsiveClassName }),
|
|
399
|
+
"data-orientation": orientation,
|
|
400
|
+
children
|
|
401
|
+
})
|
|
402
|
+
}),
|
|
403
|
+
withIndicators && /* @__PURE__ */ jsx3("div", {
|
|
404
|
+
...getStyles("indicators"),
|
|
405
|
+
role: "tablist",
|
|
406
|
+
"aria-label": "Slides",
|
|
407
|
+
"data-orientation": orientation,
|
|
408
|
+
children: indicators
|
|
409
|
+
})
|
|
410
|
+
]
|
|
411
|
+
})]
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
Carousel.classes = Carousel_module_default;
|
|
415
|
+
Carousel.varsResolver = varsResolver;
|
|
416
|
+
Carousel.displayName = "@mantine/carousel/Carousel";
|
|
417
|
+
Carousel.Slide = CarouselSlide;
|
|
418
|
+
export {
|
|
419
|
+
useCarouselContext,
|
|
420
|
+
CarouselSlide,
|
|
421
|
+
Carousel
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
//# debugId=0A5736BDFF04F01564756E2164756E21
|