@scalio-cloud/ui 0.36.0 → 0.38.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 +36 -0
- package/package.json +88 -6
- package/react/index.js +1 -1
- package/react/labels.js +2 -2
- package/react/session.js +98 -0
- package/react/unify.js +1 -1
- package/shadcn/alert-dialog.mjs +90 -0
- package/shadcn/badge.mjs +32 -0
- package/shadcn/button.mjs +47 -0
- package/shadcn/checkbox.mjs +22 -0
- package/shadcn/dialog.mjs +95 -0
- package/shadcn/dropdown-menu.mjs +143 -0
- package/shadcn/input.mjs +22 -0
- package/shadcn/label.mjs +14 -0
- package/shadcn/popover.mjs +28 -0
- package/shadcn/progress.mjs +27 -0
- package/shadcn/select.mjs +110 -0
- package/shadcn/sonner.mjs +28 -0
- package/shadcn/switch.mjs +28 -0
- package/shadcn/table.mjs +88 -0
- package/shadcn/tabs.mjs +48 -0
- package/shadcn/textarea.mjs +21 -0
- package/shadcn/utils.mjs +10 -0
- package/shell.css +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Spec: `design/UISPEC.md` in `Scalio-Cloud/scalio-docs` (single copy).
|
|
|
13
13
|
| `nav.css` | sidebar, phone drawer, switcher, context select, support drawer, page blocks |
|
|
14
14
|
| `index.css` | all of them in one import |
|
|
15
15
|
| `react/` | shared app header for React (see below), no dependency except React |
|
|
16
|
+
| `shadcn/` | shadcn/ui components on Radix UI (from 0.38.0, see below); the app installs the Radix peers it uses |
|
|
16
17
|
| `tailwind-preset.js` | colors, fonts, radius, accordion animation, `brand` colors, `h-switcher`, `h-header`, `w-sidebar` |
|
|
17
18
|
| `tokens.js` | raw values for code without CSS (charts, e-mails, PDFs) |
|
|
18
19
|
|
|
@@ -53,6 +54,22 @@ COPY --from=build /app/node_modules/@scalio-cloud/ui/nginx/security-headers.conf
|
|
|
53
54
|
include /etc/nginx/snippets/security-headers.conf;
|
|
54
55
|
```
|
|
55
56
|
|
|
57
|
+
## Sign-in state (React, from 0.37.0)
|
|
58
|
+
|
|
59
|
+
`createAuth({ api, formatError, signInWithPasskey, passkeyCancelled, legacyTokenKey })` gives `{ AuthProvider,
|
|
60
|
+
useAuth }`, the one version of the apps' `AuthContext.jsx` (code-quality plan QA5). The session lives only in the
|
|
61
|
+
HttpOnly cookie of the backend; `useAuth()` returns `user` (null while checking, false when signed out), `ready`,
|
|
62
|
+
`login`, `loginMfa`, `loginPasskey`, `loginScalio`, `register`, `setup`, `logout`, `updateUser`. The app keeps a
|
|
63
|
+
three-line `context/AuthContext.jsx`:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
import { createAuth } from "@scalio-cloud/ui/react";
|
|
67
|
+
import { api, formatApiErrorDetail } from "@/lib/api";
|
|
68
|
+
import { passkeyCancelled, signInWithPasskey } from "@/lib/passkeys";
|
|
69
|
+
export const { AuthProvider, useAuth } = createAuth({ api, formatError: formatApiErrorDetail, signInWithPasskey,
|
|
70
|
+
passkeyCancelled, legacyTokenKey: "pulse_token" });
|
|
71
|
+
```
|
|
72
|
+
|
|
56
73
|
## App header (React)
|
|
57
74
|
|
|
58
75
|
Every app shows the same header on the right, always in this order: (app actions), status, language, theme,
|
|
@@ -473,6 +490,25 @@ const jobs = jobsApi(api); // GET /jobs, /jobs/runs, /jobs/queue, POST /jobs
|
|
|
473
490
|
`queueActions(item, reload)`, `renderQueueDetail(item, reload)`.
|
|
474
491
|
- `AuditNav`: the tabs `/audit` and `/jobs` (`base` for another prefix, `jobs={false}` hides Jobs).
|
|
475
492
|
|
|
493
|
+
## shadcn/ui components (from 0.38.0)
|
|
494
|
+
|
|
495
|
+
One copy of the shadcn/ui components the apps used to keep in `src/components/ui` (code-quality plan QA6, decision
|
|
496
|
+
Markus 2026-11-05: Radix as peer dependency). Control Room style: buttons upper case, bold, `rounded-sm` (UISPEC A7).
|
|
497
|
+
Import each component by its own path, so an app only needs the Radix packages of the components it uses:
|
|
498
|
+
|
|
499
|
+
```js
|
|
500
|
+
import { Button } from "@scalio-cloud/ui/shadcn/button";
|
|
501
|
+
import { AlertDialog, AlertDialogContent } from "@scalio-cloud/ui/shadcn/alert-dialog";
|
|
502
|
+
import { cn } from "@scalio-cloud/ui/shadcn/utils";
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Components: `alert-dialog`, `badge`, `button`, `checkbox`, `dialog` (`closeLabel` for the hidden close text),
|
|
506
|
+
`dropdown-menu`, `input`, `label`, `popover`, `progress`, `select`, `sonner` (`Toaster theme`, no next-themes),
|
|
507
|
+
`switch`, `table`, `tabs`, `textarea`, `utils` (`cn`). Always needed: `clsx`, `tailwind-merge`; per component the
|
|
508
|
+
`@radix-ui/react-*` package, `class-variance-authority` (button, badge), `lucide-react` (icons), `sonner`. Tailwind
|
|
509
|
+
must scan the files: add `"./node_modules/@scalio-cloud/ui/shadcn/*.mjs"` to `content` in `tailwind.config.js`.
|
|
510
|
+
An app keeps a component in `src/components/ui` only when no other app uses it.
|
|
511
|
+
|
|
476
512
|
## Command palette (React)
|
|
477
513
|
|
|
478
514
|
Reads the same navigation data as the sidebar. Put it once into the layout; the sidebar search button opens it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalio-cloud/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "Scalio Control Room design tokens, Tailwind preset, self-hosted fonts, the shared app header, sign-in layout, sidebar and page blocks",
|
|
5
5
|
"license": "SEE LICENSE IN README.md",
|
|
6
6
|
"private": false,
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"./shell.css": "./shell.css",
|
|
24
24
|
"./auth.css": "./auth.css",
|
|
25
25
|
"./nav.css": "./nav.css",
|
|
26
|
-
"./nginx/*": "./nginx/*"
|
|
26
|
+
"./nginx/*": "./nginx/*",
|
|
27
|
+
"./shadcn/*": "./shadcn/*.mjs"
|
|
27
28
|
},
|
|
28
29
|
"files": [
|
|
29
30
|
"tokens.js",
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
"auth.css",
|
|
37
38
|
"nav.css",
|
|
38
39
|
"react/",
|
|
40
|
+
"shadcn/",
|
|
39
41
|
"nginx/",
|
|
40
42
|
"licenses/",
|
|
41
43
|
"fonts/",
|
|
@@ -47,12 +49,67 @@
|
|
|
47
49
|
"access": "public"
|
|
48
50
|
},
|
|
49
51
|
"peerDependencies": {
|
|
52
|
+
"@radix-ui/react-alert-dialog": "^1.1.0",
|
|
53
|
+
"@radix-ui/react-checkbox": "^1.1.0",
|
|
54
|
+
"@radix-ui/react-dialog": "^1.1.0",
|
|
55
|
+
"@radix-ui/react-dropdown-menu": "^2.1.0",
|
|
56
|
+
"@radix-ui/react-label": "^2.1.0",
|
|
57
|
+
"@radix-ui/react-popover": "^1.1.0",
|
|
58
|
+
"@radix-ui/react-progress": "^1.1.0",
|
|
59
|
+
"@radix-ui/react-select": "^2.1.0",
|
|
60
|
+
"@radix-ui/react-slot": "^1.1.0",
|
|
61
|
+
"@radix-ui/react-switch": "^1.1.0",
|
|
62
|
+
"@radix-ui/react-tabs": "^1.1.0",
|
|
63
|
+
"class-variance-authority": "^0.7.0",
|
|
64
|
+
"clsx": "^2.1.0",
|
|
65
|
+
"lucide-react": ">=0.400.0",
|
|
50
66
|
"react": ">=18",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
67
|
+
"react-dom": ">=18",
|
|
68
|
+
"sonner": "^2.0.0",
|
|
69
|
+
"tailwind-merge": "^3.0.0",
|
|
70
|
+
"tailwindcss": ">=3.3 <4"
|
|
53
71
|
},
|
|
54
72
|
"peerDependenciesMeta": {
|
|
55
|
-
"
|
|
73
|
+
"@radix-ui/react-alert-dialog": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"@radix-ui/react-checkbox": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"@radix-ui/react-dialog": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"@radix-ui/react-dropdown-menu": {
|
|
83
|
+
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"@radix-ui/react-label": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
88
|
+
"@radix-ui/react-popover": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
91
|
+
"@radix-ui/react-progress": {
|
|
92
|
+
"optional": true
|
|
93
|
+
},
|
|
94
|
+
"@radix-ui/react-select": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
"@radix-ui/react-slot": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"@radix-ui/react-switch": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
103
|
+
"@radix-ui/react-tabs": {
|
|
104
|
+
"optional": true
|
|
105
|
+
},
|
|
106
|
+
"class-variance-authority": {
|
|
107
|
+
"optional": true
|
|
108
|
+
},
|
|
109
|
+
"clsx": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
112
|
+
"lucide-react": {
|
|
56
113
|
"optional": true
|
|
57
114
|
},
|
|
58
115
|
"react": {
|
|
@@ -60,6 +117,15 @@
|
|
|
60
117
|
},
|
|
61
118
|
"react-dom": {
|
|
62
119
|
"optional": true
|
|
120
|
+
},
|
|
121
|
+
"sonner": {
|
|
122
|
+
"optional": true
|
|
123
|
+
},
|
|
124
|
+
"tailwind-merge": {
|
|
125
|
+
"optional": true
|
|
126
|
+
},
|
|
127
|
+
"tailwindcss": {
|
|
128
|
+
"optional": true
|
|
63
129
|
}
|
|
64
130
|
},
|
|
65
131
|
"scripts": {
|
|
@@ -69,8 +135,24 @@
|
|
|
69
135
|
"node": ">=18"
|
|
70
136
|
},
|
|
71
137
|
"devDependencies": {
|
|
138
|
+
"@radix-ui/react-alert-dialog": "1.2.1",
|
|
139
|
+
"@radix-ui/react-checkbox": "1.4.1",
|
|
140
|
+
"@radix-ui/react-dialog": "1.3.1",
|
|
141
|
+
"@radix-ui/react-dropdown-menu": "2.2.1",
|
|
142
|
+
"@radix-ui/react-label": "2.1.17",
|
|
143
|
+
"@radix-ui/react-popover": "1.3.1",
|
|
144
|
+
"@radix-ui/react-progress": "1.2.1",
|
|
145
|
+
"@radix-ui/react-select": "2.4.1",
|
|
146
|
+
"@radix-ui/react-slot": "1.5.0",
|
|
147
|
+
"@radix-ui/react-switch": "1.4.1",
|
|
148
|
+
"@radix-ui/react-tabs": "1.2.1",
|
|
149
|
+
"class-variance-authority": "0.7.1",
|
|
150
|
+
"clsx": "2.1.1",
|
|
72
151
|
"jsdom": "30.1.3",
|
|
152
|
+
"lucide-react": "1.65.0",
|
|
73
153
|
"react": "19.3.1",
|
|
74
|
-
"react-dom": "19.3.1"
|
|
154
|
+
"react-dom": "19.3.1",
|
|
155
|
+
"sonner": "2.0.8",
|
|
156
|
+
"tailwind-merge": "3.7.0"
|
|
75
157
|
}
|
|
76
158
|
}
|
package/react/index.js
CHANGED
|
@@ -380,4 +380,4 @@ module.exports = {
|
|
|
380
380
|
};
|
|
381
381
|
Object.assign(module.exports, Nav, Blocks, require("./settings"), require("./support"), require("./beacon"), require("./security"), require("./license"), require("./palette"), require("./unify"), require("./profile"), require("./actions"), require("./selftest"), require("./users"), require("./roles"), require("./wizard"), require("./maintenance"), require("./backup"), require("./audit"), require("./mail"), require("./saas_updates"), require("./pages"));
|
|
382
382
|
// After the exports above: auth.js uses LanguageToggle and ThemeToggle from this module.
|
|
383
|
-
Object.assign(module.exports, require("./auth"));
|
|
383
|
+
Object.assign(module.exports, require("./auth"), require("./session"));
|
package/react/labels.js
CHANGED
|
@@ -45,7 +45,7 @@ const LABELS = {
|
|
|
45
45
|
mfaCode: "Code aus der Authenticator-App",
|
|
46
46
|
// Navigation (same words in every app, see the glossary in UISPEC)
|
|
47
47
|
overview: "Übersicht",
|
|
48
|
-
// UI alignment round 1 (ADR
|
|
48
|
+
// UI alignment round 1 (ADR 0042)
|
|
49
49
|
later: "Später",
|
|
50
50
|
apiOffline: "API nicht erreichbar",
|
|
51
51
|
apiOfflineTip: "Die Anwendung erreicht ihr Backend nicht. Änderungen werden nicht gespeichert.",
|
|
@@ -559,7 +559,7 @@ const LABELS = {
|
|
|
559
559
|
twoFactor: "Two-factor sign-in",
|
|
560
560
|
mfaCode: "Code from the authenticator app",
|
|
561
561
|
overview: "Overview",
|
|
562
|
-
// UI alignment round 1 (ADR
|
|
562
|
+
// UI alignment round 1 (ADR 0042)
|
|
563
563
|
later: "Later",
|
|
564
564
|
apiOffline: "API unreachable",
|
|
565
565
|
apiOfflineTip: "The app cannot reach its backend. Changes are not saved.",
|
package/react/session.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Sign-in state of an app (QA5 step 3, Grundregel 8): Beacon, Pulse and Citadel had the same AuthContext.jsx.
|
|
3
|
+
// The session lives only in the HttpOnly cookie of the backend (scalio-identity localauth); the browser keeps no
|
|
4
|
+
// token. createAuth({ api, formatError, signInWithPasskey, passkeyCancelled, legacyTokenKey }) gives
|
|
5
|
+
// { AuthProvider, useAuth }. useAuth() returns
|
|
6
|
+
// { user, ready, login, loginMfa, loginPasskey, loginScalio, register, setup, logout, updateUser }:
|
|
7
|
+
// user is null while checking, false when signed out, else /api/auth/me. Every action answers { ok, error?, status? };
|
|
8
|
+
// login with an authenticator app answers { ok: false, mfaToken } and the page asks for the code.
|
|
9
|
+
const React = require("react");
|
|
10
|
+
const { setUserTimezone } = require("./profile");
|
|
11
|
+
|
|
12
|
+
function createAuth(opts) {
|
|
13
|
+
const api = opts.api;
|
|
14
|
+
const formatError = opts.formatError || ((d) => (typeof d === "string" ? d : ""));
|
|
15
|
+
const AuthContext = React.createContext(null);
|
|
16
|
+
|
|
17
|
+
function AuthProvider(props) {
|
|
18
|
+
const [user, setUser] = React.useState(null); // null = checking
|
|
19
|
+
const [ready, setReady] = React.useState(false);
|
|
20
|
+
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
if (opts.legacyTokenKey) {
|
|
23
|
+
try {
|
|
24
|
+
localStorage.removeItem(opts.legacyTokenKey); // token of older versions
|
|
25
|
+
} catch (e) {
|
|
26
|
+
/* private mode */
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
api
|
|
30
|
+
.get("/auth/me")
|
|
31
|
+
.then((r) => setUser(r.data))
|
|
32
|
+
.catch(() => setUser(false))
|
|
33
|
+
.finally(() => setReady(true));
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
const signedIn = (data) => {
|
|
37
|
+
setUser(data.user);
|
|
38
|
+
// /auth/me adds the permissions of the role (own roles).
|
|
39
|
+
api.get("/auth/me").then((r) => setUser(r.data)).catch(() => null);
|
|
40
|
+
return { ok: true };
|
|
41
|
+
};
|
|
42
|
+
const failed = (e) => ({
|
|
43
|
+
ok: false,
|
|
44
|
+
status: e.response && e.response.status,
|
|
45
|
+
error: formatError(e.response && e.response.data && e.response.data.detail) || e.message,
|
|
46
|
+
});
|
|
47
|
+
const attempt = async (fn) => {
|
|
48
|
+
try {
|
|
49
|
+
return await fn();
|
|
50
|
+
} catch (e) {
|
|
51
|
+
return failed(e);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const login = (email, password) =>
|
|
56
|
+
attempt(async () => {
|
|
57
|
+
const { data } = await api.post("/auth/login", { email, password });
|
|
58
|
+
return data.mfa_required ? { ok: false, mfaToken: data.mfa_token } : signedIn(data);
|
|
59
|
+
});
|
|
60
|
+
const loginMfa = (mfaToken, code) =>
|
|
61
|
+
attempt(async () => signedIn((await api.post("/auth/login/mfa", { mfa_token: mfaToken, code })).data));
|
|
62
|
+
const loginPasskey = async () => {
|
|
63
|
+
try {
|
|
64
|
+
return signedIn(await opts.signInWithPasskey());
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return opts.passkeyCancelled && opts.passkeyCancelled(e) ? { ok: false } : failed(e);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
// Microsoft through Scalio ID: the backend callback hands over a one-time code (URL fragment).
|
|
70
|
+
const loginScalio = (code) => attempt(async () => signedIn((await api.post("/auth/scalio/finish", { code })).data));
|
|
71
|
+
const register = (name, email, password) =>
|
|
72
|
+
attempt(async () => signedIn((await api.post("/auth/register", { name, email, password })).data));
|
|
73
|
+
const setup = (name, email, password, setup_code) =>
|
|
74
|
+
attempt(async () => signedIn((await api.post("/auth/setup", { name, email, password, setup_code })).data));
|
|
75
|
+
// Profile page: new name. After a password change the backend sets the new cookie itself.
|
|
76
|
+
const updateUser = (u) => setUser(u);
|
|
77
|
+
// Signs out at the server (the cookie is removed there), then locally even if the call fails.
|
|
78
|
+
const logout = async () => {
|
|
79
|
+
try {
|
|
80
|
+
await api.post("/auth/logout");
|
|
81
|
+
} catch (e) {
|
|
82
|
+
/* already signed out */
|
|
83
|
+
}
|
|
84
|
+
setUser(false);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Times everywhere follow the profile time zone; set before the children render.
|
|
88
|
+
setUserTimezone(user ? user.timezone : "");
|
|
89
|
+
|
|
90
|
+
const value = { user, ready, login, loginMfa, loginPasskey, loginScalio, register, setup, logout, updateUser };
|
|
91
|
+
return React.createElement(AuthContext.Provider, { value }, props.children);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const useAuth = () => React.useContext(AuthContext);
|
|
95
|
+
return { AuthProvider, useAuth, AuthContext };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { createAuth };
|
package/react/unify.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// UI alignment round 1 (scalio-docs ADR
|
|
2
|
+
// UI alignment round 1 (scalio-docs ADR 0042): edition badges next to the brand, the fixed "Verwaltung"
|
|
3
3
|
// section, the API status in the header, the tabs of "Konten und Rollen" and the password sign-in choice.
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const h = React.createElement;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/alert-dialog"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
|
5
|
+
import { cn } from "./utils.mjs";
|
|
6
|
+
import { buttonVariants } from "./button.mjs";
|
|
7
|
+
const AlertDialog = AlertDialogPrimitive.Root;
|
|
8
|
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
|
9
|
+
const AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
10
|
+
const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
11
|
+
AlertDialogPrimitive.Overlay,
|
|
12
|
+
{
|
|
13
|
+
className: cn(
|
|
14
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
15
|
+
className
|
|
16
|
+
),
|
|
17
|
+
...props,
|
|
18
|
+
ref
|
|
19
|
+
}
|
|
20
|
+
));
|
|
21
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
22
|
+
const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(AlertDialogPortal, null, /* @__PURE__ */ React.createElement(AlertDialogOverlay, null), /* @__PURE__ */ React.createElement(
|
|
23
|
+
AlertDialogPrimitive.Content,
|
|
24
|
+
{
|
|
25
|
+
ref,
|
|
26
|
+
className: cn(
|
|
27
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
28
|
+
className
|
|
29
|
+
),
|
|
30
|
+
...props
|
|
31
|
+
}
|
|
32
|
+
)));
|
|
33
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
34
|
+
const AlertDialogHeader = ({
|
|
35
|
+
className,
|
|
36
|
+
...props
|
|
37
|
+
}) => /* @__PURE__ */ React.createElement(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
className: cn("flex flex-col space-y-2 text-center sm:text-left", className),
|
|
41
|
+
...props
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
45
|
+
const AlertDialogFooter = ({
|
|
46
|
+
className,
|
|
47
|
+
...props
|
|
48
|
+
}) => /* @__PURE__ */ React.createElement(
|
|
49
|
+
"div",
|
|
50
|
+
{
|
|
51
|
+
className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
|
|
52
|
+
...props
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
56
|
+
const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(AlertDialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
|
|
57
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
58
|
+
const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
59
|
+
AlertDialogPrimitive.Description,
|
|
60
|
+
{
|
|
61
|
+
ref,
|
|
62
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
63
|
+
...props
|
|
64
|
+
}
|
|
65
|
+
));
|
|
66
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
67
|
+
const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(AlertDialogPrimitive.Action, { ref, className: cn(buttonVariants(), className), ...props }));
|
|
68
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
69
|
+
const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
70
|
+
AlertDialogPrimitive.Cancel,
|
|
71
|
+
{
|
|
72
|
+
ref,
|
|
73
|
+
className: cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className),
|
|
74
|
+
...props
|
|
75
|
+
}
|
|
76
|
+
));
|
|
77
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
78
|
+
export {
|
|
79
|
+
AlertDialog,
|
|
80
|
+
AlertDialogAction,
|
|
81
|
+
AlertDialogCancel,
|
|
82
|
+
AlertDialogContent,
|
|
83
|
+
AlertDialogDescription,
|
|
84
|
+
AlertDialogFooter,
|
|
85
|
+
AlertDialogHeader,
|
|
86
|
+
AlertDialogOverlay,
|
|
87
|
+
AlertDialogPortal,
|
|
88
|
+
AlertDialogTitle,
|
|
89
|
+
AlertDialogTrigger
|
|
90
|
+
};
|
package/shadcn/badge.mjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/badge"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import { cn } from "./utils.mjs";
|
|
6
|
+
const badgeVariants = cva(
|
|
7
|
+
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
|
12
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
13
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
|
14
|
+
outline: "text-foreground"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
variant: "default"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
function Badge({
|
|
23
|
+
className,
|
|
24
|
+
variant,
|
|
25
|
+
...props
|
|
26
|
+
}) {
|
|
27
|
+
return /* @__PURE__ */ React.createElement("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
Badge,
|
|
31
|
+
badgeVariants
|
|
32
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/button"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { cn } from "./utils.mjs";
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-sm text-xs font-bold uppercase tracking-wider transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
13
|
+
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
14
|
+
outline: "border border-input shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
15
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
16
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
17
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
18
|
+
},
|
|
19
|
+
size: {
|
|
20
|
+
default: "h-9 px-4 py-2",
|
|
21
|
+
sm: "h-8 rounded-sm px-3 text-xs",
|
|
22
|
+
lg: "h-10 rounded-sm px-8",
|
|
23
|
+
icon: "h-9 w-9"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
variant: "default",
|
|
28
|
+
size: "default"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
33
|
+
const Comp = asChild ? Slot : "button";
|
|
34
|
+
return /* @__PURE__ */ React.createElement(
|
|
35
|
+
Comp,
|
|
36
|
+
{
|
|
37
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
38
|
+
ref,
|
|
39
|
+
...props
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
Button.displayName = "Button";
|
|
44
|
+
export {
|
|
45
|
+
Button,
|
|
46
|
+
buttonVariants
|
|
47
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/checkbox"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
5
|
+
import { Check } from "lucide-react";
|
|
6
|
+
import { cn } from "./utils.mjs";
|
|
7
|
+
const Checkbox = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
8
|
+
CheckboxPrimitive.Root,
|
|
9
|
+
{
|
|
10
|
+
ref,
|
|
11
|
+
className: cn(
|
|
12
|
+
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
13
|
+
className
|
|
14
|
+
),
|
|
15
|
+
...props
|
|
16
|
+
},
|
|
17
|
+
/* @__PURE__ */ React.createElement(CheckboxPrimitive.Indicator, { className: cn("flex items-center justify-center text-current") }, /* @__PURE__ */ React.createElement(Check, { className: "h-4 w-4" }))
|
|
18
|
+
));
|
|
19
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
20
|
+
export {
|
|
21
|
+
Checkbox
|
|
22
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/dialog"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
5
|
+
import { X } from "lucide-react";
|
|
6
|
+
import { cn } from "./utils.mjs";
|
|
7
|
+
const Dialog = DialogPrimitive.Root;
|
|
8
|
+
const DialogTrigger = DialogPrimitive.Trigger;
|
|
9
|
+
const DialogPortal = DialogPrimitive.Portal;
|
|
10
|
+
const DialogClose = DialogPrimitive.Close;
|
|
11
|
+
const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
12
|
+
DialogPrimitive.Overlay,
|
|
13
|
+
{
|
|
14
|
+
ref,
|
|
15
|
+
className: cn(
|
|
16
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
17
|
+
className
|
|
18
|
+
),
|
|
19
|
+
...props
|
|
20
|
+
}
|
|
21
|
+
));
|
|
22
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
23
|
+
const DialogContent = React.forwardRef(({ className, children, closeLabel = "Close", ...props }, ref) => /* @__PURE__ */ React.createElement(DialogPortal, null, /* @__PURE__ */ React.createElement(DialogOverlay, null), /* @__PURE__ */ React.createElement(
|
|
24
|
+
DialogPrimitive.Content,
|
|
25
|
+
{
|
|
26
|
+
ref,
|
|
27
|
+
className: cn(
|
|
28
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
29
|
+
className
|
|
30
|
+
),
|
|
31
|
+
...props
|
|
32
|
+
},
|
|
33
|
+
children,
|
|
34
|
+
/* @__PURE__ */ React.createElement(
|
|
35
|
+
DialogPrimitive.Close,
|
|
36
|
+
{
|
|
37
|
+
className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
|
38
|
+
},
|
|
39
|
+
/* @__PURE__ */ React.createElement(X, { className: "h-4 w-4" }),
|
|
40
|
+
/* @__PURE__ */ React.createElement("span", { className: "sr-only" }, closeLabel)
|
|
41
|
+
)
|
|
42
|
+
)));
|
|
43
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
44
|
+
const DialogHeader = ({
|
|
45
|
+
className,
|
|
46
|
+
...props
|
|
47
|
+
}) => /* @__PURE__ */ React.createElement(
|
|
48
|
+
"div",
|
|
49
|
+
{
|
|
50
|
+
className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className),
|
|
51
|
+
...props
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
DialogHeader.displayName = "DialogHeader";
|
|
55
|
+
const DialogFooter = ({
|
|
56
|
+
className,
|
|
57
|
+
...props
|
|
58
|
+
}) => /* @__PURE__ */ React.createElement(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
|
|
62
|
+
...props
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
DialogFooter.displayName = "DialogFooter";
|
|
66
|
+
const DialogTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
67
|
+
DialogPrimitive.Title,
|
|
68
|
+
{
|
|
69
|
+
ref,
|
|
70
|
+
className: cn("text-lg font-semibold leading-none tracking-tight", className),
|
|
71
|
+
...props
|
|
72
|
+
}
|
|
73
|
+
));
|
|
74
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
75
|
+
const DialogDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
76
|
+
DialogPrimitive.Description,
|
|
77
|
+
{
|
|
78
|
+
ref,
|
|
79
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
80
|
+
...props
|
|
81
|
+
}
|
|
82
|
+
));
|
|
83
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
84
|
+
export {
|
|
85
|
+
Dialog,
|
|
86
|
+
DialogClose,
|
|
87
|
+
DialogContent,
|
|
88
|
+
DialogDescription,
|
|
89
|
+
DialogFooter,
|
|
90
|
+
DialogHeader,
|
|
91
|
+
DialogOverlay,
|
|
92
|
+
DialogPortal,
|
|
93
|
+
DialogTitle,
|
|
94
|
+
DialogTrigger
|
|
95
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/dropdown-menu"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
5
|
+
import { Check, ChevronRight, Circle } from "lucide-react";
|
|
6
|
+
import { cn } from "./utils.mjs";
|
|
7
|
+
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
8
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
9
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
10
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
11
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
12
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
13
|
+
const DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
14
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
15
|
+
{
|
|
16
|
+
ref,
|
|
17
|
+
className: cn(
|
|
18
|
+
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
19
|
+
inset && "pl-8",
|
|
20
|
+
className
|
|
21
|
+
),
|
|
22
|
+
...props
|
|
23
|
+
},
|
|
24
|
+
children,
|
|
25
|
+
/* @__PURE__ */ React.createElement(ChevronRight, { className: "ml-auto" })
|
|
26
|
+
));
|
|
27
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
28
|
+
const DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
29
|
+
DropdownMenuPrimitive.SubContent,
|
|
30
|
+
{
|
|
31
|
+
ref,
|
|
32
|
+
className: cn(
|
|
33
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
34
|
+
className
|
|
35
|
+
),
|
|
36
|
+
...props
|
|
37
|
+
}
|
|
38
|
+
));
|
|
39
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
40
|
+
const DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ React.createElement(DropdownMenuPrimitive.Portal, null, /* @__PURE__ */ React.createElement(
|
|
41
|
+
DropdownMenuPrimitive.Content,
|
|
42
|
+
{
|
|
43
|
+
ref,
|
|
44
|
+
sideOffset,
|
|
45
|
+
className: cn(
|
|
46
|
+
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
47
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
48
|
+
className
|
|
49
|
+
),
|
|
50
|
+
...props
|
|
51
|
+
}
|
|
52
|
+
)));
|
|
53
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
54
|
+
const DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
55
|
+
DropdownMenuPrimitive.Item,
|
|
56
|
+
{
|
|
57
|
+
ref,
|
|
58
|
+
className: cn(
|
|
59
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
60
|
+
inset && "pl-8",
|
|
61
|
+
className
|
|
62
|
+
),
|
|
63
|
+
...props
|
|
64
|
+
}
|
|
65
|
+
));
|
|
66
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
67
|
+
const DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
68
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
69
|
+
{
|
|
70
|
+
ref,
|
|
71
|
+
className: cn(
|
|
72
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
73
|
+
className
|
|
74
|
+
),
|
|
75
|
+
checked,
|
|
76
|
+
...props
|
|
77
|
+
},
|
|
78
|
+
/* @__PURE__ */ React.createElement("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center" }, /* @__PURE__ */ React.createElement(DropdownMenuPrimitive.ItemIndicator, null, /* @__PURE__ */ React.createElement(Check, { className: "h-4 w-4" }))),
|
|
79
|
+
children
|
|
80
|
+
));
|
|
81
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
82
|
+
const DropdownMenuRadioItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
83
|
+
DropdownMenuPrimitive.RadioItem,
|
|
84
|
+
{
|
|
85
|
+
ref,
|
|
86
|
+
className: cn(
|
|
87
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
88
|
+
className
|
|
89
|
+
),
|
|
90
|
+
...props
|
|
91
|
+
},
|
|
92
|
+
/* @__PURE__ */ React.createElement("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center" }, /* @__PURE__ */ React.createElement(DropdownMenuPrimitive.ItemIndicator, null, /* @__PURE__ */ React.createElement(Circle, { className: "h-2 w-2 fill-current" }))),
|
|
93
|
+
children
|
|
94
|
+
));
|
|
95
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
96
|
+
const DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
97
|
+
DropdownMenuPrimitive.Label,
|
|
98
|
+
{
|
|
99
|
+
ref,
|
|
100
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
|
|
101
|
+
...props
|
|
102
|
+
}
|
|
103
|
+
));
|
|
104
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
105
|
+
const DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
106
|
+
DropdownMenuPrimitive.Separator,
|
|
107
|
+
{
|
|
108
|
+
ref,
|
|
109
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
110
|
+
...props
|
|
111
|
+
}
|
|
112
|
+
));
|
|
113
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
114
|
+
const DropdownMenuShortcut = ({
|
|
115
|
+
className,
|
|
116
|
+
...props
|
|
117
|
+
}) => {
|
|
118
|
+
return /* @__PURE__ */ React.createElement(
|
|
119
|
+
"span",
|
|
120
|
+
{
|
|
121
|
+
className: cn("ml-auto text-xs tracking-widest opacity-60", className),
|
|
122
|
+
...props
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
127
|
+
export {
|
|
128
|
+
DropdownMenu,
|
|
129
|
+
DropdownMenuCheckboxItem,
|
|
130
|
+
DropdownMenuContent,
|
|
131
|
+
DropdownMenuGroup,
|
|
132
|
+
DropdownMenuItem,
|
|
133
|
+
DropdownMenuLabel,
|
|
134
|
+
DropdownMenuPortal,
|
|
135
|
+
DropdownMenuRadioGroup,
|
|
136
|
+
DropdownMenuRadioItem,
|
|
137
|
+
DropdownMenuSeparator,
|
|
138
|
+
DropdownMenuShortcut,
|
|
139
|
+
DropdownMenuSub,
|
|
140
|
+
DropdownMenuSubContent,
|
|
141
|
+
DropdownMenuSubTrigger,
|
|
142
|
+
DropdownMenuTrigger
|
|
143
|
+
};
|
package/shadcn/input.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/input"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "./utils.mjs";
|
|
5
|
+
const Input = React.forwardRef(({ className, type, ...props }, ref) => {
|
|
6
|
+
return /* @__PURE__ */ React.createElement(
|
|
7
|
+
"input",
|
|
8
|
+
{
|
|
9
|
+
type,
|
|
10
|
+
className: cn(
|
|
11
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
+
className
|
|
13
|
+
),
|
|
14
|
+
ref,
|
|
15
|
+
...props
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
Input.displayName = "Input";
|
|
20
|
+
export {
|
|
21
|
+
Input
|
|
22
|
+
};
|
package/shadcn/label.mjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/label"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { cn } from "./utils.mjs";
|
|
7
|
+
const labelVariants = cva(
|
|
8
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
9
|
+
);
|
|
10
|
+
const Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(LabelPrimitive.Root, { ref, className: cn(labelVariants(), className), ...props }));
|
|
11
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
12
|
+
export {
|
|
13
|
+
Label
|
|
14
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/popover"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
5
|
+
import { cn } from "./utils.mjs";
|
|
6
|
+
const Popover = PopoverPrimitive.Root;
|
|
7
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
8
|
+
const PopoverAnchor = PopoverPrimitive.Anchor;
|
|
9
|
+
const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ React.createElement(PopoverPrimitive.Portal, null, /* @__PURE__ */ React.createElement(
|
|
10
|
+
PopoverPrimitive.Content,
|
|
11
|
+
{
|
|
12
|
+
ref,
|
|
13
|
+
align,
|
|
14
|
+
sideOffset,
|
|
15
|
+
className: cn(
|
|
16
|
+
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-popover-content-transform-origin]",
|
|
17
|
+
className
|
|
18
|
+
),
|
|
19
|
+
...props
|
|
20
|
+
}
|
|
21
|
+
)));
|
|
22
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
23
|
+
export {
|
|
24
|
+
Popover,
|
|
25
|
+
PopoverAnchor,
|
|
26
|
+
PopoverContent,
|
|
27
|
+
PopoverTrigger
|
|
28
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/progress"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
5
|
+
import { cn } from "./utils.mjs";
|
|
6
|
+
const Progress = React.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
7
|
+
ProgressPrimitive.Root,
|
|
8
|
+
{
|
|
9
|
+
ref,
|
|
10
|
+
className: cn(
|
|
11
|
+
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
|
|
12
|
+
className
|
|
13
|
+
),
|
|
14
|
+
...props
|
|
15
|
+
},
|
|
16
|
+
/* @__PURE__ */ React.createElement(
|
|
17
|
+
ProgressPrimitive.Indicator,
|
|
18
|
+
{
|
|
19
|
+
className: "h-full w-full flex-1 bg-primary transition-all",
|
|
20
|
+
style: { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
));
|
|
24
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
25
|
+
export {
|
|
26
|
+
Progress
|
|
27
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/select"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
5
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
6
|
+
import { cn } from "./utils.mjs";
|
|
7
|
+
const Select = SelectPrimitive.Root;
|
|
8
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
9
|
+
const SelectValue = SelectPrimitive.Value;
|
|
10
|
+
const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
11
|
+
SelectPrimitive.Trigger,
|
|
12
|
+
{
|
|
13
|
+
ref,
|
|
14
|
+
className: cn(
|
|
15
|
+
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
16
|
+
className
|
|
17
|
+
),
|
|
18
|
+
...props
|
|
19
|
+
},
|
|
20
|
+
children,
|
|
21
|
+
/* @__PURE__ */ React.createElement(SelectPrimitive.Icon, { asChild: true }, /* @__PURE__ */ React.createElement(ChevronDown, { className: "h-4 w-4 opacity-50" }))
|
|
22
|
+
));
|
|
23
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
24
|
+
const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
25
|
+
SelectPrimitive.ScrollUpButton,
|
|
26
|
+
{
|
|
27
|
+
ref,
|
|
28
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
29
|
+
...props
|
|
30
|
+
},
|
|
31
|
+
/* @__PURE__ */ React.createElement(ChevronUp, { className: "h-4 w-4" })
|
|
32
|
+
));
|
|
33
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
34
|
+
const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
35
|
+
SelectPrimitive.ScrollDownButton,
|
|
36
|
+
{
|
|
37
|
+
ref,
|
|
38
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
39
|
+
...props
|
|
40
|
+
},
|
|
41
|
+
/* @__PURE__ */ React.createElement(ChevronDown, { className: "h-4 w-4" })
|
|
42
|
+
));
|
|
43
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
44
|
+
const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ React.createElement(SelectPrimitive.Portal, null, /* @__PURE__ */ React.createElement(
|
|
45
|
+
SelectPrimitive.Content,
|
|
46
|
+
{
|
|
47
|
+
ref,
|
|
48
|
+
className: cn(
|
|
49
|
+
"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
|
|
50
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
51
|
+
className
|
|
52
|
+
),
|
|
53
|
+
position,
|
|
54
|
+
...props
|
|
55
|
+
},
|
|
56
|
+
/* @__PURE__ */ React.createElement(SelectScrollUpButton, null),
|
|
57
|
+
/* @__PURE__ */ React.createElement(
|
|
58
|
+
SelectPrimitive.Viewport,
|
|
59
|
+
{
|
|
60
|
+
className: cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]")
|
|
61
|
+
},
|
|
62
|
+
children
|
|
63
|
+
),
|
|
64
|
+
/* @__PURE__ */ React.createElement(SelectScrollDownButton, null)
|
|
65
|
+
)));
|
|
66
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
67
|
+
const SelectLabel = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
68
|
+
SelectPrimitive.Label,
|
|
69
|
+
{
|
|
70
|
+
ref,
|
|
71
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", className),
|
|
72
|
+
...props
|
|
73
|
+
}
|
|
74
|
+
));
|
|
75
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
76
|
+
const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
77
|
+
SelectPrimitive.Item,
|
|
78
|
+
{
|
|
79
|
+
ref,
|
|
80
|
+
className: cn(
|
|
81
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
82
|
+
className
|
|
83
|
+
),
|
|
84
|
+
...props
|
|
85
|
+
},
|
|
86
|
+
/* @__PURE__ */ React.createElement("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center" }, /* @__PURE__ */ React.createElement(SelectPrimitive.ItemIndicator, null, /* @__PURE__ */ React.createElement(Check, { className: "h-4 w-4" }))),
|
|
87
|
+
/* @__PURE__ */ React.createElement(SelectPrimitive.ItemText, null, children)
|
|
88
|
+
));
|
|
89
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
90
|
+
const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
91
|
+
SelectPrimitive.Separator,
|
|
92
|
+
{
|
|
93
|
+
ref,
|
|
94
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
95
|
+
...props
|
|
96
|
+
}
|
|
97
|
+
));
|
|
98
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
99
|
+
export {
|
|
100
|
+
Select,
|
|
101
|
+
SelectContent,
|
|
102
|
+
SelectGroup,
|
|
103
|
+
SelectItem,
|
|
104
|
+
SelectLabel,
|
|
105
|
+
SelectScrollDownButton,
|
|
106
|
+
SelectScrollUpButton,
|
|
107
|
+
SelectSeparator,
|
|
108
|
+
SelectTrigger,
|
|
109
|
+
SelectValue
|
|
110
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/sonner"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import { Toaster as Sonner, toast } from "sonner";
|
|
4
|
+
const Toaster = ({
|
|
5
|
+
theme = "system",
|
|
6
|
+
...props
|
|
7
|
+
}) => {
|
|
8
|
+
return /* @__PURE__ */ React.createElement(
|
|
9
|
+
Sonner,
|
|
10
|
+
{
|
|
11
|
+
theme,
|
|
12
|
+
className: "toaster group",
|
|
13
|
+
toastOptions: {
|
|
14
|
+
classNames: {
|
|
15
|
+
toast: "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
|
16
|
+
description: "group-[.toast]:text-muted-foreground",
|
|
17
|
+
actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
|
18
|
+
cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
...props
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
export {
|
|
26
|
+
Toaster,
|
|
27
|
+
toast
|
|
28
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/switch"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
|
5
|
+
import { cn } from "./utils.mjs";
|
|
6
|
+
const Switch = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
7
|
+
SwitchPrimitives.Root,
|
|
8
|
+
{
|
|
9
|
+
className: cn(
|
|
10
|
+
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
|
11
|
+
className
|
|
12
|
+
),
|
|
13
|
+
...props,
|
|
14
|
+
ref
|
|
15
|
+
},
|
|
16
|
+
/* @__PURE__ */ React.createElement(
|
|
17
|
+
SwitchPrimitives.Thumb,
|
|
18
|
+
{
|
|
19
|
+
className: cn(
|
|
20
|
+
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
));
|
|
25
|
+
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
26
|
+
export {
|
|
27
|
+
Switch
|
|
28
|
+
};
|
package/shadcn/table.mjs
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/table"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "./utils.mjs";
|
|
5
|
+
const Table = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement("div", { className: "relative w-full overflow-auto" }, /* @__PURE__ */ React.createElement(
|
|
6
|
+
"table",
|
|
7
|
+
{
|
|
8
|
+
ref,
|
|
9
|
+
className: cn("w-full caption-bottom text-sm", className),
|
|
10
|
+
...props
|
|
11
|
+
}
|
|
12
|
+
)));
|
|
13
|
+
Table.displayName = "Table";
|
|
14
|
+
const TableHeader = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
|
|
15
|
+
TableHeader.displayName = "TableHeader";
|
|
16
|
+
const TableBody = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
17
|
+
"tbody",
|
|
18
|
+
{
|
|
19
|
+
ref,
|
|
20
|
+
className: cn("[&_tr:last-child]:border-0", className),
|
|
21
|
+
...props
|
|
22
|
+
}
|
|
23
|
+
));
|
|
24
|
+
TableBody.displayName = "TableBody";
|
|
25
|
+
const TableFooter = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
26
|
+
"tfoot",
|
|
27
|
+
{
|
|
28
|
+
ref,
|
|
29
|
+
className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className),
|
|
30
|
+
...props
|
|
31
|
+
}
|
|
32
|
+
));
|
|
33
|
+
TableFooter.displayName = "TableFooter";
|
|
34
|
+
const TableRow = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
35
|
+
"tr",
|
|
36
|
+
{
|
|
37
|
+
ref,
|
|
38
|
+
className: cn(
|
|
39
|
+
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
|
40
|
+
className
|
|
41
|
+
),
|
|
42
|
+
...props
|
|
43
|
+
}
|
|
44
|
+
));
|
|
45
|
+
TableRow.displayName = "TableRow";
|
|
46
|
+
const TableHead = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
47
|
+
"th",
|
|
48
|
+
{
|
|
49
|
+
ref,
|
|
50
|
+
className: cn(
|
|
51
|
+
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
52
|
+
className
|
|
53
|
+
),
|
|
54
|
+
...props
|
|
55
|
+
}
|
|
56
|
+
));
|
|
57
|
+
TableHead.displayName = "TableHead";
|
|
58
|
+
const TableCell = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
59
|
+
"td",
|
|
60
|
+
{
|
|
61
|
+
ref,
|
|
62
|
+
className: cn(
|
|
63
|
+
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
64
|
+
className
|
|
65
|
+
),
|
|
66
|
+
...props
|
|
67
|
+
}
|
|
68
|
+
));
|
|
69
|
+
TableCell.displayName = "TableCell";
|
|
70
|
+
const TableCaption = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
71
|
+
"caption",
|
|
72
|
+
{
|
|
73
|
+
ref,
|
|
74
|
+
className: cn("mt-4 text-sm text-muted-foreground", className),
|
|
75
|
+
...props
|
|
76
|
+
}
|
|
77
|
+
));
|
|
78
|
+
TableCaption.displayName = "TableCaption";
|
|
79
|
+
export {
|
|
80
|
+
Table,
|
|
81
|
+
TableBody,
|
|
82
|
+
TableCaption,
|
|
83
|
+
TableCell,
|
|
84
|
+
TableFooter,
|
|
85
|
+
TableHead,
|
|
86
|
+
TableHeader,
|
|
87
|
+
TableRow
|
|
88
|
+
};
|
package/shadcn/tabs.mjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/tabs"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
5
|
+
import { cn } from "./utils.mjs";
|
|
6
|
+
const Tabs = TabsPrimitive.Root;
|
|
7
|
+
const TabsList = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
8
|
+
TabsPrimitive.List,
|
|
9
|
+
{
|
|
10
|
+
ref,
|
|
11
|
+
className: cn(
|
|
12
|
+
"scalio-tabs",
|
|
13
|
+
className
|
|
14
|
+
),
|
|
15
|
+
...props
|
|
16
|
+
}
|
|
17
|
+
));
|
|
18
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
19
|
+
const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
20
|
+
TabsPrimitive.Trigger,
|
|
21
|
+
{
|
|
22
|
+
ref,
|
|
23
|
+
className: cn(
|
|
24
|
+
"scalio-tab",
|
|
25
|
+
className
|
|
26
|
+
),
|
|
27
|
+
...props
|
|
28
|
+
}
|
|
29
|
+
));
|
|
30
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
31
|
+
const TabsContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
|
|
32
|
+
TabsPrimitive.Content,
|
|
33
|
+
{
|
|
34
|
+
ref,
|
|
35
|
+
className: cn(
|
|
36
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
37
|
+
className
|
|
38
|
+
),
|
|
39
|
+
...props
|
|
40
|
+
}
|
|
41
|
+
));
|
|
42
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
43
|
+
export {
|
|
44
|
+
Tabs,
|
|
45
|
+
TabsContent,
|
|
46
|
+
TabsList,
|
|
47
|
+
TabsTrigger
|
|
48
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/textarea"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "./utils.mjs";
|
|
5
|
+
const Textarea = React.forwardRef(({ className, ...props }, ref) => {
|
|
6
|
+
return /* @__PURE__ */ React.createElement(
|
|
7
|
+
"textarea",
|
|
8
|
+
{
|
|
9
|
+
className: cn(
|
|
10
|
+
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
11
|
+
className
|
|
12
|
+
),
|
|
13
|
+
ref,
|
|
14
|
+
...props
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
Textarea.displayName = "Textarea";
|
|
19
|
+
export {
|
|
20
|
+
Textarea
|
|
21
|
+
};
|
package/shadcn/utils.mjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// shadcn/ui component shared by the Scalio apps (code-quality plan QA6, Grundregel 8). Import it as
|
|
2
|
+
// "@scalio-cloud/ui/shadcn/utils"; the app installs the Radix peer dependencies it uses (README).
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
function cn(...inputs) {
|
|
6
|
+
return twMerge(clsx(inputs));
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
cn
|
|
10
|
+
};
|
package/shell.css
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
}
|
|
30
30
|
.scalio-brand-text { display: none; flex-direction: column; line-height: 1.2; }
|
|
31
31
|
@media (min-width: 1024px) { .scalio-brand-text { display: flex; } }
|
|
32
|
-
/* Product name and its edition badges on one line (ADR
|
|
32
|
+
/* Product name and its edition badges on one line (ADR 0042 A1). */
|
|
33
33
|
.scalio-brand-line { display: flex; align-items: center; gap: 0.375rem; }
|
|
34
34
|
.scalio-edition-badges { display: inline-flex; align-items: center; gap: 0.25rem; }
|
|
35
35
|
.scalio-edition-badge { font-size: 9px; padding: 0.0625rem 0.3125rem; }
|