@synertech/ui 0.39.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/.claude/skills/use-synertech-ui/SKILL.md +1578 -0
- package/LICENSE +37 -0
- package/README.md +343 -0
- package/bin/setup-claude-skill.mjs +61 -0
- package/dist/index.cjs +31 -0
- package/dist/index.d.cts +2433 -0
- package/dist/index.d.ts +2433 -0
- package/dist/index.js +31 -0
- package/dist/styles.css +6863 -0
- package/package.json +112 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Synertech UI License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kevin Tremblay. All rights reserved.
|
|
4
|
+
|
|
5
|
+
Permission is granted, free of charge, to any person or organisation obtaining a
|
|
6
|
+
copy of this software and its associated files (the "Software") to install and
|
|
7
|
+
use the Software, and to distribute it as an incorporated part of an
|
|
8
|
+
application, subject to the conditions below.
|
|
9
|
+
|
|
10
|
+
1. Use. The Software may be installed and used free of charge as a dependency of
|
|
11
|
+
any application, commercial or not, and may be bundled into that application's
|
|
12
|
+
build output.
|
|
13
|
+
|
|
14
|
+
2. No redistribution. The Software may not be redistributed, republished,
|
|
15
|
+
sublicensed, rented or sold on its own, in source or compiled form, whether
|
|
16
|
+
modified or not. Publishing it, in whole or in substantial part, to any
|
|
17
|
+
package registry or public repository is not permitted.
|
|
18
|
+
|
|
19
|
+
3. No derivative works. The Software may not be modified, adapted, translated,
|
|
20
|
+
decompiled or reverse engineered in order to create a competing or derivative
|
|
21
|
+
component library. Inspecting and debugging the Software as needed to operate
|
|
22
|
+
an application that depends on it is permitted.
|
|
23
|
+
|
|
24
|
+
4. Notices. This notice must be preserved in every copy of the Software.
|
|
25
|
+
|
|
26
|
+
5. Termination. This licence terminates automatically if these conditions are
|
|
27
|
+
breached.
|
|
28
|
+
|
|
29
|
+
The name "Synertech", the Synertech logo and any other Synertech trademarks are
|
|
30
|
+
not covered by this licence.
|
|
31
|
+
|
|
32
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
33
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
34
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE
|
|
35
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
36
|
+
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
37
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# @synertech/ui
|
|
2
|
+
|
|
3
|
+
A TypeScript/React UI component library using Tailwind CSS classes. Published on the public npm registry.
|
|
4
|
+
|
|
5
|
+
📖 **[View Storybook](https://ui.synertechweb.com/)** — Interactive component documentation
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
### 1. Install the package
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @synertech/ui
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
No registry configuration, no token: the package is published on the public npm registry.
|
|
16
|
+
|
|
17
|
+
### 2. Import the styles
|
|
18
|
+
|
|
19
|
+
Import the compiled CSS in your app's entry point (e.g., `layout.tsx`, `_app.tsx`, or `main.tsx`):
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import "@synertech/ui/styles.css";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it! Components are now fully styled and ready to use. 🎉
|
|
26
|
+
|
|
27
|
+
<details>
|
|
28
|
+
<summary><strong>Advanced: Using your own Tailwind configuration (optional)</strong></summary>
|
|
29
|
+
|
|
30
|
+
If you want to customize the library's Tailwind classes or optimize bundle size, you can skip the CSS import and configure Tailwind to scan the library:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// tailwind.config.js
|
|
34
|
+
module.exports = {
|
|
35
|
+
content: [
|
|
36
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
37
|
+
"./node_modules/@synertech/ui/dist/**/*.{js,mjs}"
|
|
38
|
+
],
|
|
39
|
+
safelist: [
|
|
40
|
+
"w-4", "h-4", "w-5", "h-5", "w-6", "h-6",
|
|
41
|
+
"bg-blue-600", "bg-green-600", "bg-purple-600", "bg-orange-600",
|
|
42
|
+
],
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
</details>
|
|
47
|
+
|
|
48
|
+
### 3. Optional: Markdown editing
|
|
49
|
+
|
|
50
|
+
`<HelpButton markdown editable>` uses [MDXEditor](https://mdxeditor.dev/), which is an
|
|
51
|
+
**optional** peer dependency — it pulls in a large dependency tree, so it is not
|
|
52
|
+
installed with the library. Every other component works without it.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pnpm add @mdxeditor/editor
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import "@mdxeditor/editor/style.css";
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Without the package, the edit mode renders an explanatory message instead of the editor.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
### Button
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { Button } from "@synertech/ui";
|
|
71
|
+
|
|
72
|
+
<Button variant="primary" onClick={() => console.log("clicked")}>
|
|
73
|
+
Click me
|
|
74
|
+
</Button>
|
|
75
|
+
|
|
76
|
+
<Button variant="outline" disabled>
|
|
77
|
+
Disabled
|
|
78
|
+
</Button>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Input
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { Input } from "@synertech/ui";
|
|
85
|
+
|
|
86
|
+
<Input
|
|
87
|
+
label="Email"
|
|
88
|
+
type="email"
|
|
89
|
+
name="email"
|
|
90
|
+
value={email}
|
|
91
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
92
|
+
/>
|
|
93
|
+
|
|
94
|
+
<Input label="Name" inline />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Layout (Header & Footer)
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { Header, Footer } from "@synertech/ui";
|
|
101
|
+
|
|
102
|
+
<Header
|
|
103
|
+
logo={<span className="font-bold">MyApp</span>}
|
|
104
|
+
navItems={[
|
|
105
|
+
{ label: "Home", href: "/" },
|
|
106
|
+
{ label: "About", href: "/about" },
|
|
107
|
+
]}
|
|
108
|
+
/>
|
|
109
|
+
|
|
110
|
+
<Footer copyright="© 2026 MyApp" />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Modal
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { Modal } from "@synertech/ui";
|
|
117
|
+
|
|
118
|
+
<Modal
|
|
119
|
+
isOpen={isOpen}
|
|
120
|
+
onClose={() => setIsOpen(false)}
|
|
121
|
+
title="Confirm Action"
|
|
122
|
+
buttonLabel="Confirm"
|
|
123
|
+
onButtonClick={handleConfirm}
|
|
124
|
+
>
|
|
125
|
+
<p>Are you sure you want to proceed?</p>
|
|
126
|
+
</Modal>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`Modal`, `Drawer` and the mobile `Sidebar` are real modal dialogs: `role="dialog"` + `aria-modal`,
|
|
130
|
+
accessible name from the title, focus moved in on open and returned to the
|
|
131
|
+
trigger on close, Tab/Shift+Tab contained, Escape to close, inert background and
|
|
132
|
+
nested dialog support. The same behaviour is available for custom dialogs
|
|
133
|
+
through the exported `useDialogA11y` hook.
|
|
134
|
+
|
|
135
|
+
### Translating built-in labels
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { UILabelsProvider, frenchUILabels } from "@synertech/ui";
|
|
139
|
+
|
|
140
|
+
<UILabelsProvider labels={frenchUILabels}>
|
|
141
|
+
<App />
|
|
142
|
+
</UILabelsProvider>;
|
|
143
|
+
|
|
144
|
+
// or per instance
|
|
145
|
+
<Modal closeLabel="Fermer la fenêtre" ... />
|
|
146
|
+
<Drawer closeLabel="Fermer le panneau" ... />
|
|
147
|
+
<Sidebar closeLabel="Fermer le menu" ... />
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Table
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import {
|
|
154
|
+
Table,
|
|
155
|
+
TableHeader,
|
|
156
|
+
TableHeaderCell,
|
|
157
|
+
TableBody,
|
|
158
|
+
TableRow,
|
|
159
|
+
TableCell,
|
|
160
|
+
} from "@synertech/ui";
|
|
161
|
+
|
|
162
|
+
<Table>
|
|
163
|
+
<TableHeader>
|
|
164
|
+
<TableHeaderCell>Name</TableHeaderCell>
|
|
165
|
+
<TableHeaderCell>Email</TableHeaderCell>
|
|
166
|
+
</TableHeader>
|
|
167
|
+
<TableBody>
|
|
168
|
+
<TableRow onClick={() => console.log("row clicked")}>
|
|
169
|
+
<TableCell>John Doe</TableCell>
|
|
170
|
+
<TableCell>john@example.com</TableCell>
|
|
171
|
+
</TableRow>
|
|
172
|
+
</TableBody>
|
|
173
|
+
</Table>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Available Components
|
|
177
|
+
|
|
178
|
+
| Category | Components |
|
|
179
|
+
| -------- | ------------------------------------------------------------------------------- |
|
|
180
|
+
| Button | `Button` |
|
|
181
|
+
| Form | `Input` |
|
|
182
|
+
| Layout | `Header`, `Footer` |
|
|
183
|
+
| Modal | `Modal` |
|
|
184
|
+
| Table | `Table`, `TableHeader`, `TableHeaderCell`, `TableBody`, `TableRow`, `TableCell` |
|
|
185
|
+
|
|
186
|
+
## Contributing
|
|
187
|
+
|
|
188
|
+
### Adding a new component
|
|
189
|
+
|
|
190
|
+
1. Create the component in the appropriate category: `src/components/<category>/<Component>.tsx`
|
|
191
|
+
2. Add `"use client"` directive if using React hooks
|
|
192
|
+
3. Export the component and its props type
|
|
193
|
+
4. Add export in `src/components/<category>/index.ts`
|
|
194
|
+
5. Verify: `pnpm typecheck && pnpm build`
|
|
195
|
+
6. Create a changeset: `pnpm changeset` (see [Releasing](#releasing))
|
|
196
|
+
7. Commit and push to `main`
|
|
197
|
+
|
|
198
|
+
### Guidelines
|
|
199
|
+
|
|
200
|
+
- **No MUI** – HTML + Tailwind only
|
|
201
|
+
- **No compiled CSS** – Tailwind classes are applied, not compiled
|
|
202
|
+
- **React as peer dependency** – Don't bundle React
|
|
203
|
+
- **Next.js compatible** – Proper ESM/CJS exports
|
|
204
|
+
- **Use `"use client"`** – Only for components using hooks
|
|
205
|
+
- **Accept `className`** – All components should allow style overrides
|
|
206
|
+
- **Export types** – Each component exports its props type
|
|
207
|
+
|
|
208
|
+
## Development
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
pnpm build # Build to dist/
|
|
212
|
+
pnpm dev # Build in watch mode
|
|
213
|
+
pnpm typecheck # TypeScript check
|
|
214
|
+
pnpm lint # ESLint check
|
|
215
|
+
pnpm lint:fix # ESLint auto-fix
|
|
216
|
+
pnpm format # Prettier format
|
|
217
|
+
pnpm format:check # Prettier check
|
|
218
|
+
pnpm storybook # Run Storybook locally (port 6006)
|
|
219
|
+
pnpm build-storybook # Build static Storybook
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Storybook
|
|
223
|
+
|
|
224
|
+
Interactive component documentation is available via Storybook.
|
|
225
|
+
|
|
226
|
+
### Run locally
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pnpm storybook
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Then open [http://localhost:6006](http://localhost:6006).
|
|
233
|
+
|
|
234
|
+
### Hosted version
|
|
235
|
+
|
|
236
|
+
Storybook is automatically deployed to GitLab Pages on every push to `main`:
|
|
237
|
+
|
|
238
|
+
👉 **https://ui.synertechweb.com/**
|
|
239
|
+
|
|
240
|
+
## Releasing
|
|
241
|
+
|
|
242
|
+
This project uses [Changesets](https://github.com/changesets/changesets) for version management and changelog generation.
|
|
243
|
+
|
|
244
|
+
### Workflow
|
|
245
|
+
|
|
246
|
+
1. **Create a changeset** after making changes:
|
|
247
|
+
```bash
|
|
248
|
+
pnpm changeset
|
|
249
|
+
```
|
|
250
|
+
This will prompt you to select the version bump type (patch/minor/major) and write a summary.
|
|
251
|
+
|
|
252
|
+
2. **Commit the changeset file** (`.changeset/*.md`) along with your changes.
|
|
253
|
+
|
|
254
|
+
3. **When ready to release**, apply the version bump:
|
|
255
|
+
```bash
|
|
256
|
+
pnpm version-packages
|
|
257
|
+
```
|
|
258
|
+
This consumes all changesets, bumps `package.json` version, and updates `CHANGELOG.md`.
|
|
259
|
+
|
|
260
|
+
4. **Commit and push** to `main`:
|
|
261
|
+
```bash
|
|
262
|
+
git add -A && git commit -m "chore: release vX.Y.Z" && git push
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
5. **CI publishes automatically** to the public npm registry.
|
|
266
|
+
|
|
267
|
+
> **Note**: npm versions are immutable. Once published, a version cannot be overwritten. Changesets prevents accidental duplicate publishes.
|
|
268
|
+
|
|
269
|
+
## Conventions
|
|
270
|
+
|
|
271
|
+
### `"use client"` directive
|
|
272
|
+
|
|
273
|
+
Only add `"use client"` to components that use React hooks (`useState`, `useEffect`, etc.). Currently:
|
|
274
|
+
- ✅ `Header` – uses `useState` for mobile menu
|
|
275
|
+
- ✅ `Modal` / `Drawer` / `Sidebar` – use `useId` / `forwardRef` and `useDialogA11y`
|
|
276
|
+
- ✅ `UILabelsProvider` – uses React context
|
|
277
|
+
- ❌ All other components – no hooks, no directive needed
|
|
278
|
+
|
|
279
|
+
### `className` prop
|
|
280
|
+
|
|
281
|
+
All components accept a `className` prop for style customization. Classes are merged using the internal `cn()` utility.
|
|
282
|
+
|
|
283
|
+
### Styles CSS
|
|
284
|
+
|
|
285
|
+
The library ships with pre-compiled CSS. Simply import it once:
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
import "@synertech/ui/styles.css";
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
For advanced customization, see the [installation section](#2-import-the-styles).
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
## Table Suite
|
|
295
|
+
|
|
296
|
+
The Table components are designed to be composed together for flexibility:
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
Table
|
|
300
|
+
├── TableHeader
|
|
301
|
+
│ └── TableHeaderCell (multiple)
|
|
302
|
+
└── TableBody
|
|
303
|
+
└── TableRow (multiple)
|
|
304
|
+
└── TableCell (multiple)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Component Props
|
|
308
|
+
|
|
309
|
+
| Component | Key Props | HTML Element |
|
|
310
|
+
| ----------------- | ----------------------------------- | ------------ |
|
|
311
|
+
| `Table` | `className` | `<table>` |
|
|
312
|
+
| `TableHeader` | `className` | `<thead>` |
|
|
313
|
+
| `TableHeaderCell` | `sortable`, `onClick`, `className` | `<th>` |
|
|
314
|
+
| `TableBody` | `className` | `<tbody>` |
|
|
315
|
+
| `TableRow` | `clickable`, `onClick`, `className` | `<tr>` |
|
|
316
|
+
| `TableCell` | `className` | `<td>` |
|
|
317
|
+
|
|
318
|
+
### Example with sortable headers
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
<Table>
|
|
322
|
+
<TableHeader>
|
|
323
|
+
<TableHeaderCell sortable onClick={() => sort("name")}>
|
|
324
|
+
Name ↕
|
|
325
|
+
</TableHeaderCell>
|
|
326
|
+
<TableHeaderCell>Email</TableHeaderCell>
|
|
327
|
+
</TableHeader>
|
|
328
|
+
<TableBody>
|
|
329
|
+
{users.map((user) => (
|
|
330
|
+
<TableRow key={user.id} onClick={() => selectUser(user)}>
|
|
331
|
+
<TableCell>{user.name}</TableCell>
|
|
332
|
+
<TableCell>{user.email}</TableCell>
|
|
333
|
+
</TableRow>
|
|
334
|
+
))}
|
|
335
|
+
</TableBody>
|
|
336
|
+
</Table>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
Free to install and use in your applications, including commercial ones.
|
|
342
|
+
Redistribution and derivative component libraries are not permitted.
|
|
343
|
+
See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { cpSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageRoot = resolve(__dirname, "..");
|
|
9
|
+
const skillSource = join(packageRoot, ".claude", "skills", "use-synertech-ui");
|
|
10
|
+
const projectRoot = process.cwd();
|
|
11
|
+
const skillDest = join(projectRoot, ".claude", "skills", "use-synertech-ui");
|
|
12
|
+
|
|
13
|
+
// Read package version
|
|
14
|
+
let version = "unknown";
|
|
15
|
+
try {
|
|
16
|
+
const pkg = JSON.parse(
|
|
17
|
+
readFileSync(join(packageRoot, "package.json"), "utf-8")
|
|
18
|
+
);
|
|
19
|
+
version = pkg.version;
|
|
20
|
+
} catch {
|
|
21
|
+
// ignore
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Verify source exists
|
|
25
|
+
if (!existsSync(skillSource)) {
|
|
26
|
+
console.error(
|
|
27
|
+
"Error: Claude skill source not found in @synertech/ui package."
|
|
28
|
+
);
|
|
29
|
+
console.error("Expected at:", skillSource);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if already installed and up to date
|
|
34
|
+
if (existsSync(skillDest)) {
|
|
35
|
+
const args = process.argv.slice(2);
|
|
36
|
+
if (!args.includes("--force")) {
|
|
37
|
+
console.log("Claude skill 'use-synertech-ui' is already installed.");
|
|
38
|
+
console.log("Use --force to overwrite with the latest version.");
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
console.log("Overwriting existing skill...");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Create destination directory and copy
|
|
45
|
+
mkdirSync(dirname(skillDest), { recursive: true });
|
|
46
|
+
cpSync(skillSource, skillDest, { recursive: true });
|
|
47
|
+
|
|
48
|
+
console.log("");
|
|
49
|
+
console.log(` @synertech/ui v${version} — Claude skill installed`);
|
|
50
|
+
console.log("");
|
|
51
|
+
console.log(` Copied to: .claude/skills/use-synertech-ui/`);
|
|
52
|
+
console.log("");
|
|
53
|
+
console.log(" Usage in Claude Code:");
|
|
54
|
+
console.log(" /use-synertech-ui Button");
|
|
55
|
+
console.log(" /use-synertech-ui Select");
|
|
56
|
+
console.log(' /use-synertech-ui comment faire un formulaire');
|
|
57
|
+
console.log(" /use-synertech-ui setup");
|
|
58
|
+
console.log("");
|
|
59
|
+
console.log(" Tip: Add .claude/skills/use-synertech-ui/ to .gitignore");
|
|
60
|
+
console.log(" if you don't want to commit it to your repo.");
|
|
61
|
+
console.log("");
|