@baklavue/composables 1.0.0-preview.1
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/.releaserc.json +14 -0
- package/CHANGELOG.md +64 -0
- package/README.md +276 -0
- package/csv.ts +126 -0
- package/index.ts +26 -0
- package/notification.ts +69 -0
- package/package.json +45 -0
- package/scrollToError.ts +151 -0
- package/theme.ts +306 -0
- package/tsconfig.json +28 -0
package/.releaserc.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "semantic-release-monorepo",
|
|
3
|
+
"tagFormat": "@baklavue/composables-v${version}",
|
|
4
|
+
"branches": ["main", { "name": "preview", "prerelease": true, "channel": "beta" }],
|
|
5
|
+
"repositoryUrl": "https://github.com/erbilnas/baklavue",
|
|
6
|
+
"plugins": [
|
|
7
|
+
"@semantic-release/commit-analyzer",
|
|
8
|
+
"@semantic-release/release-notes-generator",
|
|
9
|
+
"@semantic-release/changelog",
|
|
10
|
+
["@semantic-release/npm", { "npmPublish": true }],
|
|
11
|
+
"@semantic-release/github",
|
|
12
|
+
"@semantic-release/git"
|
|
13
|
+
]
|
|
14
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @baklavue/composables-v1.0.0-preview.1 (2026-02-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* fix package version ([620618d](https://github.com/erbilnas/baklavue/commit/620618df664022f1b68859aee4b374f6586731bb))
|
|
7
|
+
* fix release ci ([6c22554](https://github.com/erbilnas/baklavue/commit/6c225541b182c4b8692af000e7c13ca6e546599a))
|
|
8
|
+
* fix release ci ([6e23d99](https://github.com/erbilnas/baklavue/commit/6e23d99cac122d033c72d6d98819e0329c92bcdc))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add accordion and alert components ([10b8155](https://github.com/erbilnas/baklavue/commit/10b81556184432deb2772d3489c24be6115e667c))
|
|
14
|
+
* add info for npm package ([8ffbb9f](https://github.com/erbilnas/baklavue/commit/8ffbb9f81d108d633608ebe8d3854a2f6861475a))
|
|
15
|
+
* add localization guide ([0367e41](https://github.com/erbilnas/baklavue/commit/0367e41741cdffb2dfb7ec631db3cb47b12fe6cc))
|
|
16
|
+
* add new components ([ec926f0](https://github.com/erbilnas/baklavue/commit/ec926f0e9ba11c1a28a97c8afee4829b38ed8539))
|
|
17
|
+
* add theme customizer tool for docs ([70c08cf](https://github.com/erbilnas/baklavue/commit/70c08cf505ff02b1ab6c3e569055b703674bb822))
|
|
18
|
+
* disable npm package-lock for bun ([d91c5ca](https://github.com/erbilnas/baklavue/commit/d91c5ca0d8566aff9c021b1cad431eda86bc2d95))
|
|
19
|
+
* disable npm package-lock for bun ([86fd15e](https://github.com/erbilnas/baklavue/commit/86fd15ed42ded4e198ed22845acdb659cdfde191))
|
|
20
|
+
* initial commit ([d98b3f9](https://github.com/erbilnas/baklavue/commit/d98b3f9e6cc6e8238f197eaa117a050365987b18))
|
|
21
|
+
* seperate publishing packages ([e8092d8](https://github.com/erbilnas/baklavue/commit/e8092d87a6579cf6d2798053cd6150fc4ccd98a8))
|
|
22
|
+
|
|
23
|
+
# [1.0.0-preview.5](https://github.com/erbilnas/baklavue/compare/v1.0.0-preview.4...v1.0.0-preview.5) (2026-02-09)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Features
|
|
27
|
+
|
|
28
|
+
* add github pages support ([c76dffa](https://github.com/erbilnas/baklavue/commit/c76dffa8eb32b8293aca7c7e1f5d2ee4139802b0))
|
|
29
|
+
|
|
30
|
+
# [1.0.0-preview.3](https://github.com/erbilnas/baklavue/compare/v1.0.0-preview.2...v1.0.0-preview.3) (2026-02-09)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Features
|
|
34
|
+
|
|
35
|
+
* add new components ([22a1506](https://github.com/erbilnas/baklavue/commit/22a150681ad8587c92ad5546fbc256d9275b6d00))
|
|
36
|
+
|
|
37
|
+
# [1.0.0-preview.2](https://github.com/erbilnas/baklavue/compare/v1.0.0-preview.1...v1.0.0-preview.2) (2026-02-09)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Features
|
|
41
|
+
|
|
42
|
+
* add accordion, alert and badge components ([b939941](https://github.com/erbilnas/baklavue/commit/b939941e469a29b10ddcc6196f97700951672f97))
|
|
43
|
+
|
|
44
|
+
# 1.0.0-preview.1 (2026-02-09)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### Bug Fixes
|
|
48
|
+
|
|
49
|
+
* fix known issue of npm@11 ([f73a8fa](https://github.com/erbilnas/baklavue/commit/f73a8fa56191c083545b0f02d0672539a8dfbfab))
|
|
50
|
+
* fix known issue of npm@11 ([6e90aeb](https://github.com/erbilnas/baklavue/commit/6e90aeb37bbd42c9d0e524735ec34481f0c87ecf))
|
|
51
|
+
* fix package version ([620618d](https://github.com/erbilnas/baklavue/commit/620618df664022f1b68859aee4b374f6586731bb))
|
|
52
|
+
* fix release ci ([6c22554](https://github.com/erbilnas/baklavue/commit/6c225541b182c4b8692af000e7c13ca6e546599a))
|
|
53
|
+
* fix release ci ([6e23d99](https://github.com/erbilnas/baklavue/commit/6e23d99cac122d033c72d6d98819e0329c92bcdc))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### Features
|
|
57
|
+
|
|
58
|
+
* add info for npm package ([8ffbb9f](https://github.com/erbilnas/baklavue/commit/8ffbb9f81d108d633608ebe8d3854a2f6861475a))
|
|
59
|
+
* add new feature ([771acd1](https://github.com/erbilnas/baklavue/commit/771acd19bbd730e4110f61d2df06a5f6a958a3a7))
|
|
60
|
+
* disable npm package-lock for bun ([d91c5ca](https://github.com/erbilnas/baklavue/commit/d91c5ca0d8566aff9c021b1cad431eda86bc2d95))
|
|
61
|
+
* disable npm package-lock for bun ([86fd15e](https://github.com/erbilnas/baklavue/commit/86fd15ed42ded4e198ed22845acdb659cdfde191))
|
|
62
|
+
* disable npm package-lock for bun ([ca4503d](https://github.com/erbilnas/baklavue/commit/ca4503d1304cacb6212c6b328426d7c51152f893))
|
|
63
|
+
* increase semantic release version\ ([b510944](https://github.com/erbilnas/baklavue/commit/b510944562238e02fa6ce34c1b10540e11d47f16))
|
|
64
|
+
* initial commit ([d98b3f9](https://github.com/erbilnas/baklavue/commit/d98b3f9e6cc6e8238f197eaa117a050365987b18))
|
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# @baklavue/composables
|
|
2
|
+
|
|
3
|
+
A collection of Vue 3 composables for seamless integration with Baklava UI components. This package provides convenient utilities and hooks to enhance your Vue applications with Baklava's design system.
|
|
4
|
+
|
|
5
|
+
## 🚀 Features
|
|
6
|
+
|
|
7
|
+
- **Vue 3 Composition API**: Built with modern Vue 3 composables
|
|
8
|
+
- **TypeScript Support**: Full TypeScript support with proper type definitions
|
|
9
|
+
- **Baklava Integration**: Seamlessly works with Baklava UI components
|
|
10
|
+
- **Lightweight**: Minimal bundle size with no unnecessary dependencies
|
|
11
|
+
- **Tree-shakable**: Only import what you need
|
|
12
|
+
|
|
13
|
+
## 📦 Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Using npm
|
|
17
|
+
npm install @baklavue/composables
|
|
18
|
+
|
|
19
|
+
# Using yarn
|
|
20
|
+
yarn add @baklavue/composables
|
|
21
|
+
|
|
22
|
+
# Using pnpm
|
|
23
|
+
pnpm add @baklavue/composables
|
|
24
|
+
|
|
25
|
+
# Using bun
|
|
26
|
+
bun add @baklavue/composables
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 🔧 Prerequisites
|
|
30
|
+
|
|
31
|
+
- Vue 3.x
|
|
32
|
+
- TypeScript 5.9.2+ (peer dependency)
|
|
33
|
+
- Baklava UI components installed in your project
|
|
34
|
+
|
|
35
|
+
## 📚 Available Composables
|
|
36
|
+
|
|
37
|
+
### `useCsv`
|
|
38
|
+
|
|
39
|
+
A composable for CSV parsing, creating, and downloading. Uses PapaParse for RFC 4180-compliant handling of quoted fields, commas in values, and edge cases.
|
|
40
|
+
|
|
41
|
+
#### Basic Usage
|
|
42
|
+
|
|
43
|
+
```vue
|
|
44
|
+
<script setup lang="ts">
|
|
45
|
+
import { useCsv } from "@baklavue/composables";
|
|
46
|
+
|
|
47
|
+
const { parse, parseFile, create, download } = useCsv();
|
|
48
|
+
|
|
49
|
+
// Parse CSV string
|
|
50
|
+
const result = parse("name,age\nAlice,30\nBob,25", { header: true });
|
|
51
|
+
console.log(result.data); // [{ name: "Alice", age: "30" }, ...]
|
|
52
|
+
|
|
53
|
+
// Parse file (async)
|
|
54
|
+
const handleFileUpload = async (event: Event) => {
|
|
55
|
+
const file = (event.target as HTMLInputElement).files?.[0];
|
|
56
|
+
if (file) {
|
|
57
|
+
const fileResult = await parseFile(file, { header: true });
|
|
58
|
+
console.log(fileResult.data);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Create CSV from array of objects
|
|
63
|
+
const csv = create(
|
|
64
|
+
[
|
|
65
|
+
{ name: "Alice", age: 30 },
|
|
66
|
+
{ name: "Bob", age: 25 },
|
|
67
|
+
]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Download CSV file
|
|
71
|
+
const exportData = () => {
|
|
72
|
+
download(
|
|
73
|
+
[{ name: "Alice", age: 30 }, { name: "Bob", age: 25 }],
|
|
74
|
+
"export.csv"
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
</script>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### API Reference
|
|
81
|
+
|
|
82
|
+
The `useCsv` composable returns an object with the following methods:
|
|
83
|
+
|
|
84
|
+
##### `parse(csv: string, options?: CsvParseOptions)`
|
|
85
|
+
|
|
86
|
+
Parses a CSV string and returns `{ data, errors, meta }`. Synchronous.
|
|
87
|
+
|
|
88
|
+
##### `parseFile(file: File, options?: CsvParseOptions)`
|
|
89
|
+
|
|
90
|
+
Parses a File or Blob asynchronously. Returns `Promise<ParseResult>`.
|
|
91
|
+
|
|
92
|
+
##### `create(data: CsvData, options?: CsvCreateOptions)`
|
|
93
|
+
|
|
94
|
+
Creates a CSV string from an array of objects, array of arrays, or `{ fields, data }` object.
|
|
95
|
+
|
|
96
|
+
##### `download(data: CsvData, filename?: string, options?: CsvCreateOptions)`
|
|
97
|
+
|
|
98
|
+
Creates a CSV string and triggers a browser download. Adds UTF-8 BOM for Excel compatibility.
|
|
99
|
+
|
|
100
|
+
#### Options
|
|
101
|
+
|
|
102
|
+
**CsvParseOptions:**
|
|
103
|
+
|
|
104
|
+
- `delimiter` – Delimiter character (default: auto-detect)
|
|
105
|
+
- `header` – If true, first row is header (returns array of objects)
|
|
106
|
+
- `dynamicTyping` – If true, numbers and booleans are converted
|
|
107
|
+
- `skipEmptyLines` – Skip empty lines (`true` or `'greedy'`)
|
|
108
|
+
|
|
109
|
+
**CsvCreateOptions:**
|
|
110
|
+
|
|
111
|
+
- `delimiter` – Delimiter character (default: comma)
|
|
112
|
+
- `header` – Include header row (default: true)
|
|
113
|
+
- `columns` – Column order for array of objects
|
|
114
|
+
- `escapeFormulae` – Escape leading `=`, `+`, `-`, `@` to prevent CSV injection
|
|
115
|
+
|
|
116
|
+
### `useNotification`
|
|
117
|
+
|
|
118
|
+
A composable for managing Baklava notification system with a simple and intuitive API.
|
|
119
|
+
|
|
120
|
+
#### Basic Usage
|
|
121
|
+
|
|
122
|
+
```vue
|
|
123
|
+
<template>
|
|
124
|
+
<div>
|
|
125
|
+
<button @click="showSuccess">Show Success</button>
|
|
126
|
+
<button @click="showError">Show Error</button>
|
|
127
|
+
<button @click="showWarning">Show Warning</button>
|
|
128
|
+
<button @click="showInfo">Show Info</button>
|
|
129
|
+
|
|
130
|
+
<!-- Required: Add the notification element to your template -->
|
|
131
|
+
<bl-notification />
|
|
132
|
+
</div>
|
|
133
|
+
</template>
|
|
134
|
+
|
|
135
|
+
<script setup lang="ts">
|
|
136
|
+
import { useNotification } from "@baklavue/composables";
|
|
137
|
+
|
|
138
|
+
const { success, error, warning, info } = useNotification();
|
|
139
|
+
|
|
140
|
+
const showSuccess = () => {
|
|
141
|
+
success({
|
|
142
|
+
caption: "Success!",
|
|
143
|
+
description: "Operation completed successfully.",
|
|
144
|
+
duration: 5,
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const showError = () => {
|
|
149
|
+
error({
|
|
150
|
+
caption: "Error!",
|
|
151
|
+
description: "Something went wrong. Please try again.",
|
|
152
|
+
duration: 8,
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const showWarning = () => {
|
|
157
|
+
warning({
|
|
158
|
+
caption: "Warning!",
|
|
159
|
+
description: "Please review your input before proceeding.",
|
|
160
|
+
duration: 6,
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const showInfo = () => {
|
|
165
|
+
info({
|
|
166
|
+
caption: "Information",
|
|
167
|
+
description: "Here is some useful information for you.",
|
|
168
|
+
duration: 4,
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
</script>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### API Reference
|
|
175
|
+
|
|
176
|
+
The `useNotification` composable returns an object with the following methods:
|
|
177
|
+
|
|
178
|
+
##### `success(notification: NotificationProps)`
|
|
179
|
+
|
|
180
|
+
Shows a success notification with green styling.
|
|
181
|
+
|
|
182
|
+
##### `error(notification: NotificationProps)`
|
|
183
|
+
|
|
184
|
+
Shows an error notification with red styling.
|
|
185
|
+
|
|
186
|
+
##### `warning(notification: NotificationProps)`
|
|
187
|
+
|
|
188
|
+
Shows a warning notification with orange styling.
|
|
189
|
+
|
|
190
|
+
##### `info(notification: NotificationProps)`
|
|
191
|
+
|
|
192
|
+
Shows an info notification with blue styling.
|
|
193
|
+
|
|
194
|
+
#### Notification Props
|
|
195
|
+
|
|
196
|
+
All notification methods accept a `NotificationProps` object with the following properties:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
interface NotificationProps {
|
|
200
|
+
caption?: string; // Notification title
|
|
201
|
+
description: string; // Notification message (required)
|
|
202
|
+
duration?: number; // Duration in seconds (optional)
|
|
203
|
+
permanent?: boolean; // Prevent auto-close
|
|
204
|
+
// ... other Baklava notification properties
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Important Notes
|
|
209
|
+
|
|
210
|
+
- **Required DOM Element**: You must include `<bl-notification>` in your template for notifications to work
|
|
211
|
+
- **Auto-icon**: All notifications automatically include appropriate icons based on their variant
|
|
212
|
+
- **Duration**: If no duration is specified, notifications will use Baklava's default duration
|
|
213
|
+
- **Error Handling**: The composable includes built-in error handling and will warn if the notification element is not found
|
|
214
|
+
|
|
215
|
+
## 🏗️ Project Structure
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
packages/composables/
|
|
219
|
+
├── index.ts # Main export file
|
|
220
|
+
├── csv.ts # CSV parsing, creating, and download composable
|
|
221
|
+
├── notification.ts # Notification composable
|
|
222
|
+
├── package.json # Package configuration
|
|
223
|
+
├── tsconfig.json # TypeScript configuration
|
|
224
|
+
└── README.md # This file
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## 🔧 Development
|
|
228
|
+
|
|
229
|
+
### Prerequisites
|
|
230
|
+
|
|
231
|
+
- [Bun](https://bun.sh) (recommended) or Node.js
|
|
232
|
+
- TypeScript 5.9.2+
|
|
233
|
+
|
|
234
|
+
### Setup
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Install dependencies
|
|
238
|
+
bun install
|
|
239
|
+
|
|
240
|
+
# Run the package
|
|
241
|
+
bun run index.ts
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### TypeScript Configuration
|
|
245
|
+
|
|
246
|
+
This package uses strict TypeScript configuration with:
|
|
247
|
+
|
|
248
|
+
- ESNext target and modules
|
|
249
|
+
- Strict type checking
|
|
250
|
+
- Bundler-friendly module resolution
|
|
251
|
+
- Unused variable/parameter detection
|
|
252
|
+
|
|
253
|
+
## 🤝 Contributing
|
|
254
|
+
|
|
255
|
+
1. Fork the repository
|
|
256
|
+
2. Create a feature branch
|
|
257
|
+
3. Make your changes
|
|
258
|
+
4. Add tests if applicable
|
|
259
|
+
5. Submit a pull request
|
|
260
|
+
|
|
261
|
+
## 📄 License
|
|
262
|
+
|
|
263
|
+
This project is part of the Baklavue ecosystem. See the main project for license information.
|
|
264
|
+
|
|
265
|
+
## 🔗 Related Packages
|
|
266
|
+
|
|
267
|
+
- `@baklavue/ui` - Baklava UI components for Vue
|
|
268
|
+
- `@trendyol/baklava` - Core Baklava design system
|
|
269
|
+
|
|
270
|
+
## 📞 Support
|
|
271
|
+
|
|
272
|
+
For issues and questions:
|
|
273
|
+
|
|
274
|
+
- Check the [Baklava documentation](https://baklava.trendyol.com/)
|
|
275
|
+
- Open an issue in the project repository
|
|
276
|
+
- Review the examples in the playground directory
|
package/csv.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import Papa from "papaparse";
|
|
2
|
+
import type { ParseError, ParseMeta, ParseResult } from "papaparse";
|
|
3
|
+
|
|
4
|
+
/** Re-exported for consumer use */
|
|
5
|
+
export type { ParseError, ParseMeta };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Options for parsing CSV strings or files.
|
|
9
|
+
*/
|
|
10
|
+
export interface CsvParseOptions {
|
|
11
|
+
/** Delimiter character. Leave empty to auto-detect. */
|
|
12
|
+
delimiter?: string;
|
|
13
|
+
/** If true, first row is treated as header (returns array of objects). */
|
|
14
|
+
header?: boolean;
|
|
15
|
+
/** If true, numeric and boolean values are converted to their types. */
|
|
16
|
+
dynamicTyping?: boolean;
|
|
17
|
+
/** If true, skip completely empty lines. Use 'greedy' to also skip whitespace-only lines. */
|
|
18
|
+
skipEmptyLines?: boolean | "greedy";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for creating CSV strings or downloading.
|
|
23
|
+
*/
|
|
24
|
+
export interface CsvCreateOptions {
|
|
25
|
+
/** Delimiter character. Default: comma. */
|
|
26
|
+
delimiter?: string;
|
|
27
|
+
/** If false, omit header row. Ignored for array of arrays. Default: true. */
|
|
28
|
+
header?: boolean;
|
|
29
|
+
/** Column order for array of objects. Uses object keys if not specified. */
|
|
30
|
+
columns?: string[];
|
|
31
|
+
/** If true, escape formulae injection (values starting with =, +, -, @). */
|
|
32
|
+
escapeFormulae?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Parse result with data, errors, and meta. Use ParseResult<T> for typed rows. */
|
|
36
|
+
export type { ParseResult };
|
|
37
|
+
|
|
38
|
+
/** Data accepted by create() and download(): array of objects, array of arrays, or explicit fields+data. */
|
|
39
|
+
export type CsvData =
|
|
40
|
+
| Record<string, unknown>[]
|
|
41
|
+
| unknown[][]
|
|
42
|
+
| { fields: string[]; data: unknown[][] };
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Composable for CSV parsing, creating, and downloading.
|
|
46
|
+
* Uses PapaParse for RFC 4180-compliant handling of quoted fields and edge cases.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const { parse, parseFile, create, download } = useCsv();
|
|
51
|
+
*
|
|
52
|
+
* // Parse string
|
|
53
|
+
* const result = parse('name,age\nAlice,30\nBob,25', { header: true });
|
|
54
|
+
*
|
|
55
|
+
* // Parse file (async)
|
|
56
|
+
* const fileResult = await parseFile(file, { header: true });
|
|
57
|
+
*
|
|
58
|
+
* // Create CSV
|
|
59
|
+
* const csv = create([{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]);
|
|
60
|
+
*
|
|
61
|
+
* // Download
|
|
62
|
+
* download([{ name: 'Alice', age: 30 }], 'export.csv');
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export const useCsv = () => {
|
|
66
|
+
const parse = (csv: string, options?: CsvParseOptions): ParseResult<unknown> => {
|
|
67
|
+
return Papa.parse(csv, {
|
|
68
|
+
delimiter: options?.delimiter ?? "",
|
|
69
|
+
header: options?.header ?? false,
|
|
70
|
+
dynamicTyping: options?.dynamicTyping ?? false,
|
|
71
|
+
skipEmptyLines: options?.skipEmptyLines ?? false,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const parseFile = (
|
|
76
|
+
file: File,
|
|
77
|
+
options?: CsvParseOptions
|
|
78
|
+
): Promise<ParseResult<unknown>> => {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
Papa.parse(file, {
|
|
81
|
+
delimiter: options?.delimiter ?? "",
|
|
82
|
+
header: options?.header ?? false,
|
|
83
|
+
dynamicTyping: options?.dynamicTyping ?? false,
|
|
84
|
+
skipEmptyLines: options?.skipEmptyLines ?? false,
|
|
85
|
+
complete: (results: ParseResult<unknown>) => resolve(results),
|
|
86
|
+
error: (err: Error) => reject(err),
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const create = (data: CsvData, options?: CsvCreateOptions): string => {
|
|
92
|
+
return Papa.unparse(data as Parameters<typeof Papa.unparse>[0], {
|
|
93
|
+
delimiter: options?.delimiter ?? ",",
|
|
94
|
+
header: options?.header ?? true,
|
|
95
|
+
columns: options?.columns ?? undefined,
|
|
96
|
+
escapeFormulae: options?.escapeFormulae ?? false,
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const download = (
|
|
101
|
+
data: CsvData,
|
|
102
|
+
filename = "export.csv",
|
|
103
|
+
options?: CsvCreateOptions
|
|
104
|
+
): void => {
|
|
105
|
+
if (typeof document === "undefined") return;
|
|
106
|
+
|
|
107
|
+
const csv = create(data, options);
|
|
108
|
+
const BOM = "\uFEFF";
|
|
109
|
+
const blob = new Blob([BOM + csv], { type: "text/csv;charset=utf-8" });
|
|
110
|
+
const url = URL.createObjectURL(blob);
|
|
111
|
+
|
|
112
|
+
const link = document.createElement("a");
|
|
113
|
+
link.href = url;
|
|
114
|
+
link.download = filename.endsWith(".csv") ? filename : `${filename}.csv`;
|
|
115
|
+
link.click();
|
|
116
|
+
|
|
117
|
+
URL.revokeObjectURL(url);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
parse,
|
|
122
|
+
parseFile,
|
|
123
|
+
create,
|
|
124
|
+
download,
|
|
125
|
+
};
|
|
126
|
+
};
|
package/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export {
|
|
2
|
+
useCsv,
|
|
3
|
+
type CsvCreateOptions,
|
|
4
|
+
type CsvData,
|
|
5
|
+
type CsvParseOptions,
|
|
6
|
+
type ParseError,
|
|
7
|
+
type ParseMeta,
|
|
8
|
+
type ParseResult,
|
|
9
|
+
} from "./csv";
|
|
10
|
+
export { useNotification } from "./notification";
|
|
11
|
+
export {
|
|
12
|
+
useScrollToError,
|
|
13
|
+
type ScrollToErrorOptions,
|
|
14
|
+
type ScrollToErrorTarget,
|
|
15
|
+
} from "./scrollToError";
|
|
16
|
+
export {
|
|
17
|
+
useBaklavaTheme,
|
|
18
|
+
type ApplyThemeOptions,
|
|
19
|
+
type BaklavaThemeBorderRadius,
|
|
20
|
+
type BaklavaThemeColors,
|
|
21
|
+
type BaklavaThemePreset,
|
|
22
|
+
type BaklavaThemePresetRecord,
|
|
23
|
+
type BaklavaThemeSize,
|
|
24
|
+
type BaklavaThemeTypography,
|
|
25
|
+
type BaklavaThemeZIndex,
|
|
26
|
+
} from "./theme";
|
package/notification.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { NotificationProps } from "@trendyol/baklava/dist/components/notification/bl-notification";
|
|
2
|
+
import type { NotificationVariant } from "@trendyol/baklava/dist/components/notification/card/bl-notification-card";
|
|
3
|
+
|
|
4
|
+
interface NotificationElement extends HTMLElement {
|
|
5
|
+
addNotification: (options: NotificationProps) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface NotificationOptions extends Omit<NotificationProps, "variant"> {
|
|
9
|
+
variant: NotificationVariant;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const useNotification = () => {
|
|
13
|
+
const getNotificationElement = (): NotificationElement | null => {
|
|
14
|
+
return document.querySelector(
|
|
15
|
+
"bl-notification",
|
|
16
|
+
) as NotificationElement | null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const createNotification = (options: NotificationOptions): void => {
|
|
20
|
+
const notificationElement = getNotificationElement();
|
|
21
|
+
|
|
22
|
+
if (!notificationElement) {
|
|
23
|
+
console.warn(
|
|
24
|
+
"Notification element not found. Make sure <bl-notification> is present in the DOM.",
|
|
25
|
+
);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
notificationElement.addNotification({
|
|
30
|
+
...options,
|
|
31
|
+
icon: true,
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const success = (notification: Omit<NotificationProps, "variant">): void => {
|
|
36
|
+
createNotification({
|
|
37
|
+
...notification,
|
|
38
|
+
variant: "success",
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const error = (notification: Omit<NotificationProps, "variant">): void => {
|
|
43
|
+
createNotification({
|
|
44
|
+
...notification,
|
|
45
|
+
variant: "error",
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const warning = (notification: Omit<NotificationProps, "variant">): void => {
|
|
50
|
+
createNotification({
|
|
51
|
+
...notification,
|
|
52
|
+
variant: "warning",
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const info = (notification: Omit<NotificationProps, "variant">): void => {
|
|
57
|
+
createNotification({
|
|
58
|
+
...notification,
|
|
59
|
+
variant: "info",
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
success,
|
|
65
|
+
error,
|
|
66
|
+
warning,
|
|
67
|
+
info,
|
|
68
|
+
};
|
|
69
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@baklavue/composables",
|
|
3
|
+
"version": "1.0.0-preview.1",
|
|
4
|
+
"description": "Vue 3 composables for Trendyol Baklava Design System",
|
|
5
|
+
"author": "erbilnas",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/erbilnas/baklavue/tree/main/packages/composables#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/erbilnas/baklavue.git",
|
|
11
|
+
"directory": "packages/composables"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/erbilnas/baklavue/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"vue",
|
|
18
|
+
"vue3",
|
|
19
|
+
"baklava",
|
|
20
|
+
"trendyol",
|
|
21
|
+
"design-system",
|
|
22
|
+
"composables"
|
|
23
|
+
],
|
|
24
|
+
"module": "index.ts",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"release": "semantic-release -e semantic-release-monorepo",
|
|
28
|
+
"release:dry-run": "semantic-release --dry-run -e semantic-release-monorepo"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@trendyol/baklava": "^3.4.2",
|
|
32
|
+
"@types/bun": "latest",
|
|
33
|
+
"@types/papaparse": "^5.5.2"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"typescript": "^5.9.2",
|
|
37
|
+
"vue": ">=3.0.0"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"papaparse": "^5.5.3"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/scrollToError.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Focusable element selectors for finding form controls.
|
|
3
|
+
* Includes native inputs and Baklava custom components.
|
|
4
|
+
*/
|
|
5
|
+
const FOCUSABLE_SELECTORS =
|
|
6
|
+
"input, select, textarea, button, bl-select, bl-input";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Options for customizing scroll-to-error behavior.
|
|
10
|
+
*/
|
|
11
|
+
export interface ScrollToErrorOptions {
|
|
12
|
+
/** Scroll behavior. Default: `'smooth'` */
|
|
13
|
+
scrollBehavior?: ScrollBehavior;
|
|
14
|
+
/** Vertical scroll position. Default: `'center'` */
|
|
15
|
+
block?: ScrollLogicalPosition;
|
|
16
|
+
/** CSS class to apply for highlight effect. Default: `'error-shine'` */
|
|
17
|
+
shineClass?: string;
|
|
18
|
+
/** Duration in ms to keep the shine class. Use `0` to disable. Default: `2500` */
|
|
19
|
+
shineDuration?: number;
|
|
20
|
+
/** Whether to attempt focusing the first focusable element. Default: `true` */
|
|
21
|
+
focus?: boolean;
|
|
22
|
+
/** Delay in ms before focus attempt. Default: `300` */
|
|
23
|
+
focusDelay?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Target that can be resolved to an HTMLElement: selector, element, or object with scrollTarget. */
|
|
27
|
+
export type ScrollToErrorTarget =
|
|
28
|
+
| string
|
|
29
|
+
| HTMLElement
|
|
30
|
+
| { scrollTarget: string };
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolves the target to an HTMLElement.
|
|
34
|
+
*
|
|
35
|
+
* @param target - CSS selector, HTMLElement, or object with scrollTarget
|
|
36
|
+
* @returns The resolved element or null if not found
|
|
37
|
+
*/
|
|
38
|
+
function resolveTarget(target: ScrollToErrorTarget): HTMLElement | null {
|
|
39
|
+
if (typeof document === "undefined") return null;
|
|
40
|
+
|
|
41
|
+
if (typeof target === "string") {
|
|
42
|
+
const el = document.querySelector(target);
|
|
43
|
+
return el instanceof HTMLElement ? el : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (target instanceof HTMLElement) {
|
|
47
|
+
return target;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (target && typeof target === "object" && "scrollTarget" in target) {
|
|
51
|
+
const selector = target.scrollTarget;
|
|
52
|
+
if (!selector || typeof selector !== "string") return null;
|
|
53
|
+
const el = document.querySelector(selector);
|
|
54
|
+
return el instanceof HTMLElement ? el : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Finds the first focusable element within the given element.
|
|
62
|
+
* Prefers inner input/select/textarea for custom components like bl-select.
|
|
63
|
+
*
|
|
64
|
+
* @param element - Container element to search within
|
|
65
|
+
* @returns The focusable element or null
|
|
66
|
+
*/
|
|
67
|
+
function findFocusable(element: HTMLElement): HTMLElement | null {
|
|
68
|
+
const focusable = element.querySelector(FOCUSABLE_SELECTORS);
|
|
69
|
+
if (!(focusable instanceof HTMLElement)) return null;
|
|
70
|
+
|
|
71
|
+
// Prefer inner input/select/textarea for custom components
|
|
72
|
+
const innerInput = focusable.querySelector("input, select, textarea");
|
|
73
|
+
if (innerInput instanceof HTMLElement) {
|
|
74
|
+
return innerInput;
|
|
75
|
+
}
|
|
76
|
+
return focusable;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Composable for scrolling to an element with validation error.
|
|
81
|
+
* Scrolls into view, optionally applies a highlight effect, and focuses the first focusable control.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* const { scrollToError } = useScrollToError();
|
|
86
|
+
*
|
|
87
|
+
* // From validation error object
|
|
88
|
+
* scrollToError(validationError);
|
|
89
|
+
*
|
|
90
|
+
* // Direct selector
|
|
91
|
+
* scrollToError('[data-field="tags"]');
|
|
92
|
+
*
|
|
93
|
+
* // With custom options
|
|
94
|
+
* scrollToError('[data-field="tags"]', { shineClass: 'my-shine', shineDuration: 1500 });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export const useScrollToError = () => {
|
|
98
|
+
/**
|
|
99
|
+
* Scrolls to the target element and optionally applies highlight and focus.
|
|
100
|
+
*
|
|
101
|
+
* @param target - CSS selector, HTMLElement, or object with scrollTarget (e.g. validation error)
|
|
102
|
+
* @param options - Optional overrides for scroll, shine, and focus behavior
|
|
103
|
+
*/
|
|
104
|
+
const scrollToError = (
|
|
105
|
+
target: ScrollToErrorTarget,
|
|
106
|
+
options?: ScrollToErrorOptions,
|
|
107
|
+
): void => {
|
|
108
|
+
if (typeof document === "undefined") return;
|
|
109
|
+
|
|
110
|
+
const element = resolveTarget(target);
|
|
111
|
+
if (!element) return;
|
|
112
|
+
|
|
113
|
+
const {
|
|
114
|
+
scrollBehavior = "smooth",
|
|
115
|
+
block = "center",
|
|
116
|
+
shineClass = "error-shine",
|
|
117
|
+
shineDuration = 2500,
|
|
118
|
+
focus = true,
|
|
119
|
+
focusDelay = 300,
|
|
120
|
+
} = options ?? {};
|
|
121
|
+
|
|
122
|
+
element.scrollIntoView({ behavior: scrollBehavior, block });
|
|
123
|
+
|
|
124
|
+
// Apply shine effect
|
|
125
|
+
let targetElement: HTMLElement | null = null;
|
|
126
|
+
const formControl =
|
|
127
|
+
element.closest(".bl-form-control, [class*='bl-']") || element;
|
|
128
|
+
targetElement = formControl instanceof HTMLElement ? formControl : element;
|
|
129
|
+
|
|
130
|
+
if (targetElement && shineClass && shineDuration > 0) {
|
|
131
|
+
targetElement.classList.add(shineClass);
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
targetElement?.classList.remove(shineClass);
|
|
134
|
+
}, shineDuration);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Focus the first focusable element
|
|
138
|
+
if (focus && element instanceof HTMLElement) {
|
|
139
|
+
const focusableElement = findFocusable(element);
|
|
140
|
+
if (focusableElement) {
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
focusableElement.focus();
|
|
143
|
+
}, focusDelay);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
scrollToError,
|
|
150
|
+
};
|
|
151
|
+
};
|
package/theme.ts
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in presets. Use 'vue' to apply Vue.js brand colors.
|
|
3
|
+
*/
|
|
4
|
+
export type BaklavaThemePreset = "vue" | "default";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Custom preset: map of CSS variable names to values.
|
|
8
|
+
* Use this to pass your own preset or compose with built-in presets.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const myPreset: BaklavaThemePresetRecord = {
|
|
13
|
+
* '--bl-color-primary': '#ea4c89',
|
|
14
|
+
* '--bl-color-primary-highlight': '#d6427a',
|
|
15
|
+
* };
|
|
16
|
+
* applyTheme({ preset: myPreset });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export type BaklavaThemePresetRecord = Record<string, string>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Custom color overrides for Baklava design tokens.
|
|
23
|
+
* Maps to --bl-color-* CSS variables.
|
|
24
|
+
*/
|
|
25
|
+
export interface BaklavaThemeColors {
|
|
26
|
+
primary?: string;
|
|
27
|
+
primaryHighlight?: string;
|
|
28
|
+
primaryContrast?: string;
|
|
29
|
+
success?: string;
|
|
30
|
+
successHighlight?: string;
|
|
31
|
+
successContrast?: string;
|
|
32
|
+
danger?: string;
|
|
33
|
+
dangerHighlight?: string;
|
|
34
|
+
dangerContrast?: string;
|
|
35
|
+
warning?: string;
|
|
36
|
+
warningHighlight?: string;
|
|
37
|
+
warningContrast?: string;
|
|
38
|
+
info?: string;
|
|
39
|
+
infoHighlight?: string;
|
|
40
|
+
infoContrast?: string;
|
|
41
|
+
neutralDarkest?: string;
|
|
42
|
+
neutralDarker?: string;
|
|
43
|
+
neutralDark?: string;
|
|
44
|
+
neutralLight?: string;
|
|
45
|
+
neutralLighter?: string;
|
|
46
|
+
neutralLightest?: string;
|
|
47
|
+
neutralFull?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Border radius overrides for Baklava design tokens.
|
|
52
|
+
* Maps to --bl-border-radius-* CSS variables.
|
|
53
|
+
*/
|
|
54
|
+
export interface BaklavaThemeBorderRadius {
|
|
55
|
+
xs?: string;
|
|
56
|
+
s?: string;
|
|
57
|
+
m?: string;
|
|
58
|
+
l?: string;
|
|
59
|
+
pill?: string;
|
|
60
|
+
circle?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Size and spacing overrides for Baklava design tokens.
|
|
65
|
+
* Maps to --bl-size-* CSS variables.
|
|
66
|
+
*/
|
|
67
|
+
export interface BaklavaThemeSize {
|
|
68
|
+
"4xs"?: string;
|
|
69
|
+
"3xs"?: string;
|
|
70
|
+
"2xs"?: string;
|
|
71
|
+
xs?: string;
|
|
72
|
+
s?: string;
|
|
73
|
+
m?: string;
|
|
74
|
+
l?: string;
|
|
75
|
+
xl?: string;
|
|
76
|
+
"2xl"?: string;
|
|
77
|
+
"3xl"?: string;
|
|
78
|
+
"4xl"?: string;
|
|
79
|
+
"5xl"?: string;
|
|
80
|
+
"6xl"?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Typography overrides for Baklava design tokens.
|
|
85
|
+
* Maps to --bl-font-* CSS variables.
|
|
86
|
+
*/
|
|
87
|
+
export interface BaklavaThemeTypography {
|
|
88
|
+
fontFamily?: string;
|
|
89
|
+
fontSize?: Record<string, string>;
|
|
90
|
+
fontWeight?: Record<string, number>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Z-index overrides for Baklava design tokens.
|
|
95
|
+
* Maps to --bl-index-* CSS variables.
|
|
96
|
+
*/
|
|
97
|
+
export interface BaklavaThemeZIndex {
|
|
98
|
+
deep?: number;
|
|
99
|
+
base?: number;
|
|
100
|
+
popover?: number;
|
|
101
|
+
tooltip?: number;
|
|
102
|
+
sticky?: number;
|
|
103
|
+
overlay?: number;
|
|
104
|
+
dialog?: number;
|
|
105
|
+
notification?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface ApplyThemeOptions {
|
|
109
|
+
/** Built-in preset name ('vue', 'default') or your own preset object */
|
|
110
|
+
preset?: BaklavaThemePreset | BaklavaThemePresetRecord;
|
|
111
|
+
colors?: Partial<BaklavaThemeColors>;
|
|
112
|
+
borderRadius?: Partial<BaklavaThemeBorderRadius>;
|
|
113
|
+
size?: Partial<BaklavaThemeSize>;
|
|
114
|
+
typography?: Partial<BaklavaThemeTypography>;
|
|
115
|
+
zIndex?: Partial<BaklavaThemeZIndex>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const VUE_PRESET: Record<string, string> = {
|
|
119
|
+
"--bl-color-primary": "#41b883",
|
|
120
|
+
"--bl-color-primary-highlight": "#3aa876",
|
|
121
|
+
"--bl-color-primary-contrast": "#e7f9ef",
|
|
122
|
+
"--bl-color-success": "#41b883",
|
|
123
|
+
"--bl-color-success-highlight": "#3aa876",
|
|
124
|
+
"--bl-color-success-contrast": "#e7f9ef",
|
|
125
|
+
"--bl-color-neutral-darker": "#34495e",
|
|
126
|
+
"--bl-color-neutral-darkest": "#2c3e50",
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const BORDER_RADIUS_MAP: Record<keyof BaklavaThemeBorderRadius, string> = {
|
|
130
|
+
xs: "--bl-border-radius-xs",
|
|
131
|
+
s: "--bl-border-radius-s",
|
|
132
|
+
m: "--bl-border-radius-m",
|
|
133
|
+
l: "--bl-border-radius-l",
|
|
134
|
+
pill: "--bl-border-radius-pill",
|
|
135
|
+
circle: "--bl-border-radius-circle",
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const SIZE_MAP: Record<keyof BaklavaThemeSize, string> = {
|
|
139
|
+
"4xs": "--bl-size-4xs",
|
|
140
|
+
"3xs": "--bl-size-3xs",
|
|
141
|
+
"2xs": "--bl-size-2xs",
|
|
142
|
+
xs: "--bl-size-xs",
|
|
143
|
+
s: "--bl-size-s",
|
|
144
|
+
m: "--bl-size-m",
|
|
145
|
+
l: "--bl-size-l",
|
|
146
|
+
xl: "--bl-size-xl",
|
|
147
|
+
"2xl": "--bl-size-2xl",
|
|
148
|
+
"3xl": "--bl-size-3xl",
|
|
149
|
+
"4xl": "--bl-size-4xl",
|
|
150
|
+
"5xl": "--bl-size-5xl",
|
|
151
|
+
"6xl": "--bl-size-6xl",
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const Z_INDEX_MAP: Record<keyof BaklavaThemeZIndex, string> = {
|
|
155
|
+
deep: "--bl-index-deep",
|
|
156
|
+
base: "--bl-index-base",
|
|
157
|
+
popover: "--bl-index-popover",
|
|
158
|
+
tooltip: "--bl-index-tooltip",
|
|
159
|
+
sticky: "--bl-index-sticky",
|
|
160
|
+
overlay: "--bl-index-overlay",
|
|
161
|
+
dialog: "--bl-index-dialog",
|
|
162
|
+
notification: "--bl-index-notification",
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
function buildCssVariables(options: ApplyThemeOptions = {}): string {
|
|
166
|
+
const vars: Record<string, string> = {};
|
|
167
|
+
const { preset, colors, borderRadius, size, typography, zIndex } = options;
|
|
168
|
+
|
|
169
|
+
if (preset) {
|
|
170
|
+
if (typeof preset === "string") {
|
|
171
|
+
if (preset === "vue") Object.assign(vars, VUE_PRESET);
|
|
172
|
+
} else {
|
|
173
|
+
Object.assign(vars, preset);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (colors) {
|
|
178
|
+
const colorMap: Record<keyof BaklavaThemeColors, string> = {
|
|
179
|
+
primary: "--bl-color-primary",
|
|
180
|
+
primaryHighlight: "--bl-color-primary-highlight",
|
|
181
|
+
primaryContrast: "--bl-color-primary-contrast",
|
|
182
|
+
success: "--bl-color-success",
|
|
183
|
+
successHighlight: "--bl-color-success-highlight",
|
|
184
|
+
successContrast: "--bl-color-success-contrast",
|
|
185
|
+
danger: "--bl-color-danger",
|
|
186
|
+
dangerHighlight: "--bl-color-danger-highlight",
|
|
187
|
+
dangerContrast: "--bl-color-danger-contrast",
|
|
188
|
+
warning: "--bl-color-warning",
|
|
189
|
+
warningHighlight: "--bl-color-warning-highlight",
|
|
190
|
+
warningContrast: "--bl-color-warning-contrast",
|
|
191
|
+
info: "--bl-color-info",
|
|
192
|
+
infoHighlight: "--bl-color-info-highlight",
|
|
193
|
+
infoContrast: "--bl-color-info-contrast",
|
|
194
|
+
neutralDarkest: "--bl-color-neutral-darkest",
|
|
195
|
+
neutralDarker: "--bl-color-neutral-darker",
|
|
196
|
+
neutralDark: "--bl-color-neutral-dark",
|
|
197
|
+
neutralLight: "--bl-color-neutral-light",
|
|
198
|
+
neutralLighter: "--bl-color-neutral-lighter",
|
|
199
|
+
neutralLightest: "--bl-color-neutral-lightest",
|
|
200
|
+
neutralFull: "--bl-color-neutral-full",
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
for (const [key, value] of Object.entries(colors)) {
|
|
204
|
+
if (value && key in colorMap) {
|
|
205
|
+
vars[colorMap[key as keyof BaklavaThemeColors]] = value;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (borderRadius) {
|
|
211
|
+
for (const [key, value] of Object.entries(borderRadius)) {
|
|
212
|
+
if (value && key in BORDER_RADIUS_MAP) {
|
|
213
|
+
vars[BORDER_RADIUS_MAP[key as keyof BaklavaThemeBorderRadius]] = value;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (size) {
|
|
219
|
+
for (const [key, value] of Object.entries(size)) {
|
|
220
|
+
if (value && key in SIZE_MAP) {
|
|
221
|
+
vars[SIZE_MAP[key as keyof BaklavaThemeSize]] = value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (typography) {
|
|
227
|
+
if (typography.fontFamily) vars["--bl-font-family"] = typography.fontFamily;
|
|
228
|
+
if (typography.fontSize) {
|
|
229
|
+
for (const [key, value] of Object.entries(typography.fontSize)) {
|
|
230
|
+
if (value) vars[`--bl-font-size-${key}`] = value;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (typography.fontWeight) {
|
|
234
|
+
for (const [key, value] of Object.entries(typography.fontWeight)) {
|
|
235
|
+
if (value != null) vars[`--bl-font-weight-${key}`] = String(value);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (zIndex) {
|
|
241
|
+
for (const [key, value] of Object.entries(zIndex)) {
|
|
242
|
+
if (value != null && key in Z_INDEX_MAP) {
|
|
243
|
+
vars[Z_INDEX_MAP[key as keyof BaklavaThemeZIndex]] = String(value);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const rules = Object.entries(vars)
|
|
249
|
+
.map(([prop, val]) => ` ${prop}: ${val};`)
|
|
250
|
+
.join("\n");
|
|
251
|
+
|
|
252
|
+
return rules ? `:root {\n${rules}\n}` : "";
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const STYLE_ID = "baklavue-theme-overrides";
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Composable to overwrite Baklava design system colors.
|
|
259
|
+
* Use the 'vue' preset, pass your own preset object, or override specific colors.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```ts
|
|
263
|
+
* import { useBaklavaTheme } from '@baklavue/composables'
|
|
264
|
+
*
|
|
265
|
+
* // Vue preset
|
|
266
|
+
* useBaklavaTheme().applyTheme({ preset: 'vue' })
|
|
267
|
+
*
|
|
268
|
+
* // Custom preset
|
|
269
|
+
* useBaklavaTheme().applyTheme({
|
|
270
|
+
* preset: {
|
|
271
|
+
* '--bl-color-primary': '#ea4c89',
|
|
272
|
+
* '--bl-color-primary-highlight': '#d6427a',
|
|
273
|
+
* }
|
|
274
|
+
* })
|
|
275
|
+
*
|
|
276
|
+
* // Custom colors
|
|
277
|
+
* useBaklavaTheme().applyTheme({
|
|
278
|
+
* colors: { primary: '#41B883', primaryHighlight: '#3aa876' }
|
|
279
|
+
* })
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export const useBaklavaTheme = () => {
|
|
283
|
+
const applyTheme = (options: ApplyThemeOptions = {}): void => {
|
|
284
|
+
if (typeof document === "undefined") return;
|
|
285
|
+
|
|
286
|
+
const css = buildCssVariables(options);
|
|
287
|
+
|
|
288
|
+
if (!css) return;
|
|
289
|
+
|
|
290
|
+
let styleEl = document.getElementById(STYLE_ID) as HTMLStyleElement | null;
|
|
291
|
+
|
|
292
|
+
if (!styleEl) {
|
|
293
|
+
styleEl = document.createElement("style");
|
|
294
|
+
styleEl.id = STYLE_ID;
|
|
295
|
+
document.head.appendChild(styleEl);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
styleEl.textContent = css;
|
|
299
|
+
// Move to end of head so we override any lazy-loaded styles (e.g. from VitePress route chunks)
|
|
300
|
+
document.head.appendChild(styleEl);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
applyTheme,
|
|
305
|
+
};
|
|
306
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Enable latest features
|
|
4
|
+
"lib": ["ESNext", "DOM"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
// Best practices
|
|
18
|
+
"strict": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
|
|
22
|
+
// Some stricter flags (disabled by default)
|
|
23
|
+
"noUnusedLocals": true,
|
|
24
|
+
"noUnusedParameters": true,
|
|
25
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
26
|
+
"noImplicitAny": true
|
|
27
|
+
}
|
|
28
|
+
}
|